Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
{"schema":"libjg2-1", "vpath":"/git/", "avatar":"/git/avatar/", "alang":"", "gen_ut":1754388181, "reponame":"libwebsockets", "desc":"libwebsockets lightweight C networking library", "owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://libwebsockets.org/repo/libwebsockets", "f":3, "items": [ {"schema":"libjg2-1", "cid":"8731141fabca6ccd8a98a1cf93a10090", "commit": {"type":"commit", "time": 1524616938, "time_ofs": 480, "oid_tree": { "oid": "788180aadc89f7eae06a9942b546f35d5d7564c6", "alias": []}, "oid":{ "oid": "27e86e2641ef417d83ae1608cdc3ca4484d0fa7d", "alias": []}, "msg": "cmake: allow setting LWS_ROLE_WS", "sig_commit": { "git_time": { "time": 1524616938, "offset": 480 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }, "sig_author": { "git_time": { "time": 1524616938, "offset": 480 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }}, "body": "cmake: allow setting LWS_ROLE_WS\n\nYou can build lws without support for ws, with -DLWS_ROLE_WS\u003d0.\n\nThis is thanks to the role ops isolating all the ws-specific business\nin the ws role.\n\nAlso retire more test apps replaced by minmal-examples." , "diff": "diff --git a/.travis.yml b/.travis.yml\nindex 099c6de..77bad91 100644\n--- a/.travis.yml\n+++ b/.travis.yml\n@@ -16,6 +16,7 @@ env:\n - LWS_METHOD\u003dcgi CMAKE_ARGS\u003d\u0022-DLWS_WITH_CGI\u003dON\u0022\n - LWS_METHOD\u003dnologs CMAKE_ARGS\u003d\u0022-DLWS_WITH_NO_LOGS\u003dON\u0022\n - LWS_METHOD\u003dsmp CMAKE_ARGS\u003d\u0022-DLWS_MAX_SMP\u003d32 -DLWS_WITH_MINIMAL_EXAMPLES\u003d1\u0022\n+ - LWS_METHOD\u003dnows CMAKE_ARGS\u003d\u0022-DLWS_ROLE_WS\u003d0\u0022\n \n os:\n - linux\ndiff --git a/CMakeLists.txt b/CMakeLists.txt\nindex 70c079a..517ea33 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -9,6 +9,8 @@ set(LWS_WITH_BUNDLED_ZLIB_DEFAULT OFF)\n if(WIN32)\n \tset(LWS_WITH_BUNDLED_ZLIB_DEFAULT ON)\n endif()\n+set(LWS_ROLE_H1 1)\n+set(LWS_ROLE_RAW 1)\n \n #\n # Select features recommended for PC distro packaging\n@@ -18,6 +20,7 @@ option(LWS_WITH_DISTRO_RECOMMENDED \u0022Enable features recommended for distro packa\n #\n # Major individual features\n #\n+option(LWS_ROLE_WS \u0022Compile with support for websockets\u0022 ON)\n option(LWS_WITH_HTTP2 \u0022Compile with server support for HTTP/2\u0022 OFF)\n option(LWS_WITH_LWSWS \u0022Libwebsockets Webserver\u0022 OFF)\n option(LWS_WITH_CGI \u0022Include CGI (spawn process with network-connected stdin/out/err) APIs\u0022 OFF)\n@@ -70,7 +73,6 @@ option(LWS_WITHOUT_TEST_SERVER \u0022Don't build the test server\u0022 OFF)\n option(LWS_WITHOUT_TEST_SERVER_EXTPOLL \u0022Don't build the test server version that uses external poll\u0022 OFF)\n option(LWS_WITHOUT_TEST_PING \u0022Don't build the ping test application\u0022 OFF)\n option(LWS_WITHOUT_TEST_CLIENT \u0022Don't build the client test application\u0022 OFF)\n-option(LWS_WITHOUT_TEST_FRAGGLE \u0022Don't build the ping test application\u0022 OFF)\n #\n # Extensions (permessage-deflate)\n #\n@@ -197,9 +199,6 @@ endif()\n \n # translate old functionality enables to set up ROLE enables so nothing changes\n \n-set(LWS_ROLE_H1 1)\n-set(LWS_ROLE_WS 1)\n-set(LWS_ROLE_RAW 1)\n if (LWS_WITH_HTTP2)\n \tset(LWS_ROLE_H2 1)\n endif()\n@@ -207,6 +206,10 @@ if (LWS_WITH_CGI)\n \tset(LWS_ROLE_CGI 1)\n endif()\n \n+if (NOT LWS_ROLE_WS)\n+\tset(LWS_WITHOUT_EXTENSIONS 1)\n+endif()\n+\n if (LWS_WITH_HTTP2 AND LWS_WITHOUT_SERVER)\n \tmessage(FATAL_ERROR \u0022HTTP2 can only be used with server at the moment\u0022)\n endif()\n@@ -709,7 +712,7 @@ if (LWS_ROLE_WS)\n \tif (NOT LWS_WITHOUT_CLIENT)\n \t\tlist(APPEND SOURCES\n \t\t\tlib/roles/ws/client-ws.c\n-\t\t\tlib/roles/ws/client-parser.c)\n+\t\t\tlib/roles/ws/client-parser-ws.c)\n \tendif()\n \tif (NOT LWS_WITHOUT_SERVER)\n \t\tlist(APPEND SOURCES\n@@ -1592,15 +1595,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)\n \t\t\t\t\t\u0022\u0022\n \t\t\t\t\t\u0022\u0022)\n \t\t\tendif()\n-\t\t\tif (UNIX AND NOT LWS_WITHOUT_SERVER AND NOT ((CMAKE_C_COMPILER_ID MATCHES \u0022Clang\u0022) OR (CMAKE_CXX_COMPILER_ID MATCHES \u0022Clang\u0022)) AND LWS_HAVE_PTHREAD_H)\n-\t\t\t\tcreate_test_app(test-server-pthreads\n-\t\t\t\t\t\u0022test-apps/test-server-pthreads.c\u0022\n-\t\t\t\t\t\u0022test-apps/test-server-http.c\u0022\n-\t\t\t\t\t\u0022test-apps/test-server-dumb-increment.c\u0022\n-\t\t\t\t\t\u0022\u0022\n-\t\t\t\t\t\u0022\u0022\n-\t\t\t\t\t\u0022\u0022)\n-\t\t\tendif()\n \t\t\tif (NOT ((CMAKE_C_COMPILER_ID MATCHES \u0022Clang\u0022) OR (CMAKE_CXX_COMPILER_ID MATCHES \u0022Clang\u0022))\n \t\t\t\tAND LWS_WITH_LIBEV)\n \t\t\t\tcreate_test_app(test-server-libev\n@@ -1721,13 +1715,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)\n \t\tendif()\n \n \t\t#\n-\t\t# test-fraggle\n-\t\t#\n-\t\tif (NOT LWS_WITHOUT_TEST_FRAGGLE AND NOT LWS_WITHOUT_SERVER)\n-\t\t\tcreate_test_app(test-fraggle \u0022test-apps/test-fraggle.c\u0022 \u0022\u0022 \u0022\u0022 \u0022\u0022 \u0022\u0022 \u0022\u0022)\n-\t\tendif()\n-\n-\t\t#\n \t\t# test-ping\n \t\t#\n \t\tif (NOT LWS_WITHOUT_TEST_PING AND NOT LWS_WITHOUT_SERVER AND NOT LWS_WITHOUT_CLIENT)\n@@ -1786,27 +1773,15 @@ if (NOT LWS_WITHOUT_TESTAPPS)\n \n \t\tendmacro()\n \t\t\n-\n-\t\tcreate_plugin(protocol_lws_meta \u0022\u0022\n-\t\t\t \u0022plugins/protocol_lws_meta.c\u0022 \u0022\u0022 \u0022\u0022)\n+if (LWS_ROLE_WS)\n \t\tcreate_plugin(protocol_dumb_increment \u0022\u0022\n \t\t\t \u0022plugins/protocol_dumb_increment.c\u0022 \u0022\u0022 \u0022\u0022)\n \t\tcreate_plugin(protocol_lws_mirror \u0022\u0022\n \t\t\t \u0022plugins/protocol_lws_mirror.c\u0022 \u0022\u0022 \u0022\u0022)\n \t\tcreate_plugin(protocol_lws_status \u0022\u0022\n \t\t\t \u0022plugins/protocol_lws_status.c\u0022 \u0022\u0022 \u0022\u0022)\n-\t\tcreate_plugin(protocol_post_demo \u0022\u0022\n-\t\t\t \u0022plugins/protocol_post_demo.c\u0022 \u0022\u0022 \u0022\u0022)\n \t\tcreate_plugin(protocol_lws_table_dirlisting \u0022\u0022\n \t\t\t \u0022plugins/generic-table/protocol_table_dirlisting.c\u0022 \u0022\u0022 \u0022\u0022)\n-if (LWS_WITH_SSL)\n-\t\tcreate_plugin(protocol_lws_ssh_base \u0022plugins/ssh-base/include\u0022\n-\t\t\t \u0022plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c\u0022 \u0022plugins/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\u0022 \u0022\u0022)\n-\t\tcreate_plugin(protocol_lws_sshd_demo \u0022plugins/ssh-base/include\u0022 \u0022plugins/protocol_lws_sshd_demo.c\u0022 \u0022\u0022 \u0022\u0022)\n-\n-\t\tinclude_directories(\u0022${PROJECT_SOURCE_DIR}/plugins/ssh-base/include\u0022)\n-endif()\n-\n \t\tif (NOT WIN32)\n \t\t\tcreate_plugin(protocol_lws_raw_test \u0022\u0022\n \t\t\t \u0022plugins/protocol_lws_raw_test.c\u0022 \u0022\u0022 \u0022\u0022)\n@@ -1820,14 +1795,29 @@ endif()\n if (NOT LWS_WITHOUT_CLIENT)\n \t\tcreate_plugin(protocol_client_loopback_test \u0022\u0022\n \u0022plugins/protocol_client_loopback_test.c\u0022 \u0022\u0022 \u0022\u0022)\n-endif(NOT LWS_WITHOUT_CLIENT)\n+endif()\n+\n+endif()\n+\n+\t\tcreate_plugin(protocol_post_demo \u0022\u0022\n+\t\t\t \u0022plugins/protocol_post_demo.c\u0022 \u0022\u0022 \u0022\u0022)\n+\n+if (LWS_WITH_SSL)\n+\t\tcreate_plugin(protocol_lws_ssh_base \u0022plugins/ssh-base/include\u0022\n+\t\t\t \u0022plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c\u0022 \u0022plugins/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\u0022 \u0022\u0022)\n+\t\tcreate_plugin(protocol_lws_sshd_demo \u0022plugins/ssh-base/include\u0022 \u0022plugins/protocol_lws_sshd_demo.c\u0022 \u0022\u0022 \u0022\u0022)\n+\n+\t\tinclude_directories(\u0022${PROJECT_SOURCE_DIR}/plugins/ssh-base/include\u0022)\n+endif()\n+\n+\n \n if (LWS_WITH_ACME)\n \t\tcreate_plugin(protocol_lws_acme_client \u0022\u0022\n \t\t\t \u0022plugins/acme-client/protocol_lws_acme_client.c\u0022 \u0022\u0022 \u0022\u0022)\n endif()\n \n-if (LWS_WITH_GENERIC_SESSIONS)\n+if (LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS)\n \tcreate_plugin(protocol_generic_sessions \u0022\u0022\n \u0022plugins/generic-sessions/protocol_generic_sessions.c\u0022\n \t\t \u0022plugins/generic-sessions/utils.c\u0022\n@@ -2157,7 +2147,6 @@ message(\u0022 LWS_WITHOUT_TEST_SERVER \u003d ${LWS_WITHOUT_TEST_SERVER}\u0022)\n message(\u0022 LWS_WITHOUT_TEST_SERVER_EXTPOLL \u003d ${LWS_WITHOUT_TEST_SERVER_EXTPOLL}\u0022)\n message(\u0022 LWS_WITHOUT_TEST_PING \u003d ${LWS_WITHOUT_TEST_PING}\u0022)\n message(\u0022 LWS_WITHOUT_TEST_CLIENT \u003d ${LWS_WITHOUT_TEST_CLIENT}\u0022)\n-message(\u0022 LWS_WITHOUT_TEST_FRAGGLE \u003d ${LWS_WITHOUT_TEST_FRAGGLE}\u0022)\n message(\u0022 LWS_WITHOUT_EXTENSIONS \u003d ${LWS_WITHOUT_EXTENSIONS}\u0022)\n message(\u0022 LWS_WITH_LATENCY \u003d ${LWS_WITH_LATENCY}\u0022)\n message(\u0022 LWS_WITHOUT_DAEMONIZE \u003d ${LWS_WITHOUT_DAEMONIZE}\u0022)\ndiff --git a/README.md b/README.md\nindex da1e2a4..2355549 100644\n--- a/README.md\n+++ b/README.md\n@@ -13,8 +13,8 @@ The Travis build of lws done on every commit now runs\n \n Tests|Count|Explanation\n ---|---|---\n-Build / Linux / gcc|12|-Wall -Werror\n-Build / Mac / Clang|12|-Wall -Werror\n+Build / Linux / gcc|13|-Wall -Werror\n+Build / Mac / Clang|13|-Wall -Werror\n Build / Windows / MSVC|7|default\n Selftests|29|minimal examples built and run against each other and remote server\n attack.sh|225|Correctness, robustness and security tests for http parser\ndiff --git a/lib/context.c b/lib/context.c\nindex 7dbf611..770edac 100644\n--- a/lib/context.c\n+++ b/lib/context.c\n@@ -25,8 +25,7 @@\n #define LWS_BUILD_HASH \u0022unknown-build-hash\u0022\n #endif\n \n-#if defined(LWS_WITH_TLS)\n-static const struct lws_role_ops * available_roles[] \u003d {\n+const struct lws_role_ops * available_roles[] \u003d {\n #if defined(LWS_ROLE_H2)\n \t\u0026role_ops_h2,\n #endif\n@@ -36,8 +35,10 @@ static const struct lws_role_ops * available_roles[] \u003d {\n #if defined(LWS_ROLE_WS)\n \t\u0026role_ops_ws,\n #endif\n+\tNULL\n };\n \n+#if defined(LWS_WITH_TLS)\n static char alpn_discovered[32];\n #endif\n \n@@ -60,17 +61,15 @@ int\n lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)\n {\n #if defined(LWS_WITH_TLS)\n-\tint n;\n-\n \tif (!alpn)\n \t\treturn 0;\n \n \tlwsl_info(\u0022%s: '%s'\u005cn\u0022, __func__, alpn);\n \n-\tfor (n \u003d 0; n \u003c (int)LWS_ARRAY_SIZE(available_roles); n++)\n-\t\tif (!strcmp(available_roles[n]-\u003ealpn, alpn) \u0026\u0026\n-\t\t available_roles[n]-\u003ealpn_negotiated)\n-\t\t\treturn available_roles[n]-\u003ealpn_negotiated(wsi, alpn);\n+\tLWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)\n+\t\tif (!strcmp(ar-\u003ealpn, alpn) \u0026\u0026 ar-\u003ealpn_negotiated)\n+\t\t\treturn ar-\u003ealpn_negotiated(wsi, alpn);\n+\tLWS_FOR_EVERY_AVAILABLE_ROLE_END;\n #endif\n \treturn 0;\n }\n@@ -565,13 +564,6 @@ lws_create_vhost(struct lws_context *context,\n \tif (info-\u003eoptions \u0026 LWS_SERVER_OPTION_ONLY_RAW)\n \t\tlwsl_info(\u0022%s set to only support RAW\u005cn\u0022, vh-\u003ename);\n \n-#if defined(LWS_WITH_HTTP2)\n-\tvh-\u003eh2.set \u003d context-\u003eset;\n-\tif (info-\u003ehttp2_settings[0])\n-\t\tfor (n \u003d 1; n \u003c LWS_H2_SETTINGS_LEN; n++)\n-\t\t\tvh-\u003eh2.set.s[n] \u003d info-\u003ehttp2_settings[n];\n-#endif\n-\n \tvh-\u003eiface \u003d info-\u003eiface;\n #if !defined(LWS_WITH_ESP32) \u0026\u0026 \u005c\n !defined(OPTEE_TA) \u0026\u0026 !defined(WIN32)\n@@ -589,9 +581,11 @@ lws_create_vhost(struct lws_context *context,\n \tvh-\u003euser \u003d info-\u003euser;\n \tvh-\u003ealpn \u003d info-\u003ealpn;\n \n-#if defined(LWS_ROLE_H2)\n-\trole_ops_h2.init_vhost(vh, info);\n-#endif\n+\tLWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)\n+\t\tif (ar-\u003einit_vhost)\n+\t\t\tif (ar-\u003einit_vhost(vh, info))\n+\t\t\t\treturn NULL;\n+\tLWS_FOR_EVERY_AVAILABLE_ROLE_END;\n \n \tvh-\u003essl_info_event_mask \u003d info-\u003essl_info_event_mask;\n \tif (info-\u003ekeepalive_timeout)\n@@ -740,40 +734,6 @@ lws_create_vhost(struct lws_context *context,\n \t\tmounts \u003d mounts-\u003emount_next;\n \t}\n \n-#if !defined(LWS_WITHOUT_EXTENSIONS)\n-#ifdef LWS_WITH_PLUGINS\n-\tif (context-\u003eplugin_extension_count) {\n-\n-\t\tm \u003d 0;\n-\t\twhile (info-\u003eextensions \u0026\u0026 info-\u003eextensions[m].callback)\n-\t\t\tm++;\n-\n-\t\t/*\n-\t\t * give the vhost a unified list of extensions including the\n-\t\t * ones that came from plugins\n-\t\t */\n-\t\tvh-\u003ews.extensions \u003d lws_zalloc(sizeof(struct lws_extension) *\n-\t\t\t\t (m + context-\u003eplugin_extension_count + 1),\n-\t\t\t\t \u0022extensions\u0022);\n-\t\tif (!vh-\u003ews.extensions)\n-\t\t\treturn NULL;\n-\n-\t\tmemcpy((struct lws_extension *)vh-\u003ews.extensions, info-\u003eextensions,\n-\t\t sizeof(struct lws_extension) * m);\n-\t\tplugin \u003d context-\u003eplugin_list;\n-\t\twhile (plugin) {\n-\t\t\tmemcpy((struct lws_extension *)\u0026vh-\u003ews.extensions[m],\n-\t\t\t\tplugin-\u003ecaps.extensions,\n-\t\t\t sizeof(struct lws_extension) *\n-\t\t\t plugin-\u003ecaps.count_extensions);\n-\t\t\tm +\u003d plugin-\u003ecaps.count_extensions;\n-\t\t\tplugin \u003d plugin-\u003elist;\n-\t\t}\n-\t} else\n-#endif\n-\t\tvh-\u003ews.extensions \u003d info-\u003eextensions;\n-#endif\n-\n \tvh-\u003elisten_port \u003d info-\u003eport;\n \tvh-\u003ehttp_proxy_port \u003d 0;\n \tvh-\u003ehttp_proxy_address[0] \u003d '\u005c0';\n@@ -1122,16 +1082,17 @@ lws_create_context(struct lws_context_creation_info *info)\n \telse {\n \t\tchar *p \u003d alpn_discovered, first \u003d 1;\n \n-\t\tfor (n \u003d 0; n \u003c (int)LWS_ARRAY_SIZE(available_roles); n++) {\n-\t\t\tif (available_roles[n]-\u003ealpn) {\n+\t\tLWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {\n+\t\t\tif (ar-\u003ealpn) {\n \t\t\t\tif (!first)\n \t\t\t\t\t*p++ \u003d ',';\n \t\t\t\tp +\u003d lws_snprintf(p, alpn_discovered +\n \t\t\t\t\t\tsizeof(alpn_discovered) - 2 - p,\n-\t\t\t\t\t \u0022%s\u0022, available_roles[n]-\u003ealpn);\n+\t\t\t\t\t \u0022%s\u0022, ar-\u003ealpn);\n \t\t\t\tfirst \u003d 0;\n \t\t\t}\n-\t\t}\n+\t\t} LWS_FOR_EVERY_AVAILABLE_ROLE_END;\n+\n \t\tcontext-\u003ealpn_default \u003d alpn_discovered;\n \t}\n \n@@ -1596,12 +1557,11 @@ lws_vhost_destroy2(struct lws_vhost *vh)\n \t\t\tlws_free((void *)vh-\u003eprotocols);\n \t}\n \n-#ifdef LWS_WITH_PLUGINS\n-#if !defined(LWS_WITHOUT_EXTENSIONS)\n-\tif (context-\u003eplugin_extension_count)\n-\t\tlws_free((void *)vh-\u003ews.extensions);\n-#endif\n-#endif\n+\tLWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)\n+\t\tif (ar-\u003edestroy_vhost)\n+\t\t\tar-\u003edestroy_vhost(vh);\n+\tLWS_FOR_EVERY_AVAILABLE_ROLE_END;\n+\n #ifdef LWS_WITH_ACCESS_LOG\n \tif (vh-\u003elog_fd !\u003d (int)LWS_INVALID_FILE)\n \t\tclose(vh-\u003elog_fd);\ndiff --git a/lib/libwebsockets.c b/lib/libwebsockets.c\nindex bf8cc7c..af77b3a 100644\n--- a/lib/libwebsockets.c\n+++ b/lib/libwebsockets.c\n@@ -95,7 +95,6 @@ __lws_free_wsi(struct lws *wsi)\n \n \tlws_buflist_destroy_all_segments(\u0026wsi-\u003ebuflist);\n \tlws_free_set_NULL(wsi-\u003etrunc_alloc);\n-\tlws_free_set_NULL(wsi-\u003ews);\n \tlws_free_set_NULL(wsi-\u003eudp);\n \n \t/* we may not have an ah, but may be on the waiting list... */\n@@ -834,11 +833,6 @@ just_kill_connection:\n \t\twsi-\u003etold_user_closed \u003d 1;\n \t}\n \n-\t/* deallocate any active extension contexts */\n-\n-\tif (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) \u003c 0)\n-\t\tlwsl_warn(\u0022extension destruction failed\u005cn\u0022);\n-\n async_close:\n \twsi-\u003esocket_is_permanently_unusable \u003d 1;\n \ndiff --git a/lib/output.c b/lib/output.c\nindex 3453086..b6149c4 100644\n--- a/lib/output.c\n+++ b/lib/output.c\n@@ -249,7 +249,6 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)\n \t\tif (wsi-\u003evhost)\n \t\t\twsi-\u003evhost-\u003econn_stats.rx +\u003d n;\n \t\tlws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);\n-\t\tlws_restart_ws_ping_pong_timer(wsi);\n \n \t\treturn n;\n \t}\ndiff --git a/lib/plat/lws-plat-esp32.c b/lib/plat/lws-plat-esp32.c\nindex 3c4dfb5..3ba6ceb 100644\n--- a/lib/plat/lws-plat-esp32.c\n+++ b/lib/plat/lws-plat-esp32.c\n@@ -218,10 +218,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)\n \t}\n \n #if defined(LWS_WITH_TLS)\n-\tif (!pt-\u003erx_draining_ext_list \u0026\u0026\n+\tif (!pt-\u003ews.rx_draining_ext_list \u0026\u0026\n \t !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) \u0026\u0026 !n) {\n #else\n-\tif (!pt-\u003erx_draining_ext_list \u0026\u0026 !n) /* poll timeout */ {\n+\tif (!pt-\u003ews.rx_draining_ext_list \u0026\u0026 !n) /* poll timeout */ {\n #endif\n \t\tlws_service_fd_tsi(context, NULL, tsi);\n \t\treturn 0;\ndiff --git a/lib/plat/lws-plat-optee.c b/lib/plat/lws-plat-optee.c\nindex 4003f7c..f635401 100644\n--- a/lib/plat/lws-plat-optee.c\n+++ b/lib/plat/lws-plat-optee.c\n@@ -141,10 +141,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)\n \tn \u003d poll(pt-\u003efds, pt-\u003efds_count, timeout_ms);\n \n #if defined(LWS_WITH_TLS)\n-\tif (!pt-\u003erx_draining_ext_list \u0026\u0026\n+\tif (!pt-\u003ews.rx_draining_ext_list \u0026\u0026\n \t !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) \u0026\u0026 !n) {\n #else\n-\tif (!pt-\u003erx_draining_ext_list \u0026\u0026 !n) /* poll timeout */ {\n+\tif (!pt-\u003ews.rx_draining_ext_list \u0026\u0026 !n) /* poll timeout */ {\n #endif\n \t\tlws_service_fd_tsi(context, NULL, tsi);\n \t\treturn 0;\ndiff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c\nindex c5c0ce6..caf4be6 100644\n--- a/lib/plat/lws-plat-unix.c\n+++ b/lib/plat/lws-plat-unix.c\n@@ -259,12 +259,16 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)\n \n \tlws_pt_unlock(pt);\n \n+\tm \u003d 0;\n+\n #if defined(LWS_WITH_TLS)\n-\tif (!n \u0026\u0026 !pt-\u003erx_draining_ext_list \u0026\u0026\n-\t !lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {\n-#else\n-\tif (!pt-\u003erx_draining_ext_list \u0026\u0026 !n) /* poll timeout */ {\n+\tm |\u003d !n \u0026\u0026 !lws_ssl_anybody_has_buffered_read_tsi(context, tsi);\n #endif\n+#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_WITHOUT_EXTENSIONS)\n+\tm |\u003d !n \u0026\u0026 !pt-\u003ews.rx_draining_ext_list;\n+#endif\n+\n+\tif (m) {\n \t\tlws_service_fd_tsi(context, NULL, tsi);\n \t\tlws_service_do_ripe_rxflow(pt);\n \ndiff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h\nindex 6bf3794..1985e0b 100644\n--- a/lib/private-libwebsockets.h\n+++ b/lib/private-libwebsockets.h\n@@ -527,12 +527,14 @@ struct lws_role_ops {\n \t * ws-over-h2 is upgraded from h2 like this.\n \t */\n \tint (*check_upgrades)(struct lws *wsi);\n-\t/* role-specific context init during vhost creation */\n+\t/* role-specific context init during context creation */\n \tint (*init_context)(struct lws_context *context,\n \t\t\t struct lws_context_creation_info *info);\n \t/* role-specific per-vhost init during vhost creation */\n \tint (*init_vhost)(struct lws_vhost *vh,\n \t\t\t struct lws_context_creation_info *info);\n+\t/* role-specific per-vhost destructor during vhost destroy */\n+\tint (*destroy_vhost)(struct lws_vhost *vh);\n \t/* generic 1Hz callback for the role itself */\n \tint (*periodic_checks)(struct lws_context *context, int tsi,\n \t\t\t time_t now);\n@@ -582,9 +584,22 @@ struct lws_role_ops {\n \tuint16_t close_cb[2];\n };\n \n+/* null-terminated array of pointers to roles lws built with */\n+extern const struct lws_role_ops *available_roles[];\n+\n+#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \u005c\n+\t\tconst struct lws_role_ops **ppxx \u003d available_roles; \u005c\n+\t\twhile (*ppxx) { \u005c\n+\t\t\tconst struct lws_role_ops *xx \u003d *ppxx++;\n+\n+#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }}\n+\n+/* core roles */\n extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,\n \t\t\t role_ops_pipe;\n \n+/* bring in role private declarations */\n+\n #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n #include \u0022roles/http/private.h\u0022\n #else\n@@ -767,9 +782,8 @@ struct lws_context_per_thread {\n #endif\n \tstruct lws_pollfd *fds;\n \tvolatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;\n-#if defined(LWS_ROLE_WS)\n-\tstruct lws *rx_draining_ext_list;\n-\tstruct lws *tx_draining_ext_list;\n+#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_WITHOUT_EXTENSIONS)\n+\tstruct lws_pt_role_ws ws;\n #endif\n \tstruct lws_dll_lws dll_head_timeout;\n \tstruct lws_dll_lws dll_head_hrtimer;\n@@ -887,7 +901,7 @@ struct lws_vhost {\n #if defined(LWS_ROLE_H2)\n \tstruct lws_vhost_role_h2 h2;\n #endif\n-#if defined(LWS_ROLE_WS)\n+#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_WITHOUT_EXTENSIONS)\n \tstruct lws_vhost_role_ws ws;\n #endif\n \n@@ -1368,6 +1382,9 @@ struct lws {\n #if defined(LWS_ROLE_H2)\n \tstruct _lws_h2_related h2;\n #endif\n+#if defined(LWS_ROLE_WS)\n+\tstruct _lws_websocket_related *ws; /* allocated if we upgrade to ws */\n+#endif\n \n \t/* lifetime members */\n \n@@ -1388,7 +1405,7 @@ struct lws {\n \tstruct lws *parent; /* points to parent, if any */\n \tstruct lws *child_list; /* points to first child */\n \tstruct lws *sibling_list; /* subsequent children at same level */\n-\tstruct _lws_websocket_related *ws; /* allocated if we upgrade to ws */\n+\n #ifdef LWS_WITH_CGI\n \tstruct lws_cgi *cgi; /* wsi being cgi master have one of these */\n #endif\ndiff --git a/lib/roles/README.md b/lib/roles/README.md\nnew file mode 100644\nindex 0000000..0e3a01c\n--- /dev/null\n+++ b/lib/roles/README.md\n@@ -0,0 +1,161 @@\n+## Information for new role implementers\n+\n+### Introduction\n+\n+In lws the \u0022role\u0022 is the job the wsi is doing in the system, eg,\n+being an http1 or h2, or ws connection, or being a listen socket, etc.\n+\n+This is different than, eg, a new ws protocol or a different callback\n+for an existing role. A new role is needed when you want to add support for\n+something completely new, like a completely new wire protocol that\n+doesn't use http or ws.\n+\n+So... what's the point of implementing the protocol inside the lws role framework?\n+\n+You inherit all the well-maintained lws core functionality around:\n+\n+ - connection lifecycle sequencing in a valgrind-clean way\n+\n+ - proxy support, HTTP and Socks5\n+\n+ - tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role\n+\n+ - apis for cert lifecycle management and parsing\n+\n+ - event loop support working on all the lws event loops (poll, libuv , ev, and event)\n+\n+ - clean connection tracking and closing even on advanced event loops\n+\n+ - user code follows the same simple callbacks on wsi\n+\n+ - multi-vhost support\n+\n+ - core multithreaded service support with usually no locking requirement on the role code\n+\n+ - direct compatibility with all other lws roles + protocols in the same event loop\n+\n+ - compatibility with higher-level stuff like lwsws as the server application\n+\n+### Code placement\n+\n+The code specific to that role should live in `./lib/roles/**role name**`\n+\n+If a role is asymmetic between a client and server side, like http is, it\n+should generally be implemented as a single role.\n+\n+### Allowing control over enabling roles\n+\n+All roles should add a cmake define `LWS_ROLE_**role name**` and make its build\n+dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`\n+as well so user builds can understand if the role is available in the lws build it is\n+trying to bind to.\n+\n+If the role is disabled in cmake, nothing in its directory is built.\n+\n+### Role ops struct\n+\n+The role is defined by `struct lws_role_ops` in `lib/private/libwebsockets.h`,\n+each role instantiates one of these and fills in the appropriate ops\n+callbacks to perform its job. By convention that lives in\n+`./lib/roles/**role name**/ops-**role_name**.c`.\n+\n+### Private role declarations\n+\n+Truly private declarations for the role can go in the role directory as you like.\n+However when the declarations must be accessible to other things in lws build, eg,\n+the role adds members to `struct lws` when enabled, they should be in the role\n+directory in a file `private.h`.\n+\n+Search for \u0022bring in role private declarations\u0022 in `./lib/private-libwebsockets.h\n+and add your private role file there following the style used for the other roles,\n+eg,\n+\n+```\n+#if defined(LWS_ROLE_WS)\n+ #include \u0022roles/ws/private.h\u0022\n+#else\n+ #define lwsi_role_ws(wsi) (0)\n+#endif\n+```\n+\n+If the role is disabled at cmake, nothing from its private.h should be used anywhere.\n+\n+### Integrating role assets to lws\n+\n+If your role needs special storage in lws objects, that's no problem. But to keep\n+things sane, there are some rules.\n+\n+ - declare a \u0022container struct\u0022 in your private.h for everything, eg, the ws role wants\n+ to add storage in lws_vhost for enabled extensions, it declares in its private.h\n+\n+```\n+struct lws_vhost_role_ws {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\tconst struct lws_extension *extensions;\n+#endif\n+};\n+```\n+\n+ - add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,\n+ eg, again for LWS_ROLE_WS\n+\n+```\n+\tstruct lws_vhost {\n+\n+...\n+\n+#if defined(LWS_ROLE_WS)\n+\tstruct lws_vhost_role_ws ws;\n+#endif\n+\n+...\n+```\n+\n+### Adding to lws available roles list\n+\n+Edit the NULL-terminated array `available_roles` at the top of `./lib/context.c` to include\n+a pointer to your new role's ops struct, following the style already there.\n+\n+```\n+const struct lws_role_ops * available_roles[] \u003d {\n+#if defined(LWS_ROLE_H2)\n+\t\u0026role_ops_h2,\n+#endif\n+...\n+```\n+\n+This makes lws aware that your role exists, and it can auto-generate some things like\n+ALPN lists, and call your role ops callbacks for things like hooking vhost creation.\n+\n+### Enabling role adoption\n+\n+The primary way wsi get bound to a specific role is via the lws adoption api\n+`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./lib/libwebsockets.h`\n+`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`\n+to bind a wsi with suitable flags to your role ops.\n+\n+### Implementation of the role\n+\n+After that plumbing-in is completed, the role ops you declare are \u0022live\u0022 on a wsi\n+bound to them via the adoption api.\n+\n+The core support for wsis in lws has some generic concepts\n+\n+ - the wsi holds a pointer member `role_ops` that indicates which role ops the\n+ wsi is bound to\n+\n+ - the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state\n+\n+ - role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between\n+ client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`\n+ and `lwsi_role_server(wsi)`.\n+\n+ - lws provides around 30 generic states for the wsi starting from 'unconnected' through\n+ various proxy or tunnel states, to 'established', and then various states shutting\n+ down until 'dead socket'. The states have testable flags and helpers to discover if\n+ the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is\n+ in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.\n+\n+ - You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards\n+ you can adjust the state using `lwsi_set_state()`.\n+\ndiff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c\nindex 091547e..d113fbf 100644\n--- a/lib/roles/cgi/ops-cgi.c\n+++ b/lib/roles/cgi/ops-cgi.c\n@@ -81,6 +81,7 @@ struct lws_role_ops role_ops_cgi \u003d {\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n \t/* init_vhost */\t\tNULL,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\trops_periodic_checks_cgi,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_cgi,\ndiff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c\nindex 976aa47..00485fa 100644\n--- a/lib/roles/h1/ops-h1.c\n+++ b/lib/roles/h1/ops-h1.c\n@@ -25,54 +25,6 @@\n #define min(a, b) ((a) \u003c (b) ? (a) : (b))\n #endif\n \n-#if !defined(LWS_NO_CLIENT)\n-static int\n-lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)\n-{\n-\tint m;\n-\n-\tif ((lwsi_state(wsi) !\u003d LRS_WAITING_PROXY_REPLY) \u0026\u0026\n-\t (lwsi_state(wsi) !\u003d LRS_H1C_ISSUE_HANDSHAKE) \u0026\u0026\n-\t (lwsi_state(wsi) !\u003d LRS_WAITING_SERVER_REPLY) \u0026\u0026\n-\t !lwsi_role_client(wsi))\n-\t\treturn 0;\n-\n-\t// lwsl_notice(\u0022%s: hs client gets %d in\u005cn\u0022, __func__, (int)len);\n-\n-\twhile (len) {\n-\t\t/*\n-\t\t * we were accepting input but now we stopped doing so\n-\t\t */\n-\t\tif (lws_is_flowcontrolled(wsi)) {\n-\t\t\t//lwsl_notice(\u0022%s: caching %ld\u005cn\u0022, __func__, (long)len);\n-\t\t\tlws_rxflow_cache(wsi, *buf, 0, (int)len);\n-\t\t\t*buf +\u003d len;\n-\t\t\treturn 0;\n-\t\t}\n-\t\tif (wsi-\u003ews-\u003erx_draining_ext) {\n-\t\t\t//lwsl_notice(\u0022%s: draining ext\u005cn\u0022, __func__);\n-\t\t\tif (lwsi_role_client(wsi))\n-\t\t\t\tm \u003d lws_ws_client_rx_sm(wsi, 0);\n-\t\t\telse\n-\t\t\t\tm \u003d lws_ws_rx_sm(wsi, 0, 0);\n-\t\t\tif (m \u003c 0)\n-\t\t\t\treturn -1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\t/* caller will account for buflist usage */\n-\n-\t\tif (lws_ws_client_rx_sm(wsi, *(*buf)++)) {\n-\t\t\tlwsl_notice(\u0022%s: client_rx_sm exited, DROPPING %d\u005cn\u0022,\n-\t\t\t\t __func__, (int)len);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tlen--;\n-\t}\n-\t// lwsl_notice(\u0022%s: finished with %ld\u005cn\u0022, __func__, (long)len);\n-\n-\treturn 0;\n-}\n-#endif\n \n /*\n * We have to take care about parsing because the headers may be split\n@@ -115,8 +67,8 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)\n \t\t\tassert(0);\n \t\t}\n \t\tlwsl_parser(\u0022issuing %d bytes to parser\u005cn\u0022, (int)len);\n-#if !defined(LWS_NO_CLIENT)\n-\t\tif (lws_handshake_client(wsi, \u0026buf, (size_t)len))\n+#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_NO_CLIENT)\n+\t\tif (lws_ws_handshake_client(wsi, \u0026buf, (size_t)len))\n \t\t\tgoto bail;\n #endif\n \t\tlast_char \u003d buf;\n@@ -248,9 +200,9 @@ postbody_completion:\n \tcase LRS_SHUTDOWN:\n \n ws_mode:\n-#if !defined(LWS_NO_CLIENT)\n+#if !defined(LWS_NO_CLIENT) \u0026\u0026 defined(LWS_ROLE_WS)\n \t\t// lwsl_notice(\u0022%s: ws_mode\u005cn\u0022, __func__);\n-\t\tif (lws_handshake_client(wsi, \u0026buf, (size_t)len))\n+\t\tif (lws_ws_handshake_client(wsi, \u0026buf, (size_t)len))\n \t\t\tgoto bail;\n #endif\n #if defined(LWS_ROLE_WS)\n@@ -263,7 +215,8 @@ ws_mode:\n \t\t\tgoto bail;\n \t\t}\n #endif\n-\t\t// lwsl_notice(\u0022%s: ws_mode: buf moved on by %d\u005cn\u0022, __func__, lws_ptr_diff(buf, oldbuf));\n+\t\t// lwsl_notice(\u0022%s: ws_mode: buf moved on by %d\u005cn\u0022, __func__,\n+\t\t//\t lws_ptr_diff(buf, oldbuf));\n \t\tbreak;\n \n \tcase LRS_DEFERRING_ACTION:\n@@ -293,14 +246,20 @@ read_ok:\n \n bail:\n \t/*\n-\t * h2 / h2-ws calls us recursively in lws_read()-\u003elws_h2_parser()-\u003e\n-\t * lws_read() pattern, having stripped the h2 framing in the middle.\n+\t * h2 / h2-ws calls us recursively in\n+\t *\n+\t * lws_read_h1()-\u003e\n+\t * lws_h2_parser()-\u003e\n+\t * lws_read_h1()\n+\t *\n+\t * pattern, having stripped the h2 framing in the middle.\n \t *\n \t * When taking down the whole connection, make sure that only the\n \t * outer lws_read() does the wsi close.\n \t */\n \tif (!wsi-\u003eouter_will_close)\n-\t\tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, \u0022lws_read bail\u0022);\n+\t\tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,\n+\t\t\t\t \u0022lws_read_h1 bail\u0022);\n \n \treturn -1;\n }\n@@ -526,19 +485,6 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,\n \t}\n #endif\n \n- if (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE ||\n- lwsi_state(wsi) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE ||\n- lwsi_state(wsi) \u003d\u003d LRS_AWAITING_CLOSE_ACK) {\n- /*\n- * we stopped caring about anything except control\n- * packets. Force flow control off, defeat tx\n- * draining.\n- */\n- lws_rx_flow_control(wsi, 1);\n- if (wsi-\u003ews)\n- wsi-\u003ews-\u003etx_draining_ext \u003d 0;\n- }\n-\n if (lws_is_flowcontrolled(wsi))\n /* We cannot deal with any kind of new RX because we are\n * RX-flowcontrolled.\n@@ -669,6 +615,7 @@ struct lws_role_ops role_ops_h1 \u003d {\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n \t/* init_vhost */\t\tNULL,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\tNULL,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_h1,\ndiff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c\nindex c663172..41bc7d2 100644\n--- a/lib/roles/h2/http2.c\n+++ b/lib/roles/h2/http2.c\n@@ -1715,7 +1715,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,\n \n \t\t\tcase LWS_H2_FRAME_TYPE_DATA:\n \n-\t\t\t\tlwsl_notice(\u0022%s: LWS_H2_FRAME_TYPE_DATA\u005cn\u0022, __func__);\n+\t\t\t\tlwsl_info(\u0022%s: LWS_H2_FRAME_TYPE_DATA\u005cn\u0022, __func__);\n \n \t\t\t\t/* let the network wsi live a bit longer if subs are active...\n \t\t\t\t * our frame may take a long time to chew through */\n@@ -1805,7 +1805,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,\n \t\t\t\t\t */\n \n \t\t\t\t\tn \u003d lws_read_h1(h2n-\u003eswsi, in - 1, n);\n-\t\t\t\t\tlwsl_notice(\u0022%s: lws_read_h1 %d\u005cn\u0022, __func__, n);\n+\t\t\t\t\t// lwsl_notice(\u0022%s: lws_read_h1 %d\u005cn\u0022, __func__, n);\n \t\t\t\t\th2n-\u003eswsi-\u003eouter_will_close \u003d 0;\n \t\t\t\t\t/*\n \t\t\t\t\t * can return 0 in POST body with\n@@ -1819,8 +1819,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,\n \t\t\t\t\t\th2n-\u003einside \u003d h2n-\u003elength;\n \t\t\t\t\t\th2n-\u003ecount \u003d h2n-\u003elength - 1;\n \n-\t\t\t\t\t\tif (n \u003c 0)\n-\t\t\t\t\t\t\tgoto already_closed_swsi;\n+\t\t\t\t\t\t//if (n \u003c 0)\n+\t\t\t\t\t\t//\tgoto already_closed_swsi;\n \t\t\t\t\t\tgoto close_swsi_and_return;\n \t\t\t\t\t}\n \n@@ -1983,7 +1983,7 @@ close_swsi_and_return:\n \th2n-\u003eframe_state \u003d 0;\n \th2n-\u003ecount \u003d 0;\n \n-already_closed_swsi:\n+// already_closed_swsi:\n \t*inused \u003d in - oldin;\n \n \treturn 2;\n@@ -2144,7 +2144,7 @@ lws_h2_ws_handshake(struct lws *wsi)\n \t */\n \n \tlwsi_set_state(wsi, LRS_ESTABLISHED);\n-\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n+\twsi-\u003elws_rx_parse_state \u003d 0; // \u003d\u003dLWS_RXPS_NEW;\n \n \turi_ptr \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH);\n \tn \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH);\ndiff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c\nindex 51845ce..47bbaff 100644\n--- a/lib/roles/h2/ops-h2.c\n+++ b/lib/roles/h2/ops-h2.c\n@@ -155,8 +155,10 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi,\n \t\t * draining.\n \t\t */\n \t\tlws_rx_flow_control(wsi, 1);\n+#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_WITHOUT_EXTENSIONS)\n \t\tif (wsi-\u003ews)\n \t\t\twsi-\u003ews-\u003etx_draining_ext \u003d 0;\n+#endif\n \t}\n \n \tif (wsi-\u003ehttp2_substream || wsi-\u003eupgraded_to_http2) {\n@@ -254,15 +256,12 @@ drain:\n \n \tif (ebuf.len) {\n \t\tn \u003d 0;\n-\t\tif (lwsi_role_h2(wsi) \u0026\u0026 lwsi_state(wsi) !\u003d LRS_BODY) {\n+\t\tif (lwsi_role_h2(wsi) \u0026\u0026 lwsi_state(wsi) !\u003d LRS_BODY)\n \t\t\tn \u003d lws_read_h2(wsi, (unsigned char *)ebuf.token,\n \t\t\t\t ebuf.len);\n-\t\t\t// lwsl_notice(\u0022h2 n \u003d %d\u005cn\u0022, n);\n-\t\t} else {\n+\t\telse\n \t\t\tn \u003d lws_read_h1(wsi, (unsigned char *)ebuf.token,\n \t\t\t\t ebuf.len);\n-\t\t\t// lwsl_notice(\u0022h1 n \u003d %d\u005cn\u0022, n);\n-\t\t}\n \n \t\tif (n \u003c 0) {\n \t\t\t/* we closed wsi */\n@@ -352,20 +351,23 @@ static int\n rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t\t enum lws_write_protocol *wp)\n {\n-\tunsigned char flags \u003d 0;\n+\tunsigned char flags \u003d 0, base \u003d (*wp) \u0026 0x1f;\n \tint n;\n \n \t/* if not in a state to send stuff, then just send nothing */\n \n \tif (!lwsi_role_ws(wsi) \u0026\u0026\n-\t ((*wp) \u0026 0x1f) !\u003d LWS_WRITE_HTTP \u0026\u0026\n-\t ((*wp) \u0026 0x1f) !\u003d LWS_WRITE_HTTP_FINAL \u0026\u0026\n-\t ((*wp) \u0026 0x1f) !\u003d LWS_WRITE_HTTP_HEADERS_CONTINUATION \u0026\u0026\n-\t ((*wp) \u0026 0x1f) !\u003d LWS_WRITE_HTTP_HEADERS \u0026\u0026\n+\t base !\u003d LWS_WRITE_HTTP \u0026\u0026\n+\t base !\u003d LWS_WRITE_HTTP_FINAL \u0026\u0026\n+\t base !\u003d LWS_WRITE_HTTP_HEADERS_CONTINUATION \u0026\u0026\n+\t base !\u003d LWS_WRITE_HTTP_HEADERS \u0026\u0026\n \t ((lwsi_state(wsi) !\u003d LRS_RETURNED_CLOSE \u0026\u0026\n \t lwsi_state(wsi) !\u003d LRS_WAITING_TO_SEND_CLOSE \u0026\u0026\n-\t lwsi_state(wsi) !\u003d LRS_AWAITING_CLOSE_ACK) ||\n-\t ((*wp) \u0026 0x1f) !\u003d LWS_WRITE_CLOSE)) {\n+\t lwsi_state(wsi) !\u003d LRS_AWAITING_CLOSE_ACK)\n+#if defined(LWS_ROLE_WS)\n+\t || base !\u003d LWS_WRITE_CLOSE\n+#endif\n+\t)) {\n \t\t//assert(0);\n \t\tlwsl_notice(\u0022binning wsistate 0x%x %d\u005cn\u0022, wsi-\u003ewsistate, *wp);\n \t\treturn 0;\n@@ -376,7 +378,7 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t */\n \n \tn \u003d LWS_H2_FRAME_TYPE_DATA;\n-\tif ((*wp \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP_HEADERS) {\n+\tif (base \u003d\u003d LWS_WRITE_HTTP_HEADERS) {\n \t\tn \u003d LWS_H2_FRAME_TYPE_HEADERS;\n \t\tif (!((*wp) \u0026 LWS_WRITE_NO_FIN))\n \t\t\tflags \u003d LWS_H2_FLAG_END_HEADERS;\n@@ -387,41 +389,35 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t}\n \t}\n \n-\tif ((*wp \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP_HEADERS_CONTINUATION) {\n+\tif (base \u003d\u003d LWS_WRITE_HTTP_HEADERS_CONTINUATION) {\n \t\tn \u003d LWS_H2_FRAME_TYPE_CONTINUATION;\n \t\tif (!((*wp) \u0026 LWS_WRITE_NO_FIN))\n \t\t\tflags \u003d LWS_H2_FLAG_END_HEADERS;\n-\t\tif (wsi-\u003eh2.send_END_STREAM ||\n-\t\t ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n+\t\tif (wsi-\u003eh2.send_END_STREAM || ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n \t\t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n \t\t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n \t\t}\n \t}\n \n-\tif (((*wp \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP ||\n-\t (*wp \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP_FINAL) \u0026\u0026\n-\t wsi-\u003ehttp.tx_content_length) {\n+\tif ((base \u003d\u003d LWS_WRITE_HTTP ||\n+\t base \u003d\u003d LWS_WRITE_HTTP_FINAL) \u0026\u0026\n+\t wsi-\u003ehttp.tx_content_length) {\n \t\twsi-\u003ehttp.tx_content_remain -\u003d len;\n-\t\tlwsl_info(\u0022%s: wsi %p: tx_content_remain \u003d %llu\u005cn\u0022,\n-\t\t\t __func__, wsi,\n+\t\tlwsl_info(\u0022%s: wsi %p: tx_content_rem \u003d %llu\u005cn\u0022, __func__, wsi,\n \t\t\t (unsigned long long)wsi-\u003ehttp.tx_content_remain);\n \t\tif (!wsi-\u003ehttp.tx_content_remain) {\n-\t\t\tlwsl_info(\u0022%s: selecting final write mode\u005cn\u0022,\n-\t\t\t\t __func__);\n-\t\t\t*wp \u003d LWS_WRITE_HTTP_FINAL;\n+\t\t\tlwsl_info(\u0022%s: selecting final write mode\u005cn\u0022, __func__);\n+\t\t\tbase \u003d *wp \u003d LWS_WRITE_HTTP_FINAL;\n \t\t}\n \t}\n \n-\tif ((*wp \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP_FINAL ||\n-\t ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n-\t //lws_get_network_wsi(wsi)-\u003eh2.END_STREAM) {\n+\tif (base \u003d\u003d LWS_WRITE_HTTP_FINAL || ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n \t\tlwsl_info(\u0022%s: setting END_STREAM\u005cn\u0022, __func__);\n \t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n \t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n \t}\n \n-\treturn lws_h2_frame_write(wsi, n, flags, wsi-\u003eh2.my_sid,\n-\t\t\t\t (int)len, buf);\n+\treturn lws_h2_frame_write(wsi, n, flags, wsi-\u003eh2.my_sid, (int)len, buf);\n }\n \n static int\n@@ -471,6 +467,13 @@ static int\n rops_init_vhost_h2(struct lws_vhost *vh,\n \t\t struct lws_context_creation_info *info)\n {\n+\tint n;\n+\n+\tvh-\u003eh2.set \u003d vh-\u003econtext-\u003eset;\n+\tif (info-\u003ehttp2_settings[0])\n+\t\tfor (n \u003d 1; n \u003c LWS_H2_SETTINGS_LEN; n++)\n+\t\t\tvh-\u003eh2.set.s[n] \u003d info-\u003ehttp2_settings[n];\n+\n \treturn 0;\n }\n \n@@ -510,7 +513,7 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)\n \tif (wsi-\u003ehttp2_substream \u0026\u0026 wsi-\u003eh2_stream_carries_ws)\n \t\tlws_h2_rst_stream(wsi, 0, \u0022none\u0022);\n \n-\tif (wsi-\u003eh2.parent_wsi) {\n+\tif (wsi-\u003eh2.parent_wsi \u0026\u0026 lwsl_visible(LLL_INFO)) {\n \t\tlwsl_info(\u0022 wsi: %p, his parent %p: siblings:\u005cn\u0022, wsi,\n \t\t\t wsi-\u003eh2.parent_wsi);\n \t\tlws_start_foreach_llp(struct lws **, w,\n@@ -523,7 +526,7 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)\n \tif (wsi-\u003eupgraded_to_http2 || wsi-\u003ehttp2_substream || wsi-\u003eclient_h2_substream) {\n \t\tlwsl_info(\u0022closing %p: parent %p\u005cn\u0022, wsi, wsi-\u003eh2.parent_wsi);\n \n-\t\tif (wsi-\u003eh2.child_list) {\n+\t\tif (wsi-\u003eh2.child_list \u0026\u0026 lwsl_visible(LLL_INFO)) {\n \t\t\tlwsl_info(\u0022 parent %p: closing children: list:\u005cn\u0022, wsi);\n \t\t\tlws_start_foreach_llp(struct lws **, w,\n \t\t\t\t\t wsi-\u003eh2.child_list) {\n@@ -531,6 +534,8 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)\n \t\t\t\t\t (*w)-\u003erole_ops ? (*w)-\u003erole_ops-\u003ename : \u0022?\u0022,\n \t\t\t\t\t *w);\n \t\t\t} lws_end_foreach_llp(w, h2.sibling_list);\n+\t\t}\n+\t\tif (wsi-\u003eh2.child_list) {\n \t\t\t/* trigger closing of all of our http2 children first */\n \t\t\tlws_start_foreach_llp(struct lws **, w,\n \t\t\t\t\t wsi-\u003eh2.child_list) {\n@@ -732,7 +737,10 @@ static int\n rops_perform_user_POLLOUT_h2(struct lws *wsi)\n {\n \tstruct lws **wsi2, *wsi2a;\n-\tint write_type \u003d LWS_WRITE_PONG, n;\n+#if defined(LWS_ROLE_WS)\n+\tint write_type \u003d LWS_WRITE_PONG;\n+#endif\n+\tint n;\n \n \twsi \u003d lws_get_network_wsi(wsi);\n \n@@ -880,9 +888,11 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)\n \t\t\tgoto next_child;\n \t\t}\n \n+#if defined(LWS_ROLE_WS)\n+\n \t\t/* Notify peer that we decided to close */\n \n-\t\tif (lwsi_state(w) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE) {\n+\t\tif (lwsi_role_ws(w) \u0026\u0026 lwsi_state(w) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE) {\n \t\t\tlwsl_debug(\u0022sending close packet\u005cn\u0022);\n \t\t\tw-\u003ewaiting_to_send_close_frame \u003d 0;\n \t\t\tn \u003d lws_write(w, \u0026w-\u003ews-\u003eping_payload_buf[LWS_PRE],\n@@ -934,7 +944,7 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)\n \t\t\t/* otherwise for PING, leave POLLOUT active either way */\n \t\t\tgoto next_child;\n \t\t}\n-\n+#endif\n \t\tif (lws_callback_as_writeable(w)) {\n \t\t\tlwsl_info(\u0022Closing POLLOUT child (end stream %d)\u005cn\u0022,\n \t\t\t\t w-\u003eh2.send_END_STREAM);\n@@ -1023,6 +1033,7 @@ struct lws_role_ops role_ops_h2 \u003d {\n \t/* check_upgrades */\t\trops_check_upgrades_h2,\n \t/* init_context */\t\trops_init_context_h2,\n \t/* init_vhost */\t\trops_init_vhost_h2,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\tNULL,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_h2,\ndiff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c\nindex b4bc63d..79811b5 100644\n--- a/lib/roles/http/client/client.c\n+++ b/lib/roles/http/client/client.c\n@@ -1027,9 +1027,10 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)\n \t\t\t\t lws_hdr_simple_ptr(wsi,\n \t\t\t\t\t\t _WSI_TOKEN_CLIENT_ORIGIN));\n \t}\n-\n+#if defined(LWS_ROLE_WS)\n \tif (wsi-\u003edo_ws)\n \t\tp \u003d lws_generate_client_ws_handshake(wsi, p);\n+#endif\n \n \t/* give userland a chance to append, eg, cookies */\n \ndiff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c\nindex 816e988..666f061 100644\n--- a/lib/roles/http/server/server.c\n+++ b/lib/roles/http/server/server.c\n@@ -1519,9 +1519,11 @@ raw_transition:\n \t\t\tif (!strcasecmp(lws_hdr_simple_ptr(wsi,\n \t\t\t\t\t\t\t WSI_TOKEN_UPGRADE),\n \t\t\t\t\t\u0022websocket\u0022)) {\n+#if defined(LWS_ROLE_WS)\n \t\t\t\twsi-\u003evhost-\u003econn_stats.ws_upg++;\n \t\t\t\tlwsl_info(\u0022Upgrade to ws\u005cn\u0022);\n \t\t\t\tgoto upgrade_ws;\n+#endif\n \t\t\t}\n #if defined(LWS_WITH_HTTP2)\n \t\t\tif (!strcasecmp(lws_hdr_simple_ptr(wsi,\n@@ -1601,13 +1603,13 @@ upgrade_h2c:\n \n \t\treturn 0;\n #endif\n-\n+#if defined(LWS_ROLE_WS)\n upgrade_ws:\n \t\tif (lws_process_ws_upgrade(wsi))\n \t\t\tgoto bail_nuke_ah;\n \n \t\treturn 0;\n-\n+#endif\n \t} /* while all chars are handled */\n \n \treturn 0;\n@@ -1871,6 +1873,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n lwsl_notice(\u0022OOM trying to get user_space\u005cn\u0022);\n \t\t\tgoto bail;\n }\n+#if defined(LWS_ROLE_WS)\n if (type \u0026 LWS_ADOPT_WS_PARENTIO) {\n \t\t\tnew_wsi-\u003edesc.sockfd \u003d LWS_SOCK_INVALID;\n \t\t\tlwsl_debug(\u0022binding to %s\u005cn\u0022, new_wsi-\u003eprotocol-\u003ename);\n@@ -1887,6 +1890,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \n \t\t\treturn new_wsi;\n }\n+#endif\n \t} else\n \t\tif (type \u0026 LWS_ADOPT_HTTP) {/* he will transition later */\n \t\t\tnew_wsi-\u003eprotocol \u003d\ndiff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c\nindex 5cbbdd6..cea1494 100644\n--- a/lib/roles/listen/ops-listen.c\n+++ b/lib/roles/listen/ops-listen.c\n@@ -155,6 +155,7 @@ struct lws_role_ops role_ops_listen \u003d {\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n \t/* init_vhost */\t\tNULL,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\tNULL,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_listen,\ndiff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c\nindex a4374a7..1367344 100644\n--- a/lib/roles/pipe/ops-pipe.c\n+++ b/lib/roles/pipe/ops-pipe.c\n@@ -60,6 +60,7 @@ struct lws_role_ops role_ops_pipe \u003d {\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n \t/* init_vhost */\t\tNULL,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\tNULL,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_pipe,\ndiff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw/ops-raw.c\nindex 7096101..24006fe 100644\n--- a/lib/roles/raw/ops-raw.c\n+++ b/lib/roles/raw/ops-raw.c\n@@ -169,6 +169,7 @@ struct lws_role_ops role_ops_raw_skt \u003d {\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n \t/* init_vhost */\t\tNULL,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\tNULL,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_raw_skt,\n@@ -195,6 +196,7 @@ struct lws_role_ops role_ops_raw_file \u003d {\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n \t/* init_vhost */\t\tNULL,\n+\t/* destroy_vhost */\t\tNULL,\n \t/* periodic_checks */\t\tNULL,\n \t/* service_flag_pending */\tNULL,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_raw_file,\ndiff --git a/lib/roles/ws/client-parser-ws.c b/lib/roles/ws/client-parser-ws.c\nnew file mode 100644\nindex 0000000..e15025c\n--- /dev/null\n+++ b/lib/roles/ws/client-parser-ws.c\n@@ -0,0 +1,601 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010-2017 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation:\n+ * version 2.1 of the License.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n+ * MA 02110-1301 USA\n+ */\n+\n+#include \u0022private-libwebsockets.h\u0022\n+\n+/*\n+ * parsers.c: lws_ws_rx_sm() needs to be roughly kept in\n+ * sync with changes here, esp related to ext draining\n+ */\n+\n+int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)\n+{\n+\tint callback_action \u003d LWS_CALLBACK_CLIENT_RECEIVE;\n+\tint handled, n, m, rx_draining_ext \u003d 0;\n+\tunsigned short close_code;\n+\tstruct lws_tokens ebuf;\n+\tunsigned char *pp;\n+\n+\tebuf.token \u003d NULL;\n+\tebuf.len \u003d 0;\n+\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\tif (wsi-\u003ews-\u003erx_draining_ext) {\n+\t\tassert(!c);\n+\n+\t\tlws_remove_wsi_from_draining_ext_list(wsi);\n+\t\trx_draining_ext \u003d 1;\n+\t\tlwsl_debug(\u0022%s: doing draining flow\u005cn\u0022, __func__);\n+\n+\t\tgoto drain_extension;\n+\t}\n+#endif\n+\n+\tif (wsi-\u003esocket_is_permanently_unusable)\n+\t\treturn -1;\n+\n+\tswitch (wsi-\u003elws_rx_parse_state) {\n+\tcase LWS_RXPS_NEW:\n+\t\t/* control frames (PING) may interrupt checkable sequences */\n+\t\twsi-\u003ews-\u003edefeat_check_utf8 \u003d 0;\n+\n+\t\tswitch (wsi-\u003ews-\u003eietf_spec_revision) {\n+\t\tcase 13:\n+\t\t\twsi-\u003ews-\u003eopcode \u003d c \u0026 0xf;\n+\t\t\t/* revisit if an extension wants them... */\n+\t\t\tswitch (wsi-\u003ews-\u003eopcode) {\n+\t\t\tcase LWSWSOPC_TEXT_FRAME:\n+\t\t\t\twsi-\u003ews-\u003ersv_first_msg \u003d (c \u0026 0x70);\n+\t\t\t\twsi-\u003ews-\u003econtinuation_possible \u003d 1;\n+\t\t\t\twsi-\u003ews-\u003echeck_utf8 \u003d lws_check_opt(\n+\t\t\t\t\twsi-\u003econtext-\u003eoptions,\n+\t\t\t\t\tLWS_SERVER_OPTION_VALIDATE_UTF8);\n+\t\t\t\twsi-\u003ews-\u003eutf8 \u003d 0;\n+\t\t\t\twsi-\u003ews-\u003efirst_fragment \u003d 1;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSWSOPC_BINARY_FRAME:\n+\t\t\t\twsi-\u003ews-\u003ersv_first_msg \u003d (c \u0026 0x70);\n+\t\t\t\twsi-\u003ews-\u003echeck_utf8 \u003d 0;\n+\t\t\t\twsi-\u003ews-\u003econtinuation_possible \u003d 1;\n+\t\t\t\twsi-\u003ews-\u003efirst_fragment \u003d 1;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSWSOPC_CONTINUATION:\n+\t\t\t\tif (!wsi-\u003ews-\u003econtinuation_possible) {\n+\t\t\t\t\tlwsl_info(\u0022disordered continuation\u005cn\u0022);\n+\t\t\t\t\treturn -1;\n+\t\t\t\t}\n+\t\t\t\twsi-\u003ews-\u003efirst_fragment \u003d 0;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSWSOPC_CLOSE:\n+\t\t\t\twsi-\u003ews-\u003echeck_utf8 \u003d 0;\n+\t\t\t\twsi-\u003ews-\u003eutf8 \u003d 0;\n+\t\t\t\tbreak;\n+\t\t\tcase 3:\n+\t\t\tcase 4:\n+\t\t\tcase 5:\n+\t\t\tcase 6:\n+\t\t\tcase 7:\n+\t\t\tcase 0xb:\n+\t\t\tcase 0xc:\n+\t\t\tcase 0xd:\n+\t\t\tcase 0xe:\n+\t\t\tcase 0xf:\n+\t\t\t\tlwsl_info(\u0022illegal opcode\u005cn\u0022);\n+\t\t\t\treturn -1;\n+\t\t\tdefault:\n+\t\t\t\twsi-\u003ews-\u003edefeat_check_utf8 \u003d 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\twsi-\u003ews-\u003ersv \u003d (c \u0026 0x70);\n+\t\t\t/* revisit if an extension wants them... */\n+\t\t\tif (\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\t\t\t\t!wsi-\u003ews-\u003ecount_act_ext \u0026\u0026\n+#endif\n+\t\t\t\twsi-\u003ews-\u003ersv) {\n+\t\t\t\tlwsl_info(\u0022illegal rsv bits set\u005cn\u0022);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\twsi-\u003ews-\u003efinal \u003d !!((c \u003e\u003e 7) \u0026 1);\n+\t\t\tlwsl_ext(\u0022%s: This RX frame Final %d\u005cn\u0022, __func__,\n+\t\t\t\t wsi-\u003ews-\u003efinal);\n+\n+\t\t\tif (wsi-\u003ews-\u003eowed_a_fin \u0026\u0026\n+\t\t\t (wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_TEXT_FRAME ||\n+\t\t\t wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_BINARY_FRAME)) {\n+\t\t\t\tlwsl_info(\u0022hey you owed us a FIN\u005cn\u0022);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tif ((!(wsi-\u003ews-\u003eopcode \u0026 8)) \u0026\u0026 wsi-\u003ews-\u003efinal) {\n+\t\t\t\twsi-\u003ews-\u003econtinuation_possible \u003d 0;\n+\t\t\t\twsi-\u003ews-\u003eowed_a_fin \u003d 0;\n+\t\t\t}\n+\n+\t\t\tif ((wsi-\u003ews-\u003eopcode \u0026 8) \u0026\u0026 !wsi-\u003ews-\u003efinal) {\n+\t\t\t\tlwsl_info(\u0022control msg can't be fragmented\u005cn\u0022);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tif (!wsi-\u003ews-\u003efinal)\n+\t\t\t\twsi-\u003ews-\u003eowed_a_fin \u003d 1;\n+\n+\t\t\tswitch (wsi-\u003ews-\u003eopcode) {\n+\t\t\tcase LWSWSOPC_TEXT_FRAME:\n+\t\t\tcase LWSWSOPC_BINARY_FRAME:\n+\t\t\t\twsi-\u003ews-\u003eframe_is_binary \u003d wsi-\u003ews-\u003eopcode \u003d\u003d\n+\t\t\t\t\t\t LWSWSOPC_BINARY_FRAME;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tlwsl_err(\u0022unknown spec version %02d\u005cn\u0022,\n+\t\t\t\t wsi-\u003ews-\u003eietf_spec_revision);\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN:\n+\n+\t\twsi-\u003ews-\u003ethis_frame_masked \u003d !!(c \u0026 0x80);\n+\n+\t\tswitch (c \u0026 0x7f) {\n+\t\tcase 126:\n+\t\t\t/* control frames are not allowed to have big lengths */\n+\t\t\tif (wsi-\u003ews-\u003eopcode \u0026 8)\n+\t\t\t\tgoto illegal_ctl_length;\n+\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN16_2;\n+\t\t\tbreak;\n+\t\tcase 127:\n+\t\t\t/* control frames are not allowed to have big lengths */\n+\t\t\tif (wsi-\u003ews-\u003eopcode \u0026 8)\n+\t\t\t\tgoto illegal_ctl_length;\n+\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_8;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\twsi-\u003ews-\u003erx_packet_length \u003d c \u0026 0x7f;\n+\t\t\tif (wsi-\u003ews-\u003ethis_frame_masked)\n+\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n+\t\t\t\t\t\tLWS_RXPS_07_COLLECT_FRAME_KEY_1;\n+\t\t\telse {\n+\t\t\t\tif (wsi-\u003ews-\u003erx_packet_length) {\n+\t\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n+\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n+\t\t\t\t} else {\n+\t\t\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n+\t\t\t\t\tgoto spill;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN16_2:\n+\t\twsi-\u003ews-\u003erx_packet_length \u003d c \u003c\u003c 8;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN16_1;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN16_1:\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d c;\n+\t\tif (wsi-\u003ews-\u003ethis_frame_masked)\n+\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_1;\n+\t\telse {\n+\t\t\tif (wsi-\u003ews-\u003erx_packet_length)\n+\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n+\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n+\t\t\telse {\n+\t\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n+\t\t\t\tgoto spill;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_8:\n+\t\tif (c \u0026 0x80) {\n+\t\t\tlwsl_warn(\u0022b63 of length must be zero\u005cn\u0022);\n+\t\t\t/* kill the connection */\n+\t\t\treturn -1;\n+\t\t}\n+#if defined __LP64__\n+\t\twsi-\u003ews-\u003erx_packet_length \u003d ((size_t)c) \u003c\u003c 56;\n+#else\n+\t\twsi-\u003ews-\u003erx_packet_length \u003d 0;\n+#endif\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_7;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_7:\n+#if defined __LP64__\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 48;\n+#endif\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_6;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_6:\n+#if defined __LP64__\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 40;\n+#endif\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_5;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_5:\n+#if defined __LP64__\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 32;\n+#endif\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_4;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_4:\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 24;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_3;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_3:\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 16;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_2;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_2:\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 8;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_1;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_04_FRAME_HDR_LEN64_1:\n+\t\twsi-\u003ews-\u003erx_packet_length |\u003d (size_t)c;\n+\t\tif (wsi-\u003ews-\u003ethis_frame_masked)\n+\t\t\twsi-\u003elws_rx_parse_state \u003d\n+\t\t\t\t\tLWS_RXPS_07_COLLECT_FRAME_KEY_1;\n+\t\telse {\n+\t\t\tif (wsi-\u003ews-\u003erx_packet_length)\n+\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n+\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n+\t\t\telse {\n+\t\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n+\t\t\t\tgoto spill;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_1:\n+\t\twsi-\u003ews-\u003emask[0] \u003d c;\n+\t\tif (c)\n+\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_2;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_2:\n+\t\twsi-\u003ews-\u003emask[1] \u003d c;\n+\t\tif (c)\n+\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_3;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_3:\n+\t\twsi-\u003ews-\u003emask[2] \u003d c;\n+\t\tif (c)\n+\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n+\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_4;\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_4:\n+\t\twsi-\u003ews-\u003emask[3] \u003d c;\n+\t\tif (c)\n+\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n+\n+\t\tif (wsi-\u003ews-\u003erx_packet_length)\n+\t\t\twsi-\u003elws_rx_parse_state \u003d\n+\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n+\t\telse {\n+\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n+\t\t\tgoto spill;\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWS_RXPS_WS_FRAME_PAYLOAD:\n+\n+\t\tassert(wsi-\u003ews-\u003erx_ubuf);\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\t\tif (wsi-\u003ews-\u003erx_draining_ext)\n+\t\t\tgoto drain_extension;\n+#endif\n+\t\tif (wsi-\u003ews-\u003ethis_frame_masked \u0026\u0026 !wsi-\u003ews-\u003eall_zero_nonce)\n+\t\t\tc ^\u003d wsi-\u003ews-\u003emask[(wsi-\u003ews-\u003emask_idx++) \u0026 3];\n+\n+\t\twsi-\u003ews-\u003erx_ubuf[LWS_PRE + (wsi-\u003ews-\u003erx_ubuf_head++)] \u003d c;\n+\n+\t\tif (--wsi-\u003ews-\u003erx_packet_length \u003d\u003d 0) {\n+\t\t\t/* spill because we have the whole frame */\n+\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n+\t\t\tgoto spill;\n+\t\t}\n+\n+\t\t/*\n+\t\t * if there's no protocol max frame size given, we are\n+\t\t * supposed to default to context-\u003ept_serv_buf_size\n+\t\t */\n+\t\tif (!wsi-\u003eprotocol-\u003erx_buffer_size \u0026\u0026\n+\t\t wsi-\u003ews-\u003erx_ubuf_head !\u003d wsi-\u003econtext-\u003ept_serv_buf_size)\n+\t\t\tbreak;\n+\n+\t\tif (wsi-\u003eprotocol-\u003erx_buffer_size \u0026\u0026\n+\t\t wsi-\u003ews-\u003erx_ubuf_head !\u003d wsi-\u003eprotocol-\u003erx_buffer_size)\n+\t\t\tbreak;\n+\n+\t\t/* spill because we filled our rx buffer */\n+spill:\n+\n+\t\thandled \u003d 0;\n+\n+\t\t/*\n+\t\t * is this frame a control packet we should take care of at this\n+\t\t * layer? If so service it and hide it from the user callback\n+\t\t */\n+\n+\t\tswitch (wsi-\u003ews-\u003eopcode) {\n+\t\tcase LWSWSOPC_CLOSE:\n+\t\t\tpp \u003d (unsigned char *)\u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE];\n+\t\t\tif (lws_check_opt(wsi-\u003econtext-\u003eoptions,\n+\t\t\t\t\t LWS_SERVER_OPTION_VALIDATE_UTF8) \u0026\u0026\n+\t\t\t wsi-\u003ews-\u003erx_ubuf_head \u003e 2 \u0026\u0026\n+\t\t\t lws_check_utf8(\u0026wsi-\u003ews-\u003eutf8, pp + 2,\n+\t\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head - 2))\n+\t\t\t\tgoto utf8_fail;\n+\n+\t\t\t/* is this an acknowledgment of our close? */\n+\t\t\tif (lwsi_state(wsi) \u003d\u003d LRS_AWAITING_CLOSE_ACK) {\n+\t\t\t\t/*\n+\t\t\t\t * fine he has told us he is closing too, let's\n+\t\t\t\t * finish our close\n+\t\t\t\t */\n+\t\t\t\tlwsl_parser(\u0022seen server's close ack\u005cn\u0022);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\tlwsl_parser(\u0022client sees server close len \u003d %d\u005cn\u0022,\n+\t\t\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n+\t\t\tif (wsi-\u003ews-\u003erx_ubuf_head \u003e\u003d 2) {\n+\t\t\t\tclose_code \u003d (pp[0] \u003c\u003c 8) | pp[1];\n+\t\t\t\tif (close_code \u003c 1000 ||\n+\t\t\t\t close_code \u003d\u003d 1004 ||\n+\t\t\t\t close_code \u003d\u003d 1005 ||\n+\t\t\t\t close_code \u003d\u003d 1006 ||\n+\t\t\t\t close_code \u003d\u003d 1012 ||\n+\t\t\t\t close_code \u003d\u003d 1013 ||\n+\t\t\t\t close_code \u003d\u003d 1014 ||\n+\t\t\t\t close_code \u003d\u003d 1015 ||\n+\t\t\t\t (close_code \u003e\u003d 1016 \u0026\u0026 close_code \u003c 3000)\n+\t\t\t\t) {\n+\t\t\t\t\tpp[0] \u003d (LWS_CLOSE_STATUS_PROTOCOL_ERR \u003e\u003e 8) \u0026 0xff;\n+\t\t\t\t\tpp[1] \u003d LWS_CLOSE_STATUS_PROTOCOL_ERR \u0026 0xff;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (user_callback_handle_rxflow(\n+\t\t\t\t\twsi-\u003eprotocol-\u003ecallback, wsi,\n+\t\t\t\t\tLWS_CALLBACK_WS_PEER_INITIATED_CLOSE,\n+\t\t\t\t\twsi-\u003euser_space, pp,\n+\t\t\t\t\twsi-\u003ews-\u003erx_ubuf_head))\n+\t\t\t\treturn -1;\n+\n+\t\t\tmemcpy(wsi-\u003ews-\u003eping_payload_buf + LWS_PRE, pp,\n+\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n+\t\t\twsi-\u003ews-\u003eclose_in_ping_buffer_len \u003d wsi-\u003ews-\u003erx_ubuf_head;\n+\n+\t\t\tlwsl_notice(\u0022%s: scheduling return close as ack\u005cn\u0022, __func__);\n+\t\t\t__lws_change_pollfd(wsi, LWS_POLLIN, 0);\n+\t\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);\n+\t\t\twsi-\u003ewaiting_to_send_close_frame \u003d 1;\n+\t\t\twsi-\u003eclose_needs_ack \u003d 0;\n+\t\t\tlwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);\n+\t\t\tlws_callback_on_writable(wsi);\n+\t\t\thandled \u003d 1;\n+\t\t\tbreak;\n+\n+\t\tcase LWSWSOPC_PING:\n+\t\t\tlwsl_info(\u0022received %d byte ping, sending pong\u005cn\u0022,\n+\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n+\n+\t\t\t/* he set a close reason on this guy, ignore PING */\n+\t\t\tif (wsi-\u003ews-\u003eclose_in_ping_buffer_len)\n+\t\t\t\tgoto ping_drop;\n+\n+\t\t\tif (wsi-\u003ews-\u003eping_pending_flag) {\n+\t\t\t\t/*\n+\t\t\t\t * there is already a pending ping payload\n+\t\t\t\t * we should just log and drop\n+\t\t\t\t */\n+\t\t\t\tlwsl_parser(\u0022DROP PING since one pending\u005cn\u0022);\n+\t\t\t\tgoto ping_drop;\n+\t\t\t}\n+\n+\t\t\t/* control packets can only be \u003c 128 bytes long */\n+\t\t\tif (wsi-\u003ews-\u003erx_ubuf_head \u003e 128 - 3) {\n+\t\t\t\tlwsl_parser(\u0022DROP PING payload too large\u005cn\u0022);\n+\t\t\t\tgoto ping_drop;\n+\t\t\t}\n+\n+\t\t\t/* stash the pong payload */\n+\t\t\tmemcpy(wsi-\u003ews-\u003eping_payload_buf + LWS_PRE,\n+\t\t\t \u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE],\n+\t\t\t\twsi-\u003ews-\u003erx_ubuf_head);\n+\n+\t\t\twsi-\u003ews-\u003eping_payload_len \u003d wsi-\u003ews-\u003erx_ubuf_head;\n+\t\t\twsi-\u003ews-\u003eping_pending_flag \u003d 1;\n+\n+\t\t\t/* get it sent as soon as possible */\n+\t\t\tlws_callback_on_writable(wsi);\n+ping_drop:\n+\t\t\twsi-\u003ews-\u003erx_ubuf_head \u003d 0;\n+\t\t\thandled \u003d 1;\n+\t\t\tbreak;\n+\n+\t\tcase LWSWSOPC_PONG:\n+\t\t\tlwsl_info(\u0022client receied pong\u005cn\u0022);\n+\t\t\tlwsl_hexdump(\u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE],\n+\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n+\n+\t\t\tif (wsi-\u003epending_timeout \u003d\u003d\n+\t\t\t\t PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {\n+\t\t\t\tlwsl_info(\u0022%p: received expected PONG\u005cn\u0022, wsi);\n+\t\t\t\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n+\t\t\t}\n+\n+\t\t\t/* issue it */\n+\t\t\tcallback_action \u003d LWS_CALLBACK_CLIENT_RECEIVE_PONG;\n+\t\t\tbreak;\n+\n+\t\tcase LWSWSOPC_CONTINUATION:\n+\t\tcase LWSWSOPC_TEXT_FRAME:\n+\t\tcase LWSWSOPC_BINARY_FRAME:\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\t/* not handled or failed */\n+\t\t\tlwsl_ext(\u0022Unhandled ext opc 0x%x\u005cn\u0022, wsi-\u003ews-\u003eopcode);\n+\t\t\twsi-\u003ews-\u003erx_ubuf_head \u003d 0;\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/*\n+\t\t * No it's real payload, pass it up to the user callback.\n+\t\t * It's nicely buffered with the pre-padding taken care of\n+\t\t * so it can be sent straight out again using lws_write\n+\t\t */\n+\t\tif (handled)\n+\t\t\tgoto already_done;\n+\n+\t\tebuf.token \u003d \u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE];\n+\t\tebuf.len \u003d wsi-\u003ews-\u003erx_ubuf_head;\n+\n+\t\tif (wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_PONG \u0026\u0026 !ebuf.len)\n+\t\t\tgoto already_done;\n+\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+drain_extension:\n+\t\tlwsl_ext(\u0022%s: passing %d to ext\u005cn\u0022, __func__, ebuf.len);\n+\n+\t\tn \u003d lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, \u0026ebuf, 0);\n+\t\tlwsl_ext(\u0022Ext RX returned %d\u005cn\u0022, n);\n+\t\tif (n \u003c 0) {\n+\t\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n+\t\t\treturn -1;\n+\t\t}\n+#else\n+\t\tn \u003d 0;\n+#endif\n+\t\tlwsl_notice(\u0022post inflate ebuf len %d\u005cn\u0022, ebuf.len);\n+\n+\t\tif (rx_draining_ext \u0026\u0026 !ebuf.len) {\n+\t\t\tlwsl_debug(\u0022 --- ending drain on 0 read result\u005cn\u0022);\n+\t\t\tgoto already_done;\n+\t\t}\n+\n+\t\tif (wsi-\u003ews-\u003echeck_utf8 \u0026\u0026 !wsi-\u003ews-\u003edefeat_check_utf8) {\n+\t\t\tif (lws_check_utf8(\u0026wsi-\u003ews-\u003eutf8,\n+\t\t\t\t\t (unsigned char *)ebuf.token,\n+\t\t\t\t\t ebuf.len)) {\n+\t\t\t\tlws_close_reason(wsi,\n+\t\t\t\t\tLWS_CLOSE_STATUS_INVALID_PAYLOAD,\n+\t\t\t\t\t(uint8_t *)\u0022bad utf8\u0022, 8);\n+\t\t\t\tgoto utf8_fail;\n+\t\t\t}\n+\n+\t\t\t/* we are ending partway through utf-8 character? */\n+\t\t\tif (!wsi-\u003ews-\u003erx_packet_length \u0026\u0026 wsi-\u003ews-\u003efinal \u0026\u0026\n+\t\t\t wsi-\u003ews-\u003eutf8 \u0026\u0026 !n) {\n+\t\t\t\tlwsl_info(\u0022FINAL utf8 error\u005cn\u0022);\n+\t\t\t\tlws_close_reason(wsi,\n+\t\t\t\t\tLWS_CLOSE_STATUS_INVALID_PAYLOAD,\n+\t\t\t\t\t(uint8_t *)\u0022partial utf8\u0022, 12);\n+utf8_fail:\n+\t\t\t\tlwsl_info(\u0022utf8 error\u005cn\u0022);\n+\t\t\t\tlwsl_hexdump_info(ebuf.token, ebuf.len);\n+\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (ebuf.len \u003c 0 \u0026\u0026\n+\t\t callback_action !\u003d LWS_CALLBACK_CLIENT_RECEIVE_PONG)\n+\t\t\tgoto already_done;\n+\n+\t\tif (!ebuf.token)\n+\t\t\tgoto already_done;\n+\n+\t\tebuf.token[ebuf.len] \u003d '\u005c0';\n+\n+\t\tif (!wsi-\u003eprotocol-\u003ecallback)\n+\t\t\tgoto already_done;\n+\n+\t\tif (callback_action \u003d\u003d LWS_CALLBACK_CLIENT_RECEIVE_PONG)\n+\t\t\tlwsl_info(\u0022Client doing pong callback\u005cn\u0022);\n+\n+\t\tif (\n+\t\t\t\t/* coverity says dead code otherwise */\n+//#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\t\t\t\tn \u0026\u0026\n+//#endif\n+\t\t\t\tebuf.len)\n+\t\t\t/* extension had more... main loop will come back\n+\t\t\t * we want callback to be done with this set, if so,\n+\t\t\t * because lws_is_final() hides it was final until the\n+\t\t\t * last chunk\n+\t\t\t */\n+\t\t\tlws_add_wsi_to_draining_ext_list(wsi);\n+\t\telse\n+\t\t\tlws_remove_wsi_from_draining_ext_list(wsi);\n+\n+\t\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE ||\n+\t\t lwsi_state(wsi) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE ||\n+\t\t lwsi_state(wsi) \u003d\u003d LRS_AWAITING_CLOSE_ACK)\n+\t\t\tgoto already_done;\n+\n+\t\tm \u003d wsi-\u003eprotocol-\u003ecallback(wsi,\n+\t\t\t(enum lws_callback_reasons)callback_action,\n+\t\t\twsi-\u003euser_space, ebuf.token, ebuf.len);\n+\n+\t\twsi-\u003ews-\u003efirst_fragment \u003d 0;\n+\n+\t\t// lwsl_notice(\u0022%s: bulk ws rx: input used %d, output %d\u005cn\u0022,\n+\t\t//\t__func__, wsi-\u003ews-\u003erx_ubuf_head, ebuf.len);\n+\n+\t\t/* if user code wants to close, let caller know */\n+\t\tif (m)\n+\t\t\treturn 1;\n+\n+already_done:\n+\t\twsi-\u003ews-\u003erx_ubuf_head \u003d 0;\n+\t\tbreak;\n+\tdefault:\n+\t\tlwsl_err(\u0022client rx illegal state\u005cn\u0022);\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+\n+illegal_ctl_length:\n+\tlwsl_warn(\u0022Control frame asking for extended length is illegal\u005cn\u0022);\n+\n+\t/* kill the connection */\n+\treturn -1;\n+}\n+\n+\ndiff --git a/lib/roles/ws/client-parser.c b/lib/roles/ws/client-parser.c\ndeleted file mode 100644\nindex 9fc87f4..0000000\n--- a/lib/roles/ws/client-parser.c\n+++ /dev/null\n@@ -1,599 +0,0 @@\n-/*\n- * libwebsockets - small server side websockets and web server implementation\n- *\n- * Copyright (C) 2010-2017 Andy Green \u003candy@warmcat.com\u003e\n- *\n- * This library is free software; you can redistribute it and/or\n- * modify it under the terms of the GNU Lesser General Public\n- * License as published by the Free Software Foundation:\n- * version 2.1 of the License.\n- *\n- * This library is distributed in the hope that it will be useful,\n- * but WITHOUT ANY WARRANTY; without even the implied warranty of\n- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n- * Lesser General Public License for more details.\n- *\n- * You should have received a copy of the GNU Lesser General Public\n- * License along with this library; if not, write to the Free Software\n- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n- * MA 02110-1301 USA\n- */\n-\n-#include \u0022private-libwebsockets.h\u0022\n-\n-/*\n- * parsers.c: lws_ws_rx_sm() needs to be roughly kept in\n- * sync with changes here, esp related to ext draining\n- */\n-\n-int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)\n-{\n-\tint callback_action \u003d LWS_CALLBACK_CLIENT_RECEIVE;\n-\tint handled, n, m, rx_draining_ext \u003d 0;\n-\tunsigned short close_code;\n-\tstruct lws_tokens ebuf;\n-\tunsigned char *pp;\n-\n-\tebuf.token \u003d NULL;\n-\tebuf.len \u003d 0;\n-\n-\tif (wsi-\u003ews-\u003erx_draining_ext) {\n-\t\tassert(!c);\n-\n-\t\tlws_remove_wsi_from_draining_ext_list(wsi);\n-\t\trx_draining_ext \u003d 1;\n-\t\tlwsl_debug(\u0022%s: doing draining flow\u005cn\u0022, __func__);\n-\n-\t\tgoto drain_extension;\n-\t}\n-\n-\tif (wsi-\u003esocket_is_permanently_unusable)\n-\t\treturn -1;\n-\n-\tswitch (wsi-\u003elws_rx_parse_state) {\n-\tcase LWS_RXPS_NEW:\n-\t\t/* control frames (PING) may interrupt checkable sequences */\n-\t\twsi-\u003ews-\u003edefeat_check_utf8 \u003d 0;\n-\n-\t\tswitch (wsi-\u003ews-\u003eietf_spec_revision) {\n-\t\tcase 13:\n-\t\t\twsi-\u003ews-\u003eopcode \u003d c \u0026 0xf;\n-\t\t\t/* revisit if an extension wants them... */\n-\t\t\tswitch (wsi-\u003ews-\u003eopcode) {\n-\t\t\tcase LWSWSOPC_TEXT_FRAME:\n-\t\t\t\twsi-\u003ews-\u003ersv_first_msg \u003d (c \u0026 0x70);\n-\t\t\t\twsi-\u003ews-\u003econtinuation_possible \u003d 1;\n-\t\t\t\twsi-\u003ews-\u003echeck_utf8 \u003d lws_check_opt(\n-\t\t\t\t\twsi-\u003econtext-\u003eoptions,\n-\t\t\t\t\tLWS_SERVER_OPTION_VALIDATE_UTF8);\n-\t\t\t\twsi-\u003ews-\u003eutf8 \u003d 0;\n-\t\t\t\twsi-\u003ews-\u003efirst_fragment \u003d 1;\n-\t\t\t\tbreak;\n-\t\t\tcase LWSWSOPC_BINARY_FRAME:\n-\t\t\t\twsi-\u003ews-\u003ersv_first_msg \u003d (c \u0026 0x70);\n-\t\t\t\twsi-\u003ews-\u003echeck_utf8 \u003d 0;\n-\t\t\t\twsi-\u003ews-\u003econtinuation_possible \u003d 1;\n-\t\t\t\twsi-\u003ews-\u003efirst_fragment \u003d 1;\n-\t\t\t\tbreak;\n-\t\t\tcase LWSWSOPC_CONTINUATION:\n-\t\t\t\tif (!wsi-\u003ews-\u003econtinuation_possible) {\n-\t\t\t\t\tlwsl_info(\u0022disordered continuation\u005cn\u0022);\n-\t\t\t\t\treturn -1;\n-\t\t\t\t}\n-\t\t\t\twsi-\u003ews-\u003efirst_fragment \u003d 0;\n-\t\t\t\tbreak;\n-\t\t\tcase LWSWSOPC_CLOSE:\n-\t\t\t\twsi-\u003ews-\u003echeck_utf8 \u003d 0;\n-\t\t\t\twsi-\u003ews-\u003eutf8 \u003d 0;\n-\t\t\t\tbreak;\n-\t\t\tcase 3:\n-\t\t\tcase 4:\n-\t\t\tcase 5:\n-\t\t\tcase 6:\n-\t\t\tcase 7:\n-\t\t\tcase 0xb:\n-\t\t\tcase 0xc:\n-\t\t\tcase 0xd:\n-\t\t\tcase 0xe:\n-\t\t\tcase 0xf:\n-\t\t\t\tlwsl_info(\u0022illegal opcode\u005cn\u0022);\n-\t\t\t\treturn -1;\n-\t\t\tdefault:\n-\t\t\t\twsi-\u003ews-\u003edefeat_check_utf8 \u003d 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\twsi-\u003ews-\u003ersv \u003d (c \u0026 0x70);\n-\t\t\t/* revisit if an extension wants them... */\n-\t\t\tif (\n-#if !defined(LWS_WITHOUT_EXTENSIONS)\n-\t\t\t\t!wsi-\u003ews-\u003ecount_act_ext \u0026\u0026\n-#endif\n-\t\t\t\twsi-\u003ews-\u003ersv) {\n-\t\t\t\tlwsl_info(\u0022illegal rsv bits set\u005cn\u0022);\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\t\t\twsi-\u003ews-\u003efinal \u003d !!((c \u003e\u003e 7) \u0026 1);\n-\t\t\tlwsl_ext(\u0022%s: This RX frame Final %d\u005cn\u0022, __func__,\n-\t\t\t\t wsi-\u003ews-\u003efinal);\n-\n-\t\t\tif (wsi-\u003ews-\u003eowed_a_fin \u0026\u0026\n-\t\t\t (wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_TEXT_FRAME ||\n-\t\t\t wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_BINARY_FRAME)) {\n-\t\t\t\tlwsl_info(\u0022hey you owed us a FIN\u005cn\u0022);\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\t\t\tif ((!(wsi-\u003ews-\u003eopcode \u0026 8)) \u0026\u0026 wsi-\u003ews-\u003efinal) {\n-\t\t\t\twsi-\u003ews-\u003econtinuation_possible \u003d 0;\n-\t\t\t\twsi-\u003ews-\u003eowed_a_fin \u003d 0;\n-\t\t\t}\n-\n-\t\t\tif ((wsi-\u003ews-\u003eopcode \u0026 8) \u0026\u0026 !wsi-\u003ews-\u003efinal) {\n-\t\t\t\tlwsl_info(\u0022control msg can't be fragmented\u005cn\u0022);\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\t\t\tif (!wsi-\u003ews-\u003efinal)\n-\t\t\t\twsi-\u003ews-\u003eowed_a_fin \u003d 1;\n-\n-\t\t\tswitch (wsi-\u003ews-\u003eopcode) {\n-\t\t\tcase LWSWSOPC_TEXT_FRAME:\n-\t\t\tcase LWSWSOPC_BINARY_FRAME:\n-\t\t\t\twsi-\u003ews-\u003eframe_is_binary \u003d wsi-\u003ews-\u003eopcode \u003d\u003d\n-\t\t\t\t\t\t LWSWSOPC_BINARY_FRAME;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN;\n-\t\t\tbreak;\n-\n-\t\tdefault:\n-\t\t\tlwsl_err(\u0022unknown spec version %02d\u005cn\u0022,\n-\t\t\t\t wsi-\u003ews-\u003eietf_spec_revision);\n-\t\t\tbreak;\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN:\n-\n-\t\twsi-\u003ews-\u003ethis_frame_masked \u003d !!(c \u0026 0x80);\n-\n-\t\tswitch (c \u0026 0x7f) {\n-\t\tcase 126:\n-\t\t\t/* control frames are not allowed to have big lengths */\n-\t\t\tif (wsi-\u003ews-\u003eopcode \u0026 8)\n-\t\t\t\tgoto illegal_ctl_length;\n-\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN16_2;\n-\t\t\tbreak;\n-\t\tcase 127:\n-\t\t\t/* control frames are not allowed to have big lengths */\n-\t\t\tif (wsi-\u003ews-\u003eopcode \u0026 8)\n-\t\t\t\tgoto illegal_ctl_length;\n-\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_8;\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\twsi-\u003ews-\u003erx_packet_length \u003d c \u0026 0x7f;\n-\t\t\tif (wsi-\u003ews-\u003ethis_frame_masked)\n-\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n-\t\t\t\t\t\tLWS_RXPS_07_COLLECT_FRAME_KEY_1;\n-\t\t\telse {\n-\t\t\t\tif (wsi-\u003ews-\u003erx_packet_length) {\n-\t\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n-\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n-\t\t\t\t} else {\n-\t\t\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n-\t\t\t\t\tgoto spill;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tbreak;\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN16_2:\n-\t\twsi-\u003ews-\u003erx_packet_length \u003d c \u003c\u003c 8;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN16_1;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN16_1:\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d c;\n-\t\tif (wsi-\u003ews-\u003ethis_frame_masked)\n-\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_1;\n-\t\telse {\n-\t\t\tif (wsi-\u003ews-\u003erx_packet_length)\n-\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n-\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n-\t\t\telse {\n-\t\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n-\t\t\t\tgoto spill;\n-\t\t\t}\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_8:\n-\t\tif (c \u0026 0x80) {\n-\t\t\tlwsl_warn(\u0022b63 of length must be zero\u005cn\u0022);\n-\t\t\t/* kill the connection */\n-\t\t\treturn -1;\n-\t\t}\n-#if defined __LP64__\n-\t\twsi-\u003ews-\u003erx_packet_length \u003d ((size_t)c) \u003c\u003c 56;\n-#else\n-\t\twsi-\u003ews-\u003erx_packet_length \u003d 0;\n-#endif\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_7;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_7:\n-#if defined __LP64__\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 48;\n-#endif\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_6;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_6:\n-#if defined __LP64__\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 40;\n-#endif\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_5;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_5:\n-#if defined __LP64__\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 32;\n-#endif\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_4;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_4:\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 24;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_3;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_3:\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 16;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_2;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_2:\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d ((size_t)c) \u003c\u003c 8;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_04_FRAME_HDR_LEN64_1;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_04_FRAME_HDR_LEN64_1:\n-\t\twsi-\u003ews-\u003erx_packet_length |\u003d (size_t)c;\n-\t\tif (wsi-\u003ews-\u003ethis_frame_masked)\n-\t\t\twsi-\u003elws_rx_parse_state \u003d\n-\t\t\t\t\tLWS_RXPS_07_COLLECT_FRAME_KEY_1;\n-\t\telse {\n-\t\t\tif (wsi-\u003ews-\u003erx_packet_length)\n-\t\t\t\twsi-\u003elws_rx_parse_state \u003d\n-\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n-\t\t\telse {\n-\t\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n-\t\t\t\tgoto spill;\n-\t\t\t}\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_1:\n-\t\twsi-\u003ews-\u003emask[0] \u003d c;\n-\t\tif (c)\n-\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_2;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_2:\n-\t\twsi-\u003ews-\u003emask[1] \u003d c;\n-\t\tif (c)\n-\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_3;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_3:\n-\t\twsi-\u003ews-\u003emask[2] \u003d c;\n-\t\tif (c)\n-\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n-\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_07_COLLECT_FRAME_KEY_4;\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_07_COLLECT_FRAME_KEY_4:\n-\t\twsi-\u003ews-\u003emask[3] \u003d c;\n-\t\tif (c)\n-\t\t\twsi-\u003ews-\u003eall_zero_nonce \u003d 0;\n-\n-\t\tif (wsi-\u003ews-\u003erx_packet_length)\n-\t\t\twsi-\u003elws_rx_parse_state \u003d\n-\t\t\t\t\tLWS_RXPS_WS_FRAME_PAYLOAD;\n-\t\telse {\n-\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n-\t\t\tgoto spill;\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_RXPS_WS_FRAME_PAYLOAD:\n-\n-\t\tassert(wsi-\u003ews-\u003erx_ubuf);\n-\n-\t\tif (wsi-\u003ews-\u003erx_draining_ext)\n-\t\t\tgoto drain_extension;\n-\n-\t\tif (wsi-\u003ews-\u003ethis_frame_masked \u0026\u0026 !wsi-\u003ews-\u003eall_zero_nonce)\n-\t\t\tc ^\u003d wsi-\u003ews-\u003emask[(wsi-\u003ews-\u003emask_idx++) \u0026 3];\n-\n-\t\twsi-\u003ews-\u003erx_ubuf[LWS_PRE + (wsi-\u003ews-\u003erx_ubuf_head++)] \u003d c;\n-\n-\t\tif (--wsi-\u003ews-\u003erx_packet_length \u003d\u003d 0) {\n-\t\t\t/* spill because we have the whole frame */\n-\t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n-\t\t\tgoto spill;\n-\t\t}\n-\n-\t\t/*\n-\t\t * if there's no protocol max frame size given, we are\n-\t\t * supposed to default to context-\u003ept_serv_buf_size\n-\t\t */\n-\t\tif (!wsi-\u003eprotocol-\u003erx_buffer_size \u0026\u0026\n-\t\t wsi-\u003ews-\u003erx_ubuf_head !\u003d wsi-\u003econtext-\u003ept_serv_buf_size)\n-\t\t\tbreak;\n-\n-\t\tif (wsi-\u003eprotocol-\u003erx_buffer_size \u0026\u0026\n-\t\t wsi-\u003ews-\u003erx_ubuf_head !\u003d wsi-\u003eprotocol-\u003erx_buffer_size)\n-\t\t\tbreak;\n-\n-\t\t/* spill because we filled our rx buffer */\n-spill:\n-\n-\t\thandled \u003d 0;\n-\n-\t\t/*\n-\t\t * is this frame a control packet we should take care of at this\n-\t\t * layer? If so service it and hide it from the user callback\n-\t\t */\n-\n-\t\tswitch (wsi-\u003ews-\u003eopcode) {\n-\t\tcase LWSWSOPC_CLOSE:\n-\t\t\tpp \u003d (unsigned char *)\u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE];\n-\t\t\tif (lws_check_opt(wsi-\u003econtext-\u003eoptions,\n-\t\t\t\t\t LWS_SERVER_OPTION_VALIDATE_UTF8) \u0026\u0026\n-\t\t\t wsi-\u003ews-\u003erx_ubuf_head \u003e 2 \u0026\u0026\n-\t\t\t lws_check_utf8(\u0026wsi-\u003ews-\u003eutf8, pp + 2,\n-\t\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head - 2))\n-\t\t\t\tgoto utf8_fail;\n-\n-\t\t\t/* is this an acknowledgment of our close? */\n-\t\t\tif (lwsi_state(wsi) \u003d\u003d LRS_AWAITING_CLOSE_ACK) {\n-\t\t\t\t/*\n-\t\t\t\t * fine he has told us he is closing too, let's\n-\t\t\t\t * finish our close\n-\t\t\t\t */\n-\t\t\t\tlwsl_parser(\u0022seen server's close ack\u005cn\u0022);\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\n-\t\t\tlwsl_parser(\u0022client sees server close len \u003d %d\u005cn\u0022,\n-\t\t\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n-\t\t\tif (wsi-\u003ews-\u003erx_ubuf_head \u003e\u003d 2) {\n-\t\t\t\tclose_code \u003d (pp[0] \u003c\u003c 8) | pp[1];\n-\t\t\t\tif (close_code \u003c 1000 ||\n-\t\t\t\t close_code \u003d\u003d 1004 ||\n-\t\t\t\t close_code \u003d\u003d 1005 ||\n-\t\t\t\t close_code \u003d\u003d 1006 ||\n-\t\t\t\t close_code \u003d\u003d 1012 ||\n-\t\t\t\t close_code \u003d\u003d 1013 ||\n-\t\t\t\t close_code \u003d\u003d 1014 ||\n-\t\t\t\t close_code \u003d\u003d 1015 ||\n-\t\t\t\t (close_code \u003e\u003d 1016 \u0026\u0026 close_code \u003c 3000)\n-\t\t\t\t) {\n-\t\t\t\t\tpp[0] \u003d (LWS_CLOSE_STATUS_PROTOCOL_ERR \u003e\u003e 8) \u0026 0xff;\n-\t\t\t\t\tpp[1] \u003d LWS_CLOSE_STATUS_PROTOCOL_ERR \u0026 0xff;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif (user_callback_handle_rxflow(\n-\t\t\t\t\twsi-\u003eprotocol-\u003ecallback, wsi,\n-\t\t\t\t\tLWS_CALLBACK_WS_PEER_INITIATED_CLOSE,\n-\t\t\t\t\twsi-\u003euser_space, pp,\n-\t\t\t\t\twsi-\u003ews-\u003erx_ubuf_head))\n-\t\t\t\treturn -1;\n-\n-\t\t\tmemcpy(wsi-\u003ews-\u003eping_payload_buf + LWS_PRE, pp,\n-\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n-\t\t\twsi-\u003ews-\u003eclose_in_ping_buffer_len \u003d wsi-\u003ews-\u003erx_ubuf_head;\n-\n-\t\t\tlwsl_notice(\u0022%s: scheduling return close as ack\u005cn\u0022, __func__);\n-\t\t\t__lws_change_pollfd(wsi, LWS_POLLIN, 0);\n-\t\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);\n-\t\t\twsi-\u003ewaiting_to_send_close_frame \u003d 1;\n-\t\t\twsi-\u003eclose_needs_ack \u003d 0;\n-\t\t\tlwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);\n-\t\t\tlws_callback_on_writable(wsi);\n-\t\t\thandled \u003d 1;\n-\t\t\tbreak;\n-\n-\t\tcase LWSWSOPC_PING:\n-\t\t\tlwsl_info(\u0022received %d byte ping, sending pong\u005cn\u0022,\n-\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n-\n-\t\t\t/* he set a close reason on this guy, ignore PING */\n-\t\t\tif (wsi-\u003ews-\u003eclose_in_ping_buffer_len)\n-\t\t\t\tgoto ping_drop;\n-\n-\t\t\tif (wsi-\u003ews-\u003eping_pending_flag) {\n-\t\t\t\t/*\n-\t\t\t\t * there is already a pending ping payload\n-\t\t\t\t * we should just log and drop\n-\t\t\t\t */\n-\t\t\t\tlwsl_parser(\u0022DROP PING since one pending\u005cn\u0022);\n-\t\t\t\tgoto ping_drop;\n-\t\t\t}\n-\n-\t\t\t/* control packets can only be \u003c 128 bytes long */\n-\t\t\tif (wsi-\u003ews-\u003erx_ubuf_head \u003e 128 - 3) {\n-\t\t\t\tlwsl_parser(\u0022DROP PING payload too large\u005cn\u0022);\n-\t\t\t\tgoto ping_drop;\n-\t\t\t}\n-\n-\t\t\t/* stash the pong payload */\n-\t\t\tmemcpy(wsi-\u003ews-\u003eping_payload_buf + LWS_PRE,\n-\t\t\t \u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE],\n-\t\t\t\twsi-\u003ews-\u003erx_ubuf_head);\n-\n-\t\t\twsi-\u003ews-\u003eping_payload_len \u003d wsi-\u003ews-\u003erx_ubuf_head;\n-\t\t\twsi-\u003ews-\u003eping_pending_flag \u003d 1;\n-\n-\t\t\t/* get it sent as soon as possible */\n-\t\t\tlws_callback_on_writable(wsi);\n-ping_drop:\n-\t\t\twsi-\u003ews-\u003erx_ubuf_head \u003d 0;\n-\t\t\thandled \u003d 1;\n-\t\t\tbreak;\n-\n-\t\tcase LWSWSOPC_PONG:\n-\t\t\tlwsl_info(\u0022client receied pong\u005cn\u0022);\n-\t\t\tlwsl_hexdump(\u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE],\n-\t\t\t\t wsi-\u003ews-\u003erx_ubuf_head);\n-\n-\t\t\tif (wsi-\u003epending_timeout \u003d\u003d\n-\t\t\t\t PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {\n-\t\t\t\tlwsl_info(\u0022%p: received expected PONG\u005cn\u0022, wsi);\n-\t\t\t\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n-\t\t\t}\n-\n-\t\t\t/* issue it */\n-\t\t\tcallback_action \u003d LWS_CALLBACK_CLIENT_RECEIVE_PONG;\n-\t\t\tbreak;\n-\n-\t\tcase LWSWSOPC_CONTINUATION:\n-\t\tcase LWSWSOPC_TEXT_FRAME:\n-\t\tcase LWSWSOPC_BINARY_FRAME:\n-\t\t\tbreak;\n-\n-\t\tdefault:\n-\t\t\t/* not handled or failed */\n-\t\t\tlwsl_ext(\u0022Unhandled ext opc 0x%x\u005cn\u0022, wsi-\u003ews-\u003eopcode);\n-\t\t\twsi-\u003ews-\u003erx_ubuf_head \u003d 0;\n-\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\t/*\n-\t\t * No it's real payload, pass it up to the user callback.\n-\t\t * It's nicely buffered with the pre-padding taken care of\n-\t\t * so it can be sent straight out again using lws_write\n-\t\t */\n-\t\tif (handled)\n-\t\t\tgoto already_done;\n-\n-\t\tebuf.token \u003d \u0026wsi-\u003ews-\u003erx_ubuf[LWS_PRE];\n-\t\tebuf.len \u003d wsi-\u003ews-\u003erx_ubuf_head;\n-\n-\t\tif (wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_PONG \u0026\u0026 !ebuf.len)\n-\t\t\tgoto already_done;\n-\n-drain_extension:\n-#if !defined(LWS_WITHOUT_EXTENSIONS)\n-\t\tlwsl_ext(\u0022%s: passing %d to ext\u005cn\u0022, __func__, ebuf.len);\n-\n-\t\tn \u003d lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, \u0026ebuf, 0);\n-\t\tlwsl_ext(\u0022Ext RX returned %d\u005cn\u0022, n);\n-\t\tif (n \u003c 0) {\n-\t\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n-\t\t\treturn -1;\n-\t\t}\n-#else\n-\t\tn \u003d 0;\n-#endif\n-\t\tlwsl_notice(\u0022post inflate ebuf len %d\u005cn\u0022, ebuf.len);\n-\n-\t\tif (rx_draining_ext \u0026\u0026 !ebuf.len) {\n-\t\t\tlwsl_debug(\u0022 --- ending drain on 0 read result\u005cn\u0022);\n-\t\t\tgoto already_done;\n-\t\t}\n-\n-\t\tif (wsi-\u003ews-\u003echeck_utf8 \u0026\u0026 !wsi-\u003ews-\u003edefeat_check_utf8) {\n-\t\t\tif (lws_check_utf8(\u0026wsi-\u003ews-\u003eutf8,\n-\t\t\t\t\t (unsigned char *)ebuf.token,\n-\t\t\t\t\t ebuf.len)) {\n-\t\t\t\tlws_close_reason(wsi,\n-\t\t\t\t\tLWS_CLOSE_STATUS_INVALID_PAYLOAD,\n-\t\t\t\t\t(uint8_t *)\u0022bad utf8\u0022, 8);\n-\t\t\t\tgoto utf8_fail;\n-\t\t\t}\n-\n-\t\t\t/* we are ending partway through utf-8 character? */\n-\t\t\tif (!wsi-\u003ews-\u003erx_packet_length \u0026\u0026 wsi-\u003ews-\u003efinal \u0026\u0026\n-\t\t\t wsi-\u003ews-\u003eutf8 \u0026\u0026 !n) {\n-\t\t\t\tlwsl_info(\u0022FINAL utf8 error\u005cn\u0022);\n-\t\t\t\tlws_close_reason(wsi,\n-\t\t\t\t\tLWS_CLOSE_STATUS_INVALID_PAYLOAD,\n-\t\t\t\t\t(uint8_t *)\u0022partial utf8\u0022, 12);\n-utf8_fail:\n-\t\t\t\tlwsl_info(\u0022utf8 error\u005cn\u0022);\n-\t\t\t\tlwsl_hexdump_info(ebuf.token, ebuf.len);\n-\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (ebuf.len \u003c 0 \u0026\u0026\n-\t\t callback_action !\u003d LWS_CALLBACK_CLIENT_RECEIVE_PONG)\n-\t\t\tgoto already_done;\n-\n-\t\tif (!ebuf.token)\n-\t\t\tgoto already_done;\n-\n-\t\tebuf.token[ebuf.len] \u003d '\u005c0';\n-\n-\t\tif (!wsi-\u003eprotocol-\u003ecallback)\n-\t\t\tgoto already_done;\n-\n-\t\tif (callback_action \u003d\u003d LWS_CALLBACK_CLIENT_RECEIVE_PONG)\n-\t\t\tlwsl_info(\u0022Client doing pong callback\u005cn\u0022);\n-\n-\t\tif (\n-\t\t\t\t/* coverity says dead code otherwise */\n-#if !defined(LWS_WITHOUT_EXTENSIONS)\n-\t\t\t\tn \u0026\u0026\n-#endif\n-\t\t\t\tebuf.len)\n-\t\t\t/* extension had more... main loop will come back\n-\t\t\t * we want callback to be done with this set, if so,\n-\t\t\t * because lws_is_final() hides it was final until the\n-\t\t\t * last chunk\n-\t\t\t */\n-\t\t\tlws_add_wsi_to_draining_ext_list(wsi);\n-\t\telse\n-\t\t\tlws_remove_wsi_from_draining_ext_list(wsi);\n-\n-\t\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE ||\n-\t\t lwsi_state(wsi) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE ||\n-\t\t lwsi_state(wsi) \u003d\u003d LRS_AWAITING_CLOSE_ACK)\n-\t\t\tgoto already_done;\n-\n-\t\tm \u003d wsi-\u003eprotocol-\u003ecallback(wsi,\n-\t\t\t(enum lws_callback_reasons)callback_action,\n-\t\t\twsi-\u003euser_space, ebuf.token, ebuf.len);\n-\n-\t\twsi-\u003ews-\u003efirst_fragment \u003d 0;\n-\n-\t\t// lwsl_notice(\u0022%s: bulk ws rx: input used %d, output %d\u005cn\u0022,\n-\t\t//\t__func__, wsi-\u003ews-\u003erx_ubuf_head, ebuf.len);\n-\n-\t\t/* if user code wants to close, let caller know */\n-\t\tif (m)\n-\t\t\treturn 1;\n-\n-already_done:\n-\t\twsi-\u003ews-\u003erx_ubuf_head \u003d 0;\n-\t\tbreak;\n-\tdefault:\n-\t\tlwsl_err(\u0022client rx illegal state\u005cn\u0022);\n-\t\treturn 1;\n-\t}\n-\n-\treturn 0;\n-\n-illegal_ctl_length:\n-\tlwsl_warn(\u0022Control frame asking for extended length is illegal\u005cn\u0022);\n-\n-\t/* kill the connection */\n-\treturn -1;\n-}\n-\n-\ndiff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c\nindex f7ef693..2fe5b4f 100644\n--- a/lib/roles/ws/client-ws.c\n+++ b/lib/roles/ws/client-ws.c\n@@ -61,6 +61,57 @@ lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)\n \treturn 0;\n }\n \n+#if !defined(LWS_NO_CLIENT)\n+int\n+lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)\n+{\n+\tif ((lwsi_state(wsi) !\u003d LRS_WAITING_PROXY_REPLY) \u0026\u0026\n+\t (lwsi_state(wsi) !\u003d LRS_H1C_ISSUE_HANDSHAKE) \u0026\u0026\n+\t (lwsi_state(wsi) !\u003d LRS_WAITING_SERVER_REPLY) \u0026\u0026\n+\t !lwsi_role_client(wsi))\n+\t\treturn 0;\n+\n+\t// lwsl_notice(\u0022%s: hs client gets %d in\u005cn\u0022, __func__, (int)len);\n+\n+\twhile (len) {\n+\t\t/*\n+\t\t * we were accepting input but now we stopped doing so\n+\t\t */\n+\t\tif (lws_is_flowcontrolled(wsi)) {\n+\t\t\t//lwsl_notice(\u0022%s: caching %ld\u005cn\u0022, __func__, (long)len);\n+\t\t\tlws_rxflow_cache(wsi, *buf, 0, (int)len);\n+\t\t\t*buf +\u003d len;\n+\t\t\treturn 0;\n+\t\t}\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\t\tif (wsi-\u003ews-\u003erx_draining_ext) {\n+\t\t\tint m;\n+\n+\t\t\t//lwsl_notice(\u0022%s: draining ext\u005cn\u0022, __func__);\n+\t\t\tif (lwsi_role_client(wsi))\n+\t\t\t\tm \u003d lws_ws_client_rx_sm(wsi, 0);\n+\t\t\telse\n+\t\t\t\tm \u003d lws_ws_rx_sm(wsi, 0, 0);\n+\t\t\tif (m \u003c 0)\n+\t\t\t\treturn -1;\n+\t\t\tcontinue;\n+\t\t}\n+#endif\n+\t\t/* caller will account for buflist usage */\n+\n+\t\tif (lws_ws_client_rx_sm(wsi, *(*buf)++)) {\n+\t\t\tlwsl_notice(\u0022%s: client_rx_sm exited, DROPPING %d\u005cn\u0022,\n+\t\t\t\t __func__, (int)len);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tlen--;\n+\t}\n+\t// lwsl_notice(\u0022%s: finished with %ld\u005cn\u0022, __func__, (long)len);\n+\n+\treturn 0;\n+}\n+#endif\n+\n char *\n lws_generate_client_ws_handshake(struct lws *wsi, char *p)\n {\ndiff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c\nindex 369b892..acc2023 100644\n--- a/lib/roles/ws/ops-ws.c\n+++ b/lib/roles/ws/ops-ws.c\n@@ -48,6 +48,7 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)\n \n \tswitch (wsi-\u003elws_rx_parse_state) {\n \tcase LWS_RXPS_NEW:\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t\tif (wsi-\u003ews-\u003erx_draining_ext) {\n \t\t\tebuf.token \u003d NULL;\n \t\t\tebuf.len \u003d 0;\n@@ -57,6 +58,7 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)\n \n \t\t\tgoto drain_extension;\n \t\t}\n+#endif\n \t\tswitch (wsi-\u003ews-\u003eietf_spec_revision) {\n \t\tcase 13:\n \t\t\t/*\n@@ -385,12 +387,12 @@ handle_first:\n \t\t\twsi-\u003elws_rx_parse_state \u003d LWS_RXPS_NEW;\n \t\t\tgoto spill;\n \t\t}\n-\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t\tif (wsi-\u003ews-\u003erx_draining_ext) {\n \t\t\tlwsl_debug(\u0022%s: UNTIL_EXHAUSTED draining\u005cn\u0022, __func__);\n \t\t\tgoto drain_extension;\n \t\t}\n-\n+#endif\n \t\t/*\n \t\t * if there's no protocol max frame size given, we are\n \t\t * supposed to default to context-\u003ept_serv_buf_size\n@@ -554,8 +556,9 @@ ping_drop:\n \n \t\tif (wsi-\u003ews-\u003eopcode \u003d\u003d LWSWSOPC_PONG \u0026\u0026 !ebuf.len)\n \t\t\tgoto already_done;\n-\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n drain_extension:\n+#endif\n \t\t// lwsl_notice(\u0022%s: passing %d to ext\u005cn\u0022, __func__, ebuf.len);\n \n \t\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE ||\n@@ -676,6 +679,7 @@ LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)\n void\n lws_add_wsi_to_draining_ext_list(struct lws *wsi)\n {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003econtext-\u003ept[(int)wsi-\u003etsi];\n \n \tif (wsi-\u003ews-\u003erx_draining_ext)\n@@ -684,15 +688,17 @@ lws_add_wsi_to_draining_ext_list(struct lws *wsi)\n \tlwsl_debug(\u0022%s: RX EXT DRAINING: Adding to list\u005cn\u0022, __func__);\n \n \twsi-\u003ews-\u003erx_draining_ext \u003d 1;\n-\twsi-\u003ews-\u003erx_draining_ext_list \u003d pt-\u003erx_draining_ext_list;\n-\tpt-\u003erx_draining_ext_list \u003d wsi;\n+\twsi-\u003ews-\u003erx_draining_ext_list \u003d pt-\u003ews.rx_draining_ext_list;\n+\tpt-\u003ews.rx_draining_ext_list \u003d wsi;\n+#endif\n }\n \n void\n lws_remove_wsi_from_draining_ext_list(struct lws *wsi)\n {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003econtext-\u003ept[(int)wsi-\u003etsi];\n-\tstruct lws **w \u003d \u0026pt-\u003erx_draining_ext_list;\n+\tstruct lws **w \u003d \u0026pt-\u003ews.rx_draining_ext_list;\n \n \tif (!wsi-\u003ews-\u003erx_draining_ext)\n \t\treturn;\n@@ -711,6 +717,7 @@ lws_remove_wsi_from_draining_ext_list(struct lws *wsi)\n \t\tw \u003d \u0026((*w)-\u003ews-\u003erx_draining_ext_list);\n \t}\n \twsi-\u003ews-\u003erx_draining_ext_list \u003d NULL;\n+#endif\n }\n \n LWS_EXTERN void\n@@ -801,11 +808,15 @@ lws_server_init_wsi_for_ws(struct lws *wsi)\n LWS_VISIBLE int\n lws_is_final_fragment(struct lws *wsi)\n {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n lwsl_debug(\u0022%s: final %d, rx pk length %ld, draining %ld\u005cn\u0022, __func__,\n \t\t\twsi-\u003ews-\u003efinal, (long)wsi-\u003ews-\u003erx_packet_length,\n \t\t\t(long)wsi-\u003ews-\u003erx_draining_ext);\n \treturn wsi-\u003ews-\u003efinal \u0026\u0026 !wsi-\u003ews-\u003erx_packet_length \u0026\u0026\n \t !wsi-\u003ews-\u003erx_draining_ext;\n+#else\n+\treturn wsi-\u003ews-\u003efinal \u0026\u0026 !wsi-\u003ews-\u003erx_packet_length;\n+#endif\n }\n \n LWS_VISIBLE int\n@@ -933,10 +944,12 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,\n \t\t * draining.\n \t\t */\n \t\tlws_rx_flow_control(wsi, 1);\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t\tif (wsi-\u003ews)\n \t\t\twsi-\u003ews-\u003etx_draining_ext \u003d 0;\n+#endif\n \t}\n-\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tif (wsi-\u003ews-\u003etx_draining_ext)\n \t\t/*\n \t\t * We cannot deal with new RX until the TX ext path has\n@@ -947,7 +960,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,\n \t\t * blocking.\n \t\t */\n \t\treturn LWS_HPI_RET_HANDLED;\n-\n+#endif\n \tif (lws_is_flowcontrolled(wsi)) {\n \t\t/* We cannot deal with any kind of new RX because we are\n \t\t * RX-flowcontrolled.\n@@ -969,6 +982,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,\n \t}\n #endif\n \n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t/* 2: RX Extension needs to be drained\n \t */\n \n@@ -995,6 +1009,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,\n \t\t * priority either.\n \t\t */\n \t\treturn LWS_HPI_RET_HANDLED;\n+#endif\n \n \t/* 3: buflist needs to be drained\n \t */\n@@ -1062,6 +1077,8 @@ read:\n \t\t}\n \t\t// lwsl_notice(\u0022Actual RX %d\u005cn\u0022, ebuf.len);\n \n+\t\tlws_restart_ws_ping_pong_timer(wsi);\n+\n \t\t/*\n \t\t * coverity thinks ssl_capable_read() may read over\n \t\t * 2GB. Dissuade it...\n@@ -1157,8 +1174,10 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)\n #endif\n \tint n;\n \n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tlwsl_debug(\u0022%s: %s: wsi-\u003ews-\u003etx_draining_ext %d\u005cn\u0022, __func__,\n \t\t\twsi-\u003eprotocol-\u003ename, wsi-\u003ews-\u003etx_draining_ext);\n+#endif\n \n \t/* Priority 3: pending control packets (pong or close)\n \t *\n@@ -1250,6 +1269,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)\n \tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE)\n \t\treturn LWS_HP_RET_USER_SERVICE;\n \n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t/* Priority 5: Tx path extension with more to send\n \t *\n \t *\t These are handled as new fragments each time around\n@@ -1267,7 +1287,6 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)\n \n \t/* Priority 6: extensions\n \t */\n-#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tif (!wsi-\u003ews-\u003eextension_data_pending)\n \t\treturn LWS_HP_RET_USER_SERVICE;\n \n@@ -1412,6 +1431,7 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)\n static int\n rops_service_flag_pending_ws(struct lws_context *context, int tsi)\n {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tstruct lws_context_per_thread *pt \u003d \u0026context-\u003ept[tsi];\n \tstruct lws *wsi;\n \tint forced \u003d 0;\n@@ -1422,7 +1442,7 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)\n \t * 1) For all guys with already-available ext data to drain, if they are\n \t * not flowcontrolled, fake their POLLIN status\n \t */\n-\twsi \u003d pt-\u003erx_draining_ext_list;\n+\twsi \u003d pt-\u003ews.rx_draining_ext_list;\n \twhile (wsi) {\n \t\tpt-\u003efds[wsi-\u003eposition_in_fds_table].revents |\u003d\n \t\t\tpt-\u003efds[wsi-\u003eposition_in_fds_table].events \u0026 LWS_POLLIN;\n@@ -1433,6 +1453,9 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)\n \t}\n \n \treturn forced;\n+#else\n+\treturn 0;\n+#endif\n }\n \n static int\n@@ -1466,8 +1489,9 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)\n static int\n rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)\n {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tif (wsi-\u003ews-\u003erx_draining_ext) {\n-\t\tstruct lws **w \u003d \u0026pt-\u003erx_draining_ext_list;\n+\t\tstruct lws **w \u003d \u0026pt-\u003ews.rx_draining_ext_list;\n \n \t\twsi-\u003ews-\u003erx_draining_ext \u003d 0;\n \t\t/* remove us from context draining ext list */\n@@ -1482,7 +1506,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)\n \t}\n \n \tif (wsi-\u003ews-\u003etx_draining_ext) {\n-\t\tstruct lws **w \u003d \u0026pt-\u003etx_draining_ext_list;\n+\t\tstruct lws **w \u003d \u0026pt-\u003ews.tx_draining_ext_list;\n \t\tlwsl_notice(\u0022%s: CLEARING tx_draining_ext\u005cn\u0022, __func__);\n \t\twsi-\u003ews-\u003etx_draining_ext \u003d 0;\n \t\t/* remove us from context draining ext list */\n@@ -1495,6 +1519,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)\n \t\t}\n \t\twsi-\u003ews-\u003etx_draining_ext_list \u003d NULL;\n \t}\n+#endif\n \tlws_free_set_NULL(wsi-\u003ews-\u003erx_ubuf);\n \n \tif (wsi-\u003etrunc_alloc)\n@@ -1504,6 +1529,11 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)\n \twsi-\u003ews-\u003eping_payload_len \u003d 0;\n \twsi-\u003ews-\u003eping_pending_flag \u003d 0;\n \n+\t/* deallocate any active extension contexts */\n+\n+\tif (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) \u003c 0)\n+\t\tlwsl_warn(\u0022extension destruction failed\u005cn\u0022);\n+\n \treturn 0;\n }\n \n@@ -1511,20 +1541,22 @@ static int\n rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t\t enum lws_write_protocol *wp)\n {\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003econtext-\u003ept[(int)wsi-\u003etsi];\n+\tenum lws_write_protocol wpt;\n+#endif\n \tint masked7 \u003d lwsi_role_client(wsi);\n \tunsigned char is_masked_bit \u003d 0;\n \tunsigned char *dropmask \u003d NULL;\n-\tenum lws_write_protocol wpt;\n \tstruct lws_tokens ebuf;\n \tsize_t orig_len \u003d len;\n \tint pre \u003d 0, n \u003d 0;\n \n \t// lwsl_err(\u0022%s: wp 0x%x len %d\u005cn\u0022, __func__, *wp, (int)len);\n-\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tif (wsi-\u003ews-\u003etx_draining_ext) {\n \t\t/* remove us from the list */\n-\t\tstruct lws **w \u003d \u0026pt-\u003etx_draining_ext_list;\n+\t\tstruct lws **w \u003d \u0026pt-\u003ews.tx_draining_ext_list;\n \n \t\tlwsl_notice(\u0022%s: CLEARING tx_draining_ext\u005cn\u0022, __func__);\n \t\twsi-\u003ews-\u003etx_draining_ext \u003d 0;\n@@ -1555,7 +1587,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t\t\twsi-\u003ews-\u003etx_draining_stashed_wp, wpt);\n \t\t// assert(0);\n \t}\n-\n+#endif\n \tlws_restart_ws_ping_pong_timer(wsi);\n \n \tif (((*wp) \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP ||\n@@ -1610,8 +1642,8 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t\tlwsl_notice(\u0022write drain len %d (wp 0x%x) SETTING tx_draining_ext\u005cn\u0022, (int)ebuf.len, *wp);\n \t\t\t/* extension requires further draining */\n \t\t\twsi-\u003ews-\u003etx_draining_ext \u003d 1;\n-\t\t\twsi-\u003ews-\u003etx_draining_ext_list \u003d pt-\u003etx_draining_ext_list;\n-\t\t\tpt-\u003etx_draining_ext_list \u003d wsi;\n+\t\t\twsi-\u003ews-\u003etx_draining_ext_list \u003d pt-\u003ews.tx_draining_ext_list;\n+\t\t\tpt-\u003ews.tx_draining_ext_list \u003d wsi;\n \t\t\t/* we must come back to do more */\n \t\t\tlws_callback_on_writable(wsi);\n \t\t\t/*\n@@ -1861,12 +1893,77 @@ rops_callback_on_writable_ws(struct lws *wsi)\n \treturn 0;\n }\n \n+static int\n+rops_init_vhost_ws(struct lws_vhost *vh, struct lws_context_creation_info *info)\n+{\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+#ifdef LWS_WITH_PLUGINS\n+\tstruct lws_plugin *plugin \u003d vh-\u003econtext-\u003eplugin_list;\n+\tint m;\n+\n+\tif (vh-\u003econtext-\u003eplugin_extension_count) {\n+\n+\t\tm \u003d 0;\n+\t\twhile (info-\u003eextensions \u0026\u0026 info-\u003eextensions[m].callback)\n+\t\t\tm++;\n+\n+\t\t/*\n+\t\t * give the vhost a unified list of extensions including the\n+\t\t * ones that came from plugins\n+\t\t */\n+\t\tvh-\u003ews.extensions \u003d lws_zalloc(sizeof(struct lws_extension) *\n+\t\t\t\t (m + vh-\u003econtext-\u003eplugin_extension_count + 1),\n+\t\t\t\t \u0022extensions\u0022);\n+\t\tif (!vh-\u003ews.extensions)\n+\t\t\treturn 1;\n+\n+\t\tmemcpy((struct lws_extension *)vh-\u003ews.extensions, info-\u003eextensions,\n+\t\t sizeof(struct lws_extension) * m);\n+\t\tplugin \u003d vh-\u003econtext-\u003eplugin_list;\n+\t\twhile (plugin) {\n+\t\t\tmemcpy((struct lws_extension *)\u0026vh-\u003ews.extensions[m],\n+\t\t\t\tplugin-\u003ecaps.extensions,\n+\t\t\t sizeof(struct lws_extension) *\n+\t\t\t plugin-\u003ecaps.count_extensions);\n+\t\t\tm +\u003d plugin-\u003ecaps.count_extensions;\n+\t\t\tplugin \u003d plugin-\u003elist;\n+\t\t}\n+\t} else\n+#endif\n+\t\tvh-\u003ews.extensions \u003d info-\u003eextensions;\n+#endif\n+\n+\treturn 0;\n+}\n+\n+static int\n+rops_destroy_vhost_ws(struct lws_vhost *vh)\n+{\n+#ifdef LWS_WITH_PLUGINS\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n+\tif (vh-\u003econtext-\u003eplugin_extension_count)\n+\t\tlws_free((void *)vh-\u003ews.extensions);\n+#endif\n+#endif\n+\n+\treturn 0;\n+}\n+\n+static int\n+rops_destroy_role_ws(struct lws *wsi)\n+{\n+\tlws_free_set_NULL(wsi-\u003ews);\n+\n+\treturn 0;\n+}\n+\n struct lws_role_ops role_ops_ws \u003d {\n \t/* role name */\t\t\t\u0022ws\u0022,\n \t/* alpn id */\t\t\tNULL,\n \t/* check_upgrades */\t\tNULL,\n \t/* init_context */\t\tNULL,\n-\t/* init_vhost */\t\tNULL,\n+\t/* init_vhost */\t\trops_init_vhost_ws,\n+\t/* destroy_vhost */\t\trops_destroy_vhost_ws,\n \t/* periodic_checks */\t\trops_periodic_checks_ws,\n \t/* service_flag_pending */\trops_service_flag_pending_ws,\n \t/* handle_POLLIN */\t\trops_handle_POLLIN_ws,\n@@ -1880,7 +1977,7 @@ struct lws_role_ops role_ops_ws \u003d {\n \t/* close_via_role_protocol */\trops_close_via_role_protocol_ws,\n \t/* close_role */\t\trops_close_role_ws,\n \t/* close_kill_connection */\trops_close_kill_connection_ws,\n-\t/* destroy_role */\t\tNULL,\n+\t/* destroy_role */\t\trops_destroy_role_ws,\n \t/* writeable cb clnt, srv */\t{ LWS_CALLBACK_CLIENT_WRITEABLE,\n \t\t\t\t\t LWS_CALLBACK_SERVER_WRITEABLE },\n \t/* close cb clnt, srv */\t{ LWS_CALLBACK_CLIENT_CLOSED,\ndiff --git a/lib/roles/ws/private.h b/lib/roles/ws/private.h\nindex 2fd1aa6..8f81b9e 100644\n--- a/lib/roles/ws/private.h\n+++ b/lib/roles/ws/private.h\n@@ -73,20 +73,25 @@ enum lws_websocket_opcodes_07 {\n #define ALREADY_PROCESSED_IGNORE_CHAR 1\n #define ALREADY_PROCESSED_NO_CB 2\n \n-struct lws_vhost_role_ws {\n #if !defined(LWS_WITHOUT_EXTENSIONS)\n+struct lws_vhost_role_ws {\n \tconst struct lws_extension *extensions;\n-#endif\n };\n \n+struct lws_pt_role_ws {\n+\tstruct lws *rx_draining_ext_list;\n+\tstruct lws *tx_draining_ext_list;\n+};\n+#endif\n+\n struct _lws_websocket_related {\n \tchar *rx_ubuf;\n #if !defined(LWS_WITHOUT_EXTENSIONS)\n \tconst struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];\n \tvoid *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];\n-#endif\n \tstruct lws *rx_draining_ext_list;\n \tstruct lws *tx_draining_ext_list;\n+#endif\n \t/* Also used for close content... control opcode \u003d\u003d \u003c 128 */\n \tuint8_t ping_payload_buf[128 - 3 + LWS_PRE];\n \tuint8_t mask[4];\n@@ -120,20 +125,22 @@ struct _lws_websocket_related {\n \tunsigned int owed_a_fin:1;\n \tunsigned int check_utf8:1;\n \tunsigned int defeat_check_utf8:1;\n-\tunsigned int pmce_compressed_message:1;\n \tunsigned int stashed_write_pending:1;\n-\tunsigned int rx_draining_ext:1;\n-\tunsigned int tx_draining_ext:1;\n \tunsigned int send_check_ping:1;\n \tunsigned int first_fragment:1;\n \tunsigned int peer_has_sent_close:1;\n #if !defined(LWS_WITHOUT_EXTENSIONS)\n \tunsigned int extension_data_pending:1;\n+\tunsigned int rx_draining_ext:1;\n+\tunsigned int tx_draining_ext:1;\n \n \tuint8_t count_act_ext;\n #endif\n };\n \n+int\n+lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);\n+\n #if !defined(LWS_WITHOUT_EXTENSIONS)\n LWS_VISIBLE void\n lws_context_init_extensions(struct lws_context_creation_info *info,\ndiff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c\nindex 3dca2e8..8327918 100644\n--- a/lib/roles/ws/server-ws.c\n+++ b/lib/roles/ws/server-ws.c\n@@ -736,8 +736,10 @@ utf8_fail:\n \n \twsi-\u003ews-\u003efirst_fragment \u003d 0;\n \n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \tlwsl_info(\u0022%s: input used %d, output %d, rem len %d, rx_draining_ext %d\u005cn\u0022,\n \t\t __func__, avail, ebuf.len, (int)len, wsi-\u003ews-\u003erx_draining_ext);\n+#endif\n \n \treturn avail; /* how much we used from the input */\n }\n@@ -765,7 +767,7 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)\n \t\t\t*buf +\u003d len; /* stashing it is taking care of it */\n \t\t\treturn 1;\n \t\t}\n-\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t\tif (wsi-\u003ews-\u003erx_draining_ext) {\n \t\t\tlwsl_debug(\u0022%s: draining rx ext\u005cn\u0022, __func__);\n \t\t\tm \u003d lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);\n@@ -773,6 +775,7 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)\n \t\t\t\treturn -1;\n \t\t\tcontinue;\n \t\t}\n+#endif\n \n \t\t/* consume payload bytes efficiently */\n \t\twhile (wsi-\u003elws_rx_parse_state \u003d\u003d LWS_RXPS_WS_FRAME_PAYLOAD \u0026\u0026\n@@ -807,10 +810,12 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)\n \t\t\t * We already handled this byte in bulk, just deal\n \t\t\t * with the ramifications\n \t\t\t */\n+#if !defined(LWS_WITHOUT_EXTENSIONS)\n \t\t\tlwsl_debug(\u0022%s: coming out of bulk with len %d, \u0022\n \t\t\t\t \u0022wsi-\u003ews-\u003erx_draining_ext %d\u005cn\u0022,\n \t\t\t\t __func__, (int)len,\n \t\t\t\t wsi-\u003ews-\u003erx_draining_ext);\n+#endif\n \t\t\tm \u003d lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |\n \t\t\t\t\t ALREADY_PROCESSED_NO_CB, 0);\n \t\t}\ndiff --git a/lib/service.c b/lib/service.c\nindex 3c44ef2..2ead7ff 100644\n--- a/lib/service.c\n+++ b/lib/service.c\n@@ -41,8 +41,6 @@ lws_callback_as_writeable(struct lws *wsi)\n \t}\n #endif\n \n-\tassert(!(lwsi_role_ws(wsi) \u0026\u0026 wsi-\u003ews-\u003etx_draining_ext));\n-\n \tn \u003d wsi-\u003erole_ops-\u003ewriteable_cb[lwsi_role_server(wsi)];\n \n \tm \u003d user_callback_handle_rxflow(wsi-\u003eprotocol-\u003ecallback,\n@@ -319,9 +317,9 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)\n \t * We only need to wait if really nothing already to do and we have\n \t * to wait for something from network\n \t */\n-#if defined(LWS_ROLE_WS)\n+#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_WITHOUT_EXTENSIONS)\n \t/* 1) if we know we are draining rx ext, do not wait in poll */\n-\tif (pt-\u003erx_draining_ext_list)\n+\tif (pt-\u003ews.rx_draining_ext_list)\n \t\treturn 0;\n #endif\n \ndiff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c\nindex b85567f..1492843 100644\n--- a/lib/tls/openssl/ssl.c\n+++ b/lib/tls/openssl/ssl.c\n@@ -255,8 +255,6 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)\n \tif (wsi-\u003evhost)\n \t\twsi-\u003evhost-\u003econn_stats.rx +\u003d n;\n \n-\tlws_restart_ws_ping_pong_timer(wsi);\n-\n \t// lwsl_hexdump_err(buf, n);\n \n \t/*\ndiff --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\nindex 6e0af9c..c76f91e 100644\n--- a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt\n+++ b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/CMakeLists.txt\n@@ -63,8 +63,8 @@ MACRO(require_lws_config reqconfig _val result)\n ENDMACRO()\n \n set(requirements 1)\n-require_lws_config(LWS_WITHOUT_SERVER 0 requirements)\n-require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)\n+require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)\n+#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)\n \n if (requirements)\n \tadd_executable(${SAMP} ${SRCS})\ndiff --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\nindex 82b7065..962c988 100644\n--- a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt\n+++ b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/CMakeLists.txt\n@@ -64,7 +64,7 @@ ENDMACRO()\n \n set(requirements 1)\n require_lws_config(LWS_WITHOUT_SERVER 0 requirements)\n-require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)\n+#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)\n \n if (requirements)\n \tadd_executable(${SAMP} ${SRCS})\n@@ -75,4 +75,4 @@ if (requirements)\n \telse()\n \t\ttarget_link_libraries(${SAMP} websockets)\n \tendif()\n-endif()\n\u005c No newline at end of file\n+endif()\ndiff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c\nindex 8af2edd..43e707d 100644\n--- a/plugins/protocol_dumb_increment.c\n+++ b/plugins/protocol_dumb_increment.c\n@@ -109,9 +109,9 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,\n \tcase LWS_CALLBACK_RECEIVE:\n \t\tif (len \u003c 6)\n \t\t\tbreak;\n-\t\tif (strcmp((const char *)in, \u0022reset\u005cn\u0022) \u003d\u003d 0)\n+\t\tif (strncmp((const char *)in, \u0022reset\u005cn\u0022, 6) \u003d\u003d 0)\n \t\t\tpss-\u003enumber \u003d 0;\n-\t\tif (strcmp((const char *)in, \u0022closeme\u005cn\u0022) \u003d\u003d 0) {\n+\t\tif (strncmp((const char *)in, \u0022closeme\u005cn\u0022, 8) \u003d\u003d 0) {\n \t\t\tlwsl_notice(\u0022dumb_inc: closing as requested\u005cn\u0022);\n \t\t\tlws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,\n \t\t\t\t\t (unsigned char *)\u0022seeya\u0022, 5);\ndiff --git a/plugins/protocol_lws_meta.c b/plugins/protocol_lws_meta.c\ndeleted file mode 100644\nindex 69324db..0000000\n--- a/plugins/protocol_lws_meta.c\n+++ /dev/null\n@@ -1,616 +0,0 @@\n-/*\n- * lws meta protocol handler\n- *\n- * Copyright (C) 2017 Andy Green \u003candy@warmcat.com\u003e\n- *\n- * This library is free software; you can redistribute it and/or\n- * modify it under the terms of the GNU Lesser General Public\n- * License as published by the Free Software Foundation:\n- * version 2.1 of the License.\n- *\n- * This library is distributed in the hope that it will be useful,\n- * but WITHOUT ANY WARRANTY; without even the implied warranty of\n- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n- * Lesser General Public License for more details.\n- *\n- * You should have received a copy of the GNU Lesser General Public\n- * License along with this library; if not, write to the Free Software\n- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n- * MA 02110-1301 USA\n- *\n- */\n-\n-#if !defined (LWS_PLUGIN_STATIC)\n-#define LWS_DLL\n-#define LWS_INTERNAL\n-#include \u0022../lib/libwebsockets.h\u0022\n-#endif\n-\n-#include \u003cstring.h\u003e\n-#include \u003cstdlib.h\u003e\n-\n-#define MAX_SUBCHANNELS 8\n-\n-enum lws_meta_parser_state {\n-\tMP_IDLE, /* in body of message */\n-\n-\tMP_CMD, /* await cmd */\n-\n-\tMP_OPEN_SUBCHANNEL_PROTOCOL,\n-\tMP_OPEN_SUBCHANNEL_URL,\n-\tMP_OPEN_SUBCHANNEL_COOKIE,\n-\n-\tMP_CLOSE_CHID,\n-\tMP_CLOSE_LEN,\n-\tMP_CLOSE_CODEM,\n-\tMP_CLOSE_CODEL,\n-\tMP_CLOSE_PAYLOAD,\n-\n-\tMP_WRITE_CHID,\n-};\n-\n-enum {\n-\tPENDING_TYPE_OPEN_RESULT \u003d 0,\n-\tPENDING_TYPE_CHILD_CLOSE\n-};\n-\n-/*\n- * while we haven't reported the result yet, we keep a linked-list of\n- * connection opens and their result.\n- */\n-struct pending_conn {\n-\tstruct pending_conn *next;\n-\tchar protocol[123];\n-\tchar cookie[8];\n-\tint ch;\n-\tint len;\n-\n-\tunsigned char type;\n-};\n-\n-/*\n- * the parent, lws-meta connection\n- */\n-struct per_session_data__lws_meta {\n-\tstruct lws *wsi[MAX_SUBCHANNELS + 1];\n-\tchar told_closing[MAX_SUBCHANNELS + 1];\n-\tstruct pending_conn *first;\n-\tstruct pending_conn *pend;\n-\tchar suburl[64];\n-\tunsigned char close[126];\n-\tint active_subchannel_tx, active_subchannel_rx;\n-\tenum lws_meta_parser_state state;\n-\tint pos;\n-\tint count_pending;\n-\tint round_robin;\n-\tint close_status_16;\n-\tint close_len;\n-\tint which_close;\n-\tint ch;\n-};\n-\n-static int\n-lws_find_free_channel(struct per_session_data__lws_meta *pss)\n-{\n-\tint n;\n-\n-\tfor (n \u003d 1; n \u003c\u003d MAX_SUBCHANNELS; n++)\n-\t\tif (pss-\u003ewsi[n] \u003d\u003d NULL)\n-\t\t\treturn n;\n-\n-\treturn 0; /* none free */\n-}\n-\n-static struct lws *\n-lws_get_channel_wsi(struct per_session_data__lws_meta *pss, int ch)\n-{\n-\tif (!ch)\n-\t\treturn 0;\n-\treturn pss-\u003ewsi[ch];\n-}\n-\n-static int\n-lws_get_channel_id(struct lws *wsi)\n-{\n-\treturn (int)(lws_intptr_t)lws_get_opaque_parent_data(wsi);\n-}\n-\n-static void\n-lws_set_channel_id(struct lws *wsi, int id)\n-{\n-\tlws_set_opaque_parent_data(wsi, (void *)(lws_intptr_t)id);\n-}\n-\n-static struct pending_conn *\n-new_pending(struct per_session_data__lws_meta *pss)\n-{\n-\tstruct pending_conn *pend;\n-\n-\tif (pss-\u003ecount_pending \u003e\u003d MAX_SUBCHANNELS * 2) {\n-\t\tlwsl_notice(\u0022too many pending open subchannel\u005cn\u0022);\n-\n-\t\treturn NULL;\n-\t}\n-\n-\tpss-\u003ecount_pending++;\n-\n-\tpend \u003d malloc(sizeof(*pend));\n-\tif (!pend) {\n-\t\tlwsl_notice(\u0022OOM\u005cn\u0022);\n-\n-\t\treturn NULL;\n-\t}\n-\n-\tmemset(pend, 0, sizeof(*pend));\n-\n-\treturn pend;\n-}\n-\n-static int\n-callback_lws_meta(struct lws *wsi, enum lws_callback_reasons reason,\n-\t\t void *user, void *in, size_t len)\n-{\n-\tstruct per_session_data__lws_meta *pss \u003d\n-\t\t\t(struct per_session_data__lws_meta *)user;\n-\tstruct lws_write_passthru *pas;\n-\tstruct pending_conn *pend, *pend1;\n-\tstruct lws *cwsi;\n-\tlws_sock_file_fd_type fd;\n-\tunsigned char *bin, buf[LWS_PRE + 512], *start \u003d \u0026buf[LWS_PRE],\n-\t\t\t*end \u003d \u0026buf[sizeof(buf) - 1], *p \u003d start;\n-\tint n, m;\n-\n-\tswitch (reason) {\n-\n-\tcase LWS_CALLBACK_ESTABLISHED:\n-\t\tlwsl_info(\u0022%s: LWS_CALLBACK_ESTABLISHED\u005cn\u0022, __func__);\n-\t\tpss-\u003estate \u003d MP_CMD;\n-\t\tpss-\u003epos \u003d 0;\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_CLOSED:\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_CHILD_CLOSING:\n-\t\tcwsi \u003d (struct lws *)in;\n-\n-\t\t/* remove it from our tracking */\n-\t\tpss-\u003ewsi[lws_get_channel_id(cwsi)] \u003d NULL;\n-\n-\t\tif (pss-\u003etold_closing[lws_get_channel_id(cwsi)]) {\n-\t\t\tpss-\u003etold_closing[lws_get_channel_id(cwsi)] \u003d 0;\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\tpend \u003d new_pending(pss);\n-\t\tif (!pend)\n-\t\t\treturn -1;\n-\n-\t\t/* note which channel id */\n-\t\tpend-\u003ech \u003d lws_get_channel_id(cwsi);\n-\n-\t\tif (lws_get_close_length(cwsi)) {\n-\t\t\tpend-\u003elen \u003d lws_get_close_length(cwsi);\n-\t\t\tmemcpy(pend-\u003eprotocol, lws_get_close_payload(cwsi),\n-\t\t\t\t\tpend-\u003elen);\n-\t\t}\n-\n-\t\tpend-\u003etype \u003d PENDING_TYPE_CHILD_CLOSE;\n-\t\tpend-\u003enext \u003d pss-\u003efirst;\n-\t\tpss-\u003efirst \u003d pend;\n-\n-\t\t/*\n-\t\t * nothing else will complete from this wsi, so abandon\n-\t\t * tracking in-process messages from this wsi.\n-\t\t */\n-\n-\t\tif (pss-\u003eactive_subchannel_tx \u003d\u003d pend-\u003ech)\n-\t\t\tpss-\u003eactive_subchannel_tx \u003d 0;\n-\n-\t\tif (pss-\u003eactive_subchannel_rx \u003d\u003d pend-\u003ech)\n-\t\t\tpss-\u003eactive_subchannel_rx \u003d 0;\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_SERVER_WRITEABLE:\n-\n-\t\tif (!pss-\u003eactive_subchannel_tx) {\n-\n-\t\t\t/* not in the middle of a message...\n-\t\t\t *\n-\t\t\t * PRIORITY 1: pending open and close notifications\n-\t\t\t */\n-\n-\t\t\tpend \u003d pss-\u003efirst;\n-\t\t\twhile (pend \u0026\u0026 p \u003c end - 128) {\n-\t\t\t\tswitch (pend-\u003etype) {\n-\t\t\t\tcase PENDING_TYPE_OPEN_RESULT:\n-\t\t\t\t\tlwsl_debug(\u0022open result %s %s\u005cn\u0022,\n-\t\t\t\t\t\tpend-\u003ecookie, pend-\u003eprotocol);\n-\t\t\t\t\t*p++ \u003d LWS_META_CMD_OPEN_RESULT;\n-\t\t\t\t\tmemcpy(p, pend-\u003ecookie,\n-\t\t\t\t\t strlen(pend-\u003ecookie) + 1);\n-\t\t\t\t\tp +\u003d strlen(pend-\u003ecookie) + 1;\n-\t\t\t\t\t*p++ \u003d LWS_META_TRANSPORT_OFFSET +\n-\t\t\t\t\t\t\tpend-\u003ech;\n-\t\t\t\t\tmemcpy(p, pend-\u003eprotocol,\n-\t\t\t\t\t strlen(pend-\u003eprotocol) + 1);\n-\t\t\t\t\tp +\u003d strlen(pend-\u003eprotocol) + 1;\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase PENDING_TYPE_CHILD_CLOSE:\n-\t\t\t\t\t*p++ \u003d LWS_META_CMD_CLOSE_NOTIFY;\n-\t\t\t\t\t*p++ \u003d LWS_META_TRANSPORT_OFFSET +\n-\t\t\t\t\t\t\tpend-\u003ech;\n-\t\t\t\t\tfor (n \u003d 0; n \u003c pend-\u003elen; n++)\n-\t\t\t\t\t\t*p++ \u003d pend-\u003eprotocol[n];\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\n-\t\t\t\tpss-\u003ecount_pending--;\n-\t\t\t\tpend1 \u003d pend;\n-\t\t\t\tpend \u003d pend-\u003enext;\n-\t\t\t\tfree(pend1);\n-\t\t\t\tpss-\u003efirst \u003d pend;\n-\t\t\t}\n-\n-\t\t\tif (p !\u003d start) {\n-\t\t\t\tif (lws_write(wsi, start, p - start,\n-\t\t\t\t\t LWS_WRITE_BINARY) \u003c 0)\n-\t\t\t\t\treturn 1;\n-\t\t\t\tif (pend) /* still more */\n-\t\t\t\t\tlws_callback_on_writable(wsi);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\t/* PRIORITY 2: pick a child for the writable callback */\n-\n-\t\t\tcwsi \u003d NULL;\n-\t\t\tfor (n \u003d 0; n \u003c MAX_SUBCHANNELS; n++) {\n-\t\t\t\tm \u003d ((pss-\u003eround_robin + n) % MAX_SUBCHANNELS) + 1;\n-\t\t\t\tif (pss-\u003ewsi[m] \u0026\u0026\n-\t\t\t\t lws_get_child_pending_on_writable(pss-\u003ewsi[m])) {\n-\t\t\t\t\tpss-\u003eround_robin \u003d m;\n-\t\t\t\t\tcwsi \u003d pss-\u003ewsi[m];\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t} else\n-\t\t\t/* one child is in middle of message, stay with it */\n-\t\t\tcwsi \u003d pss-\u003ewsi[pss-\u003eactive_subchannel_tx];\n-\n-\t\tif (!cwsi)\n-\t\t\tbreak;\n-\n-\t\tlws_clear_child_pending_on_writable(cwsi);\n-\t\tif (lws_handle_POLLOUT_event(cwsi, NULL))\n-\t\t\treturn -1;\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_RECEIVE:\n-\t\tbin \u003d (unsigned char *)in;\n-\n-\t\t/*\n-\t\t * at the start of a message, we may have one or more\n-\t\t * lws_meta command blocks.\n-\t\t */\n-\t\twhile (pss-\u003estate !\u003d MP_IDLE \u0026\u0026\n-\t\t (unsigned int)(bin - (unsigned char *)in) \u003c len) {\n-\n-\t\t\tswitch (pss-\u003estate) {\n-\t\t\tcase MP_IDLE: /* in body of message */\n-\n-\t\t\t\tif (!lws_is_first_fragment(wsi))\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\tpss-\u003estate \u003d MP_CMD;\n-\n-\t\t\t\t/* fallthru */\n-\n-\t\t\tcase MP_CMD: /* await cmd */\n-\n-\t\t\t\tpss-\u003epos \u003d 0;\n-\n-\t\t\t\tswitch (*bin++) {\n-\t\t\t\tcase LWS_META_CMD_OPEN_SUBCHANNEL:\n-\n-\t\t\t\t\tpss-\u003epend \u003d new_pending(pss);\n-\t\t\t\t\tif (!pss-\u003epend)\n-\t\t\t\t\t\treturn -1;\n-\n-\t\t\t\t\tpss-\u003estate \u003d MP_OPEN_SUBCHANNEL_PROTOCOL;\n-\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase LWS_META_CMD_CLOSE_NOTIFY:\n-\t\t\t\tcase LWS_META_CMD_CLOSE_RQ:\n-\t\t\t\t\tpss-\u003ewhich_close \u003d bin[-1];\n-\t\t\t\t\tpss-\u003estate \u003d MP_CLOSE_CHID;\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase LWS_META_CMD_WRITE:\n-\t\t\t\t\tpss-\u003estate \u003d MP_WRITE_CHID;\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\t// open result is also illegal to receive\n-\t\t\t\tdefault:\n-\t\t\t\t\tlwsl_notice(\u0022bad lws_meta cmd 0x%x\u005cn\u0022,\n-\t\t\t\t\t\t bin[-1]);\n-\n-\t\t\t\t\treturn -1;\n-\t\t\t\t}\n-\n-\t\t\t\tbreak;\n-\n-\t\t\tcase MP_OPEN_SUBCHANNEL_PROTOCOL:\n-\t\t\t\tpss-\u003epend-\u003eprotocol[pss-\u003epos++] \u003d *bin++;\n-\t\t\t\tif (pss-\u003epos \u003d\u003d sizeof(pss-\u003epend-\u003eprotocol) - 1) {\n-\t\t\t\t\tlwsl_notice(\u0022protocol name too long\u005cn\u0022);\n-\t\t\t\t\treturn -1;\n-\t\t\t\t}\n-\n-\t\t\t\tif (bin[-1] !\u003d '\u005c0')\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\tpss-\u003estate \u003d MP_OPEN_SUBCHANNEL_URL;\n-\t\t\t\tpss-\u003epos \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase MP_OPEN_SUBCHANNEL_URL:\n-\t\t\t\tpss-\u003esuburl[pss-\u003epos++] \u003d *bin++;\n-\t\t\t\tif (pss-\u003epos \u003d\u003d sizeof(pss-\u003esuburl) - 1) {\n-\t\t\t\t\tlwsl_notice(\u0022suburl too long\u005cn\u0022);\n-\t\t\t\t\treturn -1;\n-\t\t\t\t}\n-\n-\t\t\t\tif (bin[-1] !\u003d '\u005c0')\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\tpss-\u003estate \u003d MP_OPEN_SUBCHANNEL_COOKIE;\n-\t\t\t\tpss-\u003epos \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase MP_OPEN_SUBCHANNEL_COOKIE:\n-\t\t\t\tpss-\u003epend-\u003ecookie[pss-\u003epos++] \u003d *bin++;\n-\t\t\t\tif (pss-\u003epos \u003d\u003d sizeof(pss-\u003epend-\u003ecookie) - 1) {\n-\t\t\t\t\tlwsl_notice(\u0022cookie too long\u005cn\u0022);\n-\t\t\t\t\treturn -1;\n-\t\t\t\t}\n-\n-\t\t\t\tif (bin[-1] !\u003d '\u005c0')\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\tlwsl_debug(\u0022%s: %s / %s / %s\u005cn\u0022, __func__,\n-\t\t\t\t\t pss-\u003epend-\u003eprotocol,\n-\t\t\t\t\t pss-\u003esuburl,\n-\t\t\t\t\t pss-\u003epend-\u003ecookie);\n-\n-\t\t\t\tpss-\u003epend-\u003ech \u003d lws_find_free_channel(pss);\n-\t\t\t\tif (pss-\u003epend-\u003ech) {\n-\n-\t\t\t\t\tfd.sockfd \u003d 0; // not going to be used\n-\n-\t\t\t\t\tcwsi \u003d lws_adopt_descriptor_vhost(\n-\t\t\t\t\t\t\tlws_get_vhost(wsi),\n-\t\t\t\t\t\t\tLWS_ADOPT_WS_PARENTIO,\n-\t\t\t\t\t\t\tfd, pss-\u003epend-\u003eprotocol,\n-\t\t\t\t\t\t\twsi);\n-\n-\t\t\t\t\tif (!cwsi) {\n-\t\t\t\t\t\tlwsl_notice(\u0022open failed\u005cn\u0022);\n-\t\t\t\t\t\tpss-\u003epend-\u003ech \u003d 0;\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tpss-\u003ewsi[pss-\u003epend-\u003ech] \u003d cwsi;\n-\t\t\t\t\t\tlws_set_channel_id(cwsi,\n-\t\t\t\t\t\t\t\tpss-\u003epend-\u003ech);\n-\t\t\t\t\t\tlwsl_debug(\u0022cwsi %p on parent %p open OK %s\u005cn\u0022,\n-\t\t\t\t\t\t\tcwsi, wsi, pss-\u003epend-\u003eprotocol);\n-\t\t\t\t\t}\n-\n-\t\t\t\t} else\n-\t\t\t\t\tlwsl_notice(\u0022no free subchannels\u005cn\u0022);\n-\n-\t\t\t\tpss-\u003epend-\u003etype \u003d PENDING_TYPE_OPEN_RESULT;\n-\t\t\t\tpss-\u003epend-\u003enext \u003d pss-\u003efirst;\n-\t\t\t\tpss-\u003efirst \u003d pss-\u003epend;\n-\n-\t\t\t\tlws_callback_on_writable(wsi);\n-\n-\t\t\t\tpss-\u003estate \u003d MP_CMD;\n-\t\t\t\tpss-\u003epos \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase MP_CLOSE_CHID:\n-\t\t\t\tpss-\u003ech \u003d (*bin++) - LWS_META_TRANSPORT_OFFSET;\n-\t\t\t\tpss-\u003estate \u003d MP_CLOSE_LEN;\n-\t\t\t\tpss-\u003epos \u003d 0;\n-\t\t\t\tbreak;\n-\t\t\tcase MP_CLOSE_LEN:\n-\t\t\t\tpss-\u003eclose_len \u003d (*bin++) -\n-\t\t\t\t\tLWS_META_TRANSPORT_OFFSET;\n-\t\t\t\tlwsl_debug(\u0022close len %d\u005cn\u0022, pss-\u003eclose_len);\n-\t\t\t\tpss-\u003estate \u003d MP_CLOSE_CODEM;\n-\t\t\t\tpss-\u003epos \u003d 0;\n-\t\t\t\tbreak;\n-\t\t\tcase MP_CLOSE_CODEM:\n-\t\t\t\tpss-\u003eclose[pss-\u003epos++] \u003d *bin;\n-\t\t\t\tpss-\u003eclose_status_16 \u003d (*bin++) * 256;\n-\t\t\t\tpss-\u003estate \u003d MP_CLOSE_CODEL;\n-\t\t\t\tbreak;\n-\t\t\tcase MP_CLOSE_CODEL:\n-\t\t\t\tpss-\u003eclose[pss-\u003epos++] \u003d *bin;\n-\t\t\t\tpss-\u003eclose_status_16 |\u003d *bin++;\n-\t\t\t\tpss-\u003estate \u003d MP_CLOSE_PAYLOAD;\n-\t\t\t\tbreak;\n-\t\t\tcase MP_CLOSE_PAYLOAD:\n-\t\t\t\tpss-\u003eclose[pss-\u003epos++] \u003d *bin++;\n-\t\t\t\tif (pss-\u003epos \u003d\u003d sizeof(pss-\u003eclose) - 1) {\n-\t\t\t\t\tlwsl_notice(\u0022close payload too long\u005cn\u0022);\n-\t\t\t\t\treturn -1;\n-\t\t\t\t}\n-\t\t\t\tif (--pss-\u003eclose_len)\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\tpss-\u003estate \u003d MP_CMD;\n-\n-\t\t\t\tcwsi \u003d lws_get_channel_wsi(pss, pss-\u003ech);\n-\t\t\t\tif (!cwsi) {\n-\t\t\t\t\tlwsl_notice(\u0022close (%d) bad ch %d\u005cn\u0022,\n-\t\t\t\t\t\tpss-\u003ewhich_close, pss-\u003ech);\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\n-\t\t\t\tif (pss-\u003ewhich_close \u003d\u003d LWS_META_CMD_CLOSE_RQ) {\n-\t\t\t\t\tif (lws_get_protocol(cwsi)-\u003ecallback(\n-\t\t\t\t\t cwsi,\n-\t\t\t\t\t LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,\n-\t\t\t\t\t lws_wsi_user(cwsi), \u0026pss-\u003eclose,\n-\t\t\t\t\t pss-\u003epos))\n-\t\t\t\t\t\treturn -1;\n-\n-\t\t\t\t\t/*\n-\t\t\t\t\t * we need to echo back the close payload\n-\t\t\t\t\t * when we send the close notification\n-\t\t\t\t\t */\n-\t\t\t\t\tlws_close_reason(cwsi,\n-\t\t\t\t\t\t\t pss-\u003eclose_status_16,\n-\t\t\t\t\t\t\t \u0026pss-\u003eclose[2],\n-\t\t\t\t\t\t\t pss-\u003epos - 2);\n-\t\t\t\t}\n-\n-\t\t\t\t/* so force him closed */\n-\n-\t\t\t\tlws_set_timeout(cwsi,\n-\t\t\t\t\tPENDING_TIMEOUT_KILLED_BY_PARENT,\n-\t\t\t\t\tLWS_TO_KILL_SYNC);\n-\t\t\t\tbreak;\n-\n-\t\t\tcase MP_WRITE_CHID:\n-\t\t\t\tpss-\u003eactive_subchannel_rx \u003d (*bin++) -\n-\t\t\t\t\tLWS_META_TRANSPORT_OFFSET;\n-\t\t\t\tpss-\u003estate \u003d MP_IDLE;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\n-\t\tlen -\u003d bin - (unsigned char *)in;\n-\n-\t\tif (!len)\n-\t\t\tbreak;\n-\n-\t\tcwsi \u003d lws_get_channel_wsi(pss, pss-\u003eactive_subchannel_rx);\n-\t\tif (!cwsi) {\n-\t\t\tlwsl_notice(\u0022bad ch %d\u005cn\u0022, pss-\u003eactive_subchannel_rx);\n-\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\t// lwsl_debug(\u0022%s: RX len %d\u005cn\u0022, __func__, (int)len);\n-\n-\t\tif (lws_get_protocol(cwsi)-\u003ecallback(cwsi,\n-\t\t\t\t\tLWS_CALLBACK_RECEIVE,\n-\t\t\t\t\tlws_wsi_user(cwsi), bin, len))\n-\t\t\tlws_set_timeout(cwsi,\n-\t\t\t\tPENDING_TIMEOUT_KILLED_BY_PARENT,\n-\t\t\t\tLWS_TO_KILL_SYNC);\n-\n-\t\tif (lws_is_final_fragment(wsi)) {\n-\t\t\tpss-\u003eactive_subchannel_rx \u003d 0;\n-\t\t\tpss-\u003estate \u003d MP_CMD;\n-\t\t}\n-\t\tbreak;\n-\n-\t/*\n-\t * child wrote something via lws_write.... which passed it up to us to\n-\t * deal with, because we are the parent. Prepend two bytes for\n-\t * lws-meta command and channel index, and send it out on parent\n-\t */\n-\tcase LWS_CALLBACK_CHILD_WRITE_VIA_PARENT:\n-\t\tpas \u003d in;\n-\t\tbin \u003d ((unsigned char *)pas-\u003ebuf);\n-\n-\t\tif ((pas-\u003ewp \u0026 7) \u003d\u003d 4 /*LWS_WRITE_CLOSE */) {\n-\t\t\t*p++ \u003d LWS_META_CMD_CLOSE_NOTIFY;\n-\t\t\t*p++ \u003d LWS_META_TRANSPORT_OFFSET +\n-\t\t\t\t\tlws_get_channel_id(pas-\u003ewsi);\n-\t\t\t*p++ \u003d (unsigned char)pas-\u003elen +\n-\t\t\t\t\tLWS_META_TRANSPORT_OFFSET - 2;\n-\t\t\t*p++ \u003d *bin++;\n-\t\t\t*p++ \u003d *bin++;\n-\t\t\tfor (n \u003d 0; n \u003c (int)pas-\u003elen - 2; n++)\n-\t\t\t\t*p++ \u003d bin[n];\n-\n-\t\t\tif (lws_write(wsi, start, p - start,\n-\t\t\t\t LWS_WRITE_BINARY) \u003c 0)\n-\t\t\t\treturn 1;\n-\n-\t\t\tpss-\u003etold_closing[lws_get_channel_id(pas-\u003ewsi)] \u003d 1;\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\tif ((pas-\u003ewp \u0026 7) \u003d\u003d LWS_WRITE_TEXT ||\n-\t\t (pas-\u003ewp \u0026 7) \u003d\u003d LWS_WRITE_BINARY) {\n-\n-\t\t\tif (pas-\u003ewp \u0026 LWS_WRITE_NO_FIN)\n-\t\t\t\tpss-\u003eactive_subchannel_tx \u003d\n-\t\t\t\t\t\tlws_get_channel_id(pas-\u003ewsi);\n-\n-\t\t\t/* start of message, prepend the subchannel id */\n-\n-\t\t\tbin -\u003d 2;\n-\t\t\tbin[0] \u003d LWS_META_CMD_WRITE;\n-\t\t\tbin[1] \u003d lws_get_channel_id(pas-\u003ewsi) +\n-\t\t\t\t\tLWS_META_TRANSPORT_OFFSET;\n-\t\t\tif (lws_write(wsi, bin, pas-\u003elen + 2, pas-\u003ewp) \u003c 0)\n-\t\t\t\treturn 1;\n-\t\t} else\n-\t\t\tif (lws_write(wsi, bin, pas-\u003elen, pas-\u003ewp) \u003c 0)\n-\t\t\t\treturn 1;\n-\n-\t\t/* track EOM */\n-\n-\t\tif (!(pas-\u003ewp \u0026 LWS_WRITE_NO_FIN))\n-\t\t\tpss-\u003eactive_subchannel_tx \u003d 0;\n-\t\tbreak;\n-\n-\tdefault:\n-\t\tbreak;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-#define LWS_PLUGIN_PROTOCOL_LWS_META { \u005c\n-\t\t\u0022lws-meta\u0022, \u005c\n-\t\tcallback_lws_meta, \u005c\n-\t\tsizeof(struct per_session_data__lws_meta), \u005c\n-\t\t1024, /* rx buf size must be \u003e\u003d permessage-deflate rx size */ \u005c\n-\t\t0, NULL, 0 \u005c\n-\t}\n-\n-#if !defined (LWS_PLUGIN_STATIC)\n-\n-static const struct lws_protocols protocols[] \u003d {\n-\tLWS_PLUGIN_PROTOCOL_LWS_META\n-};\n-\n-LWS_EXTERN LWS_VISIBLE int\n-init_protocol_lws_meta(struct lws_context *context,\n-\t\t\t struct lws_plugin_capability *c)\n-{\n-\tif (c-\u003eapi_magic !\u003d LWS_PLUGIN_API_MAGIC) {\n-\t\tlwsl_err(\u0022Plugin API %d, library API %d\u0022, LWS_PLUGIN_API_MAGIC,\n-\t\t\t c-\u003eapi_magic);\n-\t\treturn 1;\n-\t}\n-\n-\tc-\u003eprotocols \u003d protocols;\n-\tc-\u003ecount_protocols \u003d ARRAY_SIZE(protocols);\n-\tc-\u003eextensions \u003d NULL;\n-\tc-\u003ecount_extensions \u003d 0;\n-\n-\treturn 0;\n-}\n-\n-LWS_EXTERN LWS_VISIBLE int\n-destroy_protocol_lws_meta(struct lws_context *context)\n-{\n-\treturn 0;\n-}\n-#endif\ndiff --git a/test-apps/test-fraggle.c b/test-apps/test-fraggle.c\ndeleted file mode 100644\nindex c234c00..0000000\n--- a/test-apps/test-fraggle.c\n+++ /dev/null\n@@ -1,373 +0,0 @@\n-/*\n- * libwebsockets-test-fraggle - random fragmentation test\n- *\n- * Copyright (C) 2011-2016 Andy Green \u003candy@warmcat.com\u003e\n- *\n- * This file is made available under the Creative Commons CC0 1.0\n- * Universal Public Domain Dedication.\n- *\n- * The person who associated a work with this deed has dedicated\n- * the work to the public domain by waiving all of his or her rights\n- * to the work worldwide under copyright law, including all related\n- * and neighboring rights, to the extent allowed by law. You can copy,\n- * modify, distribute and perform the work, even for commercial purposes,\n- * all without asking permission.\n- *\n- * The test apps are intended to be adapted for use in your code, which\n- * may be proprietary. So unlike the library itself, they are licensed\n- * Public Domain.\n- */\n-\n-#include \u003cstdio.h\u003e\n-#include \u003cstdlib.h\u003e\n-#include \u003cgetopt.h\u003e\n-#include \u003cstring.h\u003e\n-#include \u0022../lib/libwebsockets.h\u0022\n-\n-#define LOCAL_RESOURCE_PATH INSTALL_DATADIR\u0022/libwebsockets-test-server\u0022\n-\n-static int client;\n-static int terminate;\n-\n-enum demo_protocols {\n-\tPROTOCOL_FRAGGLE,\n-\n-\t/* always last */\n-\tDEMO_PROTOCOL_COUNT\n-};\n-\n-/* fraggle protocol */\n-\n-enum fraggle_states {\n-\tFRAGSTATE_START_MESSAGE,\n-\tFRAGSTATE_RANDOM_PAYLOAD,\n-\tFRAGSTATE_POST_PAYLOAD_SUM,\n-};\n-\n-struct per_session_data__fraggle {\n-\tint packets_left;\n-\tint total_message;\n-\tunsigned long sum;\n-\tenum fraggle_states state;\n-};\n-\n-static int\n-callback_fraggle(struct lws *wsi, enum lws_callback_reasons reason,\n-\t\t void *user, void *in, size_t len)\n-{\n-\tunsigned char buf[LWS_PRE + 8000], *bp \u003d \u0026buf[LWS_PRE];\n-\tstruct per_session_data__fraggle *psf \u003d user;\n-\tunsigned char *p \u003d (unsigned char *)in;\n-\tint n, chunk, flags, ran;\n-\tunsigned long sum;\n-\n-\tswitch (reason) {\n-\n-\tcase LWS_CALLBACK_ESTABLISHED:\n-\n-\t\tfprintf(stderr, \u0022server sees client connect\u005cn\u0022);\n-\t\tpsf-\u003estate \u003d FRAGSTATE_START_MESSAGE;\n-\t\t/* start the ball rolling */\n-\t\tlws_callback_on_writable(wsi);\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_CLIENT_ESTABLISHED:\n-\n-\t\tfprintf(stderr, \u0022client connects to server\u005cn\u0022);\n-\t\tpsf-\u003estate \u003d FRAGSTATE_START_MESSAGE;\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_CLIENT_RECEIVE:\n-\n-\t\tswitch (psf-\u003estate) {\n-\n-\t\tcase FRAGSTATE_START_MESSAGE:\n-\n-\t\t\tpsf-\u003estate \u003d FRAGSTATE_RANDOM_PAYLOAD;\n-\t\t\tpsf-\u003esum \u003d 0;\n-\t\t\tpsf-\u003etotal_message \u003d 0;\n-\t\t\tpsf-\u003epackets_left \u003d 0;\n-\n-\t\t\t/* fallthru */\n-\n-\t\tcase FRAGSTATE_RANDOM_PAYLOAD:\n-\n-\t\t\tfor (n \u003d 0; (unsigned int)n \u003c len; n++)\n-\t\t\t\tpsf-\u003esum +\u003d p[n];\n-\n-\t\t\tpsf-\u003etotal_message +\u003d (int)len;\n-\t\t\tpsf-\u003epackets_left++;\n-\n-\t\t\tif (lws_is_final_fragment(wsi))\n-\t\t\t\tpsf-\u003estate \u003d FRAGSTATE_POST_PAYLOAD_SUM;\n-\t\t\tbreak;\n-\n-\t\tcase FRAGSTATE_POST_PAYLOAD_SUM:\n-\n-\t\t\tsum \u003d ((unsigned int)p[0]) \u003c\u003c 24;\n-\t\t\tsum |\u003d p[1] \u003c\u003c 16;\n-\t\t\tsum |\u003d p[2] \u003c\u003c 8;\n-\t\t\tsum |\u003d p[3];\n-\t\t\tif (sum \u003d\u003d psf-\u003esum)\n-\t\t\t\tfprintf(stderr, \u0022EOM received %d correctly \u0022\n-\t\t\t\t\t\t\u0022from %d fragments\u005cn\u0022,\n-\t\t\t\t\tpsf-\u003etotal_message, psf-\u003epackets_left);\n-\t\t\telse\n-\t\t\t\tfprintf(stderr, \u0022**** ERROR at EOM: \u0022\n-\t\t\t\t\t\t\u0022length %d, rx sum \u003d 0x%lX, \u0022\n-\t\t\t\t\t\t\u0022server says it sent 0x%lX\u005cn\u0022,\n-\t\t\t\t\t psf-\u003etotal_message, psf-\u003esum, sum);\n-\n-\t\t\tpsf-\u003estate \u003d FRAGSTATE_START_MESSAGE;\n-\t\t\tbreak;\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_SERVER_WRITEABLE:\n-\n-\t\tswitch (psf-\u003estate) {\n-\n-\t\tcase FRAGSTATE_START_MESSAGE:\n-\t\t\tlws_get_random(lws_get_context(wsi), \u0026ran, sizeof(ran));\n-\t\t\tpsf-\u003epackets_left \u003d (ran \u0026 1023) + 1;\n-\t\t\tfprintf(stderr, \u0022Spamming %d random fragments\u005cn\u0022,\n-\t\t\t\t\t\t\t psf-\u003epackets_left);\n-\t\t\tpsf-\u003esum \u003d 0;\n-\t\t\tpsf-\u003etotal_message \u003d 0;\n-\t\t\tpsf-\u003estate \u003d FRAGSTATE_RANDOM_PAYLOAD;\n-\n-\t\t\t/* fallthru */\n-\n-\t\tcase FRAGSTATE_RANDOM_PAYLOAD:\n-\n-\t\t\t/*\n-\t\t\t * note how one chunk can be 8000, but we use the\n-\t\t\t * default rx buffer size of 4096, so we exercise the\n-\t\t\t * code for rx spill because the rx buffer is full\n-\t\t\t */\n-\n-\t\t\tlws_get_random(lws_get_context(wsi), \u0026ran, sizeof(ran));\n-\t\t\tchunk \u003d (ran \u0026 511) + 1;\n-\t\t\tpsf-\u003etotal_message +\u003d chunk;\n-\n-\t\t\tlws_get_random(lws_get_context(wsi), bp, chunk);\n-\t\t\tfor (n \u003d 0; n \u003c chunk; n++)\n-\t\t\t\tpsf-\u003esum +\u003d bp[n];\n-\n-\t\t\tpsf-\u003epackets_left--;\n-\t\t\tflags \u003d lws_write_ws_flags(LWS_WRITE_BINARY, !psf-\u003esum,\n-\t\t\t\t\t\t !psf-\u003epackets_left);\n-\t\t\tif (!psf-\u003epackets_left)\n-\t\t\t\tpsf-\u003estate \u003d FRAGSTATE_POST_PAYLOAD_SUM;\n-\n-\t\t\tn \u003d lws_write(wsi, bp, chunk, flags);\n-\t\t\tif (n \u003c 0)\n-\t\t\t\treturn -1;\n-\n-\t\t\tlws_callback_on_writable(wsi);\n-\t\t\tbreak;\n-\n-\t\tcase FRAGSTATE_POST_PAYLOAD_SUM:\n-\n-\t\t\tfprintf(stderr, \u0022Spamming session over, \u0022\n-\t\t\t\t\t\u0022len \u003d %d. sum \u003d 0x%lX\u005cn\u0022,\n-\t\t\t\t\t\t psf-\u003etotal_message, psf-\u003esum);\n-\n-\t\t\tbp[0] \u003d psf-\u003esum \u003e\u003e 24;\n-\t\t\tbp[1] \u003d (unsigned char)(psf-\u003esum \u003e\u003e 16);\n-\t\t\tbp[2] \u003d (unsigned char)(psf-\u003esum \u003e\u003e 8);\n-\t\t\tbp[3] \u003d (unsigned char)psf-\u003esum;\n-\n-\t\t\tn \u003d lws_write(wsi, (unsigned char *)bp,\n-\t\t\t\t\t\t\t 4, LWS_WRITE_BINARY);\n-\t\t\tif (n \u003c 0)\n-\t\t\t\treturn -1;\n-\t\t\tif (n \u003c 4) {\n-\t\t\t\tlwsl_err(\u0022Partial write\u005cn\u0022);\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\n-\t\t\tpsf-\u003estate \u003d FRAGSTATE_START_MESSAGE;\n-\n-\t\t\tlws_callback_on_writable(wsi);\n-\t\t\tbreak;\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_CLOSED:\n-\n-\t\tterminate \u003d 1;\n-\t\tbreak;\n-\n-\t/* because we are protocols[0] ... */\n-\n-\tcase LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:\n-\t\tif (strcmp(in, \u0022deflate-stream\u0022) \u003d\u003d 0) {\n-\t\t\tfprintf(stderr, \u0022denied deflate-stream extension\u005cn\u0022);\n-\t\t\treturn 1;\n-\t\t}\n-\t\tbreak;\n-\n-\tdefault:\n-\t\tbreak;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-\n-/* list of supported protocols and callbacks */\n-\n-static struct lws_protocols protocols[] \u003d {\n-\t{\n-\t\t\u0022fraggle-protocol\u0022,\n-\t\tcallback_fraggle,\n-\t\tsizeof(struct per_session_data__fraggle),\n-\t},\n-\t{\n-\t\tNULL, NULL, 0\t\t/* End of list */\n-\t}\n-};\n-\n-static const struct lws_extension exts[] \u003d {\n-\t{\n-\t\t\u0022permessage-deflate\u0022,\n-\t\tlws_extension_callback_pm_deflate,\n-\t\t\u0022permessage-deflate; client_no_context_takeover; client_max_window_bits\u0022\n-\t},\n-\t{\n-\t\t\u0022deflate-frame\u0022,\n-\t\tlws_extension_callback_pm_deflate,\n-\t\t\u0022deflate_frame\u0022\n-\t},\n-\t{ NULL, NULL, NULL /* terminator */ }\n-};\n-\n-static struct option options[] \u003d {\n-\t{ \u0022help\u0022,\tno_argument,\t\tNULL, 'h' },\n-\t{ \u0022debug\u0022,\trequired_argument,\tNULL, 'd' },\n-\t{ \u0022port\u0022,\trequired_argument,\tNULL, 'p' },\n-\t{ \u0022ssl\u0022,\tno_argument,\t\tNULL, 's' },\n-\t{ \u0022interface\u0022, required_argument,\tNULL, 'i' },\n-\t{ \u0022client\u0022,\tno_argument,\t\tNULL, 'c' },\n-\t{ NULL, 0, 0, 0 }\n-};\n-\n-int main(int argc, char **argv)\n-{\n-\tint n \u003d 0;\n-\tint port \u003d 7681;\n-\tint use_ssl \u003d 0;\n-\tstruct lws_context *context;\n-\tint opts \u003d 0;\n-\tchar interface_name[128] \u003d \u0022\u0022, ads_port[300];\n-\tconst char *iface \u003d NULL;\n-\tstruct lws *wsi;\n-\tconst char *address \u003d NULL;\n-\tint server_port \u003d port;\n-\tstruct lws_context_creation_info info;\n-\n-\tmemset(\u0026info, 0, sizeof info);\n-\tlwsl_notice(\u0022libwebsockets test server fraggle - license LGPL2.1+SLE\u005cn\u0022);\n-\tlwsl_notice(\u0022(C) Copyright 2010-2016 Andy Green \u003candy@warmcat.com\u003e\u005cn\u0022);\n-\n-\twhile (n \u003e\u003d 0) {\n-\t\tn \u003d getopt_long(argc, argv, \u0022ci:hsp:d:\u0022, options, NULL);\n-\t\tif (n \u003c 0)\n-\t\t\tcontinue;\n-\t\tswitch (n) {\n-\t\tcase 'd':\n-\t\t\tlws_set_log_level(atoi(optarg), NULL);\n-\t\t\tbreak;\n-\t\tcase 's':\n-\t\t\tuse_ssl \u003d 1;\n-\t\t\tbreak;\n-\t\tcase 'p':\n-\t\t\tport \u003d atoi(optarg);\n-\t\t\tserver_port \u003d port;\n-\t\t\tbreak;\n-\t\tcase 'i':\n-\t\t\tlws_strncpy(interface_name, optarg, sizeof interface_name);\n-\t\t\tiface \u003d interface_name;\n-\t\t\tbreak;\n-\t\tcase 'c':\n-\t\t\tclient \u003d 1;\n-\t\t\tfprintf(stderr, \u0022 Client mode\u005cn\u0022);\n-\t\t\tbreak;\n-\t\tcase 'h':\n-\t\t\tfprintf(stderr, \u0022Usage: libwebsockets-test-fraggle \u0022\n-\t\t\t\t\t\u0022[--port\u003d\u003cp\u003e] [--ssl] \u0022\n-\t\t\t\t\t\u0022[-d \u003clog bitfield\u003e] \u0022\n-\t\t\t\t\t\u0022[--client]\u005cn\u0022);\n-\t\t\texit(1);\n-\t\t}\n-\t}\n-\n-\tif (client) {\n-\t\tserver_port \u003d CONTEXT_PORT_NO_LISTEN;\n-\t\tif (optind \u003e\u003d argc) {\n-\t\t\tfprintf(stderr, \u0022Must give address of server\u005cn\u0022);\n-\t\t\treturn 1;\n-\t\t}\n-\t}\n-\n-\tinfo.port \u003d server_port;\n-\tinfo.iface \u003d iface;\n-\tinfo.protocols \u003d protocols;\n-\tinfo.extensions \u003d exts;\n-\n-\tif (use_ssl) {\n-\t\tinfo.ssl_cert_filepath \u003d LOCAL_RESOURCE_PATH\n-\t\t\t\t\u0022/libwebsockets-test-server.pem\u0022;\n-\t\tinfo.ssl_private_key_filepath \u003d LOCAL_RESOURCE_PATH\n-\t\t\t\t\u0022/libwebsockets-test-server.key.pem\u0022;\n-\t}\n-\tinfo.gid \u003d -1;\n-\tinfo.uid \u003d -1;\n-\tinfo.options \u003d opts;\n-\tinfo.extensions \u003d exts;\n-\n-\tif (use_ssl)\n-\t\tinfo.options |\u003d LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;\n-\n-\tcontext \u003d lws_create_context(\u0026info);\n-\tif (context \u003d\u003d NULL) {\n-\t\tfprintf(stderr, \u0022libwebsocket init failed\u005cn\u0022);\n-\t\treturn -1;\n-\t}\n-\n-\tif (client) {\n-\t\tstruct lws_client_connect_info i;\n-\n-\t\taddress \u003d argv[optind];\n-\t\tlws_snprintf(ads_port, sizeof(ads_port), \u0022%s:%u\u0022,\n-\t\t\t address, port \u0026 65535);\n-\t\tmemset(\u0026i, 0, sizeof(i));\n-\t\ti.context \u003d context;\n-\t\ti.address \u003d address;\n-\t\ti.port \u003d port;\n-\t\ti.ssl_connection \u003d use_ssl;\n-\t\ti.path \u003d \u0022/\u0022;\n-\t\ti.host \u003d ads_port;\n-\t\ti.origin \u003d ads_port;\n-\t\ti.protocol \u003d protocols[PROTOCOL_FRAGGLE].name;\n-\n-\t\tlwsl_notice(\u0022Connecting to %s:%u\u005cn\u0022, address, port);\n-\t\twsi \u003d lws_client_connect_via_info(\u0026i);\n-\t\tif (wsi \u003d\u003d NULL) {\n-\t\t\tfprintf(stderr, \u0022Client connect to server failed\u005cn\u0022);\n-\t\t\tgoto bail;\n-\t\t}\n-\t}\n-\n-\tn \u003d 0;\n-\twhile (!n \u0026\u0026 !terminate)\n-\t\tn \u003d lws_service(context, 50);\n-\n-\tfprintf(stderr, \u0022Terminating...\u005cn\u0022);\n-\n-bail:\n-\tlws_context_destroy(context);\n-\n-\treturn 0;\n-}\ndiff --git a/test-apps/test-server-dumb-increment.c b/test-apps/test-server-dumb-increment.c\nindex 36ade9d..0e30f02 100644\n--- a/test-apps/test-server-dumb-increment.c\n+++ b/test-apps/test-server-dumb-increment.c\n@@ -19,6 +19,8 @@\n */\n #include \u0022test-server.h\u0022\n \n+#if defined(LWS_ROLE_WS)\n+\n /* dumb_increment protocol */\n \n int\n@@ -53,11 +55,12 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,\n \t\tbreak;\n \n \tcase LWS_CALLBACK_RECEIVE:\n-\t\tif (len \u003c 6)\n-\t\t\tbreak;\n-\t\tif (strcmp((const char *)in, \u0022reset\u005cn\u0022) \u003d\u003d 0)\n+//\t\tif (len \u003c 6)\n+//\t\t\tbreak;\n+\t\tlwsl_hexdump_notice(in, len);\n+\t\tif (strncmp((const char *)in, \u0022reset\u005cn\u0022, 6) \u003d\u003d 0)\n \t\t\tpss-\u003enumber \u003d 0;\n-\t\tif (strcmp((const char *)in, \u0022closeme\u005cn\u0022) \u003d\u003d 0) {\n+\t\tif (strncmp((const char *)in, \u0022closeme\u005cn\u0022, 8) \u003d\u003d 0) {\n \t\t\tlwsl_notice(\u0022dumb_inc: closing as requested\u005cn\u0022);\n \t\t\tlws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,\n \t\t\t\t\t (unsigned char *)\u0022seeya\u0022, 5);\n@@ -100,3 +103,4 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,\n \n \treturn 0;\n }\n+#endif\n\u005c No newline at end of file\ndiff --git a/test-apps/test-server-libuv.c b/test-apps/test-server-libuv.c\nindex c27e779..f0602e0 100644\n--- a/test-apps/test-server-libuv.c\n+++ b/test-apps/test-server-libuv.c\n@@ -47,10 +47,11 @@ void test_server_unlock(int care)\n }\n \n #define LWS_PLUGIN_STATIC\n+#if defined(LWS_ROLE_WS)\n #include \u0022../plugins/protocol_dumb_increment.c\u0022\n #include \u0022../plugins/protocol_lws_mirror.c\u0022\n #include \u0022../plugins/protocol_lws_status.c\u0022\n-#include \u0022../plugins/protocol_lws_meta.c\u0022\n+#endif\n \n /*\n * This demo server shows how to use libwebsockets for one or more\n@@ -74,7 +75,6 @@ enum demo_protocols {\n \tPROTOCOL_DUMB_INCREMENT,\n \tPROTOCOL_LWS_MIRROR,\n \tPROTOCOL_LWS_STATUS,\n-\tPROTOCOL_LWS_META,\n \n \t/* always last */\n \tDEMO_PROTOCOL_COUNT\n@@ -91,13 +91,15 @@ static struct lws_protocols protocols[] \u003d {\n \t\tsizeof (struct per_session_data__http),\t/* per_session_data_size */\n \t\t0,\t\t\t/* max frame size / rx buffer */\n \t},\n+#if defined(LWS_ROLE_WS)\n \tLWS_PLUGIN_PROTOCOL_DUMB_INCREMENT,\n \tLWS_PLUGIN_PROTOCOL_MIRROR,\n \tLWS_PLUGIN_PROTOCOL_LWS_STATUS,\n-\tLWS_PLUGIN_PROTOCOL_LWS_META,\n+#endif\n \t{ NULL, NULL, 0, 0 } /* terminator */\n };\n \n+\n static const struct lws_extension exts[] \u003d {\n \t{\n \t\t\u0022permessage-deflate\u0022,\ndiff --git a/test-apps/test-server-pthreads.c b/test-apps/test-server-pthreads.c\ndeleted file mode 100644\nindex b7dbd42..0000000\n--- a/test-apps/test-server-pthreads.c\n+++ /dev/null\n@@ -1,401 +0,0 @@\n-/*\n- * libwebsockets-test-server - libwebsockets test implementation\n- *\n- * Copyright (C) 2010-2016 Andy Green \u003candy@warmcat.com\u003e\n- *\n- * This file is made available under the Creative Commons CC0 1.0\n- * Universal Public Domain Dedication.\n- *\n- * The person who associated a work with this deed has dedicated\n- * the work to the public domain by waiving all of his or her rights\n- * to the work worldwide under copyright law, including all related\n- * and neighboring rights, to the extent allowed by law. You can copy,\n- * modify, distribute and perform the work, even for commercial purposes,\n- * all without asking permission.\n- *\n- * The test apps are intended to be adapted for use in your code, which\n- * may be proprietary.\tSo unlike the library itself, they are licensed\n- * Public Domain.\n- */\n-\n-#include \u0022test-server.h\u0022\n-#include \u003cpthread.h\u003e\n-\n-int close_testing;\n-int max_poll_elements;\n-int debug_level \u003d 7;\n-\n-#ifdef EXTERNAL_POLL\n-struct lws_pollfd *pollfds;\n-int *fd_lookup;\n-int count_pollfds;\n-#endif\n-volatile int force_exit \u003d 0;\n-struct lws_context *context;\n-\n-#if defined(LWS_WITH_TLS) \u0026\u0026 defined(LWS_HAVE_SSL_CTX_set1_param)\n-char crl_path[1024] \u003d \u0022\u0022;\n-#endif\n-\n-#define LWS_PLUGIN_STATIC\n-#include \u0022../plugins/protocol_lws_mirror.c\u0022\n-#include \u0022../plugins/protocol_lws_status.c\u0022\n-\n-/*\n- * This mutex lock protects code that changes or relies on wsi list outside of\n- * the service thread.\tThe service thread will acquire it when changing the\n- * wsi list and other threads should acquire it while dereferencing wsis or\n- * calling apis like lws_callback_on_writable_all_protocol() which\n- * use the wsi list and wsis from a different thread context.\n- */\n-pthread_mutex_t lock_established_conns;\n-\n-/* http server gets files from this path */\n-#define LOCAL_RESOURCE_PATH INSTALL_DATADIR\u0022/libwebsockets-test-server\u0022\n-char *resource_path \u003d LOCAL_RESOURCE_PATH;\n-\n-/*\n- * multithreaded version - protect wsi lifecycle changes in the library\n- * these are called from protocol 0 callbacks\n- */\n-\n-void test_server_lock(int care)\n-{\n-\tif (care)\n-\t\tpthread_mutex_lock(\u0026lock_established_conns);\n-}\n-void test_server_unlock(int care)\n-{\n-\tif (care)\n-\t\tpthread_mutex_unlock(\u0026lock_established_conns);\n-}\n-\n-/*\n- * This demo server shows how to use libwebsockets for one or more\n- * websocket protocols in the same server\n- *\n- * It defines the following websocket protocols:\n- *\n- * dumb-increment-protocol: once the socket is opened, an incrementing\n- *\t\t\t\tascii string is sent down it every 50ms.\n- *\t\t\t\tIf you send \u0022reset\u005cn\u0022 on the websocket, then\n- *\t\t\t\tthe incrementing number is reset to 0.\n- *\n- * lws-mirror-protocol: copies any received packet to every connection also\n- *\t\t\t\tusing this protocol, including the sender\n- */\n-\n-enum demo_protocols {\n-\t/* always first */\n-\tPROTOCOL_HTTP \u003d 0,\n-\n-\tPROTOCOL_DUMB_INCREMENT,\n-\tPROTOCOL_LWS_MIRROR,\n-\tPROTOCOL_LWS_STATUS,\n-\n-\t/* always last */\n-\tDEMO_PROTOCOL_COUNT\n-};\n-\n-/* list of supported protocols and callbacks */\n-\n-static struct lws_protocols protocols[] \u003d {\n-\t/* first protocol must always be HTTP handler */\n-\n-\t{\n-\t\t\u0022http-only\u0022,\t\t/* name */\n-\t\tcallback_http,\t\t/* callback */\n-\t\tsizeof (struct per_session_data__http),\t/* per_session_data_size */\n-\t\t0,\t\t\t/* max frame size / rx buffer */\n-\t},\n-\t{\n-\t\t\u0022dumb-increment-protocol\u0022,\n-\t\tcallback_dumb_increment,\n-\t\tsizeof(struct per_session_data__dumb_increment),\n-\t\t10, /* rx buf size must be \u003e\u003d permessage-deflate rx size\n-\t\t * dumb-increment only sends very small packets, so we set\n-\t\t * this accordingly. If your protocol will send bigger\n-\t\t * things, adjust this to match */\n-\t},\n-\tLWS_PLUGIN_PROTOCOL_MIRROR,\n-\tLWS_PLUGIN_PROTOCOL_LWS_STATUS,\n-\t{ NULL, NULL, 0, 0 } /* terminator */\n-};\n-\n-void *thread_dumb_increment(void *threadid)\n-{\n-\twhile (!force_exit) {\n-\t\t/*\n-\t\t * this lock means wsi in the active list cannot\n-\t\t * disappear underneath us, because the code to add and remove\n-\t\t * them is protected by the same lock\n-\t\t */\n-\t\tpthread_mutex_lock(\u0026lock_established_conns);\n-\t\tlws_callback_on_writable_all_protocol(context,\n-\t\t\t\t\u0026protocols[PROTOCOL_DUMB_INCREMENT]);\n-\t\tpthread_mutex_unlock(\u0026lock_established_conns);\n-\t\tusleep(100000);\n-\t}\n-\n-\tpthread_exit(NULL);\n-}\n-\n-void *thread_service(void *threadid)\n-{\n-\twhile (lws_service_tsi(context, 50, (int)(lws_intptr_t)threadid) \u003e\u003d 0 \u0026\u0026 !force_exit)\n-\t\t;\n-\n-\tpthread_exit(NULL);\n-}\n-\n-void sighandler(int sig)\n-{\n-\tforce_exit \u003d 1;\n-\tlws_cancel_service(context);\n-}\n-\n-static const struct lws_extension exts[] \u003d {\n-\t{\n-\t\t\u0022permessage-deflate\u0022,\n-\t\tlws_extension_callback_pm_deflate,\n-\t\t\u0022permessage-deflate; client_no_context_takeover; client_max_window_bits\u0022\n-\t},\n-\t{\n-\t\t\u0022deflate-frame\u0022,\n-\t\tlws_extension_callback_pm_deflate,\n-\t\t\u0022deflate_frame\u0022\n-\t},\n-\t{ NULL, NULL, NULL /* terminator */ }\n-};\n-\n-static struct option options[] \u003d {\n-\t{ \u0022help\u0022,\tno_argument,\t\tNULL, 'h' },\n-\t{ \u0022debug\u0022,\trequired_argument,\tNULL, 'd' },\n-\t{ \u0022port\u0022,\trequired_argument,\tNULL, 'p' },\n-\t{ \u0022ssl\u0022,\tno_argument,\t\tNULL, 's' },\n-\t{ \u0022allow-non-ssl\u0022,\tno_argument,\tNULL, 'a' },\n-\t{ \u0022interface\u0022,\trequired_argument,\tNULL, 'i' },\n-\t{ \u0022closetest\u0022,\tno_argument,\t\tNULL, 'c' },\n-\t{ \u0022libev\u0022, no_argument,\t\tNULL, 'e' },\n-\t{ \u0022threads\u0022, required_argument,\tNULL, 'j' },\n-#ifndef LWS_NO_DAEMONIZE\n-\t{ \u0022daemonize\u0022,\tno_argument,\t\tNULL, 'D' },\n-#endif\n-\t{ \u0022resource_path\u0022, required_argument,\tNULL, 'r' },\n-\t{ NULL, 0, 0, 0 }\n-};\n-\n-int main(int argc, char **argv)\n-{\n-\tstruct lws_context_creation_info info;\n-\tchar interface_name[128] \u003d \u0022\u0022;\n-\tconst char *iface \u003d NULL;\n-\tpthread_t pthread_dumb, pthread_service[32];\n-\tchar cert_path[1024];\n-\tchar key_path[1024];\n-\tint threads \u003d 1;\n-\tint use_ssl \u003d 0;\n-\tvoid *retval;\n-\tint opts \u003d 0;\n-\tint n \u003d 0;\n-#ifndef _WIN32\n-/* LOG_PERROR is not POSIX standard, and may not be portable */\n-#ifdef __sun\n-\tint syslog_options \u003d LOG_PID;\n-#else\n-\tint syslog_options \u003d LOG_PID | LOG_PERROR;\n-#endif\n-#endif\n-#ifndef LWS_NO_DAEMONIZE\n-\tint daemonize \u003d 0;\n-#endif\n-\n-\t/*\n-\t * take care to zero down the info struct, he contains random garbaage\n-\t * from the stack otherwise\n-\t */\n-\tmemset(\u0026info, 0, sizeof info);\n-\tinfo.port \u003d 7681;\n-\n-\tpthread_mutex_init(\u0026lock_established_conns, NULL);\n-\n-\twhile (n \u003e\u003d 0) {\n-\t\tn \u003d getopt_long(argc, argv, \u0022eci:hsap:d:Dr:j:\u0022, options, NULL);\n-\t\tif (n \u003c 0)\n-\t\t\tcontinue;\n-\t\tswitch (n) {\n-\t\tcase 'j':\n-\t\t\tthreads \u003d atoi(optarg);\n-\t\t\tif (threads \u003e (int)ARRAY_SIZE(pthread_service)) {\n-\t\t\t\tlwsl_err(\u0022Max threads %lu\u005cn\u0022,\n-\t\t\t\t\t (unsigned long)ARRAY_SIZE(pthread_service));\n-\t\t\t\treturn 1;\n-\t\t\t}\n-\t\t\tbreak;\n-\t\tcase 'e':\n-\t\t\topts |\u003d LWS_SERVER_OPTION_LIBEV;\n-\t\t\tbreak;\n-#ifndef LWS_NO_DAEMONIZE\n-\t\tcase 'D':\n-\t\t\tdaemonize \u003d 1;\n-\t\t\t#if !defined(_WIN32) \u0026\u0026 !defined(__sun)\n-\t\t\tsyslog_options \u0026\u003d ~LOG_PERROR;\n-\t\t\t#endif\n-\t\t\tbreak;\n-#endif\n-\t\tcase 'd':\n-\t\t\tdebug_level \u003d atoi(optarg);\n-\t\t\tbreak;\n-\t\tcase 's':\n-\t\t\tuse_ssl \u003d 1;\n-\t\t\tbreak;\n-\t\tcase 'a':\n-\t\t\topts |\u003d LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;\n-\t\t\tbreak;\n-\t\tcase 'p':\n-\t\t\tinfo.port \u003d atoi(optarg);\n-\t\t\tbreak;\n-\t\tcase 'i':\n-\t\t\tlws_strncpy(interface_name, optarg, sizeof interface_name);\n-\t\t\tiface \u003d interface_name;\n-\t\t\tbreak;\n-\t\tcase 'c':\n-\t\t\tclose_testing \u003d 1;\n-\t\t\tfprintf(stderr, \u0022 Close testing mode -- closes on \u0022\n-\t\t\t\t\t \u0022client after 50 dumb increments\u0022\n-\t\t\t\t\t \u0022and suppresses lws_mirror spam\u005cn\u0022);\n-\t\t\tbreak;\n-\t\tcase 'r':\n-\t\t\tresource_path \u003d optarg;\n-\t\t\tprintf(\u0022Setting resource path to \u005c\u0022%s\u005c\u0022\u005cn\u0022, resource_path);\n-\t\t\tbreak;\n-\t\tcase 'h':\n-\t\t\tfprintf(stderr, \u0022Usage: test-server \u0022\n-\t\t\t\t\t\u0022[--port\u003d\u003cp\u003e] [--ssl] \u0022\n-\t\t\t\t\t\u0022[-d \u003clog bitfield\u003e] \u0022\n-\t\t\t\t\t\u0022[--resource_path \u003cpath\u003e]\u005cn\u0022);\n-\t\t\texit(1);\n-\t\t}\n-\t}\n-\n-#if !defined(LWS_NO_DAEMONIZE) \u0026\u0026 !defined(WIN32)\n-\t/*\n-\t * normally lock path would be /var/lock/lwsts or similar, to\n-\t * simplify getting started without having to take care about\n-\t * permissions or running as root, set to /tmp/.lwsts-lock\n-\t */\n-\tif (daemonize \u0026\u0026 lws_daemonize(\u0022/tmp/.lwsts-lock\u0022)) {\n-\t\tfprintf(stderr, \u0022Failed to daemonize\u005cn\u0022);\n-\t\treturn 1;\n-\t}\n-#endif\n-\n-\tsignal(SIGINT, sighandler);\n-\n-#ifndef _WIN32\n-\t/* we will only try to log things according to our debug_level */\n-\tsetlogmask(LOG_UPTO (LOG_DEBUG));\n-\topenlog(\u0022lwsts\u0022, syslog_options, LOG_DAEMON);\n-#endif\n-\n-\t/* tell the library what debug level to emit and to send it to syslog */\n-\tlws_set_log_level(debug_level, NULL);\n-\tlwsl_notice(\u0022libwebsockets test server pthreads - license LGPL2.1+SLE\u005cn\u0022);\n-\tlwsl_notice(\u0022(C) Copyright 2010-2018 Andy Green \u003candy@warmcat.com\u003e\u005cn\u0022);\n-\n-\tprintf(\u0022Using resource path \u005c\u0022%s\u005c\u0022\u005cn\u0022, resource_path);\n-#ifdef EXTERNAL_POLL\n-\tmax_poll_elements \u003d getdtablesize();\n-\tpollfds \u003d malloc(max_poll_elements * sizeof (struct lws_pollfd));\n-\tfd_lookup \u003d malloc(max_poll_elements * sizeof (int));\n-\tif (pollfds \u003d\u003d NULL || fd_lookup \u003d\u003d NULL) {\n-\t\tlwsl_err(\u0022Out of memory pollfds\u003d%d\u005cn\u0022, max_poll_elements);\n-\t\treturn -1;\n-\t}\n-#endif\n-\n-\tinfo.iface \u003d iface;\n-\tinfo.protocols \u003d protocols;\n-\tinfo.extensions \u003d exts;\n-\n-\tinfo.ssl_cert_filepath \u003d NULL;\n-\tinfo.ssl_private_key_filepath \u003d NULL;\n-\n-\tif (use_ssl) {\n-\t\tif (strlen(resource_path) \u003e sizeof(cert_path) - 32) {\n-\t\t\tlwsl_err(\u0022resource path too long\u005cn\u0022);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tsprintf(cert_path, \u0022%s/libwebsockets-test-server.pem\u0022,\n-\t\t\tresource_path);\n-\t\tif (strlen(resource_path) \u003e sizeof(key_path) - 32) {\n-\t\t\tlwsl_err(\u0022resource path too long\u005cn\u0022);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tsprintf(key_path, \u0022%s/libwebsockets-test-server.key.pem\u0022,\n-\t\t\tresource_path);\n-\n-\t\tinfo.ssl_cert_filepath \u003d cert_path;\n-\t\tinfo.ssl_private_key_filepath \u003d key_path;\n-\t}\n-\tinfo.gid \u003d -1;\n-\tinfo.uid \u003d -1;\n-\tinfo.options \u003d opts;\n-\tinfo.count_threads \u003d threads;\n-\tinfo.extensions \u003d exts;\n-\tinfo.max_http_header_pool \u003d 4;\n-\tinfo.pt_serv_buf_size \u003d 128 * 1024;\n-\n-\t/* when doing slow benchmarks with thousands of concurrent\n-\t * connections, we need wait longer\n-\t */\n-\tinfo.timeout_secs \u003d 30;\n-\tinfo.keepalive_timeout \u003d 30;\n-\n-\tcontext \u003d lws_create_context(\u0026info);\n-\tif (context \u003d\u003d NULL) {\n-\t\tlwsl_err(\u0022libwebsocket init failed\u005cn\u0022);\n-\t\treturn -1;\n-\t}\n-\n-\t/* start the dumb increment thread */\n-\n-\tn \u003d pthread_create(\u0026pthread_dumb, NULL, thread_dumb_increment, 0);\n-\tif (n) {\n-\t\tlwsl_err(\u0022Unable to create dumb thread\u005cn\u0022);\n-\t\tgoto done;\n-\t}\n-\n-\t/*\n-\t * notice the actual number of threads may be capped by the library,\n-\t * so use lws_get_count_threads() to get the actual amount of threads\n-\t * initialized.\n-\t */\n-\n-\tlwsl_notice(\u0022Service thread count: %d\u005cn\u0022, lws_get_count_threads(context));\n-\n-\tfor (n \u003d 0; n \u003c lws_get_count_threads(context); n++)\n-\t\tif (pthread_create(\u0026pthread_service[n], NULL, thread_service,\n-\t\t\t\t (void *)(lws_intptr_t)n))\n-\t\t\tlwsl_err(\u0022Failed to start service thread\u005cn\u0022);\n-\n-\t/* wait for all the service threads to exit */\n-\n-\twhile ((--n) \u003e\u003d 0)\n-\t\tpthread_join(pthread_service[n], \u0026retval);\n-\n-\t/* wait for pthread_dumb to exit */\n-\tpthread_join(pthread_dumb, \u0026retval);\n-\n-done:\n-\tlws_context_destroy(context);\n-\tpthread_mutex_destroy(\u0026lock_established_conns);\n-\n-\tlwsl_notice(\u0022libwebsockets-test-server exited cleanly\u005cn\u0022);\n-\n-#ifndef _WIN32\n-\tcloselog();\n-#endif\n-\n-\treturn 0;\n-}\ndiff --git a/test-apps/test-server.c b/test-apps/test-server.c\nindex 9e86590..edbd5b9 100644\n--- a/test-apps/test-server.c\n+++ b/test-apps/test-server.c\n@@ -66,9 +66,10 @@ char crl_path[1024] \u003d \u0022\u0022;\n */\n \n #define LWS_PLUGIN_STATIC\n+#if defined(LWS_ROLE_WS)\n #include \u0022../plugins/protocol_lws_mirror.c\u0022\n #include \u0022../plugins/protocol_lws_status.c\u0022\n-#include \u0022../plugins/protocol_lws_meta.c\u0022\n+#endif\n \n /* singlethreaded version --\u003e no locks */\n \n@@ -122,6 +123,7 @@ static struct lws_protocols protocols[] \u003d {\n \t\tsizeof (struct per_session_data__http),\t/* per_session_data_size */\n \t\t0,\t\t\t/* max frame size / rx buffer */\n \t},\n+#if defined(LWS_ROLE_WS)\n \t{\n \t\t\u0022dumb-increment-protocol\u0022,\n \t\tcallback_dumb_increment,\n@@ -133,8 +135,7 @@ static struct lws_protocols protocols[] \u003d {\n \t},\n \tLWS_PLUGIN_PROTOCOL_MIRROR,\n \tLWS_PLUGIN_PROTOCOL_LWS_STATUS,\n-\n-\tLWS_PLUGIN_PROTOCOL_LWS_META,\n+#endif\n \t{ NULL, NULL, 0, 0 } /* terminator */\n };\n \n","s":{"c":1754388181,"u": 32723}} ],"g": 45754,"chitpc": 0,"ehitpc": 0,"indexed":0 , "ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}