diff --git a/.travis.yml b/.travis.yml index 099c6de8..77bad91c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 70c079aa..517ea33a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/README.md b/README.md index da1e2a42..23555496 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/lib/context.c b/lib/context.c index 7dbf6119..770edac2 100644 --- a/lib/context.c +++ b/lib/context.c @@ -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); diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index bf8cc7c4..af77b3a8 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -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; diff --git a/lib/output.c b/lib/output.c index 34530868..b6149c47 100644 --- a/lib/output.c +++ b/lib/output.c @@ -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; } diff --git a/lib/plat/lws-plat-esp32.c b/lib/plat/lws-plat-esp32.c index 3c4dfb51..3ba6ceb8 100644 --- a/lib/plat/lws-plat-esp32.c +++ b/lib/plat/lws-plat-esp32.c @@ -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; diff --git a/lib/plat/lws-plat-optee.c b/lib/plat/lws-plat-optee.c index 4003f7c8..f6354011 100644 --- a/lib/plat/lws-plat-optee.c +++ b/lib/plat/lws-plat-optee.c @@ -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; diff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c index c5c0ce61..caf4be66 100644 --- a/lib/plat/lws-plat-unix.c +++ b/lib/plat/lws-plat-unix.c @@ -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); diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 6bf3794e..1985e0b4 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -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 diff --git a/lib/roles/README.md b/lib/roles/README.md new file mode 100644 index 00000000..0e3a01c4 --- /dev/null +++ b/lib/roles/README.md @@ -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()`. + diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c index 091547e3..d113fbf7 100644 --- a/lib/roles/cgi/ops-cgi.c +++ b/lib/roles/cgi/ops-cgi.c @@ -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, diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 976aa47b..00485faa 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -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, diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index c6631729..41bc7d23 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -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); diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 51845ce7..47bbaff1 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -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, diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index b4bc63d1..79811b54 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -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 */ diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 816e988a..666f061d 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -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 = diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c index 5cbbdd64..cea1494f 100644 --- a/lib/roles/listen/ops-listen.c +++ b/lib/roles/listen/ops-listen.c @@ -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, diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c index a4374a77..13673444 100644 --- a/lib/roles/pipe/ops-pipe.c +++ b/lib/roles/pipe/ops-pipe.c @@ -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, diff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw/ops-raw.c index 70961015..24006fed 100644 --- a/lib/roles/raw/ops-raw.c +++ b/lib/roles/raw/ops-raw.c @@ -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, diff --git a/lib/roles/ws/client-parser.c b/lib/roles/ws/client-parser-ws.c similarity index 99% rename from lib/roles/ws/client-parser.c rename to lib/roles/ws/client-parser-ws.c index 9fc87f4b..e15025c8 100644 --- a/lib/roles/ws/client-parser.c +++ b/lib/roles/ws/client-parser-ws.c @@ -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, diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c index f7ef6935..2fe5b4f9 100644 --- a/lib/roles/ws/client-ws.c +++ b/lib/roles/ws/client-ws.c @@ -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) { diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 369b8920..acc20235 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -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, diff --git a/lib/roles/ws/private.h b/lib/roles/ws/private.h index 2fd1aa65..8f81b9e8 100644 --- a/lib/roles/ws/private.h +++ b/lib/roles/ws/private.h @@ -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, diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c index 3dca2e8a..83279186 100644 --- a/lib/roles/ws/server-ws.c +++ b/lib/roles/ws/server-ws.c @@ -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); } diff --git a/lib/service.c b/lib/service.c index 3c44ef29..2ead7ffd 100644 --- a/lib/service.c +++ b/lib/service.c @@ -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 diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c index b85567f2..14928435 100644 --- a/lib/tls/openssl/ssl.c +++ b/lib/tls/openssl/ssl.c @@ -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); /* diff --git a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt index 6e0af9cb..c76f91e8 100644 --- a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt +++ b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt @@ -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}) diff --git a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt index 82b70652..962c9883 100644 --- a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt +++ b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt @@ -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() \ No newline at end of file +endif() diff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c index 8af2edd6..43e707d6 100644 --- a/plugins/protocol_dumb_increment.c +++ b/plugins/protocol_dumb_increment.c @@ -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); diff --git a/plugins/protocol_lws_meta.c b/plugins/protocol_lws_meta.c deleted file mode 100644 index 69324db0..00000000 --- a/plugins/protocol_lws_meta.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * lws meta protocol handler - * - * Copyright (C) 2017 Andy Green - * - * 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 -#include - -#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 diff --git a/test-apps/test-fraggle.c b/test-apps/test-fraggle.c deleted file mode 100644 index c234c00c..00000000 --- a/test-apps/test-fraggle.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * libwebsockets-test-fraggle - random fragmentation test - * - * Copyright (C) 2011-2016 Andy Green - * - * 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 -#include -#include -#include -#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 \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=

] [--ssl] " - "[-d ] " - "[--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; -} diff --git a/test-apps/test-server-dumb-increment.c b/test-apps/test-server-dumb-increment.c index 36ade9d6..0e30f027 100644 --- a/test-apps/test-server-dumb-increment.c +++ b/test-apps/test-server-dumb-increment.c @@ -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 \ No newline at end of file diff --git a/test-apps/test-server-libuv.c b/test-apps/test-server-libuv.c index c27e779f..f0602e0b 100644 --- a/test-apps/test-server-libuv.c +++ b/test-apps/test-server-libuv.c @@ -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", diff --git a/test-apps/test-server-pthreads.c b/test-apps/test-server-pthreads.c deleted file mode 100644 index b7dbd42c..00000000 --- a/test-apps/test-server-pthreads.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * libwebsockets-test-server - libwebsockets test implementation - * - * Copyright (C) 2010-2016 Andy Green - * - * 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 - -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=

] [--ssl] " - "[-d ] " - "[--resource_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 \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; -} diff --git a/test-apps/test-server.c b/test-apps/test-server.c index 9e86590c..edbd5b99 100644 --- a/test-apps/test-server.c +++ b/test-apps/test-server.c @@ -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 */ };