Commit f49d11d3 authored by Martí Bolívar's avatar Martí Bolívar Committed by Marti Bolivar
Browse files

civetweb: remove obsolete code

This code has gone unmaintained and bugs continue to be reported
against it. We do not have the resources as a project to maintain this
in "odd fixes" mode, and nobody has stepped up to maintain it [1], so
sadly this must be removed for now.

If anyone would like to see civetweb supported in upstream Zephyr
again, they are welcome to add it back, as long as they promise to
maintain it going forward.

Many thanks to everyone who has contributed to civetweb support in
Zephyr while it was here. So long and thanks for all the fish.

Fixes: #45807
Fixes: #43910
Fixes: #34226
Fixes: #46743

[1] https://lists.zephyrproject.org/g/devel/message/8466

Signed-off-by: default avatarMartí Bolívar <marti.bolivar@nordicsemi.no>
parent 27eeee84
sample:
description: Civetweb WebSocket Server sample
name: websocket_server
tests:
sample.net.civetweb.websocket_server:
modules:
- civetweb
platform_allow: nucleo_h745zi_q_m7
/*
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(http_server_handlers, LOG_LEVEL_DBG);
#include "http_server_handlers.h"
#define TX_CHUNK_SIZE_BYTES CONFIG_NET_TX_STACK_SIZE
#define URL_MAIN "/$"
#define URL_INDEX_CSS "/index.css"
#define URL_INDEX_HTML "/index.html"
#define URL_FAVICON_ICO "/favicon.ico"
#define URL_WS_JS "/ws.js"
#define HTTP_TEXT_HTML "text/html"
#define HTTP_TEXT_CSS "text/css"
#define HTTP_TEXT_JS "text/javascript"
#define HTTP_EOFL "\r\n" /* http end of line */
#define HTTP_CONTEND_ENCODING "Content-Encoding: "
#define HTTP_ENCODING_GZ "gzip"
#define __code_decl /* static */
#define __data_decl static
__code_decl void this_register_handlers(struct mg_context *ctx);
__code_decl int this_send_buffer_chunked(struct mg_connection *conn,
const char *mime_type,
const char *buff,
const size_t buff_len);
__code_decl int this_redirect_2_index_html(struct mg_connection *conn,
void *cbdata);
__code_decl int this_index_html_handler(struct mg_connection *conn,
void *cbdata);
__code_decl int this_index_css_handler(struct mg_connection *conn,
void *cbdata);
__code_decl int this_ws_js_handler(struct mg_connection *conn,
void *cbdata);
__code_decl int this_favicon_ico_handler(struct mg_connection *conn,
void *cbdata);
__code_decl void this_set_return_value(int *ret_val);
void init_http_server_handlers(struct mg_context *ctx)
{
this_register_handlers(ctx);
}
__code_decl void this_register_handlers(struct mg_context *ctx)
{
mg_set_request_handler(ctx, URL_MAIN,
this_redirect_2_index_html, NULL);
mg_set_request_handler(ctx, URL_INDEX_HTML,
this_index_html_handler, NULL);
mg_set_request_handler(ctx, URL_INDEX_CSS,
this_index_css_handler, NULL);
mg_set_request_handler(ctx, URL_WS_JS,
this_ws_js_handler, NULL);
mg_set_request_handler(ctx, URL_FAVICON_ICO,
this_favicon_ico_handler, NULL);
}
__code_decl int this_send_buffer_chunked(struct mg_connection *conn,
const char *mime_type,
const char *buff,
const size_t buff_len)
{
int ret = 0;
ret = mg_send_http_ok(conn, mime_type, -1);
if (ret < 0) {
goto error_this_send_buffer_chunked;
}
long left_bytes = buff_len;
char *itr = (char *)buff; /* buffer iterator */
LOG_DBG("Transferring:");
LOG_DBG("itr: 0x%08X ret: %d left_bytes: %ld chunk_size: %zd B",
(unsigned int)itr, ret, left_bytes, TX_CHUNK_SIZE_BYTES);
while (left_bytes > TX_CHUNK_SIZE_BYTES) {
ret = mg_send_chunk(conn, itr, TX_CHUNK_SIZE_BYTES);
itr += TX_CHUNK_SIZE_BYTES;
left_bytes -= TX_CHUNK_SIZE_BYTES;
LOG_DBG("itr: 0x%08X ret: %d left_bytes: %ld",
(unsigned int)itr, ret, left_bytes);
if (ret < 0) {
goto error_this_send_buffer_chunked;
}
}
if (left_bytes > 0) {
ret = mg_send_chunk(conn, itr, left_bytes);
itr += left_bytes;
left_bytes = 0;
LOG_DBG("itr: 0x%08X ret: %d left_bytes: %ld",
(unsigned int)itr, ret, left_bytes);
if (ret < 0) {
goto error_this_send_buffer_chunked;
}
}
/* Must be sent at the end of the chunked sequence */
ret = mg_send_chunk(conn, "", 0);
error_this_send_buffer_chunked:
if (ret < 0) {
LOG_ERR("aborted! ret: %d", ret);
}
return ret;
}
__code_decl int this_redirect_2_index_html(struct mg_connection *conn,
void *cbdata)
{
int ret = 0;
ret = mg_send_http_redirect(conn, URL_INDEX_HTML, 303);
return 202;
}
__code_decl int this_index_html_handler(struct mg_connection *conn,
void *cbdata)
{
__data_decl const char index_html[] = {
#include "web_page/index.html.gz.inc"
};
int ret = 0;
ret = this_send_buffer_chunked(conn, HTTP_TEXT_HTML
HTTP_EOFL
HTTP_CONTEND_ENCODING
HTTP_ENCODING_GZ,
index_html, sizeof(index_html));
this_set_return_value(&ret);
return ret;
}
__code_decl int this_index_css_handler(struct mg_connection *conn, void *cbdata)
{
__data_decl const char index_css[] = {
#include "web_page/index.css.gz.inc"
};
int ret = 0;
ret = this_send_buffer_chunked(conn, HTTP_TEXT_CSS
HTTP_EOFL
HTTP_CONTEND_ENCODING
HTTP_ENCODING_GZ,
index_css, sizeof(index_css));
this_set_return_value(&ret);
return ret;
}
__code_decl int this_ws_js_handler(struct mg_connection *conn, void *cbdata)
{
__data_decl const char ws_js[] = {
#include "web_page/ws.js.gz.inc"
};
int ret = 0;
ret = this_send_buffer_chunked(conn, HTTP_TEXT_JS
HTTP_EOFL
HTTP_CONTEND_ENCODING
HTTP_ENCODING_GZ,
ws_js, sizeof(ws_js));
this_set_return_value(&ret);
return ret;
}
__code_decl int this_favicon_ico_handler(struct mg_connection *conn,
void *cbdata)
{
int ret = 404;
return ret; /* should fail */
}
__code_decl void this_set_return_value(int *ret_val)
{
if (*ret_val < 0) {
*ret_val = 404; /* 404 - HTTP FAIL or 0 - handler fail */
} else {
*ret_val = 200; /* 200 - HTTP OK*/
}
}
/*
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
#include <zephyr/zephyr.h>
#include <zephyr/posix/pthread.h>
#include "civetweb.h"
#include "http_server_handlers.h"
#include "websocket_server_handlers.h"
#define HTTP_PORT 8080
#define HTTPS_PORT 4443
#define CIVETWEB_MAIN_THREAD_STACK_SIZE CONFIG_MAIN_STACK_SIZE
/* Use smallest possible value of 1024 (see the line 18619 of civetweb.c) */
#define MAX_REQUEST_SIZE_BYTES 1024
K_THREAD_STACK_DEFINE(civetweb_stack, CIVETWEB_MAIN_THREAD_STACK_SIZE);
void *main_pthread(void *arg)
{
static const char * const options[] = {
"listening_ports", STRINGIFY(HTTP_PORT),
"num_threads", "1",
"max_request_size", STRINGIFY(MAX_REQUEST_SIZE_BYTES),
NULL
};
struct mg_callbacks callbacks;
struct mg_context *ctx;
(void)arg;
memset(&callbacks, 0, sizeof(callbacks));
ctx = mg_start(&callbacks, NULL, (const char **)options);
if (ctx == NULL) {
LOG_ERR("Unable to start the server\n");
return 0;
}
init_http_server_handlers(ctx);
init_websocket_server_handlers(ctx);
return 0;
}
void main(void)
{
pthread_attr_t civetweb_attr;
pthread_t civetweb_thread;
(void)pthread_attr_init(&civetweb_attr);
(void)pthread_attr_setstack(&civetweb_attr, &civetweb_stack,
CIVETWEB_MAIN_THREAD_STACK_SIZE);
(void)pthread_create(&civetweb_thread, &civetweb_attr,
&main_pthread, 0);
LOG_INF("WebSocket Server was started!");
}
/*
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(websocket_server_handlers, LOG_LEVEL_DBG);
#include "websocket_server_handlers.h"
#define BOARD_REPLY_PREFIX CONFIG_BOARD" says: "
#define BOARD_REPLY_PREFIX_LEN sizeof(BOARD_REPLY_PREFIX)
#define BOARD_REPLY_SUFFIX " too!"
#define BOARD_REPLY_SUFFIX_LEN sizeof(BOARD_REPLY_SUFFIX)
#define BOARD_REPLY_TOAL_LEN (BOARD_REPLY_PREFIX_LEN +\
BOARD_REPLY_SUFFIX_LEN)
#define FIN_SHIFT 7u
#define RSV1_SHIFT 6u
#define RSV2_SHIFT 5u
#define RSV3_SHIFT 4u
#define OPCODE_SHIFT 0u
#define BOOL_MASK 0x1 /* boolean value mask */
#define HALF_BYTE_MASK 0xF /* half byte value mask */
#define WS_URL "/ws" /* WebSocket server main url */
#define __code_decl /* static */
#define __data_decl static
/* Websocket server handlers: */
__code_decl int this_connect_handler(const struct mg_connection *conn,
void *cbdata);
__code_decl void this_ready_handler(struct mg_connection *conn, void *cbdata);
__code_decl int this_data_handler(struct mg_connection *conn, int bits,
char *data, size_t data_len, void *cbdata);
__code_decl void this_close_handler(const struct mg_connection *conn,
void *cbdata);
void init_websocket_server_handlers(struct mg_context *ctx)
{
mg_set_websocket_handler(ctx, WS_URL,
this_connect_handler,
this_ready_handler,
this_data_handler,
this_close_handler,
NULL);
}
__code_decl int this_connect_handler(const struct mg_connection *conn,
void *cbdata)
{
int ret_val = 0;
return ret_val;
}
__code_decl void this_ready_handler(struct mg_connection *conn, void *cbdata)
{
}
__code_decl int this_data_handler(struct mg_connection *conn, int bits,
char *data, size_t data_len, void *cbdata)
{
int ret_state = 1;
/* Encode bits as by https://tools.ietf.org/html/rfc6455#section-5.2: */
const bool FIN = (bits >> FIN_SHIFT) & BOOL_MASK;
const bool RSV1 = (bits >> RSV1_SHIFT) & BOOL_MASK;
const bool RSV2 = (bits >> RSV2_SHIFT) & BOOL_MASK;
const bool RSV3 = (bits >> RSV2_SHIFT) & BOOL_MASK;
uint8_t OPCODE = (bits >> OPCODE_SHIFT) & HALF_BYTE_MASK;
(void)FIN;
(void)RSV1;
(void)RSV2;
(void)RSV3;
LOG_DBG("got bits: %d", bits);
LOG_DBG("\t\twith OPCODE: %d", OPCODE);
/* TODO: Protect resp_data with semaphore */
const size_t resp_data_len = BOARD_REPLY_TOAL_LEN + data_len;
if (resp_data_len > CONFIG_MAIN_STACK_SIZE) {
/* Close connection due to no memory */
OPCODE = MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE;
}
char resp_data[resp_data_len];
/* Process depending of opcode: */
switch (OPCODE) {
case MG_WEBSOCKET_OPCODE_CONTINUATION:
break;
case MG_WEBSOCKET_OPCODE_TEXT:
memcpy(resp_data,
BOARD_REPLY_PREFIX, BOARD_REPLY_PREFIX_LEN);
memcpy(resp_data + BOARD_REPLY_PREFIX_LEN,
data, data_len);
memcpy(resp_data + BOARD_REPLY_PREFIX_LEN + data_len,
BOARD_REPLY_SUFFIX, BOARD_REPLY_SUFFIX_LEN);
ret_state = mg_websocket_write(conn, OPCODE, resp_data,
resp_data_len);
break;
case MG_WEBSOCKET_OPCODE_BINARY:
ret_state = 0;
mg_websocket_write(conn,
MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE, NULL, 0);
LOG_ERR("Binary data not supported currently: "
"close connection");
break;
case MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE:
ret_state = 0;
mg_websocket_write(conn, OPCODE, NULL, 0);
break;
case MG_WEBSOCKET_OPCODE_PING:
break;
case MG_WEBSOCKET_OPCODE_PONG:
break;
default:
ret_state = 0;
mg_websocket_write(conn,
MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE, NULL, 0);
LOG_ERR("Unknown OPCODE: close connection");
break;
}
if (ret_state < 0) {
/* TODO: Maybe need we close WS connection here?! */
LOG_ERR("Unknown ERROR: ret_state = %d", ret_state);
} else if (ret_state == 0) {
LOG_DBG("Close WS connection: ret_state = %d", ret_state);
}
return ret_state;
}
__code_decl void this_close_handler(const struct mg_connection *conn,
void *cbdata)
{
}
/*
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
html, body {
border: 0;
padding: 0;
margin: 0;
height: calc(100% - 45px);
}
.frame {
display: block;
padding: 5px;
border: 2px solid #000;
margin-top: 5px;
margin-left: 5px;
margin-right: 5px;
width: auto;
box-sizing: border-box;
width: auto;
height: calc(100% - 5px);
}
.controls {
border: 2px solid #000;
height: 150px;
box-sizing: border-box;
margin-bottom: 5px;
}
.log {
padding: 5px;
border: 2px solid #000;
box-sizing: border-box;
width: 100%;
height: calc(100% - 155px);
overflow: auto;
position: relative;
}
.log #btn_clear_log {
position: absolute;
top: 10px;
right: 10px;
z-index: 1;
}
.log .entries {
display: table;
box-sizing: border-box;
width: 100%;
}
.log .entry {
display: table-row;
padding-top: 5px;
}
.log .entry .publisher,
.log .entry .content {
display: table-cell;
vertical-align: top;
padding: 5px;
font-size: 16px;
line-height: 20px;
}
.log .entry .publisher .timestamp {
font-weight: normal;
font-size: 10px;
color: black;
}
.log .entry .publisher {
font-weight: bold;
text-align: right;
width: 80px;
text-transform: uppercase;
padding-right: 10px;
}
.log .entry .publisher.system {
color: red;
}
.log .entry .publisher.system:before {
content: 'SYSTEM';
}
.log .entry .publisher.console {
color: orange;
}
.log .entry .publisher.console:before {
content: 'CONSOLE';
}
.log .entry .publisher.local {
color: grey;
}
.log .entry .publisher.local:before {
content: 'LOCAL';
}
.log .entry .publisher.remote {
color: green;
}
.log .entry .publisher.remote:before {
content: 'REMOTE';
}
.log .entry .content.text {
font-family: 'Source Code Pro', 'Courier New', monospace;
font-size: 15px;
white-space: pre-wrap;
}
.log .entry .content.binary {
font-family: 'Source Code Pro', 'Courier New', monospace;
font-size: 15px;
white-space: pre-wrap;
color: orange;
}
.controls .status {
font-weight: bold;
text-transform: uppercase;
}
.controls.closed .status {
color: red;
}
.controls.closed .status:after {
content: 'DISCONNECTED';
}
.controls.open .status {
color: green;
}
.controls.open .status:after {
content: 'CONNECTED';
}
.controls.connecting .status {
color: orange;
}
.controls.connecting .status:after {
content: 'CONNECTING';
}
.controls.closing .status {
color: red;
}
.controls.closing .status:after {
content: 'CLOSING';
}
.controls .control-pane {
display: block;
float: left;
border-left: 1px solid #000;
height: 100%;
box-sizing: border-box;
padding: 5px;
}
.controls .control-pane:first-child {
border-left: none;
}
.controls table {
padding: 0;
margin: 0;
border-collapse: collapse;
}
.controls table th {
text-align: right;
padding: 3px;
}
.controls table td {
padding: 3px;
}
.controls #message_text {
min-width: 300px;
min-height: 60px;
}
.copyright {
font-size: 10px;
line-height: 10px;
text-align: right;
color: grey;
}
<html>
<head>
<meta charset="UTF-8">
<title>Zephyr HTTP civetweb websocket server sample</title>
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="index.css"/>
<script type="text/javascript" src="ws.js"></script>
</head>
<body onload="init();">
<div id="container" class="container">
<div>
<h2>Zephyr HTTP civetweb websocket server sample</h2>
</div>
<div>
<p>HTTP connection: ok!</p>
</div>
<div class="control-pane">
<table>
<tr>
<th style="text-align: left;">Message Text</th>
</tr>
<tr>
<td><textarea id="message_text"></textarea></td>
</tr>
<tr>
<td><input type="submit" id="btn_send" value="Send" /></td>
</tr>
</table>
</div>
<div class="log">
<input type="submit" id="btn_clear_log" value="Clear" />
<div class="entries">
</div>
</div>
</div>
</body>
<div class="copyright">
<p>Copyright: Alexander Kozhinov,
SPDX-License-Identifier: Apache-2.0</p>
<div>
</html>
/*
* Copyright (c) 2020 Alexander Kozhinov <AlexanderKozhinov@yandex.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
var connected
var first_run
var ws
var LogMsgType = {
TEXT: 'text',
BINARY: 'binary'
}
var LogMsgSender = {
SYSTEM: 'system',
LOCAL: 'local',
REMOTE: 'remote',
CONSOLE: 'console'
}
var MAX_LOG_SIZE = 10
var SECOND = 1000
var MINUTE = SECOND * 60
var HOUR = MINUTE * 60
var DAY = HOUR * 24
var MONTH = DAY * 30
var YEAR = MONTH * 12
var TIME_UP_PERIOD_SEC = 5
function init() {
addLogEntry(LogMsgSender.CONSOLE,
LogMsgType.TEXT, 'establishing connection to the server...')
ws = new WebSocket(location.origin.replace("http", "ws") + "/ws")
first_run = "true"
connected = "false"
ws.onopen = function() {
addLogEntry(LogMsgSender.CONSOLE, LogMsgType.TEXT,
'connection opened')
addLogEntry(LogMsgSender.SYSTEM, LogMsgType.TEXT,
"The connection was established successfully (in " +
(Date.now() - ws.__openTime)+ " ms).\n" +
(ws.extensions ? 'Negotiated Extensions: ' +
ws.extensions : '') +
(ws.protocol ? 'Negotiated Protocol: ' +
ws.protocol : ''))
connected = "true"
}
ws.onmessage = function(e) {
addLogEntry(LogMsgSender.REMOTE,
LogMsgType.TEXT, 'data received: ' + e.data)
}
ws.onclose = function() {
addLogEntry(LogMsgSender.CONSOLE, LogMsgType.TEXT,
'connection closed')
connected = "false"
}
ws.onerror = function(e) {
addLogEntry(LogMsgSender.CONSOLE, LogMsgType.TEXT,
'data error: ' + e.data)
console.log(e)
}
var sendText = function(text) {
addLogEntry(LogMsgSender.LOCAL, LogMsgType.TEXT, text)
ws.send(text)
}
document.getElementById('btn_send').onclick = function(event) {
event.preventDefault()
var text2send = document.getElementById('message_text').value
sendText(text2send)
scrollLogToBottom()
}
document.getElementById('btn_clear_log').onclick = function(event) {
event.preventDefault()
clearLog()
}
window.setInterval(updateTimestamps, TIME_UP_PERIOD_SEC * SECOND)
}
function updateTimestamps() {
var entries = document.getElementsByClassName('entries')
var now = Date.now()
var timestamp = null
var entry = null
var old_time = null
for (var i = 0; i < entries[0].childElementCount; i++) {
entry = entries[0].children[i]
old_time = Number(entry.getAttribute('timestamp'))
timestamp = entry.getElementsByClassName('timestamp')[0]
timestamp.innerHTML = formatTimeDifference(now, old_time)
}
}
function formatTimeDifference(now, then) {
var difference = Math.abs(now - then)
if (difference < TIME_UP_PERIOD_SEC * SECOND) {
return 'just now'
}
if (difference < MINUTE) {
return '< 1 min ago'
}
if (difference < HOUR) {
return Math.round(difference / MINUTE) + ' min ago'
}
if (difference < DAY) {
return Math.round(difference / HOUR) + ' hr ago'
}
if (difference < MONTH) {
return Math.round(difference / DAY) + ' day ago'
}
return Math.round(difference / YEAR) + ' yr ago'
}
function blobToHex(blob) {
// TODO: Implement (seems to be non-trivial).
return ''
}
function logIsScrolledToBottom() {
var j = document.getElementsByClassName('log')
var e = j[0]
return e.scrollTop + j.height +
10 /* padding */ >= e.scrollHeight - 10 /* some tolerance */
}
function scrollLogToBottom() {
var e = document.getElementsByClassName('log')[0]
e.scrollTop = e.scrollHeight
}
function pruneLog() {
var e = document.getElementsByClassName('entries')
if (e.length == 0) {
return
}
if (e[0].children.length == MAX_LOG_SIZE) {
e[0].children[0].remove()
}
}
function addLogEntry(sender, type, data) {
pruneLog()
if (type == LogMsgType.BINARY) {
data = '(BINARY MESSAGE: ' + data.size + ' bytes)\n' +
blobToHex(data)
} else {
data = data || '(empty message)'
}
var entries = document.getElementsByClassName('entries')
var entry = entries[0].appendChild(document.createElement('div'))
entry.classList += 'entry'
var publisher = entry.appendChild(document.createElement('div'))
publisher.classList += 'publisher'
publisher.classList += ' ' + sender
var content = entry.appendChild(document.createElement('div'))
content.classList += 'content'
content.classList += ' ' + type
var timestamp = publisher.appendChild(document.createElement('div'))
timestamp.classList += 'timestamp'
entry.setAttribute('timestamp', '' + Date.now())
content.innerHTML = data
timestamp.innerHTML = 'just now'
var scroll = logIsScrolledToBottom()
if (scroll) {
scrollLogToBottom()
}
}
function clearLog() {
var entries = document.getElementsByClassName('entries')[0]
while (entries.firstChild) entries.removeChild(entries.firstChild)
}
......@@ -31,9 +31,6 @@ manifest:
- name: chre
revision: 0edfe2c2ec656afb910cfab8ed59a5ffd59b87c8
path: modules/lib/chre
- name: civetweb
revision: 094aeb41bb93e9199d24d665ee43e9e05d6d7b1c
path: modules/lib/civetweb
- name: cmsis
revision: 5f86244bad4ad5a590e084f0e72ba7a1416c2edf
path: modules/hal/cmsis
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment