net: websocket: Remove the websocket as HTTP APIs are removed

Remove the experimental websocket code as it uses HTTP APIs
which are being removed.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2019-01-24 11:19:17 +02:00 committed by Anas Nashif
parent a691e5e751
commit 1cba0161ed
49 changed files with 0 additions and 4175 deletions

View file

@ -216,18 +216,6 @@ HTTP
.. doxygengroup:: http .. doxygengroup:: http
:project: Zephyr :project: Zephyr
Websocket
=========
.. doxygengroup:: websocket
:project: Zephyr
Websocket console
=================
.. doxygengroup:: websocket_console
:project: Zephyr
TLS credentials TLS credentials
*************** ***************

View file

@ -111,12 +111,6 @@ can be disabled if not needed.
be prioritized depending on application needs. be prioritized depending on application needs.
See :ref:`traffic-class-support` for more details. See :ref:`traffic-class-support` for more details.
* **Websocket** Websocket (RFC 6455) server side functionality is supported.
The HTTP server API will enable websocket support if
:option:`CONFIG_WEBSOCKET` is enabled. Client side websocket functionality is
currently not supported by the websocket API.
See :ref:`websocket-server-sample` for information how to use the API.
* **Time Sensitive Networking.** The gPTP (generalized Precision Time Protocol) * **Time Sensitive Networking.** The gPTP (generalized Precision Time Protocol)
is supported. See :ref:`gptp-support` for more details. is supported. See :ref:`gptp-support` for more details.

View file

@ -8,4 +8,3 @@ zephyr_sources_if_kconfig(uart_pipe.c)
zephyr_sources_if_kconfig(telnet_console.c) zephyr_sources_if_kconfig(telnet_console.c)
zephyr_sources_if_kconfig(xtensa_sim_console.c) zephyr_sources_if_kconfig(xtensa_sim_console.c)
zephyr_sources_if_kconfig(native_posix_console.c) zephyr_sources_if_kconfig(native_posix_console.c)
zephyr_sources_if_kconfig(websocket_console.c)

View file

@ -301,5 +301,4 @@ config NATIVE_POSIX_CONSOLE_INIT_PRIORITY
Device driver initialization priority. Device driver initialization priority.
source "drivers/console/Kconfig.telnet" source "drivers/console/Kconfig.telnet"
source "drivers/console/Kconfig.ws"
endif endif

View file

@ -1,95 +0,0 @@
# Kconfig - console driver configuration options
#
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig WEBSOCKET_CONSOLE
bool "Enable websocket console service"
select NETWORKING
select NET_TCP
select HTTP_PARSER
select HTTP_SERVER
select WEBSOCKET
help
This option enables console over a websocket link. Currently,
it is basically just a redirection of the Zephyr console through
websocket. It nicely works along with another console driver (like
uart), twist being that it will take over the output only if a
successful connection to its HTTP service is done.
if WEBSOCKET_CONSOLE
config WEBSOCKET_CONSOLE_LINE_BUF_SIZE
int "WS console line buffer size"
default 128
help
This option can be used to modify the size of the buffer storing
console output line, prior to sending it through the network.
Of course an output line can be longer than such size, it just
means sending it will start as soon as it reaches this size.
It really depends on what type of output is expected.
If there is a lot of short lines, then lower this value. If there
are longer lines, then raise this value.
config WEBSOCKET_CONSOLE_LINE_BUF_NUMBERS
int "WS console line buffers"
default 4
help
This option can be used to modify the amount of line buffers the
driver can use. It really depends on how much output is meant to be
sent, depending on the system load etc. You can play on both
WEBSOCKET_CONSOLE_LINE_BUF_SIZE and this current option to get the
best possible buffer settings you need.
config WEBSOCKET_CONSOLE_SEND_TIMEOUT
int "WS console line send timeout"
default 100
help
This option can be used to modify the duration of the timer that kick
in when a line buffer is not empty but did not yet meet the line feed.
config WEBSOCKET_CONSOLE_SEND_THRESHOLD
int "WS console line send threshold"
default 5
help
This option can be used to modify the minimal amount of a line buffer
that can be sent by the WS server when nothing has happened for
a little while (see WEBSOCKET_CONSOLE_SEND_TIMEOUT) and when the line
buffer did not meet the line feed yet.
config WEBSOCKET_CONSOLE_STACK_SIZE
int "WS console inner thread stack size"
default 1500
help
This option helps to fine-tune WS console inner thread stack size.
config WEBSOCKET_CONSOLE_PRIO
int "WS console inner thread priority"
default 7
help
This option helps to fine-tune WS console inner thread priority.
module = WEBSOCKET_CONSOLE
module-str = web socket console
source "subsys/logging/Kconfig.template.log_config"
config WEBSOCKET_CONSOLE_DEBUG_DEEP
bool "Forward output to original console handler"
depends on UART_CONSOLE
help
For WS console developers only, this will forward each output to
original console handler. So if by chance WS console seems silent,
at least things will be printed to original handler, usually
UART console.
config WEBSOCKET_CONSOLE_INIT_PRIORITY
int "WS console init priority"
default 99
help
WS console driver initialization priority. Note that WS works
on application level. Usually, you won't have to tweak this.
endif

View file

@ -1,340 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Websocket console
*
*
* Websocket console driver. The console is provided over
* a websocket connection.
*/
#define LOG_LEVEL CONFIG_WEBSOCKET_CONSOLE_LOG_LEVEL
#define LOG_DOMAIN ws_console
#include <logging/log.h>
LOG_MODULE_REGISTER(LOG_DOMAIN);
#include <zephyr.h>
#include <init.h>
#include <misc/printk.h>
#include <console/console.h>
#include <net/buf.h>
#include <net/net_pkt.h>
#include <net/websocket_console.h>
#define NVT_NUL 0
#define NVT_LF 10
#define NVT_CR 13
#define WS_CONSOLE_STACK_SIZE CONFIG_WEBSOCKET_CONSOLE_STACK_SIZE
#define WS_CONSOLE_PRIORITY CONFIG_WEBSOCKET_CONSOLE_PRIO
#define WS_CONSOLE_TIMEOUT K_MSEC(CONFIG_WEBSOCKET_CONSOLE_SEND_TIMEOUT)
#define WS_CONSOLE_LINES CONFIG_WEBSOCKET_CONSOLE_LINE_BUF_NUMBERS
#define WS_CONSOLE_LINE_SIZE CONFIG_WEBSOCKET_CONSOLE_LINE_BUF_SIZE
#define WS_CONSOLE_TIMEOUT K_MSEC(CONFIG_WEBSOCKET_CONSOLE_SEND_TIMEOUT)
#define WS_CONSOLE_THRESHOLD CONFIG_WEBSOCKET_CONSOLE_SEND_THRESHOLD
#define WS_CONSOLE_MIN_MSG 2
/* These 2 structures below are used to store the console output
* before sending it to the client. This is done to keep some
* reactivity: the ring buffer is non-protected, if first line has
* not been sent yet, and if next line is reaching the same index in rb,
* the first one will be replaced. In a perfect world, this should
* not happen. However on a loaded system with a lot of debug output
* this is bound to happen eventualy, moreover if it does not have
* the luxury to bufferize as much as it wants to. Just raise
* CONFIG_WEBSOCKET_CONSOLE_LINE_BUF_NUMBERS if possible.
*/
struct line_buf {
char buf[WS_CONSOLE_LINE_SIZE];
u16_t len;
};
struct line_buf_rb {
struct line_buf l_bufs[WS_CONSOLE_LINES];
u16_t line_in;
u16_t line_out;
};
static struct line_buf_rb ws_rb;
NET_STACK_DEFINE(WS_CONSOLE, ws_console_stack,
WS_CONSOLE_STACK_SIZE, WS_CONSOLE_STACK_SIZE);
static struct k_thread ws_thread_data;
static K_SEM_DEFINE(send_lock, 0, UINT_MAX);
/* The timer is used to send non-lf terminated output that has
* been around for "tool long". This will prove to be useful
* to send the shell prompt for instance.
* ToDo: raise the time, incrementaly, when no output is coming
* so the timer will kick in less and less.
*/
static void ws_send_prematurely(struct k_timer *timer);
static K_TIMER_DEFINE(send_timer, ws_send_prematurely, NULL);
static int (*orig_printk_hook)(int);
static struct k_fifo *avail_queue;
static struct k_fifo *input_queue;
/* Websocket context that this console is related to */
static struct http_ctx *ws_console;
extern void __printk_hook_install(int (*fn)(int));
extern void *__printk_get_hook(void);
void ws_register_input(struct k_fifo *avail, struct k_fifo *lines,
u8_t (*completion)(char *str, u8_t len))
{
ARG_UNUSED(completion);
avail_queue = avail;
input_queue = lines;
}
static void ws_rb_init(void)
{
int i;
ws_rb.line_in = 0U;
ws_rb.line_out = 0U;
for (i = 0; i < WS_CONSOLE_LINES; i++) {
ws_rb.l_bufs[i].len = 0U;
}
}
static void ws_end_client_connection(struct http_ctx *console)
{
__printk_hook_install(orig_printk_hook);
orig_printk_hook = NULL;
k_timer_stop(&send_timer);
ws_send_msg(console, NULL, 0, WS_OPCODE_CLOSE, false, true,
NULL, NULL);
ws_rb_init();
}
static void ws_rb_switch(void)
{
ws_rb.line_in++;
if (ws_rb.line_in == WS_CONSOLE_LINES) {
ws_rb.line_in = 0U;
}
ws_rb.l_bufs[ws_rb.line_in].len = 0U;
/* Unfortunately, we don't have enough line buffer,
* so we eat the next to be sent.
*/
if (ws_rb.line_in == ws_rb.line_out) {
ws_rb.line_out++;
if (ws_rb.line_out == WS_CONSOLE_LINES) {
ws_rb.line_out = 0U;
}
}
k_timer_start(&send_timer, WS_CONSOLE_TIMEOUT, WS_CONSOLE_TIMEOUT);
k_sem_give(&send_lock);
}
static inline struct line_buf *ws_rb_get_line_out(void)
{
u16_t out = ws_rb.line_out;
ws_rb.line_out++;
if (ws_rb.line_out == WS_CONSOLE_LINES) {
ws_rb.line_out = 0U;
}
if (!ws_rb.l_bufs[out].len) {
return NULL;
}
return &ws_rb.l_bufs[out];
}
static inline struct line_buf *ws_rb_get_line_in(void)
{
return &ws_rb.l_bufs[ws_rb.line_in];
}
/* The actual printk hook */
static int ws_console_out(int c)
{
unsigned int key = irq_lock();
struct line_buf *lb = ws_rb_get_line_in();
bool yield = false;
lb->buf[lb->len++] = (char)c;
if (c == '\n' || lb->len == WS_CONSOLE_LINE_SIZE - 1) {
lb->buf[lb->len - 1] = NVT_CR;
lb->buf[lb->len++] = NVT_LF;
ws_rb_switch();
yield = true;
}
irq_unlock(key);
#ifdef CONFIG_WEBSOCKET_CONSOLE_DEBUG_DEEP
/* This is ugly, but if one wants to debug websocket console, it
* will also output the character to original console
*/
orig_printk_hook(c);
#endif
if (yield) {
k_yield();
}
return c;
}
static void ws_send_prematurely(struct k_timer *timer)
{
struct line_buf *lb = ws_rb_get_line_in();
if (lb->len >= WS_CONSOLE_THRESHOLD) {
ws_rb_switch();
}
}
static inline void ws_handle_input(struct net_pkt *pkt)
{
struct console_input *input;
u16_t len, offset, pos;
len = net_pkt_appdatalen(pkt);
if (len > CONSOLE_MAX_LINE_LEN || len < WS_CONSOLE_MIN_MSG) {
return;
}
if (!avail_queue || !input_queue) {
return;
}
input = k_fifo_get(avail_queue, K_NO_WAIT);
if (!input) {
return;
}
offset = net_pkt_get_len(pkt) - len;
net_frag_read(pkt->frags, offset, &pos, len, (u8_t *)input->line);
/* The data from websocket does not contain \n or NUL, so insert
* it here.
*/
input->line[len] = NVT_NUL;
/* LF/CR will be removed if only the line is not NUL terminated */
if (input->line[len - 1] != NVT_NUL) {
if (input->line[len - 1] == NVT_LF) {
input->line[len - 1] = NVT_NUL;
}
if (input->line[len - 2] == NVT_CR) {
input->line[len - 2] = NVT_NUL;
}
}
k_fifo_put(input_queue, input);
}
/* The data is coming from outside system and going into zephyr */
int ws_console_recv(struct http_ctx *ctx, struct net_pkt *pkt)
{
if (ctx != ws_console) {
return -ENOENT;
}
ws_handle_input(pkt);
net_pkt_unref(pkt);
return 0;
}
/* This is for transferring data from zephyr to outside system */
static bool ws_console_send(struct http_ctx *console)
{
struct line_buf *lb = ws_rb_get_line_out();
if (lb) {
(void)ws_send_msg(console, (u8_t *)lb->buf, lb->len,
WS_OPCODE_DATA_TEXT, false, true,
NULL, NULL);
/* We reinitialize the line buffer */
lb->len = 0U;
}
return true;
}
/* WS console loop, used to send buffered output in the RB */
static void ws_console_run(void)
{
while (true) {
k_sem_take(&send_lock, K_FOREVER);
if (!ws_console_send(ws_console)) {
ws_end_client_connection(ws_console);
}
}
}
int ws_console_enable(struct http_ctx *ctx)
{
orig_printk_hook = __printk_get_hook();
__printk_hook_install(ws_console_out);
k_timer_start(&send_timer, WS_CONSOLE_TIMEOUT, WS_CONSOLE_TIMEOUT);
ws_console = ctx;
return 0;
}
int ws_console_disable(struct http_ctx *ctx)
{
if (!ws_console) {
return 0;
}
if (ws_console != ctx) {
return -ENOENT;
}
ws_end_client_connection(ws_console);
ws_console = NULL;
return 0;
}
static int ws_console_init(struct device *arg)
{
k_thread_create(&ws_thread_data, ws_console_stack,
K_THREAD_STACK_SIZEOF(ws_console_stack),
(k_thread_entry_t)ws_console_run,
NULL, NULL, NULL,
K_PRIO_COOP(WS_CONSOLE_PRIORITY), 0, K_MSEC(10));
LOG_INF("Websocket console initialized");
return 0;
}
/* Websocket console is initialized as an application directly, as it requires
* the whole network stack to be ready.
*/
SYS_INIT(ws_console_init, APPLICATION, CONFIG_WEBSOCKET_CONSOLE_INIT_PRIORITY);

View file

@ -1,103 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_NET_WEBSOCKET_H_
#define ZEPHYR_INCLUDE_NET_WEBSOCKET_H_
#include <net/http.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Websocket library
* @defgroup websocket Websocket Library
* @ingroup networking
* @{
*/
/** Values for flag variable in HTTP receive callback */
#define WS_FLAG_FINAL 0x00000001
#define WS_FLAG_TEXT 0x00000002
#define WS_FLAG_BINARY 0x00000004
#define WS_FLAG_CLOSE 0x00000008
#define WS_FLAG_PING 0x00000010
#define WS_FLAG_PONG 0x00000011
enum ws_opcode {
WS_OPCODE_CONTINUE = 0x00,
WS_OPCODE_DATA_TEXT = 0x01,
WS_OPCODE_DATA_BINARY = 0x02,
WS_OPCODE_CLOSE = 0x08,
WS_OPCODE_PING = 0x09,
WS_OPCODE_PONG = 0x0A,
};
/**
* @brief Send websocket msg to peer.
*
* @details The function will automatically add websocket header to the
* message.
*
* @param ctx Websocket context.
* @param payload Websocket data to send.
* @param payload_len Length of the data to be sent.
* @param opcode Operation code (text, binary, ping, pong, close)
* @param mask Mask the data, see RFC 6455 for details
* @param final Is this final message for this message send. If final == false,
* then the first message must have valid opcode and subsequent messages must
* have opcode WS_OPCODE_CONTINUE. If final == true and this is the only
* message, then opcode should have proper opcode (text or binary) set.
* @param dst Remote socket address
* @param user_send_data User specific data to this connection. This is passed
* as a parameter to sent cb after the packet has been sent.
*
* @return 0 if ok, <0 if error.
*/
int ws_send_msg(struct http_ctx *ctx, u8_t *payload, size_t payload_len,
enum ws_opcode opcode, bool mask, bool final,
const struct sockaddr *dst,
void *user_send_data);
/**
* @brief Send message to client.
*
* @details The function will automatically add websocket header to the
* message.
*
* @param ctx Websocket context.
* @param payload Websocket data to send.
* @param payload_len Length of the data to be sent.
* @param opcode Operation code (text, binary, ping ,pong ,close)
* @param final Is this final message for this data send
* @param dst Remote socket address
* @param user_send_data User specific data to this connection. This is passed
* as a parameter to sent cb after the packet has been sent.
*
* @return 0 if ok, <0 if error.
*/
static inline int ws_send_msg_to_client(struct http_ctx *ctx,
u8_t *payload,
size_t payload_len,
enum ws_opcode opcode,
bool final,
const struct sockaddr *dst,
void *user_send_data)
{
return ws_send_msg(ctx, payload, payload_len, opcode, false, final,
dst, user_send_data);
}
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* __WS_H__ */

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_NET_WEBSOCKET_CONSOLE_H_
#define ZEPHYR_INCLUDE_NET_WEBSOCKET_CONSOLE_H_
#include <net/websocket.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Websocket console library
* @defgroup websocket_console Websocket Console Library
* @ingroup networking
* @{
*/
/**
* @brief Enable websocket console.
*
* @details The console can be sent over websocket to browser.
*
* @param ctx HTTP context
*
* @return 0 if ok, <0 if error
*/
int ws_console_enable(struct http_ctx *ctx);
/**
* @brief Disable websocket console.
*
* @param ctx HTTP context
*
* @return 0 if ok, <0 if error
*/
int ws_console_disable(struct http_ctx *ctx);
/**
* @brief Receive data from outside system and feed it into Zephyr.
*
* @param ctx HTTP context
* @param pkt Network packet containing the received data.
*
* @return 0 if ok, <0 if error
*/
int ws_console_recv(struct http_ctx *ctx, struct net_pkt *pkt);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_NET_WEBSOCKET_CONSOLE_H_ */

View file

@ -1,27 +0,0 @@
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(ws_console)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake)
set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/)
# List of files that are used to generate .h file that can be included
# into .c file.
foreach(inc_file
echo-apps-cert.der
echo-apps-key.der
)
generate_inc_file_for_target(
app
src/${inc_file}
${gen_dir}/${inc_file}.inc
)
endforeach()
generate_inc_file_for_target(app src/index.html ${gen_dir}/index.html.gz.inc --gzip)
generate_inc_file_for_target(app src/style.css ${gen_dir}/style.css.gz.inc --gzip)
generate_inc_file_for_target(app src/favicon.ico ${gen_dir}/favicon.ico.gz.inc --gzip)

View file

@ -1,40 +0,0 @@
.. _websocket-console-sample:
Websocket Console
#################
Overview
********
The websocket-console sample application for Zephyr implements a console
over a websocket. The websocket-console sample application listens for incoming
IPv4 or IPv6 HTTP(S) requests and provides Zephyr console to the browser over
a websocket.
The source code for this sample application can be found at:
:file:`samples/net/ws_console`.
Requirements
************
- :ref:`networking_with_qemu`
Building and Running
********************
There are multiple ways to use this application. One of the most common
usage scenario is to run websocket-console application inside QEMU. This is
described in :ref:`networking_with_qemu`.
Build ws_console sample application like this:
.. zephyr-app-commands::
:zephyr-app: samples/net/ws_console
:board: qemu_x86
:goals: run
:compact:
The default make BOARD configuration for this sample is ``qemu_x86``.
Connect to the console from your browser using these URLs http://[2001:db8::1]
or http://192.0.2.1 as configured in the project's ``prj.conf`` file.

View file

@ -1,50 +0,0 @@
# Generic IP stack options and features
CONFIG_NETWORKING=y
CONFIG_NET_UDP=n
CONFIG_NET_TCP=y
CONFIG_NET_IPV6=y
CONFIG_NET_IPV4=y
#CONFIG_NET_DHCPV4=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_INIT_STACKS=y
CONFIG_NET_MAX_CONTEXTS=8
CONFIG_NET_SHELL=y
# Number of network buffers
CONFIG_NET_PKT_RX_COUNT=32
CONFIG_NET_PKT_TX_COUNT=32
CONFIG_NET_BUF_RX_COUNT=32
CONFIG_NET_BUF_TX_COUNT=32
CONFIG_NET_CONTEXT_NET_PKT_POOL=y
# IPv6 address counts
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
# Network application settings
CONFIG_NET_APP=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV6=y
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
# HTTP & Websocket options
CONFIG_WEBSOCKET=y
CONFIG_WEBSOCKET_CONSOLE=y
CONFIG_HTTP=y
CONFIG_HTTPS=n
CONFIG_HTTP_SERVER=y
# How many URLs we are serving
CONFIG_HTTP_SERVER_NUM_URLS=5
# sha1 support needed by websocket
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
# Logging
CONFIG_NET_LOG=y
CONFIG_LOG=y
CONFIG_NET_STATISTICS=y
CONFIG_PRINTK=y

View file

@ -1,52 +0,0 @@
# Generic IP stack options and features
CONFIG_NETWORKING=y
CONFIG_NET_UDP=n
CONFIG_NET_TCP=y
CONFIG_NET_IPV6=y
CONFIG_NET_IPV4=y
#CONFIG_NET_DHCPV4=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_INIT_STACKS=y
CONFIG_NET_MAX_CONTEXTS=8
CONFIG_NET_SHELL=y
# Number of network buffers
CONFIG_NET_PKT_RX_COUNT=64
CONFIG_NET_PKT_TX_COUNT=64
CONFIG_NET_BUF_RX_COUNT=64
CONFIG_NET_BUF_TX_COUNT=64
CONFIG_NET_CONTEXT_NET_PKT_POOL=y
# IPv6 address counts
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
# Network application settings
CONFIG_NET_APP=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV6=y
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
# HTTP & Websocket options
CONFIG_WEBSOCKET=y
CONFIG_WEBSOCKET_CONSOLE=y
CONFIG_HTTP=y
CONFIG_HTTPS=y
CONFIG_HTTP_SERVER=y
# How many URLs we are serving
CONFIG_HTTP_SERVER_NUM_URLS=5
# Crypto support
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=30000
# Logging
CONFIG_NET_LOG=y
CONFIG_NET_STATISTICS=y
CONFIG_PRINTK=y

View file

@ -1,7 +0,0 @@
sample:
name: Websocket console
tests:
test:
build_only: true
platform_whitelist: qemu_x86 native_posix
tags: net websocket

View file

@ -1,12 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define MAX_DBG_PRINT 64
void start_ws_console(void);
void stop_ws_console(void);
void quit(void);

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
/* The startup time needs to be longish if DHCP is enabled as setting
* DHCP up takes some time.
*/
#define APP_STARTUP_TIME K_SECONDS(20)
#ifdef CONFIG_NET_CONFIG_SETTINGS
#ifdef CONFIG_NET_IPV6
#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV6_ADDR
#else
#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR
#endif
#else
#ifdef CONFIG_NET_IPV6
#define ZEPHYR_ADDR "2001:db8::1"
#else
#define ZEPHYR_ADDR "192.0.2.1"
#endif
#endif
#ifndef ZEPHYR_PORT
#define ZEPHYR_PORT 8080
#endif
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

View file

@ -1,137 +0,0 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="style.css">
<link href="/favicon.ico" rel="icon" type="image/x-icon" />
<title>Zephyr WS Console</title>
<script language="javascript" type="text/javascript">
var connected;
var ws;
function init() {
ws = new WebSocket(location.origin.replace("http", "ws") + "/console");
ws.onopen = function() {
output("Connection opened");
connected = "true";
};
ws.onmessage = function(e) {
zconsole(new Date().timeNow() + " : " + e.data);
};
ws.onclose = function() {
output("Connection closed");
connected = "false";
changeCloseText();
};
ws.onerror = function(e) {
output("Error: " + e.data);
};
}
Date.prototype.timeNow = function () {
return ((this.getHours() < 10)?"0":"") + this.getHours() +":"+
((this.getMinutes() < 10)?"0":"") + this.getMinutes() +":"+
((this.getSeconds() < 10)?"0":"") + this.getSeconds();
}
function onSendClick() {
var input = document.getElementById("prompt");
if (connected == "false") {
output("Not connected");
input.value = "";
input.focus();
return;
}
output("Sending: " + input.value);
ws.send(input.value);
input.value = "";
input.focus();
}
function changeCloseText() {
if (connected == "false") {
document.getElementById("closebutton").innerText= "Connect";
} else {
document.getElementById("closebutton").innerText= "Close";
}
}
function onCloseClick() {
if (connected == "true") {
ws.close();
connected = "false";
changeCloseText();
} else {
changeCloseText();
wsConnect();
}
}
function wsConnect() {
if (connected == "false") {
location.reload();
}
}
function scrollToBottom(id){
var div = document.getElementById(id);
div.scrollTop = div.scrollHeight - div.clientHeight;
}
function zconsole(str) {
var log = document.getElementById("zconsoleline");
var escaped = str.replace(/&/, "&amp;").replace(/</, "&lt;").
replace(/>/, "&gt;").replace(/\u000d/, "").
replace(/\u000a/, "").replace(/\[0\;31m/, "<em>").
replace(/\[0m/, "</em>").replace(/"/, "&quot;"); // "
log.innerHTML = log.innerHTML + escaped + "<br>";
scrollToBottom("zconsole");
}
function output(str) {
var log = document.getElementById("output");
var escaped = str.replace(/&/, "&amp;").replace(/</, "&lt;").
replace(/>/, "&gt;").replace(/"/, "&quot;"); // "
log.innerHTML = log.innerHTML + escaped + "<br>";
scrollToBottom("output");
}
</script>
</head>
<body onload="init();">
<div id="container" class="container">
<div class="textcontainer">
<div id="zconsole" class="zconsole">
<p id="zconsoleline"></p>
</div>
<div id="output" class="output">
</div>
</div>
<div id="inputbar" class="inputbar">
<table class="inputtable">
<tr class="inputrow">
<td class="input_cell">
<label for="prompt" class="command_prompt">Command:</label>
<input id="prompt" type="text" size="32"
onkeydown="if (event.keyCode == 13)
document.getElementById('sendbutton').click()" />
<button type="button" id="sendbutton"
onclick="onSendClick()">Send</button>
</td>
<td class="close_cell">
<button type="button" id="closebutton"
onclick="onCloseClick()">Close</button>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_ws_console_sample, LOG_LEVEL_DBG);
#include <zephyr.h>
#include <linker/sections.h>
#include <errno.h>
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/net_context.h>
#include "common.h"
static struct k_sem quit_lock;
void quit(void)
{
k_sem_give(&quit_lock);
}
static inline int init_app(void)
{
k_sem_init(&quit_lock, 0, UINT_MAX);
return 0;
}
void main(void)
{
init_app();
start_ws_console();
k_sem_take(&quit_lock, K_FOREVER);
stop_ws_console();
}

View file

@ -1,54 +0,0 @@
body {
background-color: black;
color: white;
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 5px;
}
th {
text-align: left;
}
div.container {
}
div.textcontainer {
font-family: monospace !important;
font-size: 150%;
}
div.zconsole {
white-space: pre-wrap !important;
scroll-behavior: auto;
border: 1px solid gray;
padding: 5px;
width: 95%;
height: 70%;
overflow: auto;
}
div.output {
scroll-behavior: auto;
border: 1px solid gray;
padding: 5px;
margin-top: 20px;
width: 95%;
height: 10%;
overflow: auto;
}
div.inputbar {
margin-top: 5px;
width: 100%;
}
.zconsole em {
color: red;
}

View file

@ -1,476 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Printing debugs from this module looks funny in console so do not
* do it unless you are debugging this module.
*/
#define EXTRA_DEBUG 0
#include <logging/log.h>
LOG_MODULE_DECLARE(net_ws_console_sample, LOG_LEVEL_DBG);
#include <zephyr.h>
#include <errno.h>
#include <stdio.h>
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/net_context.h>
#include <net/websocket_console.h>
#include "common.h"
#include "config.h"
static struct http_ctx http_ctx;
static struct http_server_urls http_urls;
#define MAX_URL_LEN 128
#define SEND_TIMEOUT K_SECONDS(10)
/* Note that both tcp and udp can share the same pool but in this
* example the UDP context and TCP context have separate pools.
*/
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
NET_PKT_TX_SLAB_DEFINE(echo_tx_tcp, 15);
NET_PKT_DATA_POOL_DEFINE(echo_data_tcp, 30);
static struct k_mem_slab *tx_tcp_slab(void)
{
return &echo_tx_tcp;
}
static struct net_buf_pool *data_tcp_pool(void)
{
return &echo_data_tcp;
}
#else
#define tx_tcp_slab NULL
#define data_tcp_pool NULL
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
/* The result buf size is set to large enough so that we can receive max size
* buf back. Note that mbedtls needs also be configured to have equal size
* value for its buffer size. See MBEDTLS_SSL_MAX_CONTENT_LEN option in TLS
* config file.
*/
#define RESULT_BUF_SIZE 1500
static u8_t result[RESULT_BUF_SIZE];
#if defined(CONFIG_HTTPS)
#if !defined(CONFIG_NET_APP_TLS_STACK_SIZE)
#define CONFIG_NET_APP_TLS_STACK_SIZE 8192
#endif /* CONFIG_NET_APP_TLS_STACK_SIZE */
#define APP_BANNER "Run TLS ws-console"
#define INSTANCE_INFO "Zephyr TLS ws-console #1"
/* Note that each net_app context needs its own stack as there will be
* a separate thread needed.
*/
NET_STACK_DEFINE(WS_TLS_CONSOLE, ws_tls_console_stack,
CONFIG_NET_APP_TLS_STACK_SIZE, CONFIG_NET_APP_TLS_STACK_SIZE);
#define RX_FIFO_DEPTH 4
K_MEM_POOL_DEFINE(ssl_pool, 4, 64, RX_FIFO_DEPTH, 4);
#endif /* CONFIG_HTTPS */
#if defined(CONFIG_HTTPS)
/* Load the certificates and private RSA key. */
static const char echo_apps_cert_der[] = {
#include "echo-apps-cert.der.inc"
};
static const char echo_apps_key_der[] = {
#include "echo-apps-key.der.inc"
};
static int setup_cert(struct net_app_ctx *ctx,
mbedtls_x509_crt *cert,
mbedtls_pk_context *pkey)
{
int ret;
ret = mbedtls_x509_crt_parse(cert, echo_apps_cert_der,
sizeof(echo_apps_cert_der));
if (ret != 0) {
LOG_ERR("mbedtls_x509_crt_parse returned %d", ret);
return ret;
}
ret = mbedtls_pk_parse_key(pkey, echo_apps_key_der,
sizeof(echo_apps_key_der), NULL, 0);
if (ret != 0) {
LOG_ERR("mbedtls_pk_parse_key returned %d", ret);
return ret;
}
return 0;
}
#endif /* CONFIG_HTTPS */
#define HTTP_STATUS_200_OK "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Transfer-Encoding: chunked\r\n"
#define HTTP_STATUS_200_OK_GZ "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Transfer-Encoding: chunked\r\n" \
"Content-Encoding: gzip\r\n"
#define HTTP_STATUS_200_OK_GZ_CSS \
"HTTP/1.1 200 OK\r\n" \
"Content-Type: text/css\r\n" \
"Transfer-Encoding: chunked\r\n" \
"Content-Encoding: gzip\r\n"
#define HTTP_STATUS_200_OK_CSS \
"HTTP/1.1 200 OK\r\n" \
"Content-Type: text/css\r\n" \
"Transfer-Encoding: chunked\r\n"
#define HTML_HEADER "<html><head>" \
"<title>Zephyr websocket console</title>" \
"</head><body><h1>" \
"<center>Zephyr websocket console</center></h1>\r\n"
#define HTML_FOOTER "</body></html>\r\n"
static int http_response(struct http_ctx *ctx, const char *header,
const char *payload, size_t payload_len,
const struct sockaddr *dst)
{
char content_length[6];
int ret;
ret = http_add_header(ctx, header, dst, NULL);
if (ret < 0) {
LOG_ERR("Cannot add HTTP header (%d)", ret);
return ret;
}
ret = snprintk(content_length, sizeof(content_length), "%zd",
payload_len);
if (ret <= 0 || ret >= sizeof(content_length)) {
ret = -ENOMEM;
return ret;
}
ret = http_add_header_field(ctx, "Content-Length", content_length,
dst, NULL);
if (ret < 0) {
LOG_ERR("Cannot add Content-Length HTTP header (%d)", ret);
return ret;
}
ret = http_add_header(ctx, HTTP_CRLF, dst, NULL);
if (ret < 0) {
return ret;
}
ret = http_send_chunk(ctx, payload, payload_len, dst, NULL);
if (ret < 0) {
LOG_ERR("Cannot send data to peer (%d)", ret);
return ret;
}
return http_send_flush(ctx, NULL);
}
static int http_response_soft_404(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char *not_found =
HTML_HEADER
"<h2><center>404 Not Found</center></h2>"
HTML_FOOTER;
return http_response(ctx, HTTP_STATUS_200_OK, not_found,
strlen(not_found), dst);
}
static int http_serve_index_html(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char index_html[] = {
#include "index.html.gz.inc"
};
LOG_DBG("Sending index.html (%zd bytes) to client",
sizeof(index_html));
return http_response(ctx, HTTP_STATUS_200_OK_GZ, index_html,
sizeof(index_html), dst);
}
static int http_serve_style_css(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char style_css_gz[] = {
#include "style.css.gz.inc"
};
LOG_DBG("Sending style.css (%zd bytes) to client",
sizeof(style_css_gz));
return http_response(ctx, HTTP_STATUS_200_OK_GZ_CSS,
style_css_gz, sizeof(style_css_gz),
dst);
}
static int http_serve_favicon_ico(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char favicon_ico_gz[] = {
#include "favicon.ico.gz.inc"
};
LOG_DBG("Sending favicon.ico (%zd bytes) to client",
sizeof(favicon_ico_gz));
return http_response(ctx, HTTP_STATUS_200_OK_GZ,
favicon_ico_gz, sizeof(favicon_ico_gz),
dst);
}
static void ws_connected(struct http_ctx *ctx,
enum http_connection_type type,
const struct sockaddr *dst,
void *user_data)
{
char url[32];
int len = min(sizeof(url) - 1, ctx->http.url_len);
memcpy(url, ctx->http.url, len);
url[len] = '\0';
LOG_DBG("%s connect attempt URL %s",
type == HTTP_CONNECTION ? "HTTP" : "WS", url);
if (type == HTTP_CONNECTION) {
if (strncmp(ctx->http.url, "/index.html",
ctx->http.url_len) == 0) {
http_serve_index_html(ctx, dst);
http_close(ctx);
return;
}
if (strncmp(ctx->http.url, "/style.css",
ctx->http.url_len) == 0) {
http_serve_style_css(ctx, dst);
http_close(ctx);
return;
}
if (strncmp(ctx->http.url, "/favicon.ico",
ctx->http.url_len) == 0) {
http_serve_favicon_ico(ctx, dst);
http_close(ctx);
return;
}
if (strncmp(ctx->http.url, "/",
ctx->http.url_len) == 0) {
http_serve_index_html(ctx, dst);
http_close(ctx);
return;
}
} else if (type == WS_CONNECTION) {
if (strncmp(ctx->http.url, "/console",
ctx->http.url_len) == 0) {
ws_console_enable(ctx);
return;
}
}
/* Give 404 error for all the other URLs we do not want to handle
* right now.
*/
http_response_soft_404(ctx, dst);
http_close(ctx);
}
static void ws_received(struct http_ctx *ctx,
struct net_pkt *pkt,
int status,
u32_t flags,
const struct sockaddr *dst,
void *user_data)
{
if (!status) {
int ret;
#if EXTRA_DEBUG
LOG_DBG("Received %d bytes data", net_pkt_appdatalen(pkt));
#endif
ret = ws_console_recv(ctx, pkt);
if (ret < 0) {
goto out;
}
} else {
LOG_ERR("Receive error (%d)", status);
goto out;
}
return;
out:
if (pkt) {
net_pkt_unref(pkt);
}
}
static void ws_sent(struct http_ctx *ctx,
int status,
void *user_data_send,
void *user_data)
{
#if EXTRA_DEBUG
LOG_DBG("Data sent status %d", status);
#endif
}
static void ws_closed(struct http_ctx *ctx,
int status,
void *user_data)
{
LOG_DBG("Connection %p closed", ctx);
ws_console_disable(ctx);
}
static const char *get_string(int str_len, const char *str)
{
static char buf[64];
int len = min(str_len, sizeof(buf) - 1);
memcpy(buf, str, len);
buf[len] = '\0';
return buf;
}
static enum http_verdict default_handler(struct http_ctx *ctx,
enum http_connection_type type,
const struct sockaddr *dst)
{
LOG_DBG("No handler for %s URL %s",
type == HTTP_CONNECTION ? "HTTP" : "WS",
get_string(ctx->http.url_len, ctx->http.url));
if (type == HTTP_CONNECTION) {
http_response_soft_404(ctx, dst);
}
return HTTP_VERDICT_DROP;
}
void start_ws_console(void)
{
struct sockaddr addr, *server_addr;
int ret;
/*
* There are several options here for binding to local address.
* 1) The server address can be left empty in which case the
* library will bind to both IPv4 and IPv6 addresses and to
* default port 80 or 443 if TLS is enabled.
* 2) The server address can be partially filled, meaning that
* the address can be left to 0 and port can be set to desired
* value. If the protocol family in sockaddr is set to AF_UNSPEC,
* then both IPv4 and IPv6 socket is bound.
* 3) The address can be set to some real value.
*/
#define ADDR_OPTION 1
#if ADDR_OPTION == 1
server_addr = NULL;
ARG_UNUSED(addr);
#elif ADDR_OPTION == 2
/* Accept any local listening address */
(void)memset(&addr, 0, sizeof(addr));
net_sin(&addr)->sin_port = htons(ZEPHYR_PORT);
/* In this example, listen both IPv4 and IPv6 */
addr.sa_family = AF_UNSPEC;
server_addr = &addr;
#elif ADDR_OPTION == 3
/* Set the bind address according to your configuration */
(void)memset(&addr, 0, sizeof(addr));
/* In this example, listen only IPv6 */
addr.sa_family = AF_INET6;
net_sin6(&addr)->sin6_port = htons(ZEPHYR_PORT);
ret = net_ipaddr_parse(ZEPHYR_ADDR, &addr);
if (ret < 0) {
LOG_ERR("Cannot set local address (%d)", ret);
panic(NULL);
}
server_addr = &addr;
#else
server_addr = NULL;
ARG_UNUSED(addr);
#endif
http_server_add_default(&http_urls, default_handler);
http_server_add_url(&http_urls, "/", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/index.html", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/style.css", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/favicon.ico", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/console", HTTP_URL_WEBSOCKET);
ret = http_server_init(&http_ctx, &http_urls, server_addr,
result, sizeof(result),
"Zephyr WS console", NULL);
if (ret < 0) {
LOG_ERR("Cannot init web server (%d)", ret);
return;
}
http_set_cb(&http_ctx, ws_connected, ws_received, ws_sent, ws_closed);
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
net_app_set_net_pkt_pool(&http_ctx.app_ctx, tx_tcp_slab,
data_tcp_pool);
#endif
#if defined(CONFIG_HTTPS)
ret = http_server_set_tls(&http_ctx,
APP_BANNER,
INSTANCE_INFO,
strlen(INSTANCE_INFO),
setup_cert,
NULL,
&ssl_pool,
ws_tls_console_stack,
K_THREAD_STACK_SIZEOF(ws_tls_console_stack));
if (ret < 0) {
LOG_ERR("Cannot enable TLS support (%d)", ret);
}
#endif
http_server_enable(&http_ctx);
}
void stop_ws_console(void)
{
http_server_disable(&http_ctx);
http_release(&http_ctx);
}

View file

@ -1,26 +0,0 @@
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(ws_echo_server)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake)
set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/)
# List of files that are used to generate .h file that can be included
# into .c file.
foreach(inc_file
echo-apps-cert.der
echo-apps-key.der
index.html
)
generate_inc_file_for_target(
app
src/${inc_file}
${gen_dir}/${inc_file}.inc
)
endforeach()
generate_inc_file_for_target(app src/ws.js ${gen_dir}/ws.js.gz.inc --gzip)

View file

@ -1,40 +0,0 @@
.. _websocket-server-sample:
Websocket Server
################
Overview
********
The websocket-server sample application for Zephyr implements a websocket
server. The websocket-server listens for incoming IPv4 or IPv6 HTTP(S)
requests and sends back the same data.
The source code for this sample application can be found at:
:file:`samples/net/ws_server`.
Requirements
************
- :ref:`networking_with_qemu`
Building and Running
********************
There are multiple ways to use this application. One of the most common
usage scenario is to run websocket-server application inside QEMU. This is
described in :ref:`networking_with_qemu`.
Build websocket-server sample application like this:
.. zephyr-app-commands::
:zephyr-app: samples/net/ws_echo_server
:board: qemu_x86
:goals: run
:compact:
The default make BOARD configuration for this sample is ``qemu_x86``.
Connect to the websocket server from your browser using these URLs
http://[2001:db8::1] or http://192.0.2.1 as configured in the project's
``prj.conf`` file.

View file

@ -1,48 +0,0 @@
# Generic IP stack options and features
CONFIG_NETWORKING=y
CONFIG_NET_UDP=n
CONFIG_NET_TCP=y
CONFIG_NET_IPV6=y
CONFIG_NET_IPV4=y
#CONFIG_NET_DHCPV4=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_INIT_STACKS=y
CONFIG_NET_MAX_CONTEXTS=8
CONFIG_NET_SHELL=y
# Number of network buffers
CONFIG_NET_PKT_RX_COUNT=16
CONFIG_NET_PKT_TX_COUNT=16
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_CONTEXT_NET_PKT_POOL=y
# IPv6 address counts
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
# Network application settings
CONFIG_NET_APP=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV6=y
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
# HTTP & Websocket options
CONFIG_WEBSOCKET=y
CONFIG_HTTP=y
CONFIG_HTTPS=n
CONFIG_HTTP_SERVER=y
# How many URLs we are serving
CONFIG_HTTP_SERVER_NUM_URLS=5
# sha1 support needed by websocket
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
# Logging
CONFIG_NET_LOG=y
CONFIG_LOG=y
CONFIG_NET_STATISTICS=y

View file

@ -1,51 +0,0 @@
# Generic IP stack options and features
CONFIG_NETWORKING=y
CONFIG_NET_UDP=n
CONFIG_NET_TCP=y
CONFIG_NET_IPV6=y
CONFIG_NET_IPV4=y
#CONFIG_NET_DHCPV4=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_INIT_STACKS=y
CONFIG_NET_MAX_CONTEXTS=8
CONFIG_NET_SHELL=y
# Number of network buffers
CONFIG_NET_PKT_RX_COUNT=64
CONFIG_NET_PKT_TX_COUNT=64
CONFIG_NET_BUF_RX_COUNT=64
CONFIG_NET_BUF_TX_COUNT=64
CONFIG_NET_CONTEXT_NET_PKT_POOL=y
# IPv6 address counts
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
# Network application settings
CONFIG_NET_APP=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV6=y
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
# HTTP & Websocket options
CONFIG_WEBSOCKET=y
CONFIG_HTTP=y
CONFIG_HTTPS=y
CONFIG_HTTP_SERVER=y
# How many URLs we are serving
CONFIG_HTTP_SERVER_NUM_URLS=5
# Crypto support
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=30000
# Logging
CONFIG_NET_LOG=y
CONFIG_NET_STATISTICS=y
CONFIG_PRINTK=y

View file

@ -1,7 +0,0 @@
sample:
name: Websocket echo-server
tests:
test:
build_only: true
platform_whitelist: qemu_x86 qemu_cortex_m3 native_posix
tags: net websocket

View file

@ -1,24 +0,0 @@
#
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
# List of files that are used to generate .h file that can be included
# into .c file.
generate_inc_file += \
echo-apps-cert.der \
echo-apps-key.der
generate_inc_file += \
index.html
generate_inc_gz_file += \
ws.js
include $(ZEPHYR_BASE)/scripts/Makefile.gen
include $(ZEPHYR_BASE)/samples/net/common/Makefile.common
obj-y += main.o
obj-y += ws.o

View file

@ -1,18 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define MAX_DBG_PRINT 64
void start_server(void);
void stop_server(void);
struct net_pkt *build_reply_pkt(const char *name,
struct net_app_ctx *ctx,
struct net_pkt *pkt);
void pkt_sent(struct net_app_ctx *ctx, int status,
void *token, void *user_data);
void panic(const char *msg);
void quit(void);

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
/* The startup time needs to be longish if DHCP is enabled as setting
* DHCP up takes some time.
*/
#define APP_STARTUP_TIME K_SECONDS(20)
#ifdef CONFIG_NET_CONFIG_SETTINGS
#ifdef CONFIG_NET_IPV6
#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV6_ADDR
#else
#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR
#endif
#else
#ifdef CONFIG_NET_IPV6
#define ZEPHYR_ADDR "2001:db8::1"
#else
#define ZEPHYR_ADDR "192.0.2.1"
#endif
#endif
#ifndef ZEPHYR_PORT
#define ZEPHYR_PORT 8080
#endif
#endif

View file

@ -1,20 +0,0 @@
<html>
<head>
<meta charset="UTF-8">
<script language="javascript" type="text/javascript" src="ws.js"></script>
<title>Zephyr HTTP websocket server sample</title>
</head>
<body onload="init();">
<div id="container" class="container">
<div>
<h2>Zephyr HTTP websocket server sample</h2>
</div>
<div>
<p>HTTP connection ok.</p>
</div>
<div id="output">
</div>
</div>
</body>
</html>

View file

@ -1,152 +0,0 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_ws_echo_server_sample, LOG_LEVEL_DBG);
#include <zephyr.h>
#include <linker/sections.h>
#include <errno.h>
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/net_context.h>
#include <net/net_app.h>
#include "common.h"
/* The startup time needs to be longish if DHCP is enabled as setting
* DHCP up takes some time.
*/
#define APP_STARTUP_TIME K_SECONDS(20)
#define APP_BANNER "Run websocket server"
static struct k_sem quit_lock;
void panic(const char *msg)
{
if (msg) {
LOG_ERR("%s", msg);
}
for (;;) {
k_sleep(K_FOREVER);
}
}
void quit(void)
{
k_sem_give(&quit_lock);
}
struct net_pkt *build_reply_pkt(const char *name,
struct net_app_ctx *ctx,
struct net_pkt *pkt)
{
struct net_pkt *reply_pkt;
struct net_buf *frag, *tmp;
int header_len = 0, recv_len, reply_len;
LOG_INF("%s received %d bytes", name, net_pkt_appdatalen(pkt));
if (net_pkt_appdatalen(pkt) == 0) {
return NULL;
}
reply_pkt = net_app_get_net_pkt(ctx, net_pkt_family(pkt), K_FOREVER);
NET_ASSERT(reply_pkt);
NET_ASSERT(net_pkt_family(reply_pkt) == net_pkt_family(pkt));
recv_len = net_pkt_get_len(pkt);
tmp = pkt->frags;
/* If we have link layer headers, then get rid of them here. */
if (recv_len != net_pkt_appdatalen(pkt)) {
/* First fragment will contain IP header so move the data
* down in order to get rid of it.
*/
header_len = net_pkt_appdata(pkt) - tmp->data;
NET_ASSERT(header_len < CONFIG_NET_BUF_DATA_SIZE);
/* After this pull, the tmp->data points directly to application
* data.
*/
net_buf_pull(tmp, header_len);
}
net_pkt_set_appdatalen(reply_pkt, net_pkt_appdatalen(pkt));
while (tmp) {
frag = net_app_get_net_buf(ctx, reply_pkt, K_FOREVER);
if (net_buf_headroom(tmp) == 0) {
/* If there is no link layer headers in the
* received fragment, then get rid of that also
* in the sending fragment. We end up here
* if MTU is larger than fragment size, this
* is typical for ethernet.
*/
net_buf_push(frag, net_buf_headroom(frag));
frag->len = 0; /* to make fragment empty */
}
NET_ASSERT_INFO(net_buf_tailroom(frag) >= tmp->len,
"tail %zd longer than len %d",
net_buf_tailroom(frag), tmp->len);
memcpy(net_buf_add(frag, tmp->len), tmp->data, tmp->len);
tmp = net_pkt_frag_del(pkt, NULL, tmp);
}
reply_len = net_pkt_get_len(reply_pkt);
NET_ASSERT_INFO((recv_len - header_len) == reply_len,
"Received %d bytes, sending %d bytes",
recv_len - header_len, reply_len);
return reply_pkt;
}
void pkt_sent(struct net_app_ctx *ctx,
int status,
void *user_data_send,
void *user_data)
{
if (!status) {
LOG_INF("Sent %d bytes", POINTER_TO_UINT(user_data_send));
}
}
static inline int init_app(void)
{
k_sem_init(&quit_lock, 0, UINT_MAX);
return 0;
}
void main(void)
{
init_app();
if (IS_ENABLED(CONFIG_NET_TCP)) {
start_server();
}
k_sem_take(&quit_lock, K_FOREVER);
LOG_INF("Stopping...");
if (IS_ENABLED(CONFIG_NET_TCP)) {
stop_server();
}
}

View file

@ -1,530 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_DECLARE(net_ws_echo_server_sample, LOG_LEVEL_DBG);
#include <zephyr.h>
#include <errno.h>
#include <stdio.h>
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/net_context.h>
#include <net/websocket.h>
#include "common.h"
#include "config.h"
#define MAX_BUF_LEN 128
#define MAX_URL_LEN 128
#define SEND_TIMEOUT K_SECONDS(10)
#define ALLOC_TIMEOUT 100
static struct http_ctx http_ctx;
static struct http_server_urls http_urls;
static char buf[MAX_BUF_LEN];
static int msg_count;
/* Note that both tcp and udp can share the same pool but in this
* example the UDP context and TCP context have separate pools.
*/
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
NET_PKT_TX_SLAB_DEFINE(echo_tx_tcp, 15);
NET_PKT_DATA_POOL_DEFINE(echo_data_tcp, 30);
static struct k_mem_slab *tx_tcp_slab(void)
{
return &echo_tx_tcp;
}
static struct net_buf_pool *data_tcp_pool(void)
{
return &echo_data_tcp;
}
#else
#define tx_tcp_slab NULL
#define data_tcp_pool NULL
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
/* The result buf size is set to large enough so that we can receive max size
* buf back. Note that mbedtls needs also be configured to have equal size
* value for its buffer size. See MBEDTLS_SSL_MAX_CONTENT_LEN option in TLS
* config file.
*/
#define RESULT_BUF_SIZE 1500
static u8_t result[RESULT_BUF_SIZE];
#if defined(CONFIG_HTTPS)
#if !defined(CONFIG_NET_APP_TLS_STACK_SIZE)
#define CONFIG_NET_APP_TLS_STACK_SIZE 8192
#endif /* CONFIG_NET_APP_TLS_STACK_SIZE */
#define APP_BANNER "Run TLS ws-server"
#define INSTANCE_INFO "Zephyr TLS ws-server #1"
/* Note that each net_app context needs its own stack as there will be
* a separate thread needed.
*/
NET_STACK_DEFINE(WS_ECHO_SERVER, ws_tls_stack,
CONFIG_NET_APP_TLS_STACK_SIZE, CONFIG_NET_APP_TLS_STACK_SIZE);
#define RX_FIFO_DEPTH 4
K_MEM_POOL_DEFINE(ssl_pool, 4, 64, RX_FIFO_DEPTH, 4);
#endif /* CONFIG_HTTPS */
#if defined(CONFIG_HTTPS)
/* Load the certificates and private RSA key. */
static const char echo_apps_cert_der[] = {
#include "echo-apps-cert.der.inc"
};
static const char echo_apps_key_der[] = {
#include "echo-apps-key.der.inc"
};
static int setup_cert(struct net_app_ctx *ctx,
mbedtls_x509_crt *cert,
mbedtls_pk_context *pkey)
{
int ret;
ret = mbedtls_x509_crt_parse(cert, echo_apps_cert_der,
sizeof(echo_apps_cert_der));
if (ret != 0) {
LOG_ERR("mbedtls_x509_crt_parse returned %d", ret);
return ret;
}
ret = mbedtls_pk_parse_key(pkey, echo_apps_key_der,
sizeof(echo_apps_key_der), NULL, 0);
if (ret != 0) {
LOG_ERR("mbedtls_pk_parse_key returned %d", ret);
return ret;
}
return 0;
}
#endif /* CONFIG_HTTPS */
#define HTTP_STATUS_200_OK "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Transfer-Encoding: chunked\r\n"
#define HTTP_STATUS_200_OK_GZ_CSS \
"HTTP/1.1 200 OK\r\n" \
"Content-Type: text/css\r\n" \
"Transfer-Encoding: chunked\r\n" \
"Content-Encoding: gzip\r\n"
#define HTML_HEADER "<html><head>" \
"<title>Zephyr HTTP Server</title>" \
"</head><body><h1>" \
"<center>Zephyr HTTP websocket server</center></h1>\r\n"
#define HTML_FOOTER "</body></html>\r\n"
static int http_response(struct http_ctx *ctx, const char *header,
const char *payload, size_t payload_len,
const struct sockaddr *dst)
{
char content_length[6];
int ret;
ret = http_add_header(ctx, header, dst, NULL);
if (ret < 0) {
LOG_ERR("Cannot add HTTP header (%d)", ret);
return ret;
}
ret = snprintk(content_length, sizeof(content_length), "%zd",
payload_len);
if (ret <= 0 || ret >= sizeof(content_length)) {
ret = -ENOMEM;
return ret;
}
ret = http_add_header_field(ctx, "Content-Length", content_length,
dst, NULL);
if (ret < 0) {
LOG_ERR("Cannot add Content-Length HTTP header (%d)", ret);
return ret;
}
ret = http_add_header(ctx, HTTP_CRLF, dst, NULL);
if (ret < 0) {
return ret;
}
ret = http_send_chunk(ctx, payload, payload_len, dst, NULL);
if (ret < 0) {
LOG_ERR("Cannot send data to peer (%d)", ret);
return ret;
}
return http_send_flush(ctx, NULL);
}
static int http_serve_index_html(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char index_html[] = {
#include "index.html.inc"
};
LOG_DBG("Sending index.html (%zd bytes) to client",
sizeof(index_html));
return http_response(ctx, HTTP_STATUS_200_OK, index_html,
sizeof(index_html), dst);
}
static int http_serve_js(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char js_gz[] = {
#include "ws.js.gz.inc"
};
LOG_DBG("Sending ws.js (%zd bytes) to client", sizeof(js_gz));
return http_response(ctx, HTTP_STATUS_200_OK_GZ_CSS,
js_gz, sizeof(js_gz), dst);
}
static int http_response_soft_404(struct http_ctx *ctx,
const struct sockaddr *dst)
{
static const char *not_found =
HTML_HEADER
"<h2><center>404 Not Found</center></h2>"
HTML_FOOTER;
return http_response(ctx, HTTP_STATUS_200_OK, not_found,
strlen(not_found), dst);
}
static int append_and_send_data(struct http_ctx *http_ctx,
const struct sockaddr *dst,
bool final, const char *fmt, ...)
{
enum ws_opcode opcode = WS_OPCODE_CONTINUE;
va_list ap;
size_t len;
int ret;
va_start(ap, fmt);
vsnprintk(buf, MAX_BUF_LEN, fmt, ap);
va_end(ap);
len = strlen(buf);
if (final) {
if (msg_count == 0) {
opcode = WS_OPCODE_DATA_TEXT;
}
ret = ws_send_msg_to_client(http_ctx, buf, len,
opcode, final, dst, NULL);
if (ret < 0) {
LOG_DBG("Could not send %zd bytes data to client",
len);
goto out;
} else {
LOG_DBG("Sent %zd bytes to client", len);
}
msg_count = 0;
return ret;
}
if (msg_count == 0) {
opcode = WS_OPCODE_DATA_TEXT;
}
ret = ws_send_msg_to_client(http_ctx, buf, len,
opcode, final, dst, NULL);
if (ret < 0) {
LOG_DBG("Could not send %zd bytes data to client", len);
goto out;
} else {
LOG_DBG("Sent %zd bytes to client", len);
}
msg_count++;
out:
return ret;
}
static int ws_works(struct http_ctx *ctx,
const struct sockaddr *dst)
{
LOG_INF("WS url called");
append_and_send_data(ctx, dst, false, "connection");
append_and_send_data(ctx, dst, true, " established.");
return 0;
}
static void ws_connected(struct http_ctx *ctx,
enum http_connection_type type,
const struct sockaddr *dst,
void *user_data)
{
char url[32];
int len = min(sizeof(url), ctx->http.url_len);
memcpy(url, ctx->http.url, len);
url[len] = '\0';
LOG_DBG("%s connect attempt URL %s",
type == HTTP_CONNECTION ? "HTTP" : "WS", url);
if (type == HTTP_CONNECTION) {
if (strncmp(ctx->http.url, "/index.html",
sizeof("/index.html") - 1) == 0) {
http_serve_index_html(ctx, dst);
http_close(ctx);
return;
}
if (strncmp(ctx->http.url, "/ws.js",
sizeof("/ws.js") - 1) == 0) {
http_serve_js(ctx, dst);
http_close(ctx);
return;
}
if (strncmp(ctx->http.url, "/",
ctx->http.url_len) == 0) {
http_serve_index_html(ctx, dst);
http_close(ctx);
return;
}
} else if (type == WS_CONNECTION) {
if (strncmp(ctx->http.url, "/ws",
sizeof("/ws") - 1) == 0) {
ws_works(ctx, dst);
return;
}
}
/* Give 404 error for all the other URLs we do not want to handle
* right now.
*/
http_response_soft_404(ctx, dst);
http_close(ctx);
}
static void ws_received(struct http_ctx *ctx,
struct net_pkt *pkt,
int status,
u32_t flags,
const struct sockaddr *dst,
void *user_data)
{
if (!status) {
struct net_buf *frag;
enum ws_opcode opcode;
int ret, hdr_len;
if (!pkt) {
return;
}
LOG_DBG("Received %d bytes data", net_pkt_appdatalen(pkt));
if (flags & WS_FLAG_BINARY) {
opcode = WS_OPCODE_DATA_BINARY;
} else {
opcode = WS_OPCODE_DATA_TEXT;
}
hdr_len = net_pkt_get_len(pkt) - net_pkt_appdatalen(pkt);
frag = pkt->frags;
while (frag) {
ret = ws_send_msg_to_client(ctx, frag->data + hdr_len,
frag->len - hdr_len,
opcode,
frag->frags ? true : false,
dst, user_data);
if (ret < 0) {
LOG_DBG("Cannot send ws data (%d bytes) "
"back (%d)",
frag->len - hdr_len, ret);
} else {
LOG_DBG("Sent %d bytes to client",
frag->len - hdr_len);
}
frag = frag->frags;
/* Websocket header is found in first fragment so
* reset the value here.
*/
hdr_len = 0;
}
http_send_flush(ctx, user_data);
net_pkt_unref(pkt);
} else {
LOG_ERR("Receive error (%d)", status);
if (pkt) {
net_pkt_unref(pkt);
}
}
}
static void ws_sent(struct http_ctx *ctx,
int status,
void *user_data_send,
void *user_data)
{
LOG_DBG("Data sent status %d", status);
}
static void ws_closed(struct http_ctx *ctx,
int status,
void *user_data)
{
LOG_DBG("Connection %p closed", ctx);
}
static const char *get_string(int str_len, const char *str)
{
static char buf[64];
int len = min(str_len, sizeof(buf) - 1);
memcpy(buf, str, len);
buf[len] = '\0';
return buf;
}
static enum http_verdict default_handler(struct http_ctx *ctx,
enum http_connection_type type,
const struct sockaddr *dst)
{
LOG_DBG("No handler for %s URL %s",
type == HTTP_CONNECTION ? "HTTP" : "WS",
get_string(ctx->http.url_len, ctx->http.url));
if (type == HTTP_CONNECTION) {
http_response_soft_404(ctx, dst);
}
return HTTP_VERDICT_DROP;
}
void start_server(void)
{
struct sockaddr addr, *server_addr;
int ret;
/*
* There are several options here for binding to local address.
* 1) The server address can be left empty in which case the
* library will bind to both IPv4 and IPv6 addresses and to
* default port 80 or 443 if TLS is enabled.
* 2) The server address can be partially filled, meaning that
* the address can be left to 0 and port can be set to desired
* value. If the protocol family in sockaddr is set to AF_UNSPEC,
* then both IPv4 and IPv6 socket is bound.
* 3) The address can be set to some real value.
*/
#define ADDR_OPTION 1
#if ADDR_OPTION == 1
server_addr = NULL;
ARG_UNUSED(addr);
#elif ADDR_OPTION == 2
/* Accept any local listening address */
(void)memset(&addr, 0, sizeof(addr));
net_sin(&addr)->sin_port = htons(ZEPHYR_PORT);
/* In this example, listen both IPv4 and IPv6 */
addr.sa_family = AF_UNSPEC;
server_addr = &addr;
#elif ADDR_OPTION == 3
/* Set the bind address according to your configuration */
(void)memset(&addr, 0, sizeof(addr));
/* In this example, listen only IPv6 */
addr.sa_family = AF_INET6;
net_sin6(&addr)->sin6_port = htons(ZEPHYR_PORT);
ret = net_ipaddr_parse(ZEPHYR_ADDR, &addr);
if (ret < 0) {
LOG_ERR("Cannot set local address (%d)", ret);
panic(NULL);
}
server_addr = &addr;
#else
server_addr = NULL;
ARG_UNUSED(addr);
#endif
http_server_add_default(&http_urls, default_handler);
http_server_add_url(&http_urls, "/index.html", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/ws.js", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/ws", HTTP_URL_WEBSOCKET);
ret = http_server_init(&http_ctx, &http_urls, server_addr,
result, sizeof(result),
"Zephyr WS server", NULL);
if (ret < 0) {
LOG_ERR("Cannot init web server (%d)", ret);
return;
}
http_set_cb(&http_ctx, ws_connected, ws_received, ws_sent, ws_closed);
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
net_app_set_net_pkt_pool(&http_ctx.app_ctx, tx_tcp_slab, data_tcp_pool);
#endif
#if defined(CONFIG_NET_APP_TLS)
ret = http_server_set_tls(&http_ctx,
APP_BANNER,
INSTANCE_INFO,
strlen(INSTANCE_INFO),
setup_cert,
NULL,
&ssl_pool,
ws_tls_stack,
K_THREAD_STACK_SIZEOF(ws_tls_stack));
if (ret < 0) {
LOG_ERR("Cannot enable TLS support (%d)", ret);
}
#endif
http_server_enable(&http_ctx);
}
void stop_server(void)
{
http_server_disable(&http_ctx);
http_release(&http_ctx);
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
var connected;
var first_run;
var ws;
function init() {
ws = new WebSocket(location.origin.replace("http", "ws") + "/ws");
first_run = "true";
connected = "false";
ws.onopen = function() {
output("Websocket connection opened");
connected = "true";
};
ws.onmessage = function(e) {
output("Websocket data received: " + e.data);
};
ws.onclose = function() {
output("Websocket connection closed");
connected = "false";
};
ws.onerror = function(e) {
output("Websocket data error (see console)");
console.log(e)
};
}
function escape(str) {
return str.replace(/&/, "&amp;").replace(/</, "&lt;").
replace(/>/, "&gt;").replace(/"/, "&quot;"); // "
}
function output(str) {
var log = document.getElementById("output");
log.innerHTML += escape(str) + "<br>";
}

View file

@ -8,7 +8,6 @@ add_subdirectory_ifdef(CONFIG_NET_APP app)
add_subdirectory_ifdef(CONFIG_NET_CONFIG_SETTINGS config) add_subdirectory_ifdef(CONFIG_NET_CONFIG_SETTINGS config)
add_subdirectory_ifdef(CONFIG_NET_SOCKETS sockets) add_subdirectory_ifdef(CONFIG_NET_SOCKETS sockets)
add_subdirectory_ifdef(CONFIG_TLS_CREDENTIALS tls_credentials) add_subdirectory_ifdef(CONFIG_TLS_CREDENTIALS tls_credentials)
add_subdirectory_ifdef(CONFIG_WEBSOCKET websocket)
if(CONFIG_HTTP_PARSER_URL if(CONFIG_HTTP_PARSER_URL
OR CONFIG_HTTP_PARSER OR CONFIG_HTTP_PARSER

View file

@ -20,8 +20,6 @@ source "subsys/net/lib/lwm2m/Kconfig"
source "subsys/net/lib/sntp/Kconfig" source "subsys/net/lib/sntp/Kconfig"
source "subsys/net/lib/websocket/Kconfig"
endmenu endmenu
menu "Network Libraries" menu "Network Libraries"

View file

@ -1,6 +0,0 @@
# base64 support is need from mbedtls
zephyr_include_directories(. $ENV{ZEPHYR_BASE}/ext/lib/crypto/mbedtls/include/)
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_WEBSOCKET websocket.c)

View file

@ -1,22 +0,0 @@
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig WEBSOCKET
bool "Websocket support [EXPERIMENTAL]"
depends on HTTP
select NET_TCP
select BASE64
help
This option enables the websocket library.
if WEBSOCKET
module = WEBSOCKET
module-dep = NET_LOG
module-str = Log level for weboscket library
module-help = Enables websocket library to output debug messages.
source "subsys/net/Kconfig.template.log_config.net"
endif # WEBSOCKET

View file

@ -1,482 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_websocket, CONFIG_WEBSOCKET_LOG_LEVEL);
#include <zephyr.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdlib.h>
#include <version.h>
#include <net/net_ip.h>
#include <net/websocket.h>
#include <base64.h>
#include <mbedtls/sha1.h>
#define BUF_ALLOC_TIMEOUT 100
#define HTTP_CRLF "\r\n"
/* From RFC 6455 chapter 4.2.2 */
#define WS_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
static void ws_mask_payload(u8_t *payload, size_t payload_len,
u32_t masking_value)
{
int i;
for (i = 0; i < payload_len; i++) {
payload[i] ^= masking_value >> (8 * (3 - i % 4));
}
}
void ws_mask_pkt(struct net_pkt *pkt, u32_t masking_value, u32_t *data_read)
{
struct net_buf *frag;
u16_t pos;
int i;
frag = net_frag_get_pos(pkt,
net_pkt_get_len(pkt) - net_pkt_appdatalen(pkt),
&pos);
if (!frag) {
return;
}
NET_ASSERT(net_pkt_appdata(pkt) == frag->data + pos);
while (frag) {
for (i = pos; i < frag->len; i++, (*data_read)++) {
frag->data[i] ^=
masking_value >> (8 * (3 - (*data_read) % 4));
}
pos = 0U;
frag = frag->frags;
}
}
int ws_send_msg(struct http_ctx *ctx, u8_t *payload, size_t payload_len,
enum ws_opcode opcode, bool mask, bool final,
const struct sockaddr *dst,
void *user_send_data)
{
u8_t header[14], hdr_len = 2U;
int ret;
if (ctx->state != HTTP_STATE_OPEN) {
return -ENOTCONN;
}
if (opcode != WS_OPCODE_DATA_TEXT && opcode != WS_OPCODE_DATA_BINARY &&
opcode != WS_OPCODE_CONTINUE && opcode != WS_OPCODE_CLOSE &&
opcode != WS_OPCODE_PING && opcode != WS_OPCODE_PONG) {
return -EINVAL;
}
(void)memset(header, 0, sizeof(header));
/* Is this the last packet? */
header[0] = final ? BIT(7) : 0;
/* Text, binary, ping, pong or close ? */
header[0] |= opcode;
/* Masking */
header[1] = mask ? BIT(7) : 0;
if (payload_len < 126) {
header[1] |= payload_len;
} else if (payload_len < 65536) {
header[1] |= 126;
header[2] = payload_len >> 8;
header[3] = payload_len;
hdr_len += 2;
} else {
header[1] |= 127;
header[2] = 0U;
header[3] = 0U;
header[4] = 0U;
header[5] = 0U;
header[6] = payload_len >> 24;
header[7] = payload_len >> 16;
header[8] = payload_len >> 8;
header[9] = payload_len;
hdr_len += 8;
}
/* Add masking value if needed */
if (mask) {
u32_t masking_value;
masking_value = sys_rand32_get();
header[hdr_len++] |= masking_value >> 24;
header[hdr_len++] |= masking_value >> 16;
header[hdr_len++] |= masking_value >> 8;
header[hdr_len++] |= masking_value;
ws_mask_payload(payload, payload_len, masking_value);
}
ret = http_prepare_and_send(ctx, header, hdr_len, dst, user_send_data);
if (ret < 0) {
NET_DBG("Cannot add ws header (%d)", ret);
goto quit;
}
if (payload) {
ret = http_prepare_and_send(ctx, payload, payload_len,
dst, user_send_data);
if (ret < 0) {
NET_DBG("Cannot send %zd bytes message (%d)",
payload_len, ret);
goto quit;
}
}
ret = http_send_flush(ctx, user_send_data);
quit:
return ret;
}
int ws_strip_header(struct net_pkt *pkt, bool *masked, u32_t *mask_value,
u32_t *message_length, u32_t *message_type_flag,
u32_t *header_len)
{
struct net_buf *frag;
u16_t value, pos, appdata_pos;
u8_t len; /* message length byte */
u8_t len_len; /* length of the length field in header */
frag = net_frag_read_be16(pkt->frags, 0, &pos, &value);
if (!frag && pos == 0xffff) {
return -ENOMSG;
}
if (value & 0x8000) {
*message_type_flag |= WS_FLAG_FINAL;
}
switch (value & 0x0f00) {
case 0x0100:
*message_type_flag |= WS_FLAG_TEXT;
break;
case 0x0200:
*message_type_flag |= WS_FLAG_BINARY;
break;
case 0x0800:
*message_type_flag |= WS_FLAG_CLOSE;
break;
case 0x0900:
*message_type_flag |= WS_FLAG_PING;
break;
case 0x0A00:
*message_type_flag |= WS_FLAG_PONG;
break;
}
len = value & 0x007f;
if (len < 126) {
len_len = 0U;
*message_length = len;
} else if (len == 126) {
u16_t msg_len;
len_len = 2U;
frag = net_frag_read_be16(frag, pos, &pos, &msg_len);
if (!frag && pos == 0xffff) {
return -ENOMSG;
}
*message_length = msg_len;
} else {
len_len = 4U;
frag = net_frag_read_be32(frag, pos, &pos, message_length);
if (!frag && pos == 0xffff) {
return -ENOMSG;
}
}
if (value & 0x0080) {
*masked = true;
appdata_pos = 0U;
frag = net_frag_read_be32(frag, pos, &pos, mask_value);
if (!frag && pos == 0xffff) {
return -ENOMSG;
}
} else {
*masked = false;
appdata_pos = len_len;
}
frag = net_frag_get_pos(pkt, pos + appdata_pos, &pos);
if (!frag && pos == 0xffff) {
return -ENOMSG;
}
/* Minimum websocket header is 6 bytes, header length might be
* bigger depending on length field len.
*/
*header_len = 6 + len_len;
return 0;
}
static bool field_contains(const char *field, int field_len,
const char *str, int str_len)
{
bool found = false;
char c, skip;
c = *str++;
if (c == '\0') {
return false;
}
str_len--;
do {
do {
skip = *field++;
field_len--;
if (skip == '\0' || field_len == 0) {
return false;
}
} while (skip != c);
if (field_len < str_len) {
return false;
}
if (strncasecmp(field, str, str_len) == 0) {
found = true;
break;
}
} while (field_len >= str_len);
return found;
}
static bool check_ws_headers(struct http_ctx *ctx, struct http_parser *parser,
int *ws_sec_key, int *host, int *subprotocol)
{
int i, count, connection = -1;
int ws_sec_version = -1;
if (!parser->upgrade || parser->method != HTTP_GET ||
parser->http_major != 1 || parser->http_minor != 1) {
return false;
}
for (i = 0, count = 0; i < ctx->http.field_values_ctr; i++) {
if (ctx->http.field_values[i].key_len == 0) {
continue;
}
if (strncasecmp(ctx->http.field_values[i].key,
"Sec-WebSocket-Key",
sizeof("Sec-WebSocket-Key") - 1) == 0) {
*ws_sec_key = i;
continue;
}
if (strncasecmp(ctx->http.field_values[i].key,
"Sec-WebSocket-Version",
sizeof("Sec-WebSocket-Version") - 1) == 0) {
if (strncmp(ctx->http.field_values[i].value,
"13", sizeof("13") - 1) == 0) {
ws_sec_version = i;
}
continue;
}
if (strncasecmp(ctx->http.field_values[i].key,
"Connection", sizeof("Connection") - 1) == 0) {
if (field_contains(
ctx->http.field_values[i].value,
ctx->http.field_values[i].value_len,
"Upgrade", sizeof("Upgrade") - 1)) {
connection = i;
}
continue;
}
if (strncasecmp(ctx->http.field_values[i].key, "Host",
sizeof("Host") - 1) == 0) {
*host = i;
continue;
}
if (strncasecmp(ctx->http.field_values[i].key,
"Sec-WebSocket-Protocol",
sizeof("Sec-WebSocket-Protocol") - 1) == 0) {
*subprotocol = i;
continue;
}
}
if (connection >= 0 && *ws_sec_key >= 0 && ws_sec_version >= 0 &&
*host >= 0) {
return true;
}
return false;
}
static struct net_pkt *prepare_reply(struct http_ctx *ctx,
int ws_sec_key, int host, int subprotocol)
{
static const char basic_reply_headers[] =
"HTTP/1.1 101 OK\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: ";
char key_accept[32 + sizeof(WS_MAGIC)];
char accept[20];
struct net_pkt *pkt;
char tmp[64];
int ret;
size_t key_len;
size_t olen;
pkt = net_app_get_net_pkt_with_dst(&ctx->app_ctx,
ctx->http.parser.addr,
ctx->timeout);
if (!pkt) {
return NULL;
}
if (!net_pkt_append_all(pkt, sizeof(basic_reply_headers) - 1,
(u8_t *)basic_reply_headers, ctx->timeout)) {
goto fail;
}
key_len = min(sizeof(key_accept) - 1,
ctx->http.field_values[ws_sec_key].value_len);
strncpy(key_accept, ctx->http.field_values[ws_sec_key].value,
key_len);
olen = min(sizeof(key_accept) - 1 - key_len, sizeof(WS_MAGIC) - 1);
strncpy(key_accept + key_len, WS_MAGIC, olen);
mbedtls_sha1(key_accept, olen + key_len, accept);
ret = base64_encode(tmp, sizeof(tmp) - 1, &olen, accept,
sizeof(accept));
if (ret) {
if (ret == -ENOMEM) {
NET_DBG("[%p] Too short buffer olen %zd", ctx, olen);
}
goto fail;
}
if (!net_pkt_append_all(pkt, olen, (u8_t *)tmp, ctx->timeout)) {
goto fail;
}
ret = snprintk(tmp, sizeof(tmp), "User-Agent: %s\r\n\r\n",
ZEPHYR_USER_AGENT);
if (ret < 0 || ret >= sizeof(tmp)) {
goto fail;
}
if (!net_pkt_append_all(pkt, ret, (u8_t *)tmp, ctx->timeout)) {
goto fail;
}
return pkt;
fail:
net_pkt_unref(pkt);
return NULL;
}
int ws_headers_complete(struct http_parser *parser)
{
struct http_ctx *ctx = parser->data;
int ws_sec_key = -1, host = -1, subprotocol = -1;
if (check_ws_headers(ctx, parser, &ws_sec_key, &host,
&subprotocol)) {
struct net_pkt *pkt;
struct http_root_url *url;
int ret;
url = http_url_find(ctx, HTTP_URL_WEBSOCKET);
if (!url) {
url = http_url_find(ctx, HTTP_URL_STANDARD);
if (url) {
/* Normal HTTP URL was found */
return 0;
}
/* If there is no URL to serve this websocket
* request, then just bail out.
*/
if (!ctx->http.urls) {
NET_DBG("[%p] No URL handlers found", ctx);
return 0;
}
url = &ctx->http.urls->default_url;
if (url && url->is_used &&
ctx->http.urls->default_cb) {
ret = ctx->http.urls->default_cb(ctx,
WS_CONNECTION,
ctx->http.parser.addr);
if (ret == HTTP_VERDICT_ACCEPT) {
goto accept;
}
}
if (url->flags == HTTP_URL_WEBSOCKET) {
goto fail;
}
}
if (url->flags != HTTP_URL_WEBSOCKET) {
return 0;
}
accept:
NET_DBG("[%p] ws header %d fields found", ctx,
ctx->http.field_values_ctr + 1);
pkt = prepare_reply(ctx, ws_sec_key, host, subprotocol);
if (!pkt) {
goto fail;
}
net_pkt_set_appdatalen(pkt, net_buf_frags_len(pkt->frags));
ret = net_app_send_pkt(&ctx->app_ctx, pkt, NULL, 0, 0,
INT_TO_POINTER(K_FOREVER));
if (ret) {
goto fail;
}
http_change_state(ctx, HTTP_STATE_HEADER_RECEIVED);
/* We do not expect any HTTP data after this */
return 2;
fail:
http_change_state(ctx, HTTP_STATE_CLOSED);
}
return 0;
}

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __WEBSOCKET_INTERNAL_H__
#define __WEBSOCKET_INTERNAL_H__
#include <net/http.h>
#include <net/http_parser.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Strip websocket header from the packet.
*
* @details The function will remove websocket header from the network packet.
*
* @param pkt Received network packet
* @param masked The mask status of the message is returned.
* @param mask_value The mask value of the message is returned.
* @param message_length Total length of the message from websocket header.
* @param message_type_flag Type of the websocket message (WS_FLAG_xxx value)
* @param header_len Length of the websocket header is returned to caller.
*
* @return 0 if ok, <0 if error
*/
int ws_strip_header(struct net_pkt *pkt, bool *masked, u32_t *mask_value,
u32_t *message_length, u32_t *message_type_flag,
u32_t *header_len);
/**
* @brief Mask or unmask a websocket message if needed
*
* @details The function will either add or remove the masking from the data.
*
* @param pkt Network packet to process
* @param masking_value The mask value to use.
* @param data_read How many bytes we have read. This is modified by this
* function.
*/
void ws_mask_pkt(struct net_pkt *pkt, u32_t masking_value, u32_t *data_read);
/**
* @brief This is called by HTTP server after all the HTTP headers have been
* received.
*
* @details The function will check if this is a valid websocket connection
* or not.
*
* @param parser HTTP parser instance
*
* @return 0 if ok, 1 if there is no body, 2 if HTTP connection is to be
* upgraded to websocket one
*/
int ws_headers_complete(struct http_parser *parser);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* __WS_H__ */

View file

@ -1,20 +0,0 @@
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(websocket)
target_include_directories(app PRIVATE $ENV{ZEPHYR_BASE}/subsys/net/ip)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
# List of files that are used to generate .h file that can be included
# into .c file.
foreach(inc_file
echo-apps-cert.der
echo-apps-key.der
)
generate_inc_file_for_target(
app
src/${inc_file}
${ZEPHYR_BINARY_DIR}/include/generated/${inc_file}.inc
)
endforeach()

View file

@ -1,60 +0,0 @@
# Testing infra
CONFIG_ZTEST=y
CONFIG_ZTEST_STACKSIZE=2048
CONFIG_NET_TEST=y
CONFIG_NET_LOOPBACK=y
CONFIG_MAIN_STACK_SIZE=2048
# Generic IP stack options and features
CONFIG_NETWORKING=y
CONFIG_NET_UDP=n
CONFIG_NET_TCP=y
CONFIG_NET_IPV6=y
CONFIG_NET_IPV4=y
#CONFIG_NET_DHCPV4=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_INIT_STACKS=y
CONFIG_NET_MAX_CONTEXTS=8
CONFIG_NET_SHELL=n
CONFIG_NET_IPV6_ND=n
CONFIG_NET_IPV6_MLD=n
CONFIG_NET_IPV6_DAD=n
# Number of network buffers
CONFIG_NET_PKT_RX_COUNT=32
CONFIG_NET_PKT_TX_COUNT=32
CONFIG_NET_BUF_RX_COUNT=32
CONFIG_NET_BUF_TX_COUNT=32
CONFIG_NET_CONTEXT_NET_PKT_POOL=y
# IPv6 address counts
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4
# Network application settings
CONFIG_NET_APP=y
CONFIG_NET_APP_CLIENT=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV6=y
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_CONFIG_INIT_TIMEOUT=1
# HTTP & Websocket options
CONFIG_WEBSOCKET=y
CONFIG_HTTP=y
CONFIG_HTTPS=n
CONFIG_HTTP_SERVER=y
# How many URLs we are serving
CONFIG_HTTP_SERVER_NUM_URLS=5
# sha1 support needed by websocket
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
# Logging
CONFIG_NET_LOG=y
CONFIG_NET_STATISTICS=y
CONFIG_COVERAGE=n

View file

@ -1,608 +0,0 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/* In this websocket test, we create a websocket server which starts
* to listen connections. Then we start to send data to it and verify that
* we get proper data back.
*/
#include <ztest.h>
#define NET_LOG_LEVEL CONFIG_WEBSOCKET_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL);
#include <net/net_ip.h>
#include <net/net_app.h>
#include <net/websocket.h>
static struct net_app_ctx app_ctx_v6;
static struct net_app_ctx app_ctx_v4;
#if NET_LOG_LEVEL >= LOG_LEVEL_DBG
#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
#define NET_LOG_ENABLED 1
#else
#define DBG(fmt, ...)
#endif
#include "../../../subsys/net/ip/net_private.h"
/*
* GET /ws HTTP/1.1
* Upgrade: websocket
* Connection: Upgrade
* Host: 2001:db8::1
* Origin: http://2001:db8::1
* Sec-WebSocket-Key: 8VMFeU0j8bImbjyjPVHSQg==
* Sec-WebSocket-Version: 13
*/
static char http_msg[] = {
0x47, 0x45, 0x54, 0x20, 0x2f, 0x77, 0x73, 0x20,
0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64,
0x65, 0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f,
0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x43, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64,
0x65, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a,
0x20, 0x32, 0x30, 0x30, 0x31, 0x3a, 0x64, 0x62,
0x38, 0x3a, 0x3a, 0x31, 0x0d, 0x0a, 0x4f, 0x72,
0x69, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x32, 0x30, 0x30,
0x31, 0x3a, 0x64, 0x62, 0x38, 0x3a, 0x3a, 0x31,
0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65,
0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d,
0x4b, 0x65, 0x79, 0x3a, 0x20, 0x38, 0x56, 0x4d,
0x46, 0x65, 0x55, 0x30, 0x6a, 0x38, 0x62, 0x49,
0x6d, 0x62, 0x6a, 0x79, 0x6a, 0x50, 0x56, 0x48,
0x53, 0x51, 0x67, 0x3d, 0x3d, 0x0d, 0x0a, 0x53,
0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f,
0x63, 0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33,
0x0d, 0x0a, 0x0d, 0x0a,
};
/* WebSocket:
* FIN: true
* Reserved: 0x00
* Opcode: Text (1)
* Mask: True
* Payload len: 7
* Masking key: d1ffa558
* Masked payload: 0x99, 0x9a, 0xc9, 0x34, 0xbe, 0xd3, 0x85
* Payload: "Hello, "
*/
/* This array is not modified */
static const u8_t ws_test_msg_orig[] = {
0x81, 0x87, 0xd1, 0xff, 0xa5, 0x58, 0x99, 0x9a,
0xc9, 0x34, 0xbe, 0xd3, 0x85,
};
/* We are manipulating this array in the test */
static u8_t ws_test_msg[sizeof(ws_test_msg_orig)];
static u32_t mask_value = 0xd1ffa558;
static struct sockaddr server_addr;
static s32_t timeout = K_MSEC(100);
static int bytes_received;
static bool failure;
static struct k_sem wait_data;
static struct k_sem progress;
extern struct http_ctx *ws_ctx;
#define hdr_len 6
static u8_t ws_unmasked_msg[sizeof(ws_test_msg) - hdr_len];
static int total_data_len = sizeof(ws_unmasked_msg);
#define WAIT_TIME K_SECONDS(1)
void test_websocket_init_server(void);
void websocket_cleanup_server(void);
static void ws_mask_payload(u8_t *payload, size_t payload_len,
u32_t masking_value)
{
int i;
for (i = 0; i < payload_len; i++) {
payload[i] ^= masking_value >> (8 * (3 - i % 4));
}
}
static void recv_cb(struct net_app_ctx *ctx,
struct net_pkt *pkt,
int status,
void *user_data)
{
int ret;
size_t len;
if (!pkt) {
return;
}
len = net_pkt_appdatalen(pkt);
/* The pkt can contain websocket header because we are bypassing
* any websocket message parsing here. So we need to skip the websocket
* header here in that case. The return header length is 2 bytes here
* only if the returned message < 127 bytes long.
*/
if (net_pkt_appdata(pkt)[0] == 0x01) {
/* If we received packet with websocket header, then skip it */
net_buf_pull(pkt->frags, 2);
net_pkt_set_appdata(pkt, net_pkt_appdata(pkt) + 2);
len -= 2;
net_pkt_set_appdatalen(pkt, len);
}
if (len == 0) {
goto out;
}
DBG("Received %zd bytes\n", len);
/* Note that we can only use net_pkt_appdata() here because we
* know that the data fits in first fragment.
*/
ret = memcmp(ws_unmasked_msg + bytes_received,
net_pkt_appdata(pkt), len);
if (ret != 0) {
net_hexdump("recv", net_pkt_appdata(pkt),
net_pkt_appdatalen(pkt));
net_hexdump("sent", ws_unmasked_msg, sizeof(ws_unmasked_msg));
failure = true;
zassert_equal(ret, 0, "Received data does not match");
goto out;
}
bytes_received += len;
failure = false;
if (total_data_len == bytes_received) {
bytes_received = 0;
}
out:
k_sem_give(&wait_data);
k_sem_give(&progress);
net_pkt_unref(pkt);
}
void test_init(void)
{
/* The semaphore is there to wait the data to be received. */
k_sem_init(&wait_data, 0, UINT_MAX);
k_sem_init(&progress, 0, UINT_MAX);
memcpy(ws_unmasked_msg, ws_test_msg_orig + hdr_len,
sizeof(ws_unmasked_msg));
ws_mask_payload(ws_unmasked_msg, sizeof(ws_unmasked_msg), mask_value);
}
static void test_connect(struct net_app_ctx *ctx)
{
int ret;
ret = net_app_connect(ctx, K_FOREVER);
zassert_equal(ret, 0, "websocket client connect");
}
static void test_close(struct net_app_ctx *ctx)
{
int ret;
ret = net_app_close(ctx);
zassert_equal(ret, 0, "websocket client close");
}
/* The chunk_size tells how many bytes at a time to send.
* This is not the same as HTTP chunk!
*/
static void test_send_recv(int chunk_size, struct net_app_ctx *ctx)
{
int i, j, ret;
DBG("Sending %d bytes at a time\n", chunk_size);
for (i = 0; i < sizeof(ws_test_msg); i += chunk_size) {
for (j = 0;
(NET_LOG_LEVEL >= LOG_LEVEL_DBG) && j < chunk_size;
j++) {
if ((i + chunk_size) >= sizeof(ws_test_msg)) {
break;
}
if ((i + j) < hdr_len) {
DBG("[%d] = 0x%02x\n", i + j,
ws_test_msg[i + j]);
} else {
DBG("[%d] = 0x%02x -> \"%c\"\n", i + j,
ws_test_msg[i + j],
ws_unmasked_msg[i + j - hdr_len]);
}
}
if ((i + chunk_size) >= sizeof(ws_test_msg)) {
chunk_size = sizeof(ws_test_msg) - i;
}
ret = net_app_send_buf(ctx, &ws_test_msg[i],
chunk_size,
&server_addr,
sizeof(struct sockaddr),
timeout,
NULL);
if (ret != 0) {
DBG("Cannot send %d byte(s) (%d)\n", ret, chunk_size);
zassert_equal(ret, 0, "websocket IPv6 client ws send");
}
/* Make sure the receiving side gets the data now */
k_yield();
}
}
static void test_send_multi_msg(struct net_app_ctx *ctx)
{
u8_t ws_big_msg[sizeof(ws_test_msg) * 2];
int i, j, ret, chunk_size = 4;
k_sem_take(&progress, K_FOREVER);
memcpy(ws_test_msg, ws_test_msg_orig, sizeof(ws_test_msg));
bytes_received = 0;
ws_ctx->websocket.data_waiting = 0;
if (ws_ctx->websocket.pending) {
net_pkt_unref(ws_ctx->websocket.pending);
ws_ctx->websocket.pending = NULL;
}
memcpy(ws_big_msg, ws_test_msg, sizeof(ws_test_msg));
memcpy(ws_big_msg + sizeof(ws_test_msg), ws_test_msg,
sizeof(ws_test_msg));
for (i = 0; i < sizeof(ws_big_msg); i += chunk_size) {
for (j = 0;
(NET_LOG_LEVEL >= LOG_LEVEL_DBG) && j < chunk_size;
j++) {
int first_msg = 0;
if ((i + chunk_size) >= sizeof(ws_big_msg)) {
break;
}
if (i > sizeof(ws_test_msg)) {
first_msg = sizeof(ws_test_msg);
}
if (i + j + first_msg < hdr_len + first_msg) {
DBG("[%d] = 0x%02x\n", i + j,
ws_big_msg[i + j + first_msg]);
} else {
if (i + j + first_msg <
sizeof(ws_test_msg) + first_msg) {
DBG("[%d] = 0x%02x -> \"%c\"\n", i + j,
ws_big_msg[i + j + first_msg],
ws_unmasked_msg[i + j - hdr_len +
first_msg]);
}
}
}
if ((i + chunk_size) >= sizeof(ws_big_msg)) {
chunk_size = sizeof(ws_big_msg) - i;
}
ret = net_app_send_buf(ctx, &ws_big_msg[i],
chunk_size,
&server_addr,
sizeof(struct sockaddr),
timeout,
NULL);
if (ret != 0) {
DBG("Cannot send %d byte(s) (%d)\n", ret, chunk_size);
zassert_equal(ret, 0, "websocket client ws send");
}
/* Make sure the receiving side gets the data now */
k_yield();
}
}
/* Start to send raw data and do not use websocket client API for this so
* that we can send garbage data if needed.
*/
void test_v6_init(void)
{
int ret;
ret = net_ipaddr_parse(CONFIG_NET_CONFIG_MY_IPV6_ADDR,
strlen(CONFIG_NET_CONFIG_MY_IPV6_ADDR),
&server_addr);
zassert_equal(ret, 1, "cannot parse server address");
ret = net_app_init_tcp_client(&app_ctx_v6,
NULL,
NULL,
CONFIG_NET_CONFIG_MY_IPV6_ADDR,
80,
0,
NULL);
zassert_equal(ret, 0, "websocket IPv6 client init");
net_app_set_cb(&app_ctx_v6, NULL, recv_cb, NULL, NULL);
}
void test_v6_connect(void)
{
test_connect(&app_ctx_v6);
k_sem_give(&progress);
}
void test_v6_close(void)
{
test_close(&app_ctx_v6);
}
static void test_v6_send_recv(int chunk_size)
{
static int header_sent;
int ret;
if (!header_sent) {
ret = net_app_send_buf(&app_ctx_v6, http_msg,
sizeof(http_msg) - 1,
&server_addr,
sizeof(struct sockaddr_in6),
timeout, NULL);
if (ret != 0) {
DBG("Cannot send byte (%d)\n", ret);
zassert_equal(ret, 0,
"websocket IPv6 client http send");
}
header_sent = true;
}
test_send_recv(chunk_size, &app_ctx_v6);
}
void test_v6_send_recv_n(int chunk_size)
{
k_sem_take(&progress, K_FOREVER);
/* Make sure we have a fresh start before running this specific test */
memcpy(ws_test_msg, ws_test_msg_orig, sizeof(ws_test_msg));
bytes_received = 0;
ws_ctx->websocket.data_waiting = 0;
if (ws_ctx->websocket.pending) {
net_pkt_unref(ws_ctx->websocket.pending);
ws_ctx->websocket.pending = NULL;
}
test_v6_send_recv(chunk_size);
if (k_sem_take(&wait_data, WAIT_TIME)) {
zassert_true(false, "Timeout while waiting data");
}
zassert_false(failure, "Send test failed");
}
void test_v6_send_recv_1(void)
{
test_v6_send_recv_n(1);
}
void test_v6_send_recv_2(void)
{
test_v6_send_recv_n(2);
}
void test_v6_send_recv_3(void)
{
test_v6_send_recv_n(3);
}
void test_v6_send_recv_4(void)
{
test_v6_send_recv_n(4);
}
void test_v6_send_recv_5(void)
{
test_v6_send_recv_n(5);
}
void test_v6_send_recv_6(void)
{
test_v6_send_recv_n(6);
}
void test_v6_send_recv_7(void)
{
test_v6_send_recv_n(7);
}
void test_v6_send_multi_msg(void)
{
test_send_multi_msg(&app_ctx_v6);
}
/* Start to send raw data and do not use websocket client API for this so
* that we can send garbage data if needed.
*/
void test_v4_init(void)
{
int ret;
ret = net_ipaddr_parse(CONFIG_NET_CONFIG_MY_IPV4_ADDR,
strlen(CONFIG_NET_CONFIG_MY_IPV4_ADDR),
&server_addr);
zassert_equal(ret, 1, "cannot parse server address");
ret = net_app_init_tcp_client(&app_ctx_v4,
NULL,
NULL,
CONFIG_NET_CONFIG_MY_IPV4_ADDR,
80,
0,
NULL);
zassert_equal(ret, 0, "websocket IPv4 client init");
net_app_set_cb(&app_ctx_v4, NULL, recv_cb, NULL, NULL);
}
void test_v4_connect(void)
{
test_connect(&app_ctx_v4);
k_sem_give(&progress);
}
void test_v4_close(void)
{
test_close(&app_ctx_v4);
}
static void test_v4_send_recv(int chunk_size)
{
static int header_sent;
int ret;
if (!header_sent) {
ret = net_app_send_buf(&app_ctx_v4, http_msg,
sizeof(http_msg) - 1,
&server_addr,
sizeof(struct sockaddr_in),
timeout, NULL);
if (ret != 0) {
DBG("Cannot send byte (%d)\n", ret);
zassert_equal(ret, 0,
"websocket IPv4 client http send");
}
header_sent = true;
}
test_send_recv(chunk_size, &app_ctx_v4);
}
void test_v4_send_recv_n(int chunk_size)
{
k_sem_take(&progress, K_FOREVER);
/* Make sure we have a fresh start before running this specific test */
memcpy(ws_test_msg, ws_test_msg_orig, sizeof(ws_test_msg));
bytes_received = 0;
ws_ctx->websocket.data_waiting = 0;
if (ws_ctx->websocket.pending) {
net_pkt_unref(ws_ctx->websocket.pending);
ws_ctx->websocket.pending = NULL;
}
test_v4_send_recv(chunk_size);
if (k_sem_take(&wait_data, WAIT_TIME)) {
zassert_true(false, "Timeout while waiting data");
}
zassert_false(failure, "Send test failed");
}
void test_v4_send_recv_1(void)
{
test_v4_send_recv_n(1);
}
void test_v4_send_recv_2(void)
{
test_v4_send_recv_n(2);
}
void test_v4_send_recv_3(void)
{
test_v4_send_recv_n(3);
}
void test_v4_send_recv_4(void)
{
test_v4_send_recv_n(4);
}
void test_v4_send_recv_5(void)
{
test_v4_send_recv_n(5);
}
void test_v4_send_recv_6(void)
{
test_v4_send_recv_n(6);
}
void test_v4_send_recv_7(void)
{
test_v4_send_recv_n(7);
}
void test_v4_send_multi_msg(void)
{
test_send_multi_msg(&app_ctx_v4);
}
void test_main(void)
{
ztest_test_suite(websocket,
ztest_unit_test(test_websocket_init_server),
ztest_unit_test(test_init),
ztest_unit_test(test_v6_init),
ztest_unit_test(test_v6_connect),
ztest_unit_test(test_v6_send_recv_1),
ztest_unit_test(test_v6_send_recv_2),
ztest_unit_test(test_v6_send_recv_3),
ztest_unit_test(test_v6_send_recv_4),
ztest_unit_test(test_v6_send_recv_5),
ztest_unit_test(test_v6_send_recv_6),
ztest_unit_test(test_v6_send_recv_7),
ztest_unit_test(test_v6_send_multi_msg),
ztest_unit_test(test_v6_close),
ztest_unit_test(test_websocket_init_server),
ztest_unit_test(test_v4_init),
ztest_unit_test(test_v4_connect),
ztest_unit_test(test_v4_send_recv_1),
ztest_unit_test(test_v4_send_recv_2),
ztest_unit_test(test_v4_send_recv_3),
ztest_unit_test(test_v4_send_recv_4),
ztest_unit_test(test_v4_send_recv_5),
ztest_unit_test(test_v4_send_recv_6),
ztest_unit_test(test_v4_send_recv_7),
ztest_unit_test(test_v4_send_multi_msg),
ztest_unit_test(test_v4_close)
);
ztest_run_test_suite(websocket);
}

View file

@ -1,354 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <errno.h>
#include <stdio.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(net_test, CONFIG_WEBSOCKET_LOG_LEVEL);
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/net_context.h>
#include <net/websocket.h>
#include "../../../subsys/net/ip/net_private.h"
#define MAX_BUF_LEN 128
#define MAX_URL_LEN 128
#define SEND_TIMEOUT K_SECONDS(10)
#define ALLOC_TIMEOUT 100
struct http_ctx *ws_ctx;
static struct http_ctx http_ctx;
static struct http_server_urls http_urls;
/* Note that both tcp and udp can share the same pool but in this
* example the UDP context and TCP context have separate pools.
*/
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
NET_PKT_TX_SLAB_DEFINE(echo_tx_tcp, 15);
NET_PKT_DATA_POOL_DEFINE(echo_data_tcp, 30);
static struct k_mem_slab *tx_tcp_slab(void)
{
return &echo_tx_tcp;
}
static struct net_buf_pool *data_tcp_pool(void)
{
return &echo_data_tcp;
}
#else
#define tx_tcp_slab NULL
#define data_tcp_pool NULL
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
/* The result buf size is set to large enough so that we can receive max size
* buf back. Note that mbedtls needs also be configured to have equal size
* value for its buffer size. See MBEDTLS_SSL_MAX_CONTENT_LEN option in TLS
* config file.
*/
#define RESULT_BUF_SIZE 1500
static u8_t result[RESULT_BUF_SIZE];
#if defined(CONFIG_HTTPS)
#if !defined(CONFIG_NET_APP_TLS_STACK_SIZE)
#define CONFIG_NET_APP_TLS_STACK_SIZE 8192
#endif /* CONFIG_NET_APP_TLS_STACK_SIZE */
#define APP_BANNER "Run TLS ws-server"
#define INSTANCE_INFO "Zephyr TLS ws-server #1"
/* Note that each net_app context needs its own stack as there will be
* a separate thread needed.
*/
NET_STACK_DEFINE(WS_ECHO_SERVER, ws_tls_stack,
CONFIG_NET_APP_TLS_STACK_SIZE, CONFIG_NET_APP_TLS_STACK_SIZE);
#define RX_FIFO_DEPTH 4
K_MEM_POOL_DEFINE(ssl_pool, 4, 64, RX_FIFO_DEPTH, 4);
#endif /* CONFIG_HTTPS */
#if defined(CONFIG_HTTPS)
/* Load the certificates and private RSA key. */
static const char echo_apps_cert_der[] = {
#include "echo-apps-cert.der.inc"
};
static const char echo_apps_key_der[] = {
#include "echo-apps-key.der.inc"
};
static int setup_cert(struct net_app_ctx *ctx,
mbedtls_x509_crt *cert,
mbedtls_pk_context *pkey)
{
int ret;
ret = mbedtls_x509_crt_parse(cert, echo_apps_cert_der,
sizeof(echo_apps_cert_der));
if (ret != 0) {
NET_ERR("mbedtls_x509_crt_parse returned %d", ret);
return ret;
}
ret = mbedtls_pk_parse_key(pkey, echo_apps_key_der,
sizeof(echo_apps_key_der), NULL, 0);
if (ret != 0) {
NET_ERR("mbedtls_pk_parse_key returned %d", ret);
return ret;
}
return 0;
}
#endif /* CONFIG_HTTPS */
#define HTTP_STATUS_200_OK "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Transfer-Encoding: chunked\r\n"
#define HTTP_STATUS_200_OK_CSS \
"HTTP/1.1 200 OK\r\n" \
"Content-Type: text/css\r\n" \
"Transfer-Encoding: chunked\r\n"
#define HTML_HEADER "<html><head>" \
"<title>Zephyr HTTP Server</title>" \
"</head><body><h1>" \
"<center>Zephyr HTTP websocket server</center></h1>\r\n"
#define HTML_FOOTER "</body></html>\r\n"
static int ws_works(struct http_ctx *ctx, const struct sockaddr *dst)
{
NET_INFO("WS url called");
return 0;
}
static void ws_connected(struct http_ctx *ctx,
enum http_connection_type type,
const struct sockaddr *dst,
void *user_data)
{
char url[32];
int len = min(sizeof(url) - 1, ctx->http.url_len);
memcpy(url, ctx->http.url, len);
url[len] = '\0';
NET_DBG("%s connect attempt URL %s",
type == HTTP_CONNECTION ? "HTTP" : "WS", url);
if (type == HTTP_CONNECTION) {
return;
} else if (type == WS_CONNECTION) {
if (strncmp(ctx->http.url, "/ws",
sizeof("/ws") - 1) == 0) {
ws_works(ctx, dst);
return;
}
}
}
static void ws_received(struct http_ctx *ctx,
struct net_pkt *pkt,
int status,
u32_t flags,
const struct sockaddr *dst,
void *user_data)
{
if (!status) {
struct net_buf *frag;
enum ws_opcode opcode;
int ret, hdr_len;
NET_DBG("Received %d bytes data", net_pkt_appdatalen(pkt));
if (flags & WS_FLAG_BINARY) {
opcode = WS_OPCODE_DATA_BINARY;
} else {
opcode = WS_OPCODE_DATA_TEXT;
}
hdr_len = net_pkt_get_len(pkt) - net_pkt_appdatalen(pkt);
frag = pkt->frags;
while (frag) {
net_hexdump("server recv", net_pkt_appdata(pkt),
net_pkt_appdatalen(pkt));
ret = ws_send_msg_to_client(ctx, frag->data + hdr_len,
frag->len - hdr_len,
opcode,
frag->frags ? true : false,
dst, user_data);
if (ret < 0) {
NET_DBG("Cannot send ws data (%d bytes) "
"back (%d)",
frag->len - hdr_len, ret);
} else {
NET_DBG("Sent %d bytes to client",
frag->len - hdr_len);
}
frag = frag->frags;
/* Websocket header is found in first fragment so
* reset the value here.
*/
hdr_len = 0;
}
http_send_flush(ctx, user_data);
net_pkt_unref(pkt);
} else {
NET_ERR("Receive error (%d)", status);
if (pkt) {
net_pkt_unref(pkt);
}
}
}
static void ws_sent(struct http_ctx *ctx,
int status,
void *user_data_send,
void *user_data)
{
NET_DBG("Data sent status %d", status);
}
static void ws_closed(struct http_ctx *ctx,
int status,
void *user_data)
{
NET_DBG("Connection %p closed", ctx);
}
static const char *get_string(int str_len, const char *str)
{
static char buf[64];
int len = min(str_len, sizeof(buf) - 1);
memcpy(buf, str, len);
buf[len] = '\0';
return buf;
}
static enum http_verdict default_handler(struct http_ctx *ctx,
enum http_connection_type type,
const struct sockaddr *dst)
{
NET_DBG("No handler for %s URL %s",
type == HTTP_CONNECTION ? "HTTP" : "WS",
get_string(ctx->http.url_len, ctx->http.url));
return HTTP_VERDICT_DROP;
}
void test_websocket_init_server(void)
{
struct sockaddr addr, *server_addr;
int ret;
/*
* There are several options here for binding to local address.
* 1) The server address can be left empty in which case the
* library will bind to both IPv4 and IPv6 addresses and to
* default port 80 or 443 if TLS is enabled.
* 2) The server address can be partially filled, meaning that
* the address can be left to 0 and port can be set to desired
* value. If the protocol family in sockaddr is set to AF_UNSPEC,
* then both IPv4 and IPv6 socket is bound.
* 3) The address can be set to some real value.
*/
#define ADDR_OPTION 1
#if ADDR_OPTION == 1
server_addr = NULL;
ARG_UNUSED(addr);
#elif ADDR_OPTION == 2
/* Accept any local listening address */
(void)memset(&addr, 0, sizeof(addr));
net_sin(&addr)->sin_port = htons(ZEPHYR_PORT);
/* In this example, listen both IPv4 and IPv6 */
addr.sa_family = AF_UNSPEC;
server_addr = &addr;
#elif ADDR_OPTION == 3
/* Set the bind address according to your configuration */
(void)memset(&addr, 0, sizeof(addr));
/* In this example, listen only IPv6 */
addr.sa_family = AF_INET6;
net_sin6(&addr)->sin6_port = htons(ZEPHYR_PORT);
ret = net_ipaddr_parse(ZEPHYR_ADDR, &addr);
if (ret < 0) {
NET_ERR("Cannot set local address (%d)", ret);
panic(NULL);
}
server_addr = &addr;
#else
server_addr = NULL;
ARG_UNUSED(addr);
#endif
http_server_add_default(&http_urls, default_handler);
http_server_add_url(&http_urls, "/ws", HTTP_URL_STANDARD);
http_server_add_url(&http_urls, "/ws", HTTP_URL_WEBSOCKET);
ret = http_server_init(&http_ctx, &http_urls, server_addr,
result, sizeof(result),
"Zephyr WS server", NULL);
if (ret < 0) {
NET_ERR("Cannot init web server (%d)", ret);
return;
}
http_set_cb(&http_ctx, ws_connected, ws_received, ws_sent, ws_closed);
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
net_app_set_net_pkt_pool(&http_ctx.app_ctx, tx_tcp_slab, data_tcp_pool);
#endif
#if defined(CONFIG_NET_APP_TLS)
ret = http_server_set_tls(&http_ctx,
APP_BANNER,
INSTANCE_INFO,
strlen(INSTANCE_INFO),
setup_cert,
NULL,
&ssl_pool,
ws_tls_stack,
K_THREAD_STACK_SIZEOF(ws_tls_stack));
if (ret < 0) {
NET_ERR("Cannot enable TLS support (%d)", ret);
}
#endif
http_server_enable(&http_ctx);
ws_ctx = &http_ctx;
}

View file

@ -1,7 +0,0 @@
common:
depends_on: netif
platform_whitelist: native_posix qemu_x86 qemu_cortex_m3
tests:
net.websocket:
min_ram: 46
tags: net http websocket