mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
cmake: allow setting LWS_ROLE_WS
You can build lws without support for ws, with -DLWS_ROLE_WS=0. This is thanks to the role ops isolating all the ws-specific business in the ws role. Also retire more test apps replaced by minmal-examples.
This commit is contained in:
parent
8e9751e26e
commit
27e86e2641
36 changed files with 546 additions and 1678 deletions
|
@ -16,6 +16,7 @@ env:
|
|||
- LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
|
||||
- LWS_METHOD=nologs CMAKE_ARGS="-DLWS_WITH_NO_LOGS=ON"
|
||||
- LWS_METHOD=smp CMAKE_ARGS="-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=nows CMAKE_ARGS="-DLWS_ROLE_WS=0"
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
|
|
@ -9,6 +9,8 @@ set(LWS_WITH_BUNDLED_ZLIB_DEFAULT OFF)
|
|||
if(WIN32)
|
||||
set(LWS_WITH_BUNDLED_ZLIB_DEFAULT ON)
|
||||
endif()
|
||||
set(LWS_ROLE_H1 1)
|
||||
set(LWS_ROLE_RAW 1)
|
||||
|
||||
#
|
||||
# Select features recommended for PC distro packaging
|
||||
|
@ -18,6 +20,7 @@ option(LWS_WITH_DISTRO_RECOMMENDED "Enable features recommended for distro packa
|
|||
#
|
||||
# Major individual features
|
||||
#
|
||||
option(LWS_ROLE_WS "Compile with support for websockets" ON)
|
||||
option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" OFF)
|
||||
option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
|
||||
option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
|
||||
|
@ -70,7 +73,6 @@ option(LWS_WITHOUT_TEST_SERVER "Don't build the test server" OFF)
|
|||
option(LWS_WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF)
|
||||
option(LWS_WITHOUT_TEST_PING "Don't build the ping test application" OFF)
|
||||
option(LWS_WITHOUT_TEST_CLIENT "Don't build the client test application" OFF)
|
||||
option(LWS_WITHOUT_TEST_FRAGGLE "Don't build the ping test application" OFF)
|
||||
#
|
||||
# Extensions (permessage-deflate)
|
||||
#
|
||||
|
@ -197,9 +199,6 @@ endif()
|
|||
|
||||
# translate old functionality enables to set up ROLE enables so nothing changes
|
||||
|
||||
set(LWS_ROLE_H1 1)
|
||||
set(LWS_ROLE_WS 1)
|
||||
set(LWS_ROLE_RAW 1)
|
||||
if (LWS_WITH_HTTP2)
|
||||
set(LWS_ROLE_H2 1)
|
||||
endif()
|
||||
|
@ -207,6 +206,10 @@ if (LWS_WITH_CGI)
|
|||
set(LWS_ROLE_CGI 1)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_ROLE_WS)
|
||||
set(LWS_WITHOUT_EXTENSIONS 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP2 AND LWS_WITHOUT_SERVER)
|
||||
message(FATAL_ERROR "HTTP2 can only be used with server at the moment")
|
||||
endif()
|
||||
|
@ -709,7 +712,7 @@ if (LWS_ROLE_WS)
|
|||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/ws/client-ws.c
|
||||
lib/roles/ws/client-parser.c)
|
||||
lib/roles/ws/client-parser-ws.c)
|
||||
endif()
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
|
@ -1592,15 +1595,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
""
|
||||
"")
|
||||
endif()
|
||||
if (UNIX AND NOT LWS_WITHOUT_SERVER AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND LWS_HAVE_PTHREAD_H)
|
||||
create_test_app(test-server-pthreads
|
||||
"test-apps/test-server-pthreads.c"
|
||||
"test-apps/test-server-http.c"
|
||||
"test-apps/test-server-dumb-increment.c"
|
||||
""
|
||||
""
|
||||
"")
|
||||
endif()
|
||||
if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
AND LWS_WITH_LIBEV)
|
||||
create_test_app(test-server-libev
|
||||
|
@ -1720,13 +1714,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
create_test_app(test-client "test-apps/test-client.c" "" "" "" "" "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# test-fraggle
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_FRAGGLE AND NOT LWS_WITHOUT_SERVER)
|
||||
create_test_app(test-fraggle "test-apps/test-fraggle.c" "" "" "" "" "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# test-ping
|
||||
#
|
||||
|
@ -1786,27 +1773,15 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
|
||||
endmacro()
|
||||
|
||||
|
||||
create_plugin(protocol_lws_meta ""
|
||||
"plugins/protocol_lws_meta.c" "" "")
|
||||
if (LWS_ROLE_WS)
|
||||
create_plugin(protocol_dumb_increment ""
|
||||
"plugins/protocol_dumb_increment.c" "" "")
|
||||
create_plugin(protocol_lws_mirror ""
|
||||
"plugins/protocol_lws_mirror.c" "" "")
|
||||
create_plugin(protocol_lws_status ""
|
||||
"plugins/protocol_lws_status.c" "" "")
|
||||
create_plugin(protocol_post_demo ""
|
||||
"plugins/protocol_post_demo.c" "" "")
|
||||
create_plugin(protocol_lws_table_dirlisting ""
|
||||
"plugins/generic-table/protocol_table_dirlisting.c" "" "")
|
||||
if (LWS_WITH_SSL)
|
||||
create_plugin(protocol_lws_ssh_base "plugins/ssh-base/include"
|
||||
"plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c" "plugins/ssh-base/crypto/chacha.c;plugins/ssh-base/crypto/ed25519.c;plugins/ssh-base/crypto/fe25519.c;plugins/ssh-base/crypto/ge25519.c;plugins/ssh-base/crypto/poly1305.c;plugins/ssh-base/crypto/sc25519.c;plugins/ssh-base/crypto/smult_curve25519_ref.c" "")
|
||||
create_plugin(protocol_lws_sshd_demo "plugins/ssh-base/include" "plugins/protocol_lws_sshd_demo.c" "" "")
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/plugins/ssh-base/include")
|
||||
endif()
|
||||
|
||||
if (NOT WIN32)
|
||||
create_plugin(protocol_lws_raw_test ""
|
||||
"plugins/protocol_lws_raw_test.c" "" "")
|
||||
|
@ -1820,14 +1795,29 @@ endif()
|
|||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
create_plugin(protocol_client_loopback_test ""
|
||||
"plugins/protocol_client_loopback_test.c" "" "")
|
||||
endif(NOT LWS_WITHOUT_CLIENT)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
create_plugin(protocol_post_demo ""
|
||||
"plugins/protocol_post_demo.c" "" "")
|
||||
|
||||
if (LWS_WITH_SSL)
|
||||
create_plugin(protocol_lws_ssh_base "plugins/ssh-base/include"
|
||||
"plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c" "plugins/ssh-base/crypto/chacha.c;plugins/ssh-base/crypto/ed25519.c;plugins/ssh-base/crypto/fe25519.c;plugins/ssh-base/crypto/ge25519.c;plugins/ssh-base/crypto/poly1305.c;plugins/ssh-base/crypto/sc25519.c;plugins/ssh-base/crypto/smult_curve25519_ref.c" "")
|
||||
create_plugin(protocol_lws_sshd_demo "plugins/ssh-base/include" "plugins/protocol_lws_sshd_demo.c" "" "")
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/plugins/ssh-base/include")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
if (LWS_WITH_ACME)
|
||||
create_plugin(protocol_lws_acme_client ""
|
||||
"plugins/acme-client/protocol_lws_acme_client.c" "" "")
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_GENERIC_SESSIONS)
|
||||
if (LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS)
|
||||
create_plugin(protocol_generic_sessions ""
|
||||
"plugins/generic-sessions/protocol_generic_sessions.c"
|
||||
"plugins/generic-sessions/utils.c"
|
||||
|
@ -2157,7 +2147,6 @@ message(" LWS_WITHOUT_TEST_SERVER = ${LWS_WITHOUT_TEST_SERVER}")
|
|||
message(" LWS_WITHOUT_TEST_SERVER_EXTPOLL = ${LWS_WITHOUT_TEST_SERVER_EXTPOLL}")
|
||||
message(" LWS_WITHOUT_TEST_PING = ${LWS_WITHOUT_TEST_PING}")
|
||||
message(" LWS_WITHOUT_TEST_CLIENT = ${LWS_WITHOUT_TEST_CLIENT}")
|
||||
message(" LWS_WITHOUT_TEST_FRAGGLE = ${LWS_WITHOUT_TEST_FRAGGLE}")
|
||||
message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}")
|
||||
message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}")
|
||||
message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}")
|
||||
|
|
|
@ -13,8 +13,8 @@ The Travis build of lws done on every commit now runs
|
|||
|
||||
Tests|Count|Explanation
|
||||
---|---|---
|
||||
Build / Linux / gcc|12|-Wall -Werror
|
||||
Build / Mac / Clang|12|-Wall -Werror
|
||||
Build / Linux / gcc|13|-Wall -Werror
|
||||
Build / Mac / Clang|13|-Wall -Werror
|
||||
Build / Windows / MSVC|7|default
|
||||
Selftests|29|minimal examples built and run against each other and remote server
|
||||
attack.sh|225|Correctness, robustness and security tests for http parser
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
#define LWS_BUILD_HASH "unknown-build-hash"
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
static const struct lws_role_ops * available_roles[] = {
|
||||
const struct lws_role_ops * available_roles[] = {
|
||||
#if defined(LWS_ROLE_H2)
|
||||
&role_ops_h2,
|
||||
#endif
|
||||
|
@ -36,8 +35,10 @@ static const struct lws_role_ops * available_roles[] = {
|
|||
#if defined(LWS_ROLE_WS)
|
||||
&role_ops_ws,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
static char alpn_discovered[32];
|
||||
#endif
|
||||
|
||||
|
@ -60,17 +61,15 @@ int
|
|||
lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
|
||||
{
|
||||
#if defined(LWS_WITH_TLS)
|
||||
int n;
|
||||
|
||||
if (!alpn)
|
||||
return 0;
|
||||
|
||||
lwsl_info("%s: '%s'\n", __func__, alpn);
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++)
|
||||
if (!strcmp(available_roles[n]->alpn, alpn) &&
|
||||
available_roles[n]->alpn_negotiated)
|
||||
return available_roles[n]->alpn_negotiated(wsi, alpn);
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
|
||||
if (!strcmp(ar->alpn, alpn) && ar->alpn_negotiated)
|
||||
return ar->alpn_negotiated(wsi, alpn);
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_END;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -565,13 +564,6 @@ lws_create_vhost(struct lws_context *context,
|
|||
if (info->options & LWS_SERVER_OPTION_ONLY_RAW)
|
||||
lwsl_info("%s set to only support RAW\n", vh->name);
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
vh->h2.set = context->set;
|
||||
if (info->http2_settings[0])
|
||||
for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
|
||||
vh->h2.set.s[n] = info->http2_settings[n];
|
||||
#endif
|
||||
|
||||
vh->iface = info->iface;
|
||||
#if !defined(LWS_WITH_ESP32) && \
|
||||
!defined(OPTEE_TA) && !defined(WIN32)
|
||||
|
@ -589,9 +581,11 @@ lws_create_vhost(struct lws_context *context,
|
|||
vh->user = info->user;
|
||||
vh->alpn = info->alpn;
|
||||
|
||||
#if defined(LWS_ROLE_H2)
|
||||
role_ops_h2.init_vhost(vh, info);
|
||||
#endif
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
|
||||
if (ar->init_vhost)
|
||||
if (ar->init_vhost(vh, info))
|
||||
return NULL;
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_END;
|
||||
|
||||
vh->ssl_info_event_mask = info->ssl_info_event_mask;
|
||||
if (info->keepalive_timeout)
|
||||
|
@ -740,40 +734,6 @@ lws_create_vhost(struct lws_context *context,
|
|||
mounts = mounts->mount_next;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
if (context->plugin_extension_count) {
|
||||
|
||||
m = 0;
|
||||
while (info->extensions && info->extensions[m].callback)
|
||||
m++;
|
||||
|
||||
/*
|
||||
* give the vhost a unified list of extensions including the
|
||||
* ones that came from plugins
|
||||
*/
|
||||
vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
|
||||
(m + context->plugin_extension_count + 1),
|
||||
"extensions");
|
||||
if (!vh->ws.extensions)
|
||||
return NULL;
|
||||
|
||||
memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
|
||||
sizeof(struct lws_extension) * m);
|
||||
plugin = context->plugin_list;
|
||||
while (plugin) {
|
||||
memcpy((struct lws_extension *)&vh->ws.extensions[m],
|
||||
plugin->caps.extensions,
|
||||
sizeof(struct lws_extension) *
|
||||
plugin->caps.count_extensions);
|
||||
m += plugin->caps.count_extensions;
|
||||
plugin = plugin->list;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
vh->ws.extensions = info->extensions;
|
||||
#endif
|
||||
|
||||
vh->listen_port = info->port;
|
||||
vh->http_proxy_port = 0;
|
||||
vh->http_proxy_address[0] = '\0';
|
||||
|
@ -1122,16 +1082,17 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
else {
|
||||
char *p = alpn_discovered, first = 1;
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++) {
|
||||
if (available_roles[n]->alpn) {
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
|
||||
if (ar->alpn) {
|
||||
if (!first)
|
||||
*p++ = ',';
|
||||
p += lws_snprintf(p, alpn_discovered +
|
||||
sizeof(alpn_discovered) - 2 - p,
|
||||
"%s", available_roles[n]->alpn);
|
||||
"%s", ar->alpn);
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
} LWS_FOR_EVERY_AVAILABLE_ROLE_END;
|
||||
|
||||
context->alpn_default = alpn_discovered;
|
||||
}
|
||||
|
||||
|
@ -1596,12 +1557,11 @@ lws_vhost_destroy2(struct lws_vhost *vh)
|
|||
lws_free((void *)vh->protocols);
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (context->plugin_extension_count)
|
||||
lws_free((void *)vh->ws.extensions);
|
||||
#endif
|
||||
#endif
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
|
||||
if (ar->destroy_vhost)
|
||||
ar->destroy_vhost(vh);
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_END;
|
||||
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
if (vh->log_fd != (int)LWS_INVALID_FILE)
|
||||
close(vh->log_fd);
|
||||
|
|
|
@ -95,7 +95,6 @@ __lws_free_wsi(struct lws *wsi)
|
|||
|
||||
lws_buflist_destroy_all_segments(&wsi->buflist);
|
||||
lws_free_set_NULL(wsi->trunc_alloc);
|
||||
lws_free_set_NULL(wsi->ws);
|
||||
lws_free_set_NULL(wsi->udp);
|
||||
|
||||
/* we may not have an ah, but may be on the waiting list... */
|
||||
|
@ -834,11 +833,6 @@ just_kill_connection:
|
|||
wsi->told_user_closed = 1;
|
||||
}
|
||||
|
||||
/* deallocate any active extension contexts */
|
||||
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
|
||||
lwsl_warn("extension destruction failed\n");
|
||||
|
||||
async_close:
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
|
|
|
@ -249,7 +249,6 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
|||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -218,10 +218,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
}
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!pt->rx_draining_ext_list &&
|
||||
if (!pt->ws.rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
|
||||
#else
|
||||
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
if (!pt->ws.rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
#endif
|
||||
lws_service_fd_tsi(context, NULL, tsi);
|
||||
return 0;
|
||||
|
|
|
@ -141,10 +141,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
n = poll(pt->fds, pt->fds_count, timeout_ms);
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!pt->rx_draining_ext_list &&
|
||||
if (!pt->ws.rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
|
||||
#else
|
||||
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
if (!pt->ws.rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
#endif
|
||||
lws_service_fd_tsi(context, NULL, tsi);
|
||||
return 0;
|
||||
|
|
|
@ -259,12 +259,16 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
m = 0;
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!n && !pt->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
|
||||
#else
|
||||
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
m |= !n && !lws_ssl_anybody_has_buffered_read_tsi(context, tsi);
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
m |= !n && !pt->ws.rx_draining_ext_list;
|
||||
#endif
|
||||
|
||||
if (m) {
|
||||
lws_service_fd_tsi(context, NULL, tsi);
|
||||
lws_service_do_ripe_rxflow(pt);
|
||||
|
||||
|
|
|
@ -527,12 +527,14 @@ struct lws_role_ops {
|
|||
* ws-over-h2 is upgraded from h2 like this.
|
||||
*/
|
||||
int (*check_upgrades)(struct lws *wsi);
|
||||
/* role-specific context init during vhost creation */
|
||||
/* role-specific context init during context creation */
|
||||
int (*init_context)(struct lws_context *context,
|
||||
struct lws_context_creation_info *info);
|
||||
/* role-specific per-vhost init during vhost creation */
|
||||
int (*init_vhost)(struct lws_vhost *vh,
|
||||
struct lws_context_creation_info *info);
|
||||
/* role-specific per-vhost destructor during vhost destroy */
|
||||
int (*destroy_vhost)(struct lws_vhost *vh);
|
||||
/* generic 1Hz callback for the role itself */
|
||||
int (*periodic_checks)(struct lws_context *context, int tsi,
|
||||
time_t now);
|
||||
|
@ -582,9 +584,22 @@ struct lws_role_ops {
|
|||
uint16_t close_cb[2];
|
||||
};
|
||||
|
||||
/* null-terminated array of pointers to roles lws built with */
|
||||
extern const struct lws_role_ops *available_roles[];
|
||||
|
||||
#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \
|
||||
const struct lws_role_ops **ppxx = available_roles; \
|
||||
while (*ppxx) { \
|
||||
const struct lws_role_ops *xx = *ppxx++;
|
||||
|
||||
#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }}
|
||||
|
||||
/* core roles */
|
||||
extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
|
||||
role_ops_pipe;
|
||||
|
||||
/* bring in role private declarations */
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
#include "roles/http/private.h"
|
||||
#else
|
||||
|
@ -767,9 +782,8 @@ struct lws_context_per_thread {
|
|||
#endif
|
||||
struct lws_pollfd *fds;
|
||||
volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
|
||||
#if defined(LWS_ROLE_WS)
|
||||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_pt_role_ws ws;
|
||||
#endif
|
||||
struct lws_dll_lws dll_head_timeout;
|
||||
struct lws_dll_lws dll_head_hrtimer;
|
||||
|
@ -887,7 +901,7 @@ struct lws_vhost {
|
|||
#if defined(LWS_ROLE_H2)
|
||||
struct lws_vhost_role_h2 h2;
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_vhost_role_ws ws;
|
||||
#endif
|
||||
|
||||
|
@ -1368,6 +1382,9 @@ struct lws {
|
|||
#if defined(LWS_ROLE_H2)
|
||||
struct _lws_h2_related h2;
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS)
|
||||
struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
|
||||
#endif
|
||||
|
||||
/* lifetime members */
|
||||
|
||||
|
@ -1388,7 +1405,7 @@ struct lws {
|
|||
struct lws *parent; /* points to parent, if any */
|
||||
struct lws *child_list; /* points to first child */
|
||||
struct lws *sibling_list; /* subsequent children at same level */
|
||||
struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
struct lws_cgi *cgi; /* wsi being cgi master have one of these */
|
||||
#endif
|
||||
|
|
161
lib/roles/README.md
Normal file
161
lib/roles/README.md
Normal file
|
@ -0,0 +1,161 @@
|
|||
## Information for new role implementers
|
||||
|
||||
### Introduction
|
||||
|
||||
In lws the "role" is the job the wsi is doing in the system, eg,
|
||||
being an http1 or h2, or ws connection, or being a listen socket, etc.
|
||||
|
||||
This is different than, eg, a new ws protocol or a different callback
|
||||
for an existing role. A new role is needed when you want to add support for
|
||||
something completely new, like a completely new wire protocol that
|
||||
doesn't use http or ws.
|
||||
|
||||
So... what's the point of implementing the protocol inside the lws role framework?
|
||||
|
||||
You inherit all the well-maintained lws core functionality around:
|
||||
|
||||
- connection lifecycle sequencing in a valgrind-clean way
|
||||
|
||||
- proxy support, HTTP and Socks5
|
||||
|
||||
- tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role
|
||||
|
||||
- apis for cert lifecycle management and parsing
|
||||
|
||||
- event loop support working on all the lws event loops (poll, libuv , ev, and event)
|
||||
|
||||
- clean connection tracking and closing even on advanced event loops
|
||||
|
||||
- user code follows the same simple callbacks on wsi
|
||||
|
||||
- multi-vhost support
|
||||
|
||||
- core multithreaded service support with usually no locking requirement on the role code
|
||||
|
||||
- direct compatibility with all other lws roles + protocols in the same event loop
|
||||
|
||||
- compatibility with higher-level stuff like lwsws as the server application
|
||||
|
||||
### Code placement
|
||||
|
||||
The code specific to that role should live in `./lib/roles/**role name**`
|
||||
|
||||
If a role is asymmetic between a client and server side, like http is, it
|
||||
should generally be implemented as a single role.
|
||||
|
||||
### Allowing control over enabling roles
|
||||
|
||||
All roles should add a cmake define `LWS_ROLE_**role name**` and make its build
|
||||
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
|
||||
as well so user builds can understand if the role is available in the lws build it is
|
||||
trying to bind to.
|
||||
|
||||
If the role is disabled in cmake, nothing in its directory is built.
|
||||
|
||||
### Role ops struct
|
||||
|
||||
The role is defined by `struct lws_role_ops` in `lib/private/libwebsockets.h`,
|
||||
each role instantiates one of these and fills in the appropriate ops
|
||||
callbacks to perform its job. By convention that lives in
|
||||
`./lib/roles/**role name**/ops-**role_name**.c`.
|
||||
|
||||
### Private role declarations
|
||||
|
||||
Truly private declarations for the role can go in the role directory as you like.
|
||||
However when the declarations must be accessible to other things in lws build, eg,
|
||||
the role adds members to `struct lws` when enabled, they should be in the role
|
||||
directory in a file `private.h`.
|
||||
|
||||
Search for "bring in role private declarations" in `./lib/private-libwebsockets.h
|
||||
and add your private role file there following the style used for the other roles,
|
||||
eg,
|
||||
|
||||
```
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#include "roles/ws/private.h"
|
||||
#else
|
||||
#define lwsi_role_ws(wsi) (0)
|
||||
#endif
|
||||
```
|
||||
|
||||
If the role is disabled at cmake, nothing from its private.h should be used anywhere.
|
||||
|
||||
### Integrating role assets to lws
|
||||
|
||||
If your role needs special storage in lws objects, that's no problem. But to keep
|
||||
things sane, there are some rules.
|
||||
|
||||
- declare a "container struct" in your private.h for everything, eg, the ws role wants
|
||||
to add storage in lws_vhost for enabled extensions, it declares in its private.h
|
||||
|
||||
```
|
||||
struct lws_vhost_role_ws {
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
const struct lws_extension *extensions;
|
||||
#endif
|
||||
};
|
||||
```
|
||||
|
||||
- add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,
|
||||
eg, again for LWS_ROLE_WS
|
||||
|
||||
```
|
||||
struct lws_vhost {
|
||||
|
||||
...
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
struct lws_vhost_role_ws ws;
|
||||
#endif
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
### Adding to lws available roles list
|
||||
|
||||
Edit the NULL-terminated array `available_roles` at the top of `./lib/context.c` to include
|
||||
a pointer to your new role's ops struct, following the style already there.
|
||||
|
||||
```
|
||||
const struct lws_role_ops * available_roles[] = {
|
||||
#if defined(LWS_ROLE_H2)
|
||||
&role_ops_h2,
|
||||
#endif
|
||||
...
|
||||
```
|
||||
|
||||
This makes lws aware that your role exists, and it can auto-generate some things like
|
||||
ALPN lists, and call your role ops callbacks for things like hooking vhost creation.
|
||||
|
||||
### Enabling role adoption
|
||||
|
||||
The primary way wsi get bound to a specific role is via the lws adoption api
|
||||
`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./lib/libwebsockets.h`
|
||||
`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`
|
||||
to bind a wsi with suitable flags to your role ops.
|
||||
|
||||
### Implementation of the role
|
||||
|
||||
After that plumbing-in is completed, the role ops you declare are "live" on a wsi
|
||||
bound to them via the adoption api.
|
||||
|
||||
The core support for wsis in lws has some generic concepts
|
||||
|
||||
- the wsi holds a pointer member `role_ops` that indicates which role ops the
|
||||
wsi is bound to
|
||||
|
||||
- the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state
|
||||
|
||||
- role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between
|
||||
client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`
|
||||
and `lwsi_role_server(wsi)`.
|
||||
|
||||
- lws provides around 30 generic states for the wsi starting from 'unconnected' through
|
||||
various proxy or tunnel states, to 'established', and then various states shutting
|
||||
down until 'dead socket'. The states have testable flags and helpers to discover if
|
||||
the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is
|
||||
in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.
|
||||
|
||||
- You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards
|
||||
you can adjust the state using `lwsi_set_state()`.
|
||||
|
|
@ -81,6 +81,7 @@ struct lws_role_ops role_ops_cgi = {
|
|||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ rops_periodic_checks_cgi,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_cgi,
|
||||
|
|
|
@ -25,54 +25,6 @@
|
|||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
static int
|
||||
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m;
|
||||
|
||||
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
|
||||
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
|
||||
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
|
||||
!lwsi_role_client(wsi))
|
||||
return 0;
|
||||
|
||||
// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
*buf += len;
|
||||
return 0;
|
||||
}
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
//lwsl_notice("%s: draining ext\n", __func__);
|
||||
if (lwsi_role_client(wsi))
|
||||
m = lws_ws_client_rx_sm(wsi, 0);
|
||||
else
|
||||
m = lws_ws_rx_sm(wsi, 0, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
/* caller will account for buflist usage */
|
||||
|
||||
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
|
||||
__func__, (int)len);
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We have to take care about parsing because the headers may be split
|
||||
|
@ -115,8 +67,8 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
|||
assert(0);
|
||||
}
|
||||
lwsl_parser("issuing %d bytes to parser\n", (int)len);
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
|
||||
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
#endif
|
||||
last_char = buf;
|
||||
|
@ -248,9 +200,9 @@ postbody_completion:
|
|||
case LRS_SHUTDOWN:
|
||||
|
||||
ws_mode:
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
|
||||
// lwsl_notice("%s: ws_mode\n", __func__);
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS)
|
||||
|
@ -263,7 +215,8 @@ ws_mode:
|
|||
goto bail;
|
||||
}
|
||||
#endif
|
||||
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__, lws_ptr_diff(buf, oldbuf));
|
||||
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
|
||||
// lws_ptr_diff(buf, oldbuf));
|
||||
break;
|
||||
|
||||
case LRS_DEFERRING_ACTION:
|
||||
|
@ -293,14 +246,20 @@ read_ok:
|
|||
|
||||
bail:
|
||||
/*
|
||||
* h2 / h2-ws calls us recursively in lws_read()->lws_h2_parser()->
|
||||
* lws_read() pattern, having stripped the h2 framing in the middle.
|
||||
* h2 / h2-ws calls us recursively in
|
||||
*
|
||||
* lws_read_h1()->
|
||||
* lws_h2_parser()->
|
||||
* lws_read_h1()
|
||||
*
|
||||
* pattern, having stripped the h2 framing in the middle.
|
||||
*
|
||||
* When taking down the whole connection, make sure that only the
|
||||
* outer lws_read() does the wsi close.
|
||||
*/
|
||||
if (!wsi->outer_will_close)
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "lws_read bail");
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"lws_read_h1 bail");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -526,19 +485,6 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
|
||||
/*
|
||||
* we stopped caring about anything except control
|
||||
* packets. Force flow control off, defeat tx
|
||||
* draining.
|
||||
*/
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
if (wsi->ws)
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
}
|
||||
|
||||
if (lws_is_flowcontrolled(wsi))
|
||||
/* We cannot deal with any kind of new RX because we are
|
||||
* RX-flowcontrolled.
|
||||
|
@ -669,6 +615,7 @@ struct lws_role_ops role_ops_h1 = {
|
|||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_h1,
|
||||
|
|
|
@ -1715,7 +1715,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
|
||||
case LWS_H2_FRAME_TYPE_DATA:
|
||||
|
||||
lwsl_notice("%s: LWS_H2_FRAME_TYPE_DATA\n", __func__);
|
||||
lwsl_info("%s: LWS_H2_FRAME_TYPE_DATA\n", __func__);
|
||||
|
||||
/* let the network wsi live a bit longer if subs are active...
|
||||
* our frame may take a long time to chew through */
|
||||
|
@ -1805,7 +1805,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
*/
|
||||
|
||||
n = lws_read_h1(h2n->swsi, in - 1, n);
|
||||
lwsl_notice("%s: lws_read_h1 %d\n", __func__, n);
|
||||
// lwsl_notice("%s: lws_read_h1 %d\n", __func__, n);
|
||||
h2n->swsi->outer_will_close = 0;
|
||||
/*
|
||||
* can return 0 in POST body with
|
||||
|
@ -1819,8 +1819,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
h2n->inside = h2n->length;
|
||||
h2n->count = h2n->length - 1;
|
||||
|
||||
if (n < 0)
|
||||
goto already_closed_swsi;
|
||||
//if (n < 0)
|
||||
// goto already_closed_swsi;
|
||||
goto close_swsi_and_return;
|
||||
}
|
||||
|
||||
|
@ -1983,7 +1983,7 @@ close_swsi_and_return:
|
|||
h2n->frame_state = 0;
|
||||
h2n->count = 0;
|
||||
|
||||
already_closed_swsi:
|
||||
// already_closed_swsi:
|
||||
*inused = in - oldin;
|
||||
|
||||
return 2;
|
||||
|
@ -2144,7 +2144,7 @@ lws_h2_ws_handshake(struct lws *wsi)
|
|||
*/
|
||||
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
wsi->lws_rx_parse_state = 0; // ==LWS_RXPS_NEW;
|
||||
|
||||
uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH);
|
||||
n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH);
|
||||
|
|
|
@ -155,8 +155,10 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
* draining.
|
||||
*/
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws)
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (wsi->http2_substream || wsi->upgraded_to_http2) {
|
||||
|
@ -254,15 +256,12 @@ drain:
|
|||
|
||||
if (ebuf.len) {
|
||||
n = 0;
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY) {
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
|
||||
n = lws_read_h2(wsi, (unsigned char *)ebuf.token,
|
||||
ebuf.len);
|
||||
// lwsl_notice("h2 n = %d\n", n);
|
||||
} else {
|
||||
else
|
||||
n = lws_read_h1(wsi, (unsigned char *)ebuf.token,
|
||||
ebuf.len);
|
||||
// lwsl_notice("h1 n = %d\n", n);
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
/* we closed wsi */
|
||||
|
@ -352,20 +351,23 @@ static int
|
|||
rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol *wp)
|
||||
{
|
||||
unsigned char flags = 0;
|
||||
unsigned char flags = 0, base = (*wp) & 0x1f;
|
||||
int n;
|
||||
|
||||
/* if not in a state to send stuff, then just send nothing */
|
||||
|
||||
if (!lwsi_role_ws(wsi) &&
|
||||
((*wp) & 0x1f) != LWS_WRITE_HTTP &&
|
||||
((*wp) & 0x1f) != LWS_WRITE_HTTP_FINAL &&
|
||||
((*wp) & 0x1f) != LWS_WRITE_HTTP_HEADERS_CONTINUATION &&
|
||||
((*wp) & 0x1f) != LWS_WRITE_HTTP_HEADERS &&
|
||||
base != LWS_WRITE_HTTP &&
|
||||
base != LWS_WRITE_HTTP_FINAL &&
|
||||
base != LWS_WRITE_HTTP_HEADERS_CONTINUATION &&
|
||||
base != LWS_WRITE_HTTP_HEADERS &&
|
||||
((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK) ||
|
||||
((*wp) & 0x1f) != LWS_WRITE_CLOSE)) {
|
||||
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)
|
||||
#if defined(LWS_ROLE_WS)
|
||||
|| base != LWS_WRITE_CLOSE
|
||||
#endif
|
||||
)) {
|
||||
//assert(0);
|
||||
lwsl_notice("binning wsistate 0x%x %d\n", wsi->wsistate, *wp);
|
||||
return 0;
|
||||
|
@ -376,7 +378,7 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
*/
|
||||
|
||||
n = LWS_H2_FRAME_TYPE_DATA;
|
||||
if ((*wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
|
||||
if (base == LWS_WRITE_HTTP_HEADERS) {
|
||||
n = LWS_H2_FRAME_TYPE_HEADERS;
|
||||
if (!((*wp) & LWS_WRITE_NO_FIN))
|
||||
flags = LWS_H2_FLAG_END_HEADERS;
|
||||
|
@ -387,41 +389,35 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
}
|
||||
}
|
||||
|
||||
if ((*wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
|
||||
if (base == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
|
||||
n = LWS_H2_FRAME_TYPE_CONTINUATION;
|
||||
if (!((*wp) & LWS_WRITE_NO_FIN))
|
||||
flags = LWS_H2_FLAG_END_HEADERS;
|
||||
if (wsi->h2.send_END_STREAM ||
|
||||
((*wp) & LWS_WRITE_H2_STREAM_END)) {
|
||||
if (wsi->h2.send_END_STREAM || ((*wp) & LWS_WRITE_H2_STREAM_END)) {
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->h2.send_END_STREAM = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (((*wp & 0x1f) == LWS_WRITE_HTTP ||
|
||||
(*wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->http.tx_content_length) {
|
||||
if ((base == LWS_WRITE_HTTP ||
|
||||
base == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->http.tx_content_length) {
|
||||
wsi->http.tx_content_remain -= len;
|
||||
lwsl_info("%s: wsi %p: tx_content_remain = %llu\n",
|
||||
__func__, wsi,
|
||||
lwsl_info("%s: wsi %p: tx_content_rem = %llu\n", __func__, wsi,
|
||||
(unsigned long long)wsi->http.tx_content_remain);
|
||||
if (!wsi->http.tx_content_remain) {
|
||||
lwsl_info("%s: selecting final write mode\n",
|
||||
__func__);
|
||||
*wp = LWS_WRITE_HTTP_FINAL;
|
||||
lwsl_info("%s: selecting final write mode\n", __func__);
|
||||
base = *wp = LWS_WRITE_HTTP_FINAL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
|
||||
((*wp) & LWS_WRITE_H2_STREAM_END)) {
|
||||
//lws_get_network_wsi(wsi)->h2.END_STREAM) {
|
||||
if (base == LWS_WRITE_HTTP_FINAL || ((*wp) & LWS_WRITE_H2_STREAM_END)) {
|
||||
lwsl_info("%s: setting END_STREAM\n", __func__);
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->h2.send_END_STREAM = 1;
|
||||
}
|
||||
|
||||
return lws_h2_frame_write(wsi, n, flags, wsi->h2.my_sid,
|
||||
(int)len, buf);
|
||||
return lws_h2_frame_write(wsi, n, flags, wsi->h2.my_sid, (int)len, buf);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -471,6 +467,13 @@ static int
|
|||
rops_init_vhost_h2(struct lws_vhost *vh,
|
||||
struct lws_context_creation_info *info)
|
||||
{
|
||||
int n;
|
||||
|
||||
vh->h2.set = vh->context->set;
|
||||
if (info->http2_settings[0])
|
||||
for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
|
||||
vh->h2.set.s[n] = info->http2_settings[n];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -510,7 +513,7 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
|
|||
if (wsi->http2_substream && wsi->h2_stream_carries_ws)
|
||||
lws_h2_rst_stream(wsi, 0, "none");
|
||||
|
||||
if (wsi->h2.parent_wsi) {
|
||||
if (wsi->h2.parent_wsi && lwsl_visible(LLL_INFO)) {
|
||||
lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi,
|
||||
wsi->h2.parent_wsi);
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
|
@ -523,7 +526,7 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
|
|||
if (wsi->upgraded_to_http2 || wsi->http2_substream || wsi->client_h2_substream) {
|
||||
lwsl_info("closing %p: parent %p\n", wsi, wsi->h2.parent_wsi);
|
||||
|
||||
if (wsi->h2.child_list) {
|
||||
if (wsi->h2.child_list && lwsl_visible(LLL_INFO)) {
|
||||
lwsl_info(" parent %p: closing children: list:\n", wsi);
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
wsi->h2.child_list) {
|
||||
|
@ -531,6 +534,8 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
|
|||
(*w)->role_ops ? (*w)->role_ops->name : "?",
|
||||
*w);
|
||||
} lws_end_foreach_llp(w, h2.sibling_list);
|
||||
}
|
||||
if (wsi->h2.child_list) {
|
||||
/* trigger closing of all of our http2 children first */
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
wsi->h2.child_list) {
|
||||
|
@ -732,7 +737,10 @@ static int
|
|||
rops_perform_user_POLLOUT_h2(struct lws *wsi)
|
||||
{
|
||||
struct lws **wsi2, *wsi2a;
|
||||
int write_type = LWS_WRITE_PONG, n;
|
||||
#if defined(LWS_ROLE_WS)
|
||||
int write_type = LWS_WRITE_PONG;
|
||||
#endif
|
||||
int n;
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
|
@ -880,9 +888,11 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)
|
|||
goto next_child;
|
||||
}
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
|
||||
/* Notify peer that we decided to close */
|
||||
|
||||
if (lwsi_state(w) == LRS_WAITING_TO_SEND_CLOSE) {
|
||||
if (lwsi_role_ws(w) && lwsi_state(w) == LRS_WAITING_TO_SEND_CLOSE) {
|
||||
lwsl_debug("sending close packet\n");
|
||||
w->waiting_to_send_close_frame = 0;
|
||||
n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE],
|
||||
|
@ -934,7 +944,7 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)
|
|||
/* otherwise for PING, leave POLLOUT active either way */
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (lws_callback_as_writeable(w)) {
|
||||
lwsl_info("Closing POLLOUT child (end stream %d)\n",
|
||||
w->h2.send_END_STREAM);
|
||||
|
@ -1023,6 +1033,7 @@ struct lws_role_ops role_ops_h2 = {
|
|||
/* check_upgrades */ rops_check_upgrades_h2,
|
||||
/* init_context */ rops_init_context_h2,
|
||||
/* init_vhost */ rops_init_vhost_h2,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_h2,
|
||||
|
|
|
@ -1027,9 +1027,10 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
lws_hdr_simple_ptr(wsi,
|
||||
_WSI_TOKEN_CLIENT_ORIGIN));
|
||||
}
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
if (wsi->do_ws)
|
||||
p = lws_generate_client_ws_handshake(wsi, p);
|
||||
#endif
|
||||
|
||||
/* give userland a chance to append, eg, cookies */
|
||||
|
||||
|
|
|
@ -1519,9 +1519,11 @@ raw_transition:
|
|||
if (!strcasecmp(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_UPGRADE),
|
||||
"websocket")) {
|
||||
#if defined(LWS_ROLE_WS)
|
||||
wsi->vhost->conn_stats.ws_upg++;
|
||||
lwsl_info("Upgrade to ws\n");
|
||||
goto upgrade_ws;
|
||||
#endif
|
||||
}
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (!strcasecmp(lws_hdr_simple_ptr(wsi,
|
||||
|
@ -1601,13 +1603,13 @@ upgrade_h2c:
|
|||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
upgrade_ws:
|
||||
if (lws_process_ws_upgrade(wsi))
|
||||
goto bail_nuke_ah;
|
||||
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
} /* while all chars are handled */
|
||||
|
||||
return 0;
|
||||
|
@ -1871,6 +1873,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
lwsl_notice("OOM trying to get user_space\n");
|
||||
goto bail;
|
||||
}
|
||||
#if defined(LWS_ROLE_WS)
|
||||
if (type & LWS_ADOPT_WS_PARENTIO) {
|
||||
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
|
||||
lwsl_debug("binding to %s\n", new_wsi->protocol->name);
|
||||
|
@ -1887,6 +1890,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
|
||||
return new_wsi;
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
if (type & LWS_ADOPT_HTTP) {/* he will transition later */
|
||||
new_wsi->protocol =
|
||||
|
|
|
@ -155,6 +155,7 @@ struct lws_role_ops role_ops_listen = {
|
|||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_listen,
|
||||
|
|
|
@ -60,6 +60,7 @@ struct lws_role_ops role_ops_pipe = {
|
|||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_pipe,
|
||||
|
|
|
@ -169,6 +169,7 @@ struct lws_role_ops role_ops_raw_skt = {
|
|||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_raw_skt,
|
||||
|
@ -195,6 +196,7 @@ struct lws_role_ops role_ops_raw_file = {
|
|||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_raw_file,
|
||||
|
|
|
@ -37,6 +37,7 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
assert(!c);
|
||||
|
||||
|
@ -46,6 +47,7 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
|
||||
goto drain_extension;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wsi->socket_is_permanently_unusable)
|
||||
return -1;
|
||||
|
@ -310,10 +312,10 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
case LWS_RXPS_WS_FRAME_PAYLOAD:
|
||||
|
||||
assert(wsi->ws->rx_ubuf);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
goto drain_extension;
|
||||
|
||||
#endif
|
||||
if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
|
||||
c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
|
||||
|
||||
|
@ -486,8 +488,8 @@ ping_drop:
|
|||
if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
|
||||
goto already_done;
|
||||
|
||||
drain_extension:
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
drain_extension:
|
||||
lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
|
||||
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
|
||||
|
@ -548,9 +550,9 @@ utf8_fail:
|
|||
|
||||
if (
|
||||
/* coverity says dead code otherwise */
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
//#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
n &&
|
||||
#endif
|
||||
//#endif
|
||||
ebuf.len)
|
||||
/* extension had more... main loop will come back
|
||||
* we want callback to be done with this set, if so,
|
|
@ -61,6 +61,57 @@ lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
int
|
||||
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
|
||||
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
|
||||
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
|
||||
!lwsi_role_client(wsi))
|
||||
return 0;
|
||||
|
||||
// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
*buf += len;
|
||||
return 0;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
int m;
|
||||
|
||||
//lwsl_notice("%s: draining ext\n", __func__);
|
||||
if (lwsi_role_client(wsi))
|
||||
m = lws_ws_client_rx_sm(wsi, 0);
|
||||
else
|
||||
m = lws_ws_rx_sm(wsi, 0, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* caller will account for buflist usage */
|
||||
|
||||
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
|
||||
__func__, (int)len);
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *
|
||||
lws_generate_client_ws_handshake(struct lws *wsi, char *p)
|
||||
{
|
||||
|
|
|
@ -48,6 +48,7 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
|
|||
|
||||
switch (wsi->lws_rx_parse_state) {
|
||||
case LWS_RXPS_NEW:
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
@ -57,6 +58,7 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
|
|||
|
||||
goto drain_extension;
|
||||
}
|
||||
#endif
|
||||
switch (wsi->ws->ietf_spec_revision) {
|
||||
case 13:
|
||||
/*
|
||||
|
@ -385,12 +387,12 @@ handle_first:
|
|||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
|
||||
goto drain_extension;
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
* if there's no protocol max frame size given, we are
|
||||
* supposed to default to context->pt_serv_buf_size
|
||||
|
@ -554,8 +556,9 @@ ping_drop:
|
|||
|
||||
if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
|
||||
goto already_done;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
drain_extension:
|
||||
#endif
|
||||
// lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
|
||||
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
|
||||
|
@ -676,6 +679,7 @@ LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
|
|||
void
|
||||
lws_add_wsi_to_draining_ext_list(struct lws *wsi)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
|
@ -684,15 +688,17 @@ lws_add_wsi_to_draining_ext_list(struct lws *wsi)
|
|||
lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
|
||||
|
||||
wsi->ws->rx_draining_ext = 1;
|
||||
wsi->ws->rx_draining_ext_list = pt->rx_draining_ext_list;
|
||||
pt->rx_draining_ext_list = wsi;
|
||||
wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list;
|
||||
pt->ws.rx_draining_ext_list = wsi;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct lws **w = &pt->rx_draining_ext_list;
|
||||
struct lws **w = &pt->ws.rx_draining_ext_list;
|
||||
|
||||
if (!wsi->ws->rx_draining_ext)
|
||||
return;
|
||||
|
@ -711,6 +717,7 @@ lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
|
|||
w = &((*w)->ws->rx_draining_ext_list);
|
||||
}
|
||||
wsi->ws->rx_draining_ext_list = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_EXTERN void
|
||||
|
@ -801,11 +808,15 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
|
|||
LWS_VISIBLE int
|
||||
lws_is_final_fragment(struct lws *wsi)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
|
||||
wsi->ws->final, (long)wsi->ws->rx_packet_length,
|
||||
(long)wsi->ws->rx_draining_ext);
|
||||
return wsi->ws->final && !wsi->ws->rx_packet_length &&
|
||||
!wsi->ws->rx_draining_ext;
|
||||
#else
|
||||
return wsi->ws->final && !wsi->ws->rx_packet_length;
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
|
@ -933,10 +944,12 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
* draining.
|
||||
*/
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws)
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->tx_draining_ext)
|
||||
/*
|
||||
* We cannot deal with new RX until the TX ext path has
|
||||
|
@ -947,7 +960,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
* blocking.
|
||||
*/
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
#endif
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
/* We cannot deal with any kind of new RX because we are
|
||||
* RX-flowcontrolled.
|
||||
|
@ -969,6 +982,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/* 2: RX Extension needs to be drained
|
||||
*/
|
||||
|
||||
|
@ -995,6 +1009,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
* priority either.
|
||||
*/
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
#endif
|
||||
|
||||
/* 3: buflist needs to be drained
|
||||
*/
|
||||
|
@ -1062,6 +1077,8 @@ read:
|
|||
}
|
||||
// lwsl_notice("Actual RX %d\n", ebuf.len);
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
/*
|
||||
* coverity thinks ssl_capable_read() may read over
|
||||
* 2GB. Dissuade it...
|
||||
|
@ -1157,8 +1174,10 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
#endif
|
||||
int n;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
|
||||
wsi->protocol->name, wsi->ws->tx_draining_ext);
|
||||
#endif
|
||||
|
||||
/* Priority 3: pending control packets (pong or close)
|
||||
*
|
||||
|
@ -1250,6 +1269,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/* Priority 5: Tx path extension with more to send
|
||||
*
|
||||
* These are handled as new fragments each time around
|
||||
|
@ -1267,7 +1287,6 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
|
||||
/* Priority 6: extensions
|
||||
*/
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (!wsi->ws->extension_data_pending)
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
|
@ -1412,6 +1431,7 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
|
|||
static int
|
||||
rops_service_flag_pending_ws(struct lws_context *context, int tsi)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws *wsi;
|
||||
int forced = 0;
|
||||
|
@ -1422,7 +1442,7 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)
|
|||
* 1) For all guys with already-available ext data to drain, if they are
|
||||
* not flowcontrolled, fake their POLLIN status
|
||||
*/
|
||||
wsi = pt->rx_draining_ext_list;
|
||||
wsi = pt->ws.rx_draining_ext_list;
|
||||
while (wsi) {
|
||||
pt->fds[wsi->position_in_fds_table].revents |=
|
||||
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
|
||||
|
@ -1433,6 +1453,9 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)
|
|||
}
|
||||
|
||||
return forced;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1466,8 +1489,9 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
|
|||
static int
|
||||
rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
struct lws **w = &pt->rx_draining_ext_list;
|
||||
struct lws **w = &pt->ws.rx_draining_ext_list;
|
||||
|
||||
wsi->ws->rx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
|
@ -1482,7 +1506,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
|
|||
}
|
||||
|
||||
if (wsi->ws->tx_draining_ext) {
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
struct lws **w = &pt->ws.tx_draining_ext_list;
|
||||
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
|
@ -1495,6 +1519,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
|
|||
}
|
||||
wsi->ws->tx_draining_ext_list = NULL;
|
||||
}
|
||||
#endif
|
||||
lws_free_set_NULL(wsi->ws->rx_ubuf);
|
||||
|
||||
if (wsi->trunc_alloc)
|
||||
|
@ -1504,6 +1529,11 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
|
|||
wsi->ws->ping_payload_len = 0;
|
||||
wsi->ws->ping_pending_flag = 0;
|
||||
|
||||
/* deallocate any active extension contexts */
|
||||
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
|
||||
lwsl_warn("extension destruction failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1511,20 +1541,22 @@ static int
|
|||
rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol *wp)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
enum lws_write_protocol wpt;
|
||||
#endif
|
||||
int masked7 = lwsi_role_client(wsi);
|
||||
unsigned char is_masked_bit = 0;
|
||||
unsigned char *dropmask = NULL;
|
||||
enum lws_write_protocol wpt;
|
||||
struct lws_tokens ebuf;
|
||||
size_t orig_len = len;
|
||||
int pre = 0, n = 0;
|
||||
|
||||
// lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->tx_draining_ext) {
|
||||
/* remove us from the list */
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
struct lws **w = &pt->ws.tx_draining_ext_list;
|
||||
|
||||
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
|
@ -1555,7 +1587,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
wsi->ws->tx_draining_stashed_wp, wpt);
|
||||
// assert(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
if (((*wp) & 0x1f) == LWS_WRITE_HTTP ||
|
||||
|
@ -1610,8 +1642,8 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
|
||||
/* extension requires further draining */
|
||||
wsi->ws->tx_draining_ext = 1;
|
||||
wsi->ws->tx_draining_ext_list = pt->tx_draining_ext_list;
|
||||
pt->tx_draining_ext_list = wsi;
|
||||
wsi->ws->tx_draining_ext_list = pt->ws.tx_draining_ext_list;
|
||||
pt->ws.tx_draining_ext_list = wsi;
|
||||
/* we must come back to do more */
|
||||
lws_callback_on_writable(wsi);
|
||||
/*
|
||||
|
@ -1861,12 +1893,77 @@ rops_callback_on_writable_ws(struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_init_vhost_ws(struct lws_vhost *vh, struct lws_context_creation_info *info)
|
||||
{
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
struct lws_plugin *plugin = vh->context->plugin_list;
|
||||
int m;
|
||||
|
||||
if (vh->context->plugin_extension_count) {
|
||||
|
||||
m = 0;
|
||||
while (info->extensions && info->extensions[m].callback)
|
||||
m++;
|
||||
|
||||
/*
|
||||
* give the vhost a unified list of extensions including the
|
||||
* ones that came from plugins
|
||||
*/
|
||||
vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
|
||||
(m + vh->context->plugin_extension_count + 1),
|
||||
"extensions");
|
||||
if (!vh->ws.extensions)
|
||||
return 1;
|
||||
|
||||
memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
|
||||
sizeof(struct lws_extension) * m);
|
||||
plugin = vh->context->plugin_list;
|
||||
while (plugin) {
|
||||
memcpy((struct lws_extension *)&vh->ws.extensions[m],
|
||||
plugin->caps.extensions,
|
||||
sizeof(struct lws_extension) *
|
||||
plugin->caps.count_extensions);
|
||||
m += plugin->caps.count_extensions;
|
||||
plugin = plugin->list;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
vh->ws.extensions = info->extensions;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_destroy_vhost_ws(struct lws_vhost *vh)
|
||||
{
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (vh->context->plugin_extension_count)
|
||||
lws_free((void *)vh->ws.extensions);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_destroy_role_ws(struct lws *wsi)
|
||||
{
|
||||
lws_free_set_NULL(wsi->ws);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_ws = {
|
||||
/* role name */ "ws",
|
||||
/* alpn id */ NULL,
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* init_vhost */ rops_init_vhost_ws,
|
||||
/* destroy_vhost */ rops_destroy_vhost_ws,
|
||||
/* periodic_checks */ rops_periodic_checks_ws,
|
||||
/* service_flag_pending */ rops_service_flag_pending_ws,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_ws,
|
||||
|
@ -1880,7 +1977,7 @@ struct lws_role_ops role_ops_ws = {
|
|||
/* close_via_role_protocol */ rops_close_via_role_protocol_ws,
|
||||
/* close_role */ rops_close_role_ws,
|
||||
/* close_kill_connection */ rops_close_kill_connection_ws,
|
||||
/* destroy_role */ NULL,
|
||||
/* destroy_role */ rops_destroy_role_ws,
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE,
|
||||
LWS_CALLBACK_SERVER_WRITEABLE },
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED,
|
||||
|
|
|
@ -73,20 +73,25 @@ enum lws_websocket_opcodes_07 {
|
|||
#define ALREADY_PROCESSED_IGNORE_CHAR 1
|
||||
#define ALREADY_PROCESSED_NO_CB 2
|
||||
|
||||
struct lws_vhost_role_ws {
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_vhost_role_ws {
|
||||
const struct lws_extension *extensions;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lws_pt_role_ws {
|
||||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct _lws_websocket_related {
|
||||
char *rx_ubuf;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
#endif
|
||||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
#endif
|
||||
/* Also used for close content... control opcode == < 128 */
|
||||
uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
|
||||
uint8_t mask[4];
|
||||
|
@ -120,20 +125,22 @@ struct _lws_websocket_related {
|
|||
unsigned int owed_a_fin:1;
|
||||
unsigned int check_utf8:1;
|
||||
unsigned int defeat_check_utf8:1;
|
||||
unsigned int pmce_compressed_message:1;
|
||||
unsigned int stashed_write_pending:1;
|
||||
unsigned int rx_draining_ext:1;
|
||||
unsigned int tx_draining_ext:1;
|
||||
unsigned int send_check_ping:1;
|
||||
unsigned int first_fragment:1;
|
||||
unsigned int peer_has_sent_close:1;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
unsigned int extension_data_pending:1;
|
||||
unsigned int rx_draining_ext:1;
|
||||
unsigned int tx_draining_ext:1;
|
||||
|
||||
uint8_t count_act_ext;
|
||||
#endif
|
||||
};
|
||||
|
||||
int
|
||||
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
LWS_VISIBLE void
|
||||
lws_context_init_extensions(struct lws_context_creation_info *info,
|
||||
|
|
|
@ -736,8 +736,10 @@ utf8_fail:
|
|||
|
||||
wsi->ws->first_fragment = 0;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
|
||||
__func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
|
||||
#endif
|
||||
|
||||
return avail; /* how much we used from the input */
|
||||
}
|
||||
|
@ -765,7 +767,7 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
*buf += len; /* stashing it is taking care of it */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
lwsl_debug("%s: draining rx ext\n", __func__);
|
||||
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
|
||||
|
@ -773,6 +775,7 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
return -1;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* consume payload bytes efficiently */
|
||||
while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
|
||||
|
@ -807,10 +810,12 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
* We already handled this byte in bulk, just deal
|
||||
* with the ramifications
|
||||
*/
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_debug("%s: coming out of bulk with len %d, "
|
||||
"wsi->ws->rx_draining_ext %d\n",
|
||||
__func__, (int)len,
|
||||
wsi->ws->rx_draining_ext);
|
||||
#endif
|
||||
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
|
||||
ALREADY_PROCESSED_NO_CB, 0);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,6 @@ lws_callback_as_writeable(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
assert(!(lwsi_role_ws(wsi) && wsi->ws->tx_draining_ext));
|
||||
|
||||
n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
|
||||
|
||||
m = user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
|
@ -319,9 +317,9 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
|
|||
* We only need to wait if really nothing already to do and we have
|
||||
* to wait for something from network
|
||||
*/
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/* 1) if we know we are draining rx ext, do not wait in poll */
|
||||
if (pt->rx_draining_ext_list)
|
||||
if (pt->ws.rx_draining_ext_list)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -255,8 +255,6 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
|||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
// lwsl_hexdump_err(buf, n);
|
||||
|
||||
/*
|
||||
|
|
|
@ -63,8 +63,8 @@ MACRO(require_lws_config reqconfig _val result)
|
|||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
|
||||
require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
|
|
@ -64,7 +64,7 @@ ENDMACRO()
|
|||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
|
||||
require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
|
||||
#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
@ -75,4 +75,4 @@ if (requirements)
|
|||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -109,9 +109,9 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
case LWS_CALLBACK_RECEIVE:
|
||||
if (len < 6)
|
||||
break;
|
||||
if (strcmp((const char *)in, "reset\n") == 0)
|
||||
if (strncmp((const char *)in, "reset\n", 6) == 0)
|
||||
pss->number = 0;
|
||||
if (strcmp((const char *)in, "closeme\n") == 0) {
|
||||
if (strncmp((const char *)in, "closeme\n", 8) == 0) {
|
||||
lwsl_notice("dumb_inc: closing as requested\n");
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
|
||||
(unsigned char *)"seeya", 5);
|
||||
|
|
|
@ -1,616 +0,0 @@
|
|||
/*
|
||||
* lws meta protocol handler
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined (LWS_PLUGIN_STATIC)
|
||||
#define LWS_DLL
|
||||
#define LWS_INTERNAL
|
||||
#include "../lib/libwebsockets.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_SUBCHANNELS 8
|
||||
|
||||
enum lws_meta_parser_state {
|
||||
MP_IDLE, /* in body of message */
|
||||
|
||||
MP_CMD, /* await cmd */
|
||||
|
||||
MP_OPEN_SUBCHANNEL_PROTOCOL,
|
||||
MP_OPEN_SUBCHANNEL_URL,
|
||||
MP_OPEN_SUBCHANNEL_COOKIE,
|
||||
|
||||
MP_CLOSE_CHID,
|
||||
MP_CLOSE_LEN,
|
||||
MP_CLOSE_CODEM,
|
||||
MP_CLOSE_CODEL,
|
||||
MP_CLOSE_PAYLOAD,
|
||||
|
||||
MP_WRITE_CHID,
|
||||
};
|
||||
|
||||
enum {
|
||||
PENDING_TYPE_OPEN_RESULT = 0,
|
||||
PENDING_TYPE_CHILD_CLOSE
|
||||
};
|
||||
|
||||
/*
|
||||
* while we haven't reported the result yet, we keep a linked-list of
|
||||
* connection opens and their result.
|
||||
*/
|
||||
struct pending_conn {
|
||||
struct pending_conn *next;
|
||||
char protocol[123];
|
||||
char cookie[8];
|
||||
int ch;
|
||||
int len;
|
||||
|
||||
unsigned char type;
|
||||
};
|
||||
|
||||
/*
|
||||
* the parent, lws-meta connection
|
||||
*/
|
||||
struct per_session_data__lws_meta {
|
||||
struct lws *wsi[MAX_SUBCHANNELS + 1];
|
||||
char told_closing[MAX_SUBCHANNELS + 1];
|
||||
struct pending_conn *first;
|
||||
struct pending_conn *pend;
|
||||
char suburl[64];
|
||||
unsigned char close[126];
|
||||
int active_subchannel_tx, active_subchannel_rx;
|
||||
enum lws_meta_parser_state state;
|
||||
int pos;
|
||||
int count_pending;
|
||||
int round_robin;
|
||||
int close_status_16;
|
||||
int close_len;
|
||||
int which_close;
|
||||
int ch;
|
||||
};
|
||||
|
||||
static int
|
||||
lws_find_free_channel(struct per_session_data__lws_meta *pss)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 1; n <= MAX_SUBCHANNELS; n++)
|
||||
if (pss->wsi[n] == NULL)
|
||||
return n;
|
||||
|
||||
return 0; /* none free */
|
||||
}
|
||||
|
||||
static struct lws *
|
||||
lws_get_channel_wsi(struct per_session_data__lws_meta *pss, int ch)
|
||||
{
|
||||
if (!ch)
|
||||
return 0;
|
||||
return pss->wsi[ch];
|
||||
}
|
||||
|
||||
static int
|
||||
lws_get_channel_id(struct lws *wsi)
|
||||
{
|
||||
return (int)(lws_intptr_t)lws_get_opaque_parent_data(wsi);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_set_channel_id(struct lws *wsi, int id)
|
||||
{
|
||||
lws_set_opaque_parent_data(wsi, (void *)(lws_intptr_t)id);
|
||||
}
|
||||
|
||||
static struct pending_conn *
|
||||
new_pending(struct per_session_data__lws_meta *pss)
|
||||
{
|
||||
struct pending_conn *pend;
|
||||
|
||||
if (pss->count_pending >= MAX_SUBCHANNELS * 2) {
|
||||
lwsl_notice("too many pending open subchannel\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pss->count_pending++;
|
||||
|
||||
pend = malloc(sizeof(*pend));
|
||||
if (!pend) {
|
||||
lwsl_notice("OOM\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(pend, 0, sizeof(*pend));
|
||||
|
||||
return pend;
|
||||
}
|
||||
|
||||
static int
|
||||
callback_lws_meta(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct per_session_data__lws_meta *pss =
|
||||
(struct per_session_data__lws_meta *)user;
|
||||
struct lws_write_passthru *pas;
|
||||
struct pending_conn *pend, *pend1;
|
||||
struct lws *cwsi;
|
||||
lws_sock_file_fd_type fd;
|
||||
unsigned char *bin, buf[LWS_PRE + 512], *start = &buf[LWS_PRE],
|
||||
*end = &buf[sizeof(buf) - 1], *p = start;
|
||||
int n, m;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
|
||||
pss->state = MP_CMD;
|
||||
pss->pos = 0;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CHILD_CLOSING:
|
||||
cwsi = (struct lws *)in;
|
||||
|
||||
/* remove it from our tracking */
|
||||
pss->wsi[lws_get_channel_id(cwsi)] = NULL;
|
||||
|
||||
if (pss->told_closing[lws_get_channel_id(cwsi)]) {
|
||||
pss->told_closing[lws_get_channel_id(cwsi)] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pend = new_pending(pss);
|
||||
if (!pend)
|
||||
return -1;
|
||||
|
||||
/* note which channel id */
|
||||
pend->ch = lws_get_channel_id(cwsi);
|
||||
|
||||
if (lws_get_close_length(cwsi)) {
|
||||
pend->len = lws_get_close_length(cwsi);
|
||||
memcpy(pend->protocol, lws_get_close_payload(cwsi),
|
||||
pend->len);
|
||||
}
|
||||
|
||||
pend->type = PENDING_TYPE_CHILD_CLOSE;
|
||||
pend->next = pss->first;
|
||||
pss->first = pend;
|
||||
|
||||
/*
|
||||
* nothing else will complete from this wsi, so abandon
|
||||
* tracking in-process messages from this wsi.
|
||||
*/
|
||||
|
||||
if (pss->active_subchannel_tx == pend->ch)
|
||||
pss->active_subchannel_tx = 0;
|
||||
|
||||
if (pss->active_subchannel_rx == pend->ch)
|
||||
pss->active_subchannel_rx = 0;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
|
||||
if (!pss->active_subchannel_tx) {
|
||||
|
||||
/* not in the middle of a message...
|
||||
*
|
||||
* PRIORITY 1: pending open and close notifications
|
||||
*/
|
||||
|
||||
pend = pss->first;
|
||||
while (pend && p < end - 128) {
|
||||
switch (pend->type) {
|
||||
case PENDING_TYPE_OPEN_RESULT:
|
||||
lwsl_debug("open result %s %s\n",
|
||||
pend->cookie, pend->protocol);
|
||||
*p++ = LWS_META_CMD_OPEN_RESULT;
|
||||
memcpy(p, pend->cookie,
|
||||
strlen(pend->cookie) + 1);
|
||||
p += strlen(pend->cookie) + 1;
|
||||
*p++ = LWS_META_TRANSPORT_OFFSET +
|
||||
pend->ch;
|
||||
memcpy(p, pend->protocol,
|
||||
strlen(pend->protocol) + 1);
|
||||
p += strlen(pend->protocol) + 1;
|
||||
break;
|
||||
case PENDING_TYPE_CHILD_CLOSE:
|
||||
*p++ = LWS_META_CMD_CLOSE_NOTIFY;
|
||||
*p++ = LWS_META_TRANSPORT_OFFSET +
|
||||
pend->ch;
|
||||
for (n = 0; n < pend->len; n++)
|
||||
*p++ = pend->protocol[n];
|
||||
break;
|
||||
}
|
||||
|
||||
pss->count_pending--;
|
||||
pend1 = pend;
|
||||
pend = pend->next;
|
||||
free(pend1);
|
||||
pss->first = pend;
|
||||
}
|
||||
|
||||
if (p != start) {
|
||||
if (lws_write(wsi, start, p - start,
|
||||
LWS_WRITE_BINARY) < 0)
|
||||
return 1;
|
||||
if (pend) /* still more */
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
|
||||
/* PRIORITY 2: pick a child for the writable callback */
|
||||
|
||||
cwsi = NULL;
|
||||
for (n = 0; n < MAX_SUBCHANNELS; n++) {
|
||||
m = ((pss->round_robin + n) % MAX_SUBCHANNELS) + 1;
|
||||
if (pss->wsi[m] &&
|
||||
lws_get_child_pending_on_writable(pss->wsi[m])) {
|
||||
pss->round_robin = m;
|
||||
cwsi = pss->wsi[m];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
/* one child is in middle of message, stay with it */
|
||||
cwsi = pss->wsi[pss->active_subchannel_tx];
|
||||
|
||||
if (!cwsi)
|
||||
break;
|
||||
|
||||
lws_clear_child_pending_on_writable(cwsi);
|
||||
if (lws_handle_POLLOUT_event(cwsi, NULL))
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
bin = (unsigned char *)in;
|
||||
|
||||
/*
|
||||
* at the start of a message, we may have one or more
|
||||
* lws_meta command blocks.
|
||||
*/
|
||||
while (pss->state != MP_IDLE &&
|
||||
(unsigned int)(bin - (unsigned char *)in) < len) {
|
||||
|
||||
switch (pss->state) {
|
||||
case MP_IDLE: /* in body of message */
|
||||
|
||||
if (!lws_is_first_fragment(wsi))
|
||||
break;
|
||||
|
||||
pss->state = MP_CMD;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case MP_CMD: /* await cmd */
|
||||
|
||||
pss->pos = 0;
|
||||
|
||||
switch (*bin++) {
|
||||
case LWS_META_CMD_OPEN_SUBCHANNEL:
|
||||
|
||||
pss->pend = new_pending(pss);
|
||||
if (!pss->pend)
|
||||
return -1;
|
||||
|
||||
pss->state = MP_OPEN_SUBCHANNEL_PROTOCOL;
|
||||
|
||||
break;
|
||||
case LWS_META_CMD_CLOSE_NOTIFY:
|
||||
case LWS_META_CMD_CLOSE_RQ:
|
||||
pss->which_close = bin[-1];
|
||||
pss->state = MP_CLOSE_CHID;
|
||||
break;
|
||||
case LWS_META_CMD_WRITE:
|
||||
pss->state = MP_WRITE_CHID;
|
||||
break;
|
||||
|
||||
// open result is also illegal to receive
|
||||
default:
|
||||
lwsl_notice("bad lws_meta cmd 0x%x\n",
|
||||
bin[-1]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MP_OPEN_SUBCHANNEL_PROTOCOL:
|
||||
pss->pend->protocol[pss->pos++] = *bin++;
|
||||
if (pss->pos == sizeof(pss->pend->protocol) - 1) {
|
||||
lwsl_notice("protocol name too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bin[-1] != '\0')
|
||||
break;
|
||||
|
||||
pss->state = MP_OPEN_SUBCHANNEL_URL;
|
||||
pss->pos = 0;
|
||||
break;
|
||||
|
||||
case MP_OPEN_SUBCHANNEL_URL:
|
||||
pss->suburl[pss->pos++] = *bin++;
|
||||
if (pss->pos == sizeof(pss->suburl) - 1) {
|
||||
lwsl_notice("suburl too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bin[-1] != '\0')
|
||||
break;
|
||||
|
||||
pss->state = MP_OPEN_SUBCHANNEL_COOKIE;
|
||||
pss->pos = 0;
|
||||
break;
|
||||
|
||||
case MP_OPEN_SUBCHANNEL_COOKIE:
|
||||
pss->pend->cookie[pss->pos++] = *bin++;
|
||||
if (pss->pos == sizeof(pss->pend->cookie) - 1) {
|
||||
lwsl_notice("cookie too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bin[-1] != '\0')
|
||||
break;
|
||||
|
||||
lwsl_debug("%s: %s / %s / %s\n", __func__,
|
||||
pss->pend->protocol,
|
||||
pss->suburl,
|
||||
pss->pend->cookie);
|
||||
|
||||
pss->pend->ch = lws_find_free_channel(pss);
|
||||
if (pss->pend->ch) {
|
||||
|
||||
fd.sockfd = 0; // not going to be used
|
||||
|
||||
cwsi = lws_adopt_descriptor_vhost(
|
||||
lws_get_vhost(wsi),
|
||||
LWS_ADOPT_WS_PARENTIO,
|
||||
fd, pss->pend->protocol,
|
||||
wsi);
|
||||
|
||||
if (!cwsi) {
|
||||
lwsl_notice("open failed\n");
|
||||
pss->pend->ch = 0;
|
||||
} else {
|
||||
pss->wsi[pss->pend->ch] = cwsi;
|
||||
lws_set_channel_id(cwsi,
|
||||
pss->pend->ch);
|
||||
lwsl_debug("cwsi %p on parent %p open OK %s\n",
|
||||
cwsi, wsi, pss->pend->protocol);
|
||||
}
|
||||
|
||||
} else
|
||||
lwsl_notice("no free subchannels\n");
|
||||
|
||||
pss->pend->type = PENDING_TYPE_OPEN_RESULT;
|
||||
pss->pend->next = pss->first;
|
||||
pss->first = pss->pend;
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
pss->state = MP_CMD;
|
||||
pss->pos = 0;
|
||||
break;
|
||||
|
||||
case MP_CLOSE_CHID:
|
||||
pss->ch = (*bin++) - LWS_META_TRANSPORT_OFFSET;
|
||||
pss->state = MP_CLOSE_LEN;
|
||||
pss->pos = 0;
|
||||
break;
|
||||
case MP_CLOSE_LEN:
|
||||
pss->close_len = (*bin++) -
|
||||
LWS_META_TRANSPORT_OFFSET;
|
||||
lwsl_debug("close len %d\n", pss->close_len);
|
||||
pss->state = MP_CLOSE_CODEM;
|
||||
pss->pos = 0;
|
||||
break;
|
||||
case MP_CLOSE_CODEM:
|
||||
pss->close[pss->pos++] = *bin;
|
||||
pss->close_status_16 = (*bin++) * 256;
|
||||
pss->state = MP_CLOSE_CODEL;
|
||||
break;
|
||||
case MP_CLOSE_CODEL:
|
||||
pss->close[pss->pos++] = *bin;
|
||||
pss->close_status_16 |= *bin++;
|
||||
pss->state = MP_CLOSE_PAYLOAD;
|
||||
break;
|
||||
case MP_CLOSE_PAYLOAD:
|
||||
pss->close[pss->pos++] = *bin++;
|
||||
if (pss->pos == sizeof(pss->close) - 1) {
|
||||
lwsl_notice("close payload too long\n");
|
||||
return -1;
|
||||
}
|
||||
if (--pss->close_len)
|
||||
break;
|
||||
|
||||
pss->state = MP_CMD;
|
||||
|
||||
cwsi = lws_get_channel_wsi(pss, pss->ch);
|
||||
if (!cwsi) {
|
||||
lwsl_notice("close (%d) bad ch %d\n",
|
||||
pss->which_close, pss->ch);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pss->which_close == LWS_META_CMD_CLOSE_RQ) {
|
||||
if (lws_get_protocol(cwsi)->callback(
|
||||
cwsi,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
|
||||
lws_wsi_user(cwsi), &pss->close,
|
||||
pss->pos))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* we need to echo back the close payload
|
||||
* when we send the close notification
|
||||
*/
|
||||
lws_close_reason(cwsi,
|
||||
pss->close_status_16,
|
||||
&pss->close[2],
|
||||
pss->pos - 2);
|
||||
}
|
||||
|
||||
/* so force him closed */
|
||||
|
||||
lws_set_timeout(cwsi,
|
||||
PENDING_TIMEOUT_KILLED_BY_PARENT,
|
||||
LWS_TO_KILL_SYNC);
|
||||
break;
|
||||
|
||||
case MP_WRITE_CHID:
|
||||
pss->active_subchannel_rx = (*bin++) -
|
||||
LWS_META_TRANSPORT_OFFSET;
|
||||
pss->state = MP_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
len -= bin - (unsigned char *)in;
|
||||
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
cwsi = lws_get_channel_wsi(pss, pss->active_subchannel_rx);
|
||||
if (!cwsi) {
|
||||
lwsl_notice("bad ch %d\n", pss->active_subchannel_rx);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// lwsl_debug("%s: RX len %d\n", __func__, (int)len);
|
||||
|
||||
if (lws_get_protocol(cwsi)->callback(cwsi,
|
||||
LWS_CALLBACK_RECEIVE,
|
||||
lws_wsi_user(cwsi), bin, len))
|
||||
lws_set_timeout(cwsi,
|
||||
PENDING_TIMEOUT_KILLED_BY_PARENT,
|
||||
LWS_TO_KILL_SYNC);
|
||||
|
||||
if (lws_is_final_fragment(wsi)) {
|
||||
pss->active_subchannel_rx = 0;
|
||||
pss->state = MP_CMD;
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* child wrote something via lws_write.... which passed it up to us to
|
||||
* deal with, because we are the parent. Prepend two bytes for
|
||||
* lws-meta command and channel index, and send it out on parent
|
||||
*/
|
||||
case LWS_CALLBACK_CHILD_WRITE_VIA_PARENT:
|
||||
pas = in;
|
||||
bin = ((unsigned char *)pas->buf);
|
||||
|
||||
if ((pas->wp & 7) == 4 /*LWS_WRITE_CLOSE */) {
|
||||
*p++ = LWS_META_CMD_CLOSE_NOTIFY;
|
||||
*p++ = LWS_META_TRANSPORT_OFFSET +
|
||||
lws_get_channel_id(pas->wsi);
|
||||
*p++ = (unsigned char)pas->len +
|
||||
LWS_META_TRANSPORT_OFFSET - 2;
|
||||
*p++ = *bin++;
|
||||
*p++ = *bin++;
|
||||
for (n = 0; n < (int)pas->len - 2; n++)
|
||||
*p++ = bin[n];
|
||||
|
||||
if (lws_write(wsi, start, p - start,
|
||||
LWS_WRITE_BINARY) < 0)
|
||||
return 1;
|
||||
|
||||
pss->told_closing[lws_get_channel_id(pas->wsi)] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pas->wp & 7) == LWS_WRITE_TEXT ||
|
||||
(pas->wp & 7) == LWS_WRITE_BINARY) {
|
||||
|
||||
if (pas->wp & LWS_WRITE_NO_FIN)
|
||||
pss->active_subchannel_tx =
|
||||
lws_get_channel_id(pas->wsi);
|
||||
|
||||
/* start of message, prepend the subchannel id */
|
||||
|
||||
bin -= 2;
|
||||
bin[0] = LWS_META_CMD_WRITE;
|
||||
bin[1] = lws_get_channel_id(pas->wsi) +
|
||||
LWS_META_TRANSPORT_OFFSET;
|
||||
if (lws_write(wsi, bin, pas->len + 2, pas->wp) < 0)
|
||||
return 1;
|
||||
} else
|
||||
if (lws_write(wsi, bin, pas->len, pas->wp) < 0)
|
||||
return 1;
|
||||
|
||||
/* track EOM */
|
||||
|
||||
if (!(pas->wp & LWS_WRITE_NO_FIN))
|
||||
pss->active_subchannel_tx = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LWS_PLUGIN_PROTOCOL_LWS_META { \
|
||||
"lws-meta", \
|
||||
callback_lws_meta, \
|
||||
sizeof(struct per_session_data__lws_meta), \
|
||||
1024, /* rx buf size must be >= permessage-deflate rx size */ \
|
||||
0, NULL, 0 \
|
||||
}
|
||||
|
||||
#if !defined (LWS_PLUGIN_STATIC)
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
LWS_PLUGIN_PROTOCOL_LWS_META
|
||||
};
|
||||
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
init_protocol_lws_meta(struct lws_context *context,
|
||||
struct lws_plugin_capability *c)
|
||||
{
|
||||
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
|
||||
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
|
||||
c->api_magic);
|
||||
return 1;
|
||||
}
|
||||
|
||||
c->protocols = protocols;
|
||||
c->count_protocols = ARRAY_SIZE(protocols);
|
||||
c->extensions = NULL;
|
||||
c->count_extensions = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
destroy_protocol_lws_meta(struct lws_context *context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -1,373 +0,0 @@
|
|||
/*
|
||||
* libwebsockets-test-fraggle - random fragmentation test
|
||||
*
|
||||
* Copyright (C) 2011-2016 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* The person who associated a work with this deed has dedicated
|
||||
* the work to the public domain by waiving all of his or her rights
|
||||
* to the work worldwide under copyright law, including all related
|
||||
* and neighboring rights, to the extent allowed by law. You can copy,
|
||||
* modify, distribute and perform the work, even for commercial purposes,
|
||||
* all without asking permission.
|
||||
*
|
||||
* The test apps are intended to be adapted for use in your code, which
|
||||
* may be proprietary. So unlike the library itself, they are licensed
|
||||
* Public Domain.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include "../lib/libwebsockets.h"
|
||||
|
||||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
|
||||
static int client;
|
||||
static int terminate;
|
||||
|
||||
enum demo_protocols {
|
||||
PROTOCOL_FRAGGLE,
|
||||
|
||||
/* always last */
|
||||
DEMO_PROTOCOL_COUNT
|
||||
};
|
||||
|
||||
/* fraggle protocol */
|
||||
|
||||
enum fraggle_states {
|
||||
FRAGSTATE_START_MESSAGE,
|
||||
FRAGSTATE_RANDOM_PAYLOAD,
|
||||
FRAGSTATE_POST_PAYLOAD_SUM,
|
||||
};
|
||||
|
||||
struct per_session_data__fraggle {
|
||||
int packets_left;
|
||||
int total_message;
|
||||
unsigned long sum;
|
||||
enum fraggle_states state;
|
||||
};
|
||||
|
||||
static int
|
||||
callback_fraggle(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
unsigned char buf[LWS_PRE + 8000], *bp = &buf[LWS_PRE];
|
||||
struct per_session_data__fraggle *psf = user;
|
||||
unsigned char *p = (unsigned char *)in;
|
||||
int n, chunk, flags, ran;
|
||||
unsigned long sum;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
|
||||
fprintf(stderr, "server sees client connect\n");
|
||||
psf->state = FRAGSTATE_START_MESSAGE;
|
||||
/* start the ball rolling */
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
|
||||
fprintf(stderr, "client connects to server\n");
|
||||
psf->state = FRAGSTATE_START_MESSAGE;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
|
||||
switch (psf->state) {
|
||||
|
||||
case FRAGSTATE_START_MESSAGE:
|
||||
|
||||
psf->state = FRAGSTATE_RANDOM_PAYLOAD;
|
||||
psf->sum = 0;
|
||||
psf->total_message = 0;
|
||||
psf->packets_left = 0;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case FRAGSTATE_RANDOM_PAYLOAD:
|
||||
|
||||
for (n = 0; (unsigned int)n < len; n++)
|
||||
psf->sum += p[n];
|
||||
|
||||
psf->total_message += (int)len;
|
||||
psf->packets_left++;
|
||||
|
||||
if (lws_is_final_fragment(wsi))
|
||||
psf->state = FRAGSTATE_POST_PAYLOAD_SUM;
|
||||
break;
|
||||
|
||||
case FRAGSTATE_POST_PAYLOAD_SUM:
|
||||
|
||||
sum = ((unsigned int)p[0]) << 24;
|
||||
sum |= p[1] << 16;
|
||||
sum |= p[2] << 8;
|
||||
sum |= p[3];
|
||||
if (sum == psf->sum)
|
||||
fprintf(stderr, "EOM received %d correctly "
|
||||
"from %d fragments\n",
|
||||
psf->total_message, psf->packets_left);
|
||||
else
|
||||
fprintf(stderr, "**** ERROR at EOM: "
|
||||
"length %d, rx sum = 0x%lX, "
|
||||
"server says it sent 0x%lX\n",
|
||||
psf->total_message, psf->sum, sum);
|
||||
|
||||
psf->state = FRAGSTATE_START_MESSAGE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
|
||||
switch (psf->state) {
|
||||
|
||||
case FRAGSTATE_START_MESSAGE:
|
||||
lws_get_random(lws_get_context(wsi), &ran, sizeof(ran));
|
||||
psf->packets_left = (ran & 1023) + 1;
|
||||
fprintf(stderr, "Spamming %d random fragments\n",
|
||||
psf->packets_left);
|
||||
psf->sum = 0;
|
||||
psf->total_message = 0;
|
||||
psf->state = FRAGSTATE_RANDOM_PAYLOAD;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case FRAGSTATE_RANDOM_PAYLOAD:
|
||||
|
||||
/*
|
||||
* note how one chunk can be 8000, but we use the
|
||||
* default rx buffer size of 4096, so we exercise the
|
||||
* code for rx spill because the rx buffer is full
|
||||
*/
|
||||
|
||||
lws_get_random(lws_get_context(wsi), &ran, sizeof(ran));
|
||||
chunk = (ran & 511) + 1;
|
||||
psf->total_message += chunk;
|
||||
|
||||
lws_get_random(lws_get_context(wsi), bp, chunk);
|
||||
for (n = 0; n < chunk; n++)
|
||||
psf->sum += bp[n];
|
||||
|
||||
psf->packets_left--;
|
||||
flags = lws_write_ws_flags(LWS_WRITE_BINARY, !psf->sum,
|
||||
!psf->packets_left);
|
||||
if (!psf->packets_left)
|
||||
psf->state = FRAGSTATE_POST_PAYLOAD_SUM;
|
||||
|
||||
n = lws_write(wsi, bp, chunk, flags);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case FRAGSTATE_POST_PAYLOAD_SUM:
|
||||
|
||||
fprintf(stderr, "Spamming session over, "
|
||||
"len = %d. sum = 0x%lX\n",
|
||||
psf->total_message, psf->sum);
|
||||
|
||||
bp[0] = psf->sum >> 24;
|
||||
bp[1] = (unsigned char)(psf->sum >> 16);
|
||||
bp[2] = (unsigned char)(psf->sum >> 8);
|
||||
bp[3] = (unsigned char)psf->sum;
|
||||
|
||||
n = lws_write(wsi, (unsigned char *)bp,
|
||||
4, LWS_WRITE_BINARY);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
if (n < 4) {
|
||||
lwsl_err("Partial write\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
psf->state = FRAGSTATE_START_MESSAGE;
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
|
||||
terminate = 1;
|
||||
break;
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
|
||||
if (strcmp(in, "deflate-stream") == 0) {
|
||||
fprintf(stderr, "denied deflate-stream extension\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* list of supported protocols and callbacks */
|
||||
|
||||
static struct lws_protocols protocols[] = {
|
||||
{
|
||||
"fraggle-protocol",
|
||||
callback_fraggle,
|
||||
sizeof(struct per_session_data__fraggle),
|
||||
},
|
||||
{
|
||||
NULL, NULL, 0 /* End of list */
|
||||
}
|
||||
};
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate; client_no_context_takeover; client_max_window_bits"
|
||||
},
|
||||
{
|
||||
"deflate-frame",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"deflate_frame"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
static struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "debug", required_argument, NULL, 'd' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "ssl", no_argument, NULL, 's' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "client", no_argument, NULL, 'c' },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n = 0;
|
||||
int port = 7681;
|
||||
int use_ssl = 0;
|
||||
struct lws_context *context;
|
||||
int opts = 0;
|
||||
char interface_name[128] = "", ads_port[300];
|
||||
const char *iface = NULL;
|
||||
struct lws *wsi;
|
||||
const char *address = NULL;
|
||||
int server_port = port;
|
||||
struct lws_context_creation_info info;
|
||||
|
||||
memset(&info, 0, sizeof info);
|
||||
lwsl_notice("libwebsockets test server fraggle - license LGPL2.1+SLE\n");
|
||||
lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
|
||||
|
||||
while (n >= 0) {
|
||||
n = getopt_long(argc, argv, "ci:hsp:d:", options, NULL);
|
||||
if (n < 0)
|
||||
continue;
|
||||
switch (n) {
|
||||
case 'd':
|
||||
lws_set_log_level(atoi(optarg), NULL);
|
||||
break;
|
||||
case 's':
|
||||
use_ssl = 1;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
server_port = port;
|
||||
break;
|
||||
case 'i':
|
||||
lws_strncpy(interface_name, optarg, sizeof interface_name);
|
||||
iface = interface_name;
|
||||
break;
|
||||
case 'c':
|
||||
client = 1;
|
||||
fprintf(stderr, " Client mode\n");
|
||||
break;
|
||||
case 'h':
|
||||
fprintf(stderr, "Usage: libwebsockets-test-fraggle "
|
||||
"[--port=<p>] [--ssl] "
|
||||
"[-d <log bitfield>] "
|
||||
"[--client]\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (client) {
|
||||
server_port = CONTEXT_PORT_NO_LISTEN;
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "Must give address of server\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
info.port = server_port;
|
||||
info.iface = iface;
|
||||
info.protocols = protocols;
|
||||
info.extensions = exts;
|
||||
|
||||
if (use_ssl) {
|
||||
info.ssl_cert_filepath = LOCAL_RESOURCE_PATH
|
||||
"/libwebsockets-test-server.pem";
|
||||
info.ssl_private_key_filepath = LOCAL_RESOURCE_PATH
|
||||
"/libwebsockets-test-server.key.pem";
|
||||
}
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.options = opts;
|
||||
info.extensions = exts;
|
||||
|
||||
if (use_ssl)
|
||||
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
fprintf(stderr, "libwebsocket init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (client) {
|
||||
struct lws_client_connect_info i;
|
||||
|
||||
address = argv[optind];
|
||||
lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
|
||||
address, port & 65535);
|
||||
memset(&i, 0, sizeof(i));
|
||||
i.context = context;
|
||||
i.address = address;
|
||||
i.port = port;
|
||||
i.ssl_connection = use_ssl;
|
||||
i.path = "/";
|
||||
i.host = ads_port;
|
||||
i.origin = ads_port;
|
||||
i.protocol = protocols[PROTOCOL_FRAGGLE].name;
|
||||
|
||||
lwsl_notice("Connecting to %s:%u\n", address, port);
|
||||
wsi = lws_client_connect_via_info(&i);
|
||||
if (wsi == NULL) {
|
||||
fprintf(stderr, "Client connect to server failed\n");
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
n = 0;
|
||||
while (!n && !terminate)
|
||||
n = lws_service(context, 50);
|
||||
|
||||
fprintf(stderr, "Terminating...\n");
|
||||
|
||||
bail:
|
||||
lws_context_destroy(context);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
*/
|
||||
#include "test-server.h"
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
|
||||
/* dumb_increment protocol */
|
||||
|
||||
int
|
||||
|
@ -53,11 +55,12 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
if (len < 6)
|
||||
break;
|
||||
if (strcmp((const char *)in, "reset\n") == 0)
|
||||
// if (len < 6)
|
||||
// break;
|
||||
lwsl_hexdump_notice(in, len);
|
||||
if (strncmp((const char *)in, "reset\n", 6) == 0)
|
||||
pss->number = 0;
|
||||
if (strcmp((const char *)in, "closeme\n") == 0) {
|
||||
if (strncmp((const char *)in, "closeme\n", 8) == 0) {
|
||||
lwsl_notice("dumb_inc: closing as requested\n");
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
|
||||
(unsigned char *)"seeya", 5);
|
||||
|
@ -100,3 +103,4 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -47,10 +47,11 @@ void test_server_unlock(int care)
|
|||
}
|
||||
|
||||
#define LWS_PLUGIN_STATIC
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#include "../plugins/protocol_dumb_increment.c"
|
||||
#include "../plugins/protocol_lws_mirror.c"
|
||||
#include "../plugins/protocol_lws_status.c"
|
||||
#include "../plugins/protocol_lws_meta.c"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This demo server shows how to use libwebsockets for one or more
|
||||
|
@ -74,7 +75,6 @@ enum demo_protocols {
|
|||
PROTOCOL_DUMB_INCREMENT,
|
||||
PROTOCOL_LWS_MIRROR,
|
||||
PROTOCOL_LWS_STATUS,
|
||||
PROTOCOL_LWS_META,
|
||||
|
||||
/* always last */
|
||||
DEMO_PROTOCOL_COUNT
|
||||
|
@ -91,13 +91,15 @@ static struct lws_protocols protocols[] = {
|
|||
sizeof (struct per_session_data__http), /* per_session_data_size */
|
||||
0, /* max frame size / rx buffer */
|
||||
},
|
||||
#if defined(LWS_ROLE_WS)
|
||||
LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT,
|
||||
LWS_PLUGIN_PROTOCOL_MIRROR,
|
||||
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
|
||||
LWS_PLUGIN_PROTOCOL_LWS_META,
|
||||
#endif
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
|
|
|
@ -1,401 +0,0 @@
|
|||
/*
|
||||
* libwebsockets-test-server - libwebsockets test implementation
|
||||
*
|
||||
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* The person who associated a work with this deed has dedicated
|
||||
* the work to the public domain by waiving all of his or her rights
|
||||
* to the work worldwide under copyright law, including all related
|
||||
* and neighboring rights, to the extent allowed by law. You can copy,
|
||||
* modify, distribute and perform the work, even for commercial purposes,
|
||||
* all without asking permission.
|
||||
*
|
||||
* The test apps are intended to be adapted for use in your code, which
|
||||
* may be proprietary. So unlike the library itself, they are licensed
|
||||
* Public Domain.
|
||||
*/
|
||||
|
||||
#include "test-server.h"
|
||||
#include <pthread.h>
|
||||
|
||||
int close_testing;
|
||||
int max_poll_elements;
|
||||
int debug_level = 7;
|
||||
|
||||
#ifdef EXTERNAL_POLL
|
||||
struct lws_pollfd *pollfds;
|
||||
int *fd_lookup;
|
||||
int count_pollfds;
|
||||
#endif
|
||||
volatile int force_exit = 0;
|
||||
struct lws_context *context;
|
||||
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
#define LWS_PLUGIN_STATIC
|
||||
#include "../plugins/protocol_lws_mirror.c"
|
||||
#include "../plugins/protocol_lws_status.c"
|
||||
|
||||
/*
|
||||
* This mutex lock protects code that changes or relies on wsi list outside of
|
||||
* the service thread. The service thread will acquire it when changing the
|
||||
* wsi list and other threads should acquire it while dereferencing wsis or
|
||||
* calling apis like lws_callback_on_writable_all_protocol() which
|
||||
* use the wsi list and wsis from a different thread context.
|
||||
*/
|
||||
pthread_mutex_t lock_established_conns;
|
||||
|
||||
/* http server gets files from this path */
|
||||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
|
||||
/*
|
||||
* multithreaded version - protect wsi lifecycle changes in the library
|
||||
* these are called from protocol 0 callbacks
|
||||
*/
|
||||
|
||||
void test_server_lock(int care)
|
||||
{
|
||||
if (care)
|
||||
pthread_mutex_lock(&lock_established_conns);
|
||||
}
|
||||
void test_server_unlock(int care)
|
||||
{
|
||||
if (care)
|
||||
pthread_mutex_unlock(&lock_established_conns);
|
||||
}
|
||||
|
||||
/*
|
||||
* This demo server shows how to use libwebsockets for one or more
|
||||
* websocket protocols in the same server
|
||||
*
|
||||
* It defines the following websocket protocols:
|
||||
*
|
||||
* dumb-increment-protocol: once the socket is opened, an incrementing
|
||||
* ascii string is sent down it every 50ms.
|
||||
* If you send "reset\n" on the websocket, then
|
||||
* the incrementing number is reset to 0.
|
||||
*
|
||||
* lws-mirror-protocol: copies any received packet to every connection also
|
||||
* using this protocol, including the sender
|
||||
*/
|
||||
|
||||
enum demo_protocols {
|
||||
/* always first */
|
||||
PROTOCOL_HTTP = 0,
|
||||
|
||||
PROTOCOL_DUMB_INCREMENT,
|
||||
PROTOCOL_LWS_MIRROR,
|
||||
PROTOCOL_LWS_STATUS,
|
||||
|
||||
/* always last */
|
||||
DEMO_PROTOCOL_COUNT
|
||||
};
|
||||
|
||||
/* list of supported protocols and callbacks */
|
||||
|
||||
static struct lws_protocols protocols[] = {
|
||||
/* first protocol must always be HTTP handler */
|
||||
|
||||
{
|
||||
"http-only", /* name */
|
||||
callback_http, /* callback */
|
||||
sizeof (struct per_session_data__http), /* per_session_data_size */
|
||||
0, /* max frame size / rx buffer */
|
||||
},
|
||||
{
|
||||
"dumb-increment-protocol",
|
||||
callback_dumb_increment,
|
||||
sizeof(struct per_session_data__dumb_increment),
|
||||
10, /* rx buf size must be >= permessage-deflate rx size
|
||||
* dumb-increment only sends very small packets, so we set
|
||||
* this accordingly. If your protocol will send bigger
|
||||
* things, adjust this to match */
|
||||
},
|
||||
LWS_PLUGIN_PROTOCOL_MIRROR,
|
||||
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
void *thread_dumb_increment(void *threadid)
|
||||
{
|
||||
while (!force_exit) {
|
||||
/*
|
||||
* this lock means wsi in the active list cannot
|
||||
* disappear underneath us, because the code to add and remove
|
||||
* them is protected by the same lock
|
||||
*/
|
||||
pthread_mutex_lock(&lock_established_conns);
|
||||
lws_callback_on_writable_all_protocol(context,
|
||||
&protocols[PROTOCOL_DUMB_INCREMENT]);
|
||||
pthread_mutex_unlock(&lock_established_conns);
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void *thread_service(void *threadid)
|
||||
{
|
||||
while (lws_service_tsi(context, 50, (int)(lws_intptr_t)threadid) >= 0 && !force_exit)
|
||||
;
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void sighandler(int sig)
|
||||
{
|
||||
force_exit = 1;
|
||||
lws_cancel_service(context);
|
||||
}
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate; client_no_context_takeover; client_max_window_bits"
|
||||
},
|
||||
{
|
||||
"deflate-frame",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"deflate_frame"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
static struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "debug", required_argument, NULL, 'd' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "ssl", no_argument, NULL, 's' },
|
||||
{ "allow-non-ssl", no_argument, NULL, 'a' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "closetest", no_argument, NULL, 'c' },
|
||||
{ "libev", no_argument, NULL, 'e' },
|
||||
{ "threads", required_argument, NULL, 'j' },
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
{ "daemonize", no_argument, NULL, 'D' },
|
||||
#endif
|
||||
{ "resource_path", required_argument, NULL, 'r' },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
char interface_name[128] = "";
|
||||
const char *iface = NULL;
|
||||
pthread_t pthread_dumb, pthread_service[32];
|
||||
char cert_path[1024];
|
||||
char key_path[1024];
|
||||
int threads = 1;
|
||||
int use_ssl = 0;
|
||||
void *retval;
|
||||
int opts = 0;
|
||||
int n = 0;
|
||||
#ifndef _WIN32
|
||||
/* LOG_PERROR is not POSIX standard, and may not be portable */
|
||||
#ifdef __sun
|
||||
int syslog_options = LOG_PID;
|
||||
#else
|
||||
int syslog_options = LOG_PID | LOG_PERROR;
|
||||
#endif
|
||||
#endif
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
int daemonize = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* take care to zero down the info struct, he contains random garbaage
|
||||
* from the stack otherwise
|
||||
*/
|
||||
memset(&info, 0, sizeof info);
|
||||
info.port = 7681;
|
||||
|
||||
pthread_mutex_init(&lock_established_conns, NULL);
|
||||
|
||||
while (n >= 0) {
|
||||
n = getopt_long(argc, argv, "eci:hsap:d:Dr:j:", options, NULL);
|
||||
if (n < 0)
|
||||
continue;
|
||||
switch (n) {
|
||||
case 'j':
|
||||
threads = atoi(optarg);
|
||||
if (threads > (int)ARRAY_SIZE(pthread_service)) {
|
||||
lwsl_err("Max threads %lu\n",
|
||||
(unsigned long)ARRAY_SIZE(pthread_service));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
opts |= LWS_SERVER_OPTION_LIBEV;
|
||||
break;
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
#if !defined(_WIN32) && !defined(__sun)
|
||||
syslog_options &= ~LOG_PERROR;
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
case 'd':
|
||||
debug_level = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
use_ssl = 1;
|
||||
break;
|
||||
case 'a':
|
||||
opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
|
||||
break;
|
||||
case 'p':
|
||||
info.port = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
lws_strncpy(interface_name, optarg, sizeof interface_name);
|
||||
iface = interface_name;
|
||||
break;
|
||||
case 'c':
|
||||
close_testing = 1;
|
||||
fprintf(stderr, " Close testing mode -- closes on "
|
||||
"client after 50 dumb increments"
|
||||
"and suppresses lws_mirror spam\n");
|
||||
break;
|
||||
case 'r':
|
||||
resource_path = optarg;
|
||||
printf("Setting resource path to \"%s\"\n", resource_path);
|
||||
break;
|
||||
case 'h':
|
||||
fprintf(stderr, "Usage: test-server "
|
||||
"[--port=<p>] [--ssl] "
|
||||
"[-d <log bitfield>] "
|
||||
"[--resource_path <path>]\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
|
||||
/*
|
||||
* normally lock path would be /var/lock/lwsts or similar, to
|
||||
* simplify getting started without having to take care about
|
||||
* permissions or running as root, set to /tmp/.lwsts-lock
|
||||
*/
|
||||
if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
|
||||
fprintf(stderr, "Failed to daemonize\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
signal(SIGINT, sighandler);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* we will only try to log things according to our debug_level */
|
||||
setlogmask(LOG_UPTO (LOG_DEBUG));
|
||||
openlog("lwsts", syslog_options, LOG_DAEMON);
|
||||
#endif
|
||||
|
||||
/* tell the library what debug level to emit and to send it to syslog */
|
||||
lws_set_log_level(debug_level, NULL);
|
||||
lwsl_notice("libwebsockets test server pthreads - license LGPL2.1+SLE\n");
|
||||
lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
|
||||
|
||||
printf("Using resource path \"%s\"\n", resource_path);
|
||||
#ifdef EXTERNAL_POLL
|
||||
max_poll_elements = getdtablesize();
|
||||
pollfds = malloc(max_poll_elements * sizeof (struct lws_pollfd));
|
||||
fd_lookup = malloc(max_poll_elements * sizeof (int));
|
||||
if (pollfds == NULL || fd_lookup == NULL) {
|
||||
lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
info.iface = iface;
|
||||
info.protocols = protocols;
|
||||
info.extensions = exts;
|
||||
|
||||
info.ssl_cert_filepath = NULL;
|
||||
info.ssl_private_key_filepath = NULL;
|
||||
|
||||
if (use_ssl) {
|
||||
if (strlen(resource_path) > sizeof(cert_path) - 32) {
|
||||
lwsl_err("resource path too long\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(cert_path, "%s/libwebsockets-test-server.pem",
|
||||
resource_path);
|
||||
if (strlen(resource_path) > sizeof(key_path) - 32) {
|
||||
lwsl_err("resource path too long\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
|
||||
resource_path);
|
||||
|
||||
info.ssl_cert_filepath = cert_path;
|
||||
info.ssl_private_key_filepath = key_path;
|
||||
}
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.options = opts;
|
||||
info.count_threads = threads;
|
||||
info.extensions = exts;
|
||||
info.max_http_header_pool = 4;
|
||||
info.pt_serv_buf_size = 128 * 1024;
|
||||
|
||||
/* when doing slow benchmarks with thousands of concurrent
|
||||
* connections, we need wait longer
|
||||
*/
|
||||
info.timeout_secs = 30;
|
||||
info.keepalive_timeout = 30;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
lwsl_err("libwebsocket init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start the dumb increment thread */
|
||||
|
||||
n = pthread_create(&pthread_dumb, NULL, thread_dumb_increment, 0);
|
||||
if (n) {
|
||||
lwsl_err("Unable to create dumb thread\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* notice the actual number of threads may be capped by the library,
|
||||
* so use lws_get_count_threads() to get the actual amount of threads
|
||||
* initialized.
|
||||
*/
|
||||
|
||||
lwsl_notice("Service thread count: %d\n", lws_get_count_threads(context));
|
||||
|
||||
for (n = 0; n < lws_get_count_threads(context); n++)
|
||||
if (pthread_create(&pthread_service[n], NULL, thread_service,
|
||||
(void *)(lws_intptr_t)n))
|
||||
lwsl_err("Failed to start service thread\n");
|
||||
|
||||
/* wait for all the service threads to exit */
|
||||
|
||||
while ((--n) >= 0)
|
||||
pthread_join(pthread_service[n], &retval);
|
||||
|
||||
/* wait for pthread_dumb to exit */
|
||||
pthread_join(pthread_dumb, &retval);
|
||||
|
||||
done:
|
||||
lws_context_destroy(context);
|
||||
pthread_mutex_destroy(&lock_established_conns);
|
||||
|
||||
lwsl_notice("libwebsockets-test-server exited cleanly\n");
|
||||
|
||||
#ifndef _WIN32
|
||||
closelog();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -66,9 +66,10 @@ char crl_path[1024] = "";
|
|||
*/
|
||||
|
||||
#define LWS_PLUGIN_STATIC
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#include "../plugins/protocol_lws_mirror.c"
|
||||
#include "../plugins/protocol_lws_status.c"
|
||||
#include "../plugins/protocol_lws_meta.c"
|
||||
#endif
|
||||
|
||||
/* singlethreaded version --> no locks */
|
||||
|
||||
|
@ -122,6 +123,7 @@ static struct lws_protocols protocols[] = {
|
|||
sizeof (struct per_session_data__http), /* per_session_data_size */
|
||||
0, /* max frame size / rx buffer */
|
||||
},
|
||||
#if defined(LWS_ROLE_WS)
|
||||
{
|
||||
"dumb-increment-protocol",
|
||||
callback_dumb_increment,
|
||||
|
@ -133,8 +135,7 @@ static struct lws_protocols protocols[] = {
|
|||
},
|
||||
LWS_PLUGIN_PROTOCOL_MIRROR,
|
||||
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
|
||||
|
||||
LWS_PLUGIN_PROTOCOL_LWS_META,
|
||||
#endif
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue