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:
parent
a691e5e751
commit
1cba0161ed
|
@ -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
|
||||||
***************
|
***************
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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);
|
|
|
@ -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__ */
|
|
|
@ -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_ */
|
|
|
@ -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)
|
|
|
@ -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.
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,7 +0,0 @@
|
||||||
sample:
|
|
||||||
name: Websocket console
|
|
||||||
tests:
|
|
||||||
test:
|
|
||||||
build_only: true
|
|
||||||
platform_whitelist: qemu_x86 native_posix
|
|
||||||
tags: net websocket
|
|
|
@ -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);
|
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 318 B |
|
@ -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(/&/, "&").replace(/</, "<").
|
|
||||||
replace(/>/, ">").replace(/\u000d/, "").
|
|
||||||
replace(/\u000a/, "").replace(/\[0\;31m/, "<em>").
|
|
||||||
replace(/\[0m/, "</em>").replace(/"/, """); // "
|
|
||||||
log.innerHTML = log.innerHTML + escaped + "<br>";
|
|
||||||
scrollToBottom("zconsole");
|
|
||||||
}
|
|
||||||
|
|
||||||
function output(str) {
|
|
||||||
var log = document.getElementById("output");
|
|
||||||
var escaped = str.replace(/&/, "&").replace(/</, "<").
|
|
||||||
replace(/>/, ">").replace(/"/, """); // "
|
|
||||||
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>
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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)
|
|
|
@ -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.
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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);
|
|
|
@ -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.
Binary file not shown.
|
@ -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>
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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(/&/, "&").replace(/</, "<").
|
|
||||||
replace(/>/, ">").replace(/"/, """); // "
|
|
||||||
}
|
|
||||||
|
|
||||||
function output(str) {
|
|
||||||
var log = document.getElementById("output");
|
|
||||||
log.innerHTML += escape(str) + "<br>";
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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()
|
|
|
@ -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
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
Loading…
Reference in a new issue