{"schema":"libjg2-1",
"vpath":"/git/",
"avatar":"/git/avatar/",
"alang":"",
"gen_ut":1747289810,
"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":"43c2695728fb88eefa189bf755c353fc",
"commit": {"type":"commit",
"time": 1633682921,
"time_ofs": 60,
"oid_tree": { "oid": "48cd563e3511ff267adcc20c66946fbb49bcc933", "alias": []},
"oid":{ "oid": "2cfa260e625882fa7646bab4a4f699935e2a39e8", "alias": []},
"msg": "sspc: refactor to allow different transports",
"sig_commit": { "git_time": { "time": 1633682921, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },
"sig_author": { "git_time": { "time": 1629880507, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }},
"body": "sspc: refactor to allow different transports\n\nThis is a NOP for existing usecases.\n\nAt the moment the only implemented transport for serialized SS is wsi, it's\ntypically used with Unix Domain Sockets, but it also works over tcp the\nsame.\n\nIt generalizes the interface between serialized chunks and the\ntransport, separately for client and proxy. The wsi transport is migrated\nto use the new transport ops structs.\n\nIt will then be possible to \u0022bring your own transport\u0022, so long as it is\nreliable, and in-order, both for proxy and client / sspc.\n\nWe also adapt minimal-secure-streams-binance to build the -client variant\nvia SS proxy as well.\n\nLWS_ONLY_SSPC is added so libwebsockets can be produced with just sspc\nclient support even for tiny targets.\n\nA new embedded minimal example for rpi pico is also provided that\ndemonstrates using Serialized SS over a UART to an SS proxy, to implement\nthe SS Binance example on the pico, even though it has no networking itself.\n"
,
"diff": "diff --git a/CMakeLists.txt b/CMakeLists.txt\nindex 9993779..3565589 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -159,7 +159,7 @@ option(LWS_WITH_SYS_METRICS \u0022Lws Metrics API\u0022 OFF)\n #\n # Secure Streams\n #\n-option(LWS_WITH_SECURE_STREAMS \u0022Secure Streams protocol-agnostic API\u0022 OFF)\n+option(LWS_WITH_SECURE_STREAMS \u0022Secure Streams protocol-agnostic API\u0022 OM)\n option(LWS_WITH_SECURE_STREAMS_CPP \u0022Secure Streams C++ classes\u0022 OFF)\n option(LWS_WITH_SECURE_STREAMS_PROXY_API \u0022Secure Streams support to work across processes\u0022 OFF)\n option(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM \u0022Auth support for api.amazon.com\u0022 OFF)\n@@ -167,6 +167,7 @@ option(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY \u0022Secure Streams Policy is hard\n option(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4 \u0022Secure Streams Auth support for AWS Sigv4\u0022 OFF)\n option(LWS_WITH_SECURE_STREAMS_BUFFER_DUMP \u0022Secure Streams protocol buffer dump\u0022 OFF)\n option(LWS_WITH_SS_DIRECT_PROTOCOL_STR \u0022Secure Streams directly set/get metadata w/o policy\u0022 OFF)\n+option(LWS_ONLY_SSPC \u0022Remove everything from library except standalone SSPC client support\u0022 OFF)\n \n #\n # CTest options\n@@ -620,6 +621,9 @@ CHECK_C_SOURCE_COMPILES(\u0022#include \u003cpthread.h\u003e\u005cnvoid main(void) { while(1) ; } vo\n CHECK_C_SOURCE_COMPILES(\u0022#include \u003cinttypes.h\u003e\u005cnvoid main(void) { while(1) ; } void xxexit(void){}\u0022 LWS_HAVE_INTTYPES_H)\n CHECK_C_SOURCE_COMPILES(\u0022#include \u003csys/resource.h\u003e\u005cnvoid main(void) { while(1) ; } void xxexit(void){}\u0022 LWS_HAVE_SYS_RESOURCE_H)\n \n+if (LWS_EXT_PTHREAD_INCLUDE_DIR)\n+\tset(LWS_HAVE_PTHREAD_H 1)\n+endif()\n \n if(CMAKE_SYSTEM_NAME MATCHES \u0022Darwin\u0022)\n \tif(CMAKE_OSX_DEPLOYMENT_TARGET LESS \u002210.12\u0022)\n@@ -1053,6 +1057,9 @@ if (LWS_WITH_SHARED)\n \tadd_dependencies(websockets_shared GENHDR)\n endif()\n \n+if (LWS_ONLY_SSPC)\n+\t\ttarget_compile_definitions(websockets PUBLIC STANDALONE)\n+endif()\n \n \n #\ndiff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in\nindex b973f93..26b349f 100644\n--- a/cmake/lws_config.h.in\n+++ b/cmake/lws_config.h.in\n@@ -118,6 +118,7 @@\n #cmakedefine LWS_MINGW_SUPPORT\n #cmakedefine LWS_NO_CLIENT\n #cmakedefine LWS_NO_DAEMONIZE\n+#cmakedefine LWS_ONLY_SSPC\n #cmakedefine LWS_OPENSSL_CLIENT_CERTS \u0022${LWS_OPENSSL_CLIENT_CERTS}\u0022\n #cmakedefine LWS_OPENSSL_SUPPORT\n #cmakedefine LWS_PLAT_OPTEE\ndiff --git a/doc-assets/lws-sspc-1.png b/doc-assets/lws-sspc-1.png\nnew file mode 100644\nindex 0000000..1d5c430\nBinary files /dev/null and b/doc-assets/lws-sspc-1.png differ\ndiff --git a/doc-assets/lws-sspc-2.png b/doc-assets/lws-sspc-2.png\nnew file mode 100644\nindex 0000000..d407b80\nBinary files /dev/null and b/doc-assets/lws-sspc-2.png differ\ndiff --git a/doc-assets/rpi-uart-1.png b/doc-assets/rpi-uart-1.png\nnew file mode 100644\nindex 0000000..34f7821\nBinary files /dev/null and b/doc-assets/rpi-uart-1.png differ\ndiff --git a/doc-assets/rpi-uart.png b/doc-assets/rpi-uart.png\nnew file mode 100644\nindex 0000000..0aad79c\nBinary files /dev/null and b/doc-assets/rpi-uart.png differ\ndiff --git a/include/libwebsockets.h b/include/libwebsockets.h\nindex 3219295..9f529fb 100644\n--- a/include/libwebsockets.h\n+++ b/include/libwebsockets.h\n@@ -74,6 +74,8 @@ struct lws_dsh;\n \n #define LWS_US_TO_MS(x) ((x + (LWS_US_PER_MS / 2)) / LWS_US_PER_MS)\n \n+#define LWS_FOURCC(a, b, c, d) ((a \u003c\u003c 24) | (b \u003c\u003c 16) | (c \u003c\u003c 8) | d)\n+\n #if defined(LWS_HAS_INTPTR_T)\n #include \u003cstdint.h\u003e\n #define lws_intptr_t intptr_t\n@@ -664,8 +666,10 @@ struct lws;\n #include \u003clibwebsockets/lws-diskcache.h\u003e\n #include \u003clibwebsockets/lws-sequencer.h\u003e\n #include \u003clibwebsockets/lws-secure-streams.h\u003e\n+#include \u003clibwebsockets/lws-secure-streams-serialization.h\u003e\n #include \u003clibwebsockets/lws-secure-streams-policy.h\u003e\n #include \u003clibwebsockets/lws-secure-streams-client.h\u003e\n+#include \u003clibwebsockets/lws-secure-streams-transport-proxy.h\u003e\n \n #if !defined(LWS_PLAT_FREERTOS)\n #include \u003clibwebsockets/abstract/abstract.h\u003e\ndiff --git a/include/libwebsockets/lws-adopt.h b/include/libwebsockets/lws-adopt.h\nindex 94a1818..c44080b 100644\n--- a/include/libwebsockets/lws-adopt.h\n+++ b/include/libwebsockets/lws-adopt.h\n@@ -47,6 +47,7 @@\n */\n LWS_VISIBLE LWS_EXTERN struct lws *\n lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd);\n+\n /**\n * lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted\n * it for vhost\n@@ -146,13 +147,20 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \t\t\t struct lws *parent);\n \n typedef struct lws_adopt_desc {\n-\tstruct lws_vhost *vh;\t\t/**\u003c vhost the wsi should belong to */\n-\tlws_adoption_type type;\t\t/**\u003c OR-ed combinations of lws_adoption_type flags */\n-\tlws_sock_file_fd_type fd;\t/**\u003c union with either .sockfd or .filefd set */\n-\tconst char *vh_prot_name;\t/**\u003c NULL or vh protocol name to bind raw connection to */\n-\tstruct lws *parent;\t\t/**\u003c NULL or struct lws to attach new_wsi to as a child */\n-\tvoid *opaque;\t\t\t/**\u003c opaque pointer to set on created wsi */\n-\tconst char *fi_wsi_name;\t/**\u003c NULL, or Fault Injection inheritence filter for wsi\u003dstring/ context faults */\n+\tstruct lws_vhost\t*vh;\t\t/**\u003c vhost the wsi should belong to */\n+\tlws_adoption_type\ttype;\t\t/**\u003c OR-ed combinations of\n+\t\t\t\t\t\t * lws_adoption_type flags */\n+\tlws_sock_file_fd_type\tfd;\t\t/**\u003c union with either .sockfd\n+\t\t\t\t\t\t * or .filefd set */\n+\tconst char\t\t*vh_prot_name;\t/**\u003c NULL or vh protocol name to\n+\t\t\t\t\t\t * bind raw connection to */\n+\tstruct lws\t\t*parent; \t/**\u003c NULL or struct lws to\n+\t\t\t\t\t\t * attach new_wsi to as a child */\n+\tvoid\t\t\t*opaque;\t/**\u003c opaque pointer to set on\n+\t\t\t\t\t\t *created wsi */\n+\tconst char\t\t*fi_wsi_name;\t/**\u003c NULL, or Fault Injection\n+\t\t\t\t\t\t * inheritence filter for\n+\t\t\t\t\t\t * wsi\u003dstring/ context faults */\n } lws_adopt_desc_t;\n \n /**\ndiff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h\nindex 8ca4601..2ea966f 100644\n--- a/include/libwebsockets/lws-context-vhost.h\n+++ b/include/libwebsockets/lws-context-vhost.h\n@@ -252,6 +252,7 @@ struct lws_plat_file_ops;\n struct lws_ss_policy;\n struct lws_ss_plugin;\n struct lws_metric_policy;\n+struct lws_sss_ops;\n \n typedef int (*lws_context_ready_cb_t)(struct lws_context *context);\n \n@@ -797,6 +798,14 @@ struct lws_context_creation_info {\n \t * the socket path given in ss_proxy_bind (start it with a + or +@);\n \t * nonzero means connect via a tcp socket to the tcp address in\n \t * ss_proxy_bind and the given port */\n+\tconst struct lws_transport_proxy_ops *txp_ops_ssproxy; /**\u003c CONTEXT: NULL, or\n+\t * custom sss transport ops used for ss proxy communication. NULL means\n+\t * to use the default wsi-based proxy server */\n+\tconst void *txp_ssproxy_info; /**\u003c CONTEXT: NULL, or extra transport-\n+\t * specifi creation info to be used at \u005cp txp_ops_ssproxy creation */\n+\tconst struct lws_transport_client_ops *txp_ops_sspc; /**\u003c CONTEXT: NULL, or\n+\t * custom sss transport ops used for ss client communication to the ss\n+\t * proxy. NULL means to use the default wsi-based client support */\n #endif\n \n \tint rlimit_nofile;\ndiff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h\nindex c6dc439..58217e8 100644\n--- a/include/libwebsockets/lws-misc.h\n+++ b/include/libwebsockets/lws-misc.h\n@@ -882,6 +882,13 @@ LWS_VISIBLE extern const lws_humanize_unit_t humanize_schema_si[7];\n LWS_VISIBLE extern const lws_humanize_unit_t humanize_schema_si_bytes[7];\n LWS_VISIBLE extern const lws_humanize_unit_t humanize_schema_us[8];\n \n+#if defined(_DEBUG)\n+void\n+lws_assert_fourcc(uint32_t fourcc, uint32_t expected);\n+#else\n+#define lws_assert_fourcc(_a, _b)\n+#endif\n+\n /**\n * lws_humanize() - Convert possibly large number to human-readable uints\n *\ndiff --git a/include/libwebsockets/lws-secure-streams-client.h b/include/libwebsockets/lws-secure-streams-client.h\nindex f644ab5..361b980 100644\n--- a/include/libwebsockets/lws-secure-streams-client.h\n+++ b/include/libwebsockets/lws-secure-streams-client.h\n@@ -29,6 +29,9 @@\n * lws_sspc_ when client is in a different process to the event loop\n *\n * The client api is almost the same except the slightly diffent names.\n+ *\n+ * This header is included as part of libwebsockets.h, for link against the\n+ * libwebsockets library.\n */\n \n /*\n@@ -40,6 +43,7 @@\n * lws_sspc_ in one step by #define LWS_SS_USE_SSPC before including\n */\n \n+\n struct lws_sspc_handle;\n \n #if defined(LWS_SS_USE_SSPC)\n@@ -66,12 +70,6 @@ struct lws_sspc_handle;\n #define _lws_fi_user_ss_fi\t\t_lws_fi_user_sspc_fi\n #define lwsl_ss_get_cx\t\t\tlwsl_sspc_get_cx\n \n-LWS_VISIBLE LWS_EXTERN void\n-lws_log_prepend_sspc(struct lws_log_cx *cx, void *obj, char **p, char *e);\n-\n-LWS_VISIBLE LWS_EXTERN struct lws_log_cx *\n-lwsl_sspc_get_cx(struct lws_sspc_handle *ss);\n-\n #undef lwsl_ss\n #define lwsl_ss lwsl_sspc\n \n@@ -79,6 +77,12 @@ lwsl_sspc_get_cx(struct lws_sspc_handle *ss);\n #define lwsl_hexdump_ss lwsl_hexdump_sspc\n #endif\n \n+LWS_VISIBLE LWS_EXTERN void\n+lws_log_prepend_sspc(struct lws_log_cx *cx, void *obj, char **p, char *e);\n+\n+LWS_VISIBLE LWS_EXTERN struct lws_log_cx *\n+lwsl_sspc_get_cx(struct lws_sspc_handle *ss);\n+\n #define lwsl_sspc(_h, _fil, ...) \u005c\n \t\t _lws_log_cx(lwsl_sspc_get_cx(_h), lws_log_prepend_sspc, _h, \u005c\n \t\t\t\t\t_fil, __func__, __VA_ARGS__)\n@@ -170,6 +174,28 @@ lwsl_sspc_get_cx(struct lws_sspc_handle *ss);\n #define lwsl_hexdump_sspc_info(_v, ...) lwsl_hexdump_sspc(_v, LLL_INFO, __VA_ARGS__)\n #define lwsl_hexdump_sspc_debug(_v, ...) lwsl_hexdump_sspc(_v, LLL_DEBUG, __VA_ARGS__)\n \n+/*\n+ * How lws refers to your per-proxy-link private data... not allocated or freed\n+ * by lws, nor used except to pass a pointer to it through to ops callbacks\n+ * below. Should be set to your transport private instance object, it's set to\n+ * the wsi for the wsi transport. Notice it is provided as a ** (ptr-to-ptr) in\n+ * most apis.\n+ */\n+\n+/*\n+ * Stub context when using LWS_ONLY_SSPC\n+ */\n+\n+struct lws_context_standalone {\n+\tlws_txp_path_client_t\t\t\ttxp_cpath;\n+\tlws_dll2_owner_t\t\t\tss_client_owner;\n+\tuint32_t\t\t\t\tssidx;\n+};\n+\n+#if defined(STANDALONE)\n+#define lws_context lws_context_standalone\n+struct lws_context_standalone;\n+#endif\n \n LWS_VISIBLE LWS_EXTERN int\n lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,\n@@ -321,14 +347,26 @@ lws_sspc_to_user_object(struct lws_sspc_handle *h);\n \n LWS_VISIBLE LWS_EXTERN void\n lws_sspc_change_handlers(struct lws_sspc_handle *h,\n-\tlws_ss_state_return_t (*rx)(void *userobj, const uint8_t *buf,\n-\t\t\t\t size_t len, int flags),\n-\tlws_ss_state_return_t (*tx)(void *userobj, lws_ss_tx_ordinal_t ord,\n-\t\t\t\t uint8_t *buf, size_t *len, int *flags),\n-\tlws_ss_state_return_t (*state)(void *userobj, void *h_src\n-\t\t\t\t\t/* ss handle type */,\n-\t\t\t\t lws_ss_constate_t state,\n-\t\t\t\t lws_ss_tx_ordinal_t ack));\n-\n-const char *\n+\t\t\t lws_sscb_rx rx,lws_sscb_tx tx, lws_sscb_state state);\n+\n+\n+/*\n+ * Helpers offered by lws to handle transport SSPC-side proxy link events\n+ */\n+\n+/**\n+ * lws_sspc_tag() - get the sspc log tag\n+ *\n+ * \u005cparam h: the sspc handle\n+ *\n+ * Returns the sspc log tag, to assist in logging traceability\n+ */\n+LWS_VISIBLE LWS_EXTERN const char *\n lws_sspc_tag(struct lws_sspc_handle *h);\n+\n+\n+#if defined(STANDALONE)\n+#undef lws_context\n+#endif\n+\n+\ndiff --git a/include/libwebsockets/lws-secure-streams-serialization.h b/include/libwebsockets/lws-secure-streams-serialization.h\nnew file mode 100644\nindex 0000000..9115537\n--- /dev/null\n+++ b/include/libwebsockets/lws-secure-streams-serialization.h\n@@ -0,0 +1,601 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ * included from libwebsockets.h\n+ *\n+ * This defines the Serialized Secure Streams framing, and the optional\n+ * lws_transport_mux framing.\n+ *\n+ * APIs are declared for lws_transport and binding those to the SSPC and proxy\n+ * sides in lws.\n+ */\n+\n+#if defined(STANDALONE)\n+struct lws_context_standalone;\n+#define lws_context lws_context_standalone\n+#endif\n+\n+#define LWSSSS_VERSION 1\n+\n+typedef enum {\n+\t/*\n+\t * This is the Serialized Serure Streams framing. It's sufficient to\n+\t * carry all SS API actions over a point-to-point bytestream between\n+\t * an SSPC client and an SS oroxy, in both directions.\n+\t *\n+\t * These serialized streams may be multiplexed by the transport (eg,\n+\t * for unix domain sockets transport, each SS opens its own UDS socket\n+\t * to the proxy) or via lws_transport_mux framing encapsulation.\n+\t *\n+\t *\n+\t * Framing for Proxy -\u003e Client direction\n+\t */\n+\n+\tLWSSS_SER_RXPRE_RX_PAYLOAD\t\t\t\t\u003d 0x55,\n+\t/*\n+\t * Proxied rx\n+\t *\n+\t * - 0: LWSSS_SER_RXPRE_RX_PAYLOAD\n+\t * - 1: 2 byte MSB-first rest-of-frame length\n+\t * - 3: 4-byte MSB-first flags\n+\t * - 7: 4-byte MSB-first us between inbound read and wrote to client\n+\t * - 11: 8-byte MSB-first us resolution unix time proxy wrote to client\n+\t * - 17: (rideshare name len + rideshare name if flags \u0026\n+\t * \t\tLWSSS_FLAG_RIDESHARE) payload\n+\t */\n+\tLWSSS_SER_RXPRE_CREATE_RESULT,\n+\t/*\n+\t * Proxied connection setup result\n+\t *\n+\t * - 0: LWSSS_SER_RXPRE_CREATE_RESULT\n+\t * - 1: 2 byte MSB-first rest-of-frame length (usually 00, 03)\n+\t * - 3: 1 byte result, 0 \u003d success. On failure, proxy will close\n+\t * \t\tconnection.\n+\t * - 4: 4 byte client dsh allocation recommended for stream type,\n+\t * \t\tfrom policy (introduced in SSSv1)\n+\t * - 8: 2 byte MSB-first initial tx credit\n+\t * - 10: if present, comma-sep list of rideshare types from policy\n+\t */\n+\tLWSSS_SER_RXPRE_CONNSTATE,\n+\t/*\n+\t * Proxied state (8 or 11 byte packet)\n+\t *\n+\t * - 0: LWSSS_SER_RXPRE_CONNSTATE\n+\t * - 1: 00, 05 if state \u003c 256, else 00, 08\n+\t * - 3: 1 byte state index if state \u003c 256, else 4-byte MSB-first\n+\t * \t\tstate index\n+\t * - 4 or 7: 4-byte MSB-first ordinal\n+\t */\n+\tLWSSS_SER_RXPRE_TXCR_UPDATE,\n+\t/*\n+\t * Proxied tx credit\n+\t *\n+\t * - 0: LWSSS_SER_RXPRE_TXCR_UPDATE\n+\t * - 1: 00, 04\n+\t * - 3: 4-byte MSB-first addition tx credit bytes\n+\t */\n+\tLWSSS_SER_RXPRE_METADATA,\n+\t/*\n+\t * Proxied rx metadata\n+\t *\n+\t * - 0: LWSSS_SER_RXPRE_METADATA\n+\t * - 1: 2-byte MSB-first rest-of-frame length\n+\t * - 3: 1-byte metadata name length\n+\t * - 4: metadata name\n+\t * - ...: metadata value (for rest of packet)\n+\t */\n+\tLWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN,\n+\t/* reserved */\n+\tLWSSS_SER_RXPRE_PERF,\n+\t/*\n+\t * Proxied performance information\n+\t *\n+\t * - 0: LWSSS_SER_RXPRE_PERF\n+\t * - 1: 2-byte MSB-first rest-of-frame length\n+\t * - 3: ... performance JSON (for rest of packet)\n+\t */\n+\n+\t/*\n+\t * Framing for Client -\u003e Proxy direction\n+\t */\n+\n+\tLWSSS_SER_TXPRE_STREAMTYPE\t\t\t\t\u003d 0xaa,\n+\t/*\n+\t * Proxied connection setup\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_STREAMTYPE\n+\t * - 1: 2-byte MSB-first rest-of-frame length\n+\t * - 3: 1-byte Client SSS protocol version (introduced in SSSv1)\n+\t * - 4: 4-byte Client PID (introduced in SSSv1)\n+\t * - 8: 4-byte MSB-first initial tx credit\n+\t * - 12: the streamtype name with no NUL\n+\t */\n+\tLWSSS_SER_TXPRE_ONWARD_CONNECT,\n+\t/*\n+\t * Proxied request for onward connection\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_ONWARD_CONNECT\n+\t * - 1: 00, 00\n+\t */\n+\tLWSSS_SER_TXPRE_DESTROYING,\n+\t/*\n+\t * Proxied secure stream destroy\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_DESTROYING\n+\t * - 1: 00, 00\n+\t */\n+\tLWSSS_SER_TXPRE_TX_PAYLOAD,\n+\t/*\n+\t * Proxied tx\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_TX_PAYLOAD\n+\t * - 1: 2 byte MSB-first rest-of-frame length\n+\t * - 3: 4-byte MSB-first flags\n+\t * - 7: 4-byte MSB-first us between client requested write and wrote\n+\t * \t\t\t to proxy\n+\t * - 11: 8-byte MSB-first us resolution unix time client wrote to proxy\n+\t * - 19: ...payload (for rest of packet)\n+\t */\n+\tLWSSS_SER_TXPRE_METADATA,\n+\t/*\n+\t * Proxied metadata - sent when one metadata item set clientside\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_METADATA\n+\t * - 1: 2-byte MSB-first rest-of-frame length\n+\t * - 3: 1-byte metadata name length\n+\t * - 4: metadata name\n+\t * - ...: metadata value (for rest of packet)\n+\t */\n+\tLWSSS_SER_TXPRE_TXCR_UPDATE,\n+\t/*\n+\t * TX credit management - sent when using tx credit apis, cf METADATA\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_TXCR_UPDATE\n+\t * - 1: 2-byte MSB-first rest-of-frame length 00, 04\n+\t * - 3: 4-byte additional tx credit adjust value\n+\t */\n+\tLWSSS_SER_TXPRE_TIMEOUT_UPDATE,\n+\t/*\n+\t * Stream timeout management - forwarded when user applying or\n+\t * \tcancelling t.o.\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_TIMEOUT_UPDATE\n+\t * - 1: 2-byte MSB-first rest-of-frame length 00, 04\n+\t * - 3: 4-byte MSB-first unsigned 32-bit timeout,\n+\t * \t\t\t0 \u003d use policy, -1 \u003d cancel\n+\t */\n+\tLWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT,\n+\t/*\n+\t * Passing up payload length hint\n+\t *\n+\t * - 0: LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT\n+\t * - 1: 2-byte MSB-first rest-of-frame length 00, 04\n+\t * - 3: 4-byte MSB-first unsigned 32-bit payload length hint\n+\t */\n+\tLWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED,\n+\t/* reserved */\n+\tLWSSS_SER_TXPRE_LINK_VALIDITY_PROBE,\n+} lws_sss_cmds_t;\n+\n+/* SSPC serialization states */\n+\n+ typedef enum {\n+ \tLPCSPROX_WAIT_INITIAL_TX \u003d 1, /* after connect, must send streamtype */\n+ \tLPCSPROX_REPORTING_FAIL, /* stream creation failed, wait to to tell */\n+ \tLPCSPROX_REPORTING_OK, /* stream creation succeeded, wait to to tell */\n+ \tLPCSPROX_OPERATIONAL, /* ready for payloads */\n+ \tLPCSPROX_DESTROYED,\n+\n+ \tLPCSCLI_SENDING_INITIAL_TX, /* after connect, must send streamtype */\n+ \tLPCSCLI_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */\n+ \tLPCSCLI_LOCAL_CONNECTED,\t /* we are in touch with the proxy */\n+ \tLPCSCLI_ONWARD_CONNECT,\t /* request onward ss connection */\n+ \tLPCSCLI_OPERATIONAL, /* ready for payloads */\n+\n+ } lws_ss_conn_states_t;\n+\n+ /*\n+ * Optional multiplexing layer\n+ *\n+ * Either side can:\n+ *\n+ * - open and close channels asynchronously\n+ * - send and receive transport-level (not mux channel) timed PINGs / PONGs\n+ * - send and receive data bound to an open mux channel\n+ *\n+ * PONGs are produced and sent automatically on recipt of a PING from the peer\n+ * The peer sends a PONGACK so the single transaction can validate connection\n+ * viability in both directions.\n+ */\n+\n+ enum {\n+\t LWSSSS_LLM_CHANNEL_REQ\t\t\t\t\t\u003d 0xf0,\n+\t /**\u003c\n+\t * Either side proposes to open a new mux channel\n+\t *\n+\t * - 0: LWSSSS_LLM_CHANNEL_REQ\n+\t * - 1: 1-byte mux channel index, client initiated: first free from\n+\t * zero up, server initiated: first free from 0xff down\n+\t */\n+\t LWSSSS_LLM_CHANNEL_ACK,\n+\t /**\u003c\n+\t * Positive response to earlier LWSSSS_LLM_CHANNEL_REQ\n+\t *\n+\t * - 0: LWSSSS_LLM_CHANNEL_ACK\n+\t * - 1: 1-byte mux channel index, from the reqyuest\n+\t */\n+\t LWSSSS_LLM_CHANNEL_NACK,\n+\t /**\u003c\n+\t * Negative response to earlier LWSSSS_LLM_CHANNEL_REQ. This also acts\n+\t * as a FIN if one arrives on a channel unsolicited.\n+\t *\n+\t * - 0: LWSSSS_LLM_CHANNEL_NACK\n+\t * - 1: 1-byte mux channel index, from the reqyuest\n+\t */\n+\t LWSSSS_LLM_CHANNEL_CLOSE,\n+\t /**\u003c\n+\t * Either side informs peer it is closing a mux channel\n+\t *\n+\t * - 0: LWSSSS_LLM_CHANNEL_CLOSE\n+\t * - 1: 1-byte mux channel index\n+\t */\n+\t LWSSSS_LLM_CHANNEL_CLOSE_ACK,\n+\t /**\u003c\n+\t * Peer acknowledges closing a mux channel, so it can be reused\n+\t *\n+\t * - 0: LWSSSS_LLM_CHANNEL_CLOSE_ACK\n+\t * - 1: 1-byte mux channel index\n+\t */\n+\t LWSSSS_LLM_MUX,\n+\t /**\u003c\n+\t * Encapsulate data on an open mux channel\n+\t *\n+\t * - 0: LWSSSS_LLM_MUX\n+\t * - 1: 1-byte mux channel index\n+\t * - 2: 2-byte MSB-first rest-of-frame length\n+\t * - 4... mux payload\n+\t */\n+\t LWSSSS_LLM_PING,\n+\t /**\u003c\n+\t * Either side wants to validate communication on mux transport\n+\t *\n+\t * - 0: LWSSSS_LLM_PING\n+\t * - 1: 8-byte MSB-first us resolution unix time this was issued\n+\t */\n+\t LWSSSS_LLM_PONG,\n+\t /**\u003c\n+\t * Either side responds to peer's PING.\n+\t *\n+\t * - 0: LWSSSS_LLM_PONG\n+\t * - 1: 8-byte MSB-first us resolution unix time from PING\n+\t * - 9: 8-byte MSB-first us resolution unix time this PONG sent\n+\t */\n+\t LWSSSS_LLM_PONGACK,\n+\t /**\u003c\n+\t * When the original PING sender receives a PONG, it immediately sends\n+\t * a PINGACK, which is not replied to. This allows the other side to\n+\t * also know the connection is valid in both directions, with only one\n+\t * side needing to issue PINGs.\n+\t *\n+\t * It also synchronizes both sides' understanding of the transport\n+\t * validity in one transaction.\n+\t *\n+\t * - 0: LWSSSS_LLM_PONGACK\n+\t * - 1: 8-byte MSB-first us resolution unix time from PING\n+\t */\n+\t LWSSSS_LLM_RESET_TRANSPORT,\n+\t /**\u003c\n+\t * Either side can issue this to indicate they no longer trust the\n+\t * transport link. They should close all their channels and enter a\n+\t * state trying to resync using 3-way PINGs\n+\t */\n+};\n+\n+typedef void * lws_transport_priv_t; /* care - this is a pointer type already */\n+struct lws_transport_mux;\n+struct lws_sss_proxy_conn;\n+struct lws_transport_client_ops;\n+struct lws_transport_proxy_ops;\n+struct lws_sspc_handle;\n+\n+/*\n+ * These describe the path through different transport layers. Each has an\n+ * 'in' and 'onw' (onward) side that can be bound to different parts in lws.\n+ * SSPC and the SS Proxy code in lws each exposes one of these as terminals\n+ * for the \u0022path\u0022 to handle the SS Serialization on each side.\n+ *\n+ * sspc-transport-wsi and proxy-transport-wsi expose possible endpoints for the\n+ * paths, so you can simply \u0022wire SSPC and proxy up to a wsi transport\u0022.\n+ *\n+ * You can also create a lws_transport_mux_t and interpose it in the transport\n+ * path on each side, and produce your own custom lws_transport ops implementing\n+ * arbitrary transport support.\n+ */\n+\n+typedef struct lws_txp_path_client {\n+\tconst struct lws_transport_client_ops\t*ops_in;\n+\tlws_transport_priv_t\t\t\tpriv_in;\n+\tconst struct lws_transport_client_ops\t*ops_onw;\n+\tlws_transport_priv_t\t\t\tpriv_onw;\n+\tstruct lws_transport_mux\t\t*mux;\n+} lws_txp_path_client_t;\n+\n+typedef struct lws_txp_path_proxy {\n+\tconst struct lws_transport_proxy_ops\t*ops_in;\n+\tlws_transport_priv_t\t\t\tpriv_in;\n+\tconst struct lws_transport_proxy_ops\t*ops_onw;\n+\tlws_transport_priv_t\t\t\tpriv_onw;\n+\tstruct lws_transport_mux\t\t*mux;\n+} lws_txp_path_proxy_t;\n+\n+/*\n+ * Operations for client-side transport\n+ */\n+\n+typedef struct lws_transport_client_ops {\n+\tconst char *name;\n+\n+ \tint (*event_retry_connect)(lws_txp_path_client_t *path,\n+ \t\t\t\t struct lws_sspc_handle *h);\n+ \t/**\u003c Attempt to create a new connection / channel to the proxy */\n+ \tlws_ss_state_return_t (*event_connect_disposition)(\n+ \t\t\t struct lws_sspc_handle *h, int disposition);\n+ \t/**\u003c Connection attempt result, disposition 9 \u003d success, else failed */\n+ \tvoid (*req_write)(lws_transport_priv_t priv);\n+ \t/**\u003c Request a write to the proxy on this channel */\n+ \tint (*_write)(lws_transport_priv_t priv, uint8_t *buf, size_t len);\n+ \t/**\u003c Write the requested data on the channel to the proxy *** MUST have\n+ \t * LWS_PRE usable behind buf */\n+ \tlws_ss_state_return_t (*event_read)(lws_transport_priv_t priv,\n+ \t\t\t\t\t const uint8_t *buf, size_t len);\n+ \t/**\u003c len bytes at buf have been received */\n+ \tvoid (*lost_coherence)(lws_transport_priv_t priv);\n+ \t/**\u003c report that the framing inside the mux channel is broken */\n+ \tvoid (*_close)(lws_transport_priv_t priv);\n+ \t/**\u003c Close the channel to the proxy */\n+ \tvoid (*event_stream_up)(lws_transport_priv_t priv);\n+ \t/**\u003c Called when a new channel to the proxy is acknowledged as up */\n+\tvoid (*event_client_up)(lws_transport_priv_t priv);\n+\t/**\u003c Called when a client channel is acknowledged as up */\n+\tlws_ss_state_return_t (*event_can_write)(struct lws_sspc_handle *h,\n+\t\t\t\t\t\t size_t metadata_limit);\n+\t/**\u003c Called when possible to write on the transport, after req_write */\n+\tlws_ss_state_return_t (*event_closed)(lws_transport_priv_t priv /*struct lws_sspc_handle *h */);\n+\t/**\u003c we notice an onward proxy connection had closed */\n+\tuint32_t\t\t\tflags;\n+\t/**\u003c Used for DSH creation flags */\n+\tuint32_t\t\t\tdsh_splitat;\n+} lws_transport_client_ops_t;\n+\n+/*\n+ * Operations for proxy-side transport\n+ */\n+\n+typedef struct lws_transport_proxy_ops {\n+\tconst char *name;\n+\tint (*init_proxy_server)(struct lws_context *context,\n+\t\t\t const struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t lws_transport_priv_t txp_priv_inward,\n+\t\t\t lws_txp_path_proxy_t *txp_ppath, const void *aux,\n+\t\t\t const char *bind, int port);\n+\t/**\u003c Instantiate a proxy transport... bind/port are as shown for wsi\n+\t * transport, but may be overloaded to provide transport-specific init */\n+\tint (*destroy_proxy_server)(struct lws_context *context);\n+\tlws_ss_state_return_t (*event_new_conn)(struct lws_context *cx,\n+\t\t\t\tconst struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t\tlws_transport_priv_t txp_priv_inward,\n+\t#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t const lws_fi_ctx_t *fic,\n+\t#endif\n+\t\t\t\tstruct lws_sss_proxy_conn **conn,\n+\t\t\t\tlws_transport_priv_t txp_priv);\n+\t/**\u003c proxy has received a new connection from client */\n+\tvoid (*event_onward_bind)(lws_transport_priv_t priv,\n+\t\t\t\t struct lws_ss_handle *h);\n+\t/**\u003c Called when the proxy creates an onward SS for a client channel */\n+ \tvoid (*proxy_req_write)(lws_transport_priv_t priv);\n+ \t/**\u003c Request a write to the proxy on this channel */\n+\tlws_ss_state_return_t (*event_proxy_can_write)(\n+\t\t\tlws_transport_priv_t priv\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t\t, const lws_fi_ctx_t *fic\n+#endif\n+\t\t\t);\n+\t/**\u003c Transport can now be written on, after earlier proxy_req_write */\n+ \tint (*proxy_write)(lws_transport_priv_t priv, uint8_t *buf, size_t *len);\n+ \t/**\u003c Write the requested data on the channel to the proxy *** MUST have\n+ \t * LWS_PRE usable behind buf. May do partial writes, len is set on return\n+ \t * to actual length written*/\n+\tlws_ss_state_return_t (*event_close_conn)(\n+\t\t\t\tstruct lws_sss_proxy_conn *conn);\n+\t/**\u003c proxy sees an existing conn closes */\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\tconst lws_fi_ctx_t * (*fault_context)(lws_transport_priv_t priv);\n+\t/**\u003c Get the fault context relating to the proxy connection, if any */\n+#endif\n+ \tlws_ss_state_return_t (*close_conn)(struct lws_sss_proxy_conn *conn);\n+ \t/**\u003c called to handle closure of underlying transport */\n+ \tlws_ss_state_return_t (*proxy_read)(lws_transport_priv_t priv,\n+ \t\t\t\t\t const uint8_t *buf, size_t len);\n+\tvoid (*event_client_up)(lws_transport_priv_t priv);\n+\t/**\u003c Called when the proxy has accepted a new client conn */\n+\tint (*proxy_check_write_more)(lws_transport_priv_t priv);\n+\t/**\u003c optional, allows checking if we can write again */\n+\tuint32_t\t\t\tflags; /* dsh flags */\n+} lws_transport_proxy_ops_t;\n+\n+/* lws_transport_mux parser states */\n+\n+enum lwstmc_parser {\n+\tLWSTMCPAR_CMD,\n+\tLWSTMCPAR_CHIDX_DONE,\n+\tLWSTMCPAR_CHIDX,\n+\tLWSTMCPAR_PLENH,\n+\tLWSTMCPAR_PLENL,\n+\tLWSTMCPAR_PAY,\n+\tLWSTMCPAR_T64_1,\n+\tLWSTMCPAR_T64_2\n+};\n+\n+/* lws_transport_mux channel definitions */\n+\n+typedef uint8_t lws_mux_ch_idx_t;\n+#define LWS_MUCH_RANGE 256\n+\n+/* lws_transport mux states */\n+\n+enum {\n+\t/* lws_transport_mux_ch_t created */\n+\tLWSTMC_PENDING_CREATE_CHANNEL,\t /* waiting to send create channel */\n+\tLWSTMC_AWAITING_CREATE_CHANNEL_ACK, /* sent create ch, awaiting ack */\n+\tLWSTMC_PENDING_CREATE_CHANNEL_NACK, /* waiting to send create ch ack */\n+\tLWSTMC_PENDING_CREATE_CHANNEL_ACK, /* waiting to send create ch ack */\n+\tLWSTMC_OPERATIONAL,\t\t /* had ack, we are operational */\n+\tLWSTMC_PENDING_CLOSE_CHANNEL,\t /* waiting to send close channel */\n+\tLWSTMC_AWAITING_CLOSE_CHANNEL_ACK, /* sent close ch, awaiting ack */\n+\tLWSTMC_PENDING_CLOSE_CHANNEL_ACK, /* waiting to send close ch ack */\n+\t/* lws_transport_mux_ch_t destroyed */\n+};\n+\n+#define LWS_TRANSPORT_MUXCH_MAGIC LWS_FOURCC('T', 'm', 'C', 'h')\n+#define assert_is_tmch(_tm) lws_assert_fourcc(_tm-\u003emagic, LWS_TRANSPORT_MUXCH_MAGIC)\n+\n+typedef struct lws_transport_mux_ch {\n+#if defined(_DEBUG)\n+\tuint32_t\t\t\t\tmagic;\n+#endif\n+\tlws_dll2_t\t\t\t\tlist;\n+\tlws_dll2_t\t\t\t\tlist_pending_tx;\n+\tlws_transport_priv_t\t\t\tpriv;\n+\tlws_sorted_usec_list_t\t\t\tsul;\n+\tvoid\t\t\t\t\t*opaque;\n+\tlws_mux_ch_idx_t\t\t\tch_idx;\n+\tuint8_t\t\t\t\t\tstate;\n+\tuint8_t\t\t\t\t\tserver:1;\n+} lws_transport_mux_ch_t;\n+\n+enum { /* states of the transport */\n+\tLWSTM_TRANSPORT_DOWN,\n+\tLWSTM_OPERATIONAL,\n+};\n+\n+#define LWSTMINFO_SERVER\t\t\t(1 \u003c\u003c 0)\n+\n+typedef struct lws_transport_info {\n+\tuint32_t\t\t\t\tping_interval_us;\n+\t/**\u003c us inbetween transport mux sending pings on transport */\n+\tuint32_t\t\t\t\tpong_grace_us;\n+\t/**\u003c us we should wait for pong before assuming transport down */\n+\tlws_txp_path_client_t\t\t\ttxp_cpath;\n+\tlws_txp_path_proxy_t\t\t\ttxp_ppath;\n+\tstruct lws_transport_info\t\t*onward_txp_info;\n+\tuint32_t\t\t\t\tflags; /* LWSTMINFO_.... */\n+} lws_transport_info_t;\n+\n+#define LWS_TRANSPORT_MUX_MAGIC LWS_FOURCC('I', 's', 'T', 'M')\n+#define assert_is_tm(_tm) lws_assert_fourcc(_tm-\u003emagic, LWS_TRANSPORT_MUX_MAGIC)\n+\n+typedef struct lws_transport_mux {\n+#if defined(_DEBUG)\n+\tuint32_t\t\t\t\tmagic;\n+#endif\n+\tstruct lws_context\t\t\t*cx;\n+\tlws_transport_info_t\t\t\tinfo;\n+\tlws_sorted_usec_list_t\t\t\tsul_ping;\n+\tvoid\t\t\t\t\t*txp_handle;\n+\tvoid\t\t\t\t\t*txp_aux;\n+\tuint64_t\t\t\t\tus_ping_in;\n+\tuint64_t\t\t\t\tus_ping_out;\n+\tuint64_t\t\t\t\tus_unixtime_peer;\n+\tuint64_t\t\t\t\tmp_time;\n+\tuint64_t\t\t\t\tmp_time1;\n+\tenum lwstmc_parser\t\t\tmp_state;\n+\tuint32_t\t\t\t\tmp_pay; /* remaining payload */\n+\tuint8_t\t\t\t\t\tmp_cmd;\n+\tlws_mux_ch_idx_t\t\t\tmp_idx;\n+\tuint8_t\t\t\t\t\tmp_ctr;\n+\tuint32_t\t\t\t\t_open[LWS_MUCH_RANGE / 32];\n+\tuint32_t\t\t\t\tfin[LWS_MUCH_RANGE / 32];\n+\tlws_dll2_owner_t\t\t\tpending_tx;\n+\tlws_dll2_owner_t\t\t\towner; /* lws_mux_ch_t */\n+\tuint8_t\t\t\t\t\tlink_state;\n+\tuint8_t\t\t\t\t\tissue_ping:1;\n+\tuint8_t\t\t\t\t\tissue_pong:1;\n+\tuint8_t\t\t\t\t\tissue_pongack:1;\n+\tuint8_t\t\t\t\t\tawaiting_pong:1;\n+} lws_transport_mux_t;\n+\n+lws_transport_mux_t *\n+lws_transport_mux_create(struct lws_context *cx, lws_transport_info_t *info,\n+\t\t\t void *txp_handle);\n+\n+void\n+lws_transport_mux_destroy(lws_transport_mux_t **tm);\n+\n+void\n+lws_transport_mux_request_tx(lws_transport_mux_t *tm);\n+\n+void\n+lws_transport_mux_destroy(lws_transport_mux_t **tm);\n+\n+#if defined(_DEBUG)\n+void\n+lws_transport_path_client_dump(lws_txp_path_client_t *path, const char *ctx);\n+void\n+lws_transport_path_proxy_dump(lws_txp_path_proxy_t *path, const char *ctx);\n+#else\n+#define lws_transport_path_client_dump(_a, _b)\n+#define lws_transport_path_proxy_dump(_a, _b)\n+#endif\n+\n+/*\n+ * Callback set used to customize parser and _pending apis\n+ */\n+\n+typedef struct lws_txp_mux_parse_cbs {\n+\tint (*payload)(lws_transport_mux_ch_t *tmc, const uint8_t *buf,\n+\t\t\tsize_t len);\n+\tint (*ch_opens)(lws_transport_mux_ch_t *tmc, int determination);\n+\tint (*ch_closes)(lws_transport_mux_ch_t *tmc);\n+\tvoid (*txp_req_write)(lws_transport_mux_t *tm);\n+\tint (*txp_can_write)(lws_transport_mux_ch_t *tmc);\n+} lws_txp_mux_parse_cbs_t;\n+\n+int\n+lws_transport_mux_rx_parse(lws_transport_mux_t *tm, const uint8_t *buf,\n+\t\t\t size_t len, const lws_txp_mux_parse_cbs_t *cbs);\n+\n+int /* nonzero if the transport mux has filled buf and wants to write it */\n+lws_transport_mux_pending(lws_transport_mux_t *tm, uint8_t *buf, size_t *len,\n+\t\t\t const lws_txp_mux_parse_cbs_t *cbs);\n+\n+extern const lws_transport_client_ops_t lws_transport_mux_client_ops;\n+extern const lws_transport_proxy_ops_t lws_transport_mux_proxy_ops;\n+\n+extern const lws_transport_client_ops_t lws_txp_inside_sspc;\n+extern const lws_transport_proxy_ops_t lws_txp_inside_proxy;\n+\n+#if defined(STANDALONE)\n+#undef lws_context\n+#endif\n+\ndiff --git a/include/libwebsockets/lws-secure-streams-transport-proxy.h b/include/libwebsockets/lws-secure-streams-transport-proxy.h\nnew file mode 100644\nindex 0000000..c9a3423\n--- /dev/null\n+++ b/include/libwebsockets/lws-secure-streams-transport-proxy.h\n@@ -0,0 +1,47 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * These headers are related to providing user Secure Streams Serialization\n+ * transport implementations in user code.\n+ *\n+ * The default implementation uses wsi for proxy serving and connecting clients,\n+ * but it's also possible to provide user implementations of the operations\n+ * needed to serve on a different transport for proxy, and to connect out on\n+ * the different transport for client.\n+ *\n+ * You can provide your own lws_sss_ops_client_t and lws_sss_ops_proxy_t to\n+ * control how serialized data is transmitted and received, to use SS\n+ * serialization over, eg, UART instead.\n+ *\n+ * This allows situations where full SS proxy services can be offered to much\n+ * weker devices, without any networking stack or tls library being needed.\n+ */\n+\n+/*\n+ * SSS Proxy Transport-related implementation apis\n+ */\n+\n+struct lws_sss_proxy_conn;\n+\n+\ndiff --git a/include/libwebsockets/lws-secure-streams.h b/include/libwebsockets/lws-secure-streams.h\nindex 8157c70..4a0aa44 100644\n--- a/include/libwebsockets/lws-secure-streams.h\n+++ b/include/libwebsockets/lws-secure-streams.h\n@@ -1,7 +1,7 @@\n /*\n * libwebsockets - small server side websockets and web server implementation\n *\n- * Copyright (C) 2019 - 2020 Andy Green \u003candy@warmcat.com\u003e\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \u0022Software\u0022), to\n@@ -48,134 +48,6 @@\n * In the second, IPC, case, all packets are prepended by one or more bytes\n * indicating the packet type and serializing any associated data, known as\n * Serialized Secure Streams or SSS.\n- *\n- * Serialized Secure Streams\n- * -------------------------\n- *\n- * On the transport, adjacent packets may be coalesced, that is, the original\n- * packet sizes are lost and two or more packets are combined. For that reason\n- * the serialization format always contains a 1-byte type and then a 2-byte\n- * frame length.\n- *\n- * Client to proxy\n- *\n- * - Proxied connection setup\n- *\n- * - 0: LWSSS_SER_TXPRE_STREAMTYPE\n- * - 1: 2-byte MSB-first rest-of-frame length\n- * - 3: 1-byte Client SSS protocol version (introduced in SSSv1)\n- * - 4: 4-byte Client PID (introduced in SSSv1)\n- * - 8: 4-byte MSB-first initial tx credit\n- * - 12: the streamtype name with no NUL\n- *\n- * - Proxied tx\n- *\n- * - 0: LWSSS_SER_TXPRE_TX_PAYLOAD\n- * - 1: 2 byte MSB-first rest-of-frame length\n- * - 3: 4-byte MSB-first flags\n- * - 7: 4-byte MSB-first us between client requested write and wrote to proxy\n- * - 11: 8-byte MSB-first us resolution unix time client wrote to proxy\n- * - 19: payload\n- *\n- * - Proxied secure stream destroy\n- *\n- * - 0: LWSSS_SER_TXPRE_DESTROYING\n- * - 1: 00, 00\n- *\n- * - Proxied metadata - sent when one metadata item set clientside\n- *\n- * - 0: LWSSS_SER_TXPRE_METADATA\n- * - 1: 2-byte MSB-first rest-of-frame length\n- * - 3: 1-byte metadata name length\n- * - 4: metadata name\n- * - ...: metadata value (for rest of packet)\n- *\n- * - TX credit management - sent when using tx credit apis, cf METADATA\n- *\n- * - 0: LWSSS_SER_TXPRE_TXCR_UPDATE\n- * - 1: 2-byte MSB-first rest-of-frame length 00, 04\n- * - 3: 4-byte additional tx credit adjust value\n- *\n- * - Stream timeout management - forwarded when user applying or cancelling t.o.\n- *\n- * - 0: LWSSS_SER_TXPRE_TIMEOUT_UPDATE\n- * - 1: 2-byte MSB-first rest-of-frame length 00, 04\n- * - 3: 4-byte MSB-first unsigned 32-bit timeout, 0 \u003d use policy, -1 \u003d cancel\n- *\n- * - Passing up payload length hint\n- *\n- * - 0: LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT\n- * - 1: 2-byte MSB-first rest-of-frame length 00, 04\n- * - 3: 4-byte MSB-first unsigned 32-bit payload length hint\n- *\n- * Proxy to client\n- *\n- * - Proxied connection setup result\n- *\n- * - 0: LWSSS_SER_RXPRE_CREATE_RESULT\n- * - 1: 2 byte MSB-first rest-of-frame length (usually 00, 03)\n- * - 3: 1 byte result, 0 \u003d success. On failure, proxy will close connection.\n- * - 4: 4 byte client dsh allocation recommended for stream type, from policy\n- * (introduced in SSSv1)\n- * - 8: 2 byte MSB-first initial tx credit\n- * - 10: if present, comma-sep list of rideshare types from policy\n- *\n- * - Proxied rx\n- *\n- * - 0: LWSSS_SER_RXPRE_RX_PAYLOAD\n- * - 1: 2 byte MSB-first rest-of-frame length\n- * - 3: 4-byte MSB-first flags\n- * - 7: 4-byte MSB-first us between inbound read and wrote to client\n- * - 11: 8-byte MSB-first us resolution unix time proxy wrote to client\n- * - 17: (rideshare name len + rideshare name if flags \u0026 LWSSS_FLAG_RIDESHARE)\n- * payload\n- *\n- * - Proxied tx credit\n- *\n- * - 0: LWSSS_SER_RXPRE_TXCR_UPDATE\n- * - 1: 00, 04\n- * - 3: 4-byte MSB-first addition tx credit bytes\n- *\n- * - Proxied rx metadata\n- *\n- * - 0: LWSSS_SER_RXPRE_METADATA\n- * - 1: 2-byte MSB-first rest-of-frame length\n- * - 3: 1-byte metadata name length\n- * - 4: metadata name\n- * - ...: metadata value (for rest of packet)\n- *\n- * - Proxied state (8 or 11 byte packet)\n- *\n- * - 0: LWSSS_SER_RXPRE_CONNSTATE\n- * - 1: 00, 05 if state \u003c 256, else 00, 08\n- * - 3: 1 byte state index if state \u003c 256, else 4-byte MSB-first state index\n- * - 4 or 7: 4-byte MSB-first ordinal\n- *\n- * - Proxied performance information\n- *\n- * - 0: LWSSS_SER_RXPRE_PERF\n- * - 1: 2-byte MSB-first rest-of-frame length\n- * - 3: ... performance JSON (for rest of packet)\n- *\n- * Proxied tx may be read by the proxy but rejected due to lack of buffer space\n- * at the proxy. For that reason, tx must be held at the sender until it has\n- * been acknowledged or denied.\n- *\n- * Sinks\n- * -----\n- *\n- * Sinks are logical \u0022servers\u0022, you can register as a sink for a particular\n- * streamtype by using the lws_ss_create() api with ssi-\u003eregister_sink set to 1.\n- *\n- * For directly fulfilled Secure Streams, new streams of that streamtype bind\n- * to the rx, tx and state handlers given when it was registered.\n- *\n- * - When new streams are created the registered sink handler for (*state) is\n- * called with event LWSSSCS_SINK_JOIN and the new client stream handle in\n- * the h_src parameter.\n- *\n- * - When the client stream sends something to the sink, it calls the sink's\n- * (*rx) with the client stream's\n */\n \n /** \u005cdefgroup secstr Secure Streams\n@@ -190,6 +62,11 @@\n struct lws_ss_handle;\n typedef uint32_t lws_ss_tx_ordinal_t;\n \n+#if defined(STANDALONE)\n+#define lws_context lws_context_standalone\n+struct lws_context_standalone;\n+#endif\n+\n /*\n * connection state events\n *\n@@ -253,55 +130,8 @@ enum {\n \tLWSSS_FLAG_PERF_JSON\t\t\t\t\t\u003d (1 \u003c\u003c 6),\n \t/* This RX is JSON performance data, only on streams with \u0022perf\u0022 flag\n \t * set */\n-\n-\t/*\n-\t * In the case the secure stream is proxied across a process or thread\n-\t * boundary, eg by proxying through a socket for IPC, metadata must be\n-\t * carried in-band. A byte is prepended to each rx payload to\n-\t * differentiate what it is.\n-\t *\n-\t * Secure streams where the user is called back directly does not need\n-\t * any of this and only pure payloads are passed.\n-\t *\n-\t * rx (received by client) prepends for proxied connections\n-\t */\n-\n-\tLWSSS_SER_RXPRE_RX_PAYLOAD\t\t\t\t\u003d 0x55,\n-\tLWSSS_SER_RXPRE_CREATE_RESULT,\n-\tLWSSS_SER_RXPRE_CONNSTATE,\n-\tLWSSS_SER_RXPRE_TXCR_UPDATE,\n-\tLWSSS_SER_RXPRE_METADATA,\n-\tLWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN,\n-\tLWSSS_SER_RXPRE_PERF,\n-\n-\t/* tx (send by client) prepends for proxied connections */\n-\n-\tLWSSS_SER_TXPRE_STREAMTYPE\t\t\t\t\u003d 0xaa,\n-\tLWSSS_SER_TXPRE_ONWARD_CONNECT,\n-\tLWSSS_SER_TXPRE_DESTROYING,\n-\tLWSSS_SER_TXPRE_TX_PAYLOAD,\n-\tLWSSS_SER_TXPRE_METADATA,\n-\tLWSSS_SER_TXPRE_TXCR_UPDATE,\n-\tLWSSS_SER_TXPRE_TIMEOUT_UPDATE,\n-\tLWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT,\n-\tLWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED,\n };\n \n-typedef enum {\n-\tLPCSPROX_WAIT_INITIAL_TX \u003d 1, /* after connect, must send streamtype */\n-\tLPCSPROX_REPORTING_FAIL, /* stream creation failed, wait to to tell */\n-\tLPCSPROX_REPORTING_OK, /* stream creation succeeded, wait to to tell */\n-\tLPCSPROX_OPERATIONAL, /* ready for payloads */\n-\tLPCSPROX_DESTROYED,\n-\n-\tLPCSCLI_SENDING_INITIAL_TX, /* after connect, must send streamtype */\n-\tLPCSCLI_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */\n-\tLPCSCLI_LOCAL_CONNECTED,\t /* we are in touch with the proxy */\n-\tLPCSCLI_ONWARD_CONNECT,\t /* request onward ss connection */\n-\tLPCSCLI_OPERATIONAL, /* ready for payloads */\n-\n-} lws_ss_conn_states_t;\n-\n /*\n * Returns from state() callback can tell the caller what the user code\n * wants to do\n@@ -842,5 +672,9 @@ lws_aws_filesystem_credentials_helper(const char *path, const char *kid,\n \n #endif\n \n+#if defined(STANDALONE)\n+#undef lws_context\n+#endif\n+\n ///@}\n \ndiff --git a/include/libwebsockets/lws-timeout-timer.h b/include/libwebsockets/lws-timeout-timer.h\nindex 102a25f..ef1e382 100644\n--- a/include/libwebsockets/lws-timeout-timer.h\n+++ b/include/libwebsockets/lws-timeout-timer.h\n@@ -28,6 +28,11 @@\n */\n //@{\n \n+#if defined(STANDALONE)\n+struct lws_context_standalone;\n+#define lws_context lws_context_standalone\n+#endif\n+\n /*\n * NOTE: These public enums are part of the abi. If you want to add one,\n * add it at where specified so existing users are unaffected.\n@@ -301,4 +306,8 @@ __lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul);\n LWS_VISIBLE LWS_EXTERN lws_usec_t\n __lws_sul_service_ripe(lws_dll2_owner_t *own, int own_len, lws_usec_t usnow);\n \n+#if defined(STANDALONE)\n+#undef lws_context\n+#endif\n+\n ///@}\ndiff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt\nindex da64e4a..b005399 100644\n--- a/lib/CMakeLists.txt\n+++ b/lib/CMakeLists.txt\n@@ -75,7 +75,7 @@ if (LWS_PLAT_FREERTOS)\n \t\n else()\n \tif (LWS_PLAT_BAREMETAL)\n-\t\tadd_subdir_include_dirs(plat/baremetal)\n+\t\t#\t\tadd_subdir_include_dirs(plat/baremetal)\n \telse()\n \t\tif (LWS_PLAT_OPTEE)\n \t\t\tadd_subdir_include_dirs(plat/optee)\n@@ -152,12 +152,13 @@ if (LWS_WITH_COSE)\n \tadd_subdir_include_dirs(cose)\n endif()\n \n+include_directories(secure-streams)\n+add_subdir_include_dirs(event-libs)\n \n if (LWS_WITH_SECURE_STREAMS)\n \tadd_subdir_include_dirs(secure-streams)\n endif()\n-\n-add_subdir_include_dirs(event-libs)\n+add_subdir_include_dirs(secure-streams/serialized/client)\n \n if (LWS_WITH_STATIC)\n if (LWS_STATIC_PIC)\ndiff --git a/lib/core-net/CMakeLists.txt b/lib/core-net/CMakeLists.txt\nindex fd56927..2627289 100644\n--- a/lib/core-net/CMakeLists.txt\n+++ b/lib/core-net/CMakeLists.txt\n@@ -69,7 +69,7 @@ if (LWS_WITH_CLIENT)\n \t\tcore-net/client/connect3.c\n \t\tcore-net/client/connect4.c\n \t\tcore-net/client/sort-dns.c\n-\t)\n+\t\t)\n \tif (LWS_WITH_CONMON)\n \t\tlist(APPEND SOURCES\n \t\t\tcore-net/client/conmon.c\ndiff --git a/lib/core-net/client/conmon.c b/lib/core-net/client/conmon.c\nindex 21f67c9..53c946d 100644\n--- a/lib/core-net/client/conmon.c\n+++ b/lib/core-net/client/conmon.c\n@@ -109,7 +109,8 @@ lws_conmon_append_copy_new_dns_results(struct lws *wsi,\n \t\t\t\tai-\u003eai_canonname \u003d ((char *)ai-\u003eai_addr) +\n \t\t\t\t\t\t\tcai-\u003eai_addrlen;\n \t\t\t\tmemcpy(ai-\u003eai_canonname, cai-\u003eai_canonname,\n-\t\t\t\t cl + 1);\n+\t\t\t\t cl);\n+\t\t\t\tai-\u003eai_canonname[cl] \u003d '\u005c0';\n \t\t\t}\n \t\t\tai-\u003eai_next \u003d wsi-\u003econmon.dns_results_copy;\n \t\t\twsi-\u003econmon.dns_results_copy \u003d ai;\ndiff --git a/lib/core-net/close.c b/lib/core-net/close.c\nindex 1a4e0f7..07c781b 100644\n--- a/lib/core-net/close.c\n+++ b/lib/core-net/close.c\n@@ -220,7 +220,7 @@ __lws_free_wsi(struct lws *wsi)\n \t\t\tlws_sspc_handle_t *h \u003d (lws_sspc_handle_t *)\n \t\t\t\t\t\t\twsi-\u003ea.opaque_user_data;\n \t\t\tif (h) {\n-\t\t\t\th-\u003ecwsi \u003d NULL;\n+\t\t\t\th-\u003etxp_path.priv_onw \u003d NULL;\n \t\t\t\twsi-\u003ea.opaque_user_data \u003d NULL;\n \t\t\t}\n \t\t} else\n@@ -786,7 +786,7 @@ just_kill_connection:\n \t\tif (!wsi-\u003ea.protocol \u0026\u0026 wsi-\u003ea.vhost \u0026\u0026 wsi-\u003ea.vhost-\u003eprotocols)\n \t\t\tpro \u003d \u0026wsi-\u003ea.vhost-\u003eprotocols[0];\n \n-\t\tif (pro)\n+\t\tif (pro \u0026\u0026 pro-\u003ecallback \u0026\u0026 wsi-\u003erole_ops)\n \t\t\tpro-\u003ecallback(wsi,\n \t\t\t\twsi-\u003erole_ops-\u003eclose_cb[lwsi_role_server(wsi)],\n \t\t\t\twsi-\u003euser_space, NULL, 0);\n@@ -829,7 +829,7 @@ async_close:\n \t\t\t\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n #endif\n \n-\t\t\t\t\th-\u003ecwsi \u003d NULL;\n+\t\t\t\t\th-\u003etxp_path.priv_onw \u003d NULL;\n \t\t\t\t\t//wsi-\u003ea.opaque_user_data \u003d NULL;\n \t\t\t\t}\n \t\t\t} else\ndiff --git a/lib/core-net/transport-mux-client.c b/lib/core-net/transport-mux-client.c\nnew file mode 100644\nindex 0000000..d7c1562\n--- /dev/null\n+++ b/lib/core-net/transport-mux-client.c\n@@ -0,0 +1,339 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * Transport mux / demux\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+#if defined(STANDALONE)\n+struct lws_context_standalone;\n+#define lws_context lws_context_standalone\n+#endif\n+\n+void\n+lws_transport_mux_client_request_tx(lws_transport_mux_t *tm)\n+{\n+\tassert_is_tm(tm);\n+\ttm-\u003einfo.txp_cpath.ops_onw-\u003ereq_write(tm-\u003einfo.txp_cpath.priv_onw);\n+}\n+\n+\n+void\n+lws_transport_mux_destroy(lws_transport_mux_t **tm);\n+\n+#if defined(_DEBUG)\n+void\n+lws_transport_path_client_dump(lws_txp_path_client_t *path, const char *ctx)\n+{\n+\tchar buf[200], *p \u003d buf, *end \u003d buf + sizeof(buf) - 1;\n+\tuint32_t magic;\n+\tint n;\n+\n+\tn \u003d snprintf(p, lws_ptr_diff_size_t(end, p),\n+\t\t\t\u0022MUX: %p, IN: ops\u003d%s, priv\u003d%p\u0022,\n+\t\t\tpath-\u003emux, path-\u003eops_in ? path-\u003eops_in-\u003ename : \u0022null\u0022,\n+\t\t\tpath-\u003epriv_in);\n+\tp \u003d (p + n \u003e end) ? end : p + n;\n+\tif (path-\u003epriv_in) {\n+\t\tmagic \u003d *(uint32_t *)path-\u003epriv_in;\n+\t\tif (magic \u0026 0xff000000) {\n+\t\t\tn \u003d snprintf(p, lws_ptr_diff_size_t(end, p), \u0022 (%c%c%c%c)\u0022,\n+\t\t\t\t\t(int)(magic \u003e\u003e 24), (int)((magic \u003e\u003e 16) \u0026 0xff),\n+\t\t\t\t\t(int)((magic \u003e\u003e 8) \u0026 0xff), (int)(magic \u0026 0xff));\n+\t\t\tp \u003d (p + n \u003e end) ? end : p + n;\n+\t\t}\n+\t}\n+\n+\tn \u003d snprintf(p, lws_ptr_diff_size_t(end, p), \u0022, ONW: ops\u003d%s, priv\u003d%p\u0022,\n+\t\t\tpath-\u003eops_onw ? path-\u003eops_onw-\u003ename : \u0022null\u0022, path-\u003epriv_onw);\n+\tp \u003d (p + n \u003e end) ? end : p + n;\n+\tif (path-\u003epriv_onw) {\n+\t\tmagic \u003d *(uint32_t *)path-\u003epriv_onw;\n+\t\tif (magic \u0026 0xff000000) {\n+\t\t\tn \u003d snprintf(p, lws_ptr_diff_size_t(end, p), \u0022 (%c%c%c%c)\u0022,\n+\t\t\t\t(int)(magic \u003e\u003e 24), (int)((magic \u003e\u003e 16) \u0026 0xff),\n+\t\t\t\t(int)((magic \u003e\u003e 8) \u0026 0xff), (int)(magic \u0026 0xff));\n+\t\t\tp \u003d (p + n \u003e end) ? end : p + n;\n+\t\t}\n+\t}\n+\n+\t*end \u003d '\u005c0';\n+\tlwsl_notice(\u0022%s: %s: %s\u005cn\u0022, __func__, ctx, buf);\n+}\n+#endif\n+\n+/*\n+ * These are transport ops that let the mux transport encapsulate another\n+ * transport transparently.\n+ */\n+\n+static int\n+lws_transport_mux_retry_connect(lws_txp_path_client_t *path,\n+\t\t\t\tstruct lws_sspc_handle *h)\n+{\n+\tlws_transport_mux_ch_t *tmc;\n+\n+\tlwsl_user(\u0022%s\u005cn\u0022, __func__);\n+\n+\tlws_transport_path_client_dump(path, __func__);\n+\n+\tif (path-\u003emux-\u003elink_state !\u003d LWSTM_OPERATIONAL) {\n+\t\tlwsl_user(\u0022%s: transport not operational\u005cn\u0022, __func__);\n+\t\tgoto fail;\n+\t}\n+\n+\ttmc \u003d lws_transport_mux_add_channel(path-\u003emux, (lws_transport_priv_t)h);\n+\tif (!tmc)\n+\t\tgoto fail;\n+\n+\tlwsl_notice(\u0022%s: added channel\u005cn\u0022, __func__);\n+\n+\tpath-\u003epriv_onw \u003d (lws_transport_priv_t)tmc;\n+\n+\ttmc-\u003estate \u003d LWSTMC_PENDING_CREATE_CHANNEL;\n+\tlws_dll2_add_tail(\u0026tmc-\u003elist_pending_tx, \u0026path-\u003emux-\u003epending_tx);\n+\tlws_transport_mux_client_request_tx(path-\u003emux);\n+\n+\treturn 0;\n+\n+fail:\n+\th-\u003etxp_path.ops_in-\u003eevent_connect_disposition(h, 1);\n+\n+\treturn 1;\n+}\n+\n+static void\n+lws_transport_mux_ch_req_write(lws_transport_priv_t priv)\n+{\n+\tlws_transport_mux_ch_t *tmc \u003d (lws_transport_mux_ch_t *)priv;\n+\tlws_transport_mux_t *tm;\n+\n+\tassert_is_tmch(tmc);\n+\tif (!tmc-\u003elist.owner) {\n+\t\tlwsl_err(\u0022%s: unlisted tmc %p\u005cn\u0022, __func__, tmc);\n+\t\treturn;\n+\t}\n+\ttm \u003d lws_container_of(tmc-\u003elist.owner, lws_transport_mux_t, owner);\n+\tassert_is_tm(tm);\n+\n+\tlws_transport_mux_client_request_tx(tm);\n+\t/* we want to write inside the channel, so register ch as pending */\n+\tif (lws_dll2_is_detached(\u0026tmc-\u003elist_pending_tx))\n+\t\tlws_dll2_add_tail(\u0026tmc-\u003elist_pending_tx, \u0026tm-\u003epending_tx);\n+}\n+#if 0\n+static void\n+lws_transport_mux_req_write(lws_transport_priv_t priv)\n+{\n+\tlws_transport_mux_t *tm \u003d (lws_transport_mux_t *)priv;\n+\tassert_is_tm(tm);\n+\tlws_transport_mux_client_request_tx(tm);\n+}\n+#endif\n+\n+static int\n+lws_transport_mux_write(lws_transport_priv_t priv, uint8_t *buf, size_t len)\n+{\n+\tlws_transport_mux_ch_t *tmc \u003d (lws_transport_mux_ch_t *)priv;\n+\tlws_transport_mux_t *tm \u003d lws_container_of(tmc-\u003elist.owner,\n+\t\t\t\t\t\t lws_transport_mux_t, owner);\n+\n+\tassert_is_tmch(tmc);\n+\tlwsl_user(\u0022%s: %d\u005cn\u0022, __func__, (int)len);\n+\n+\tassert(len \u003c 0xffff);\n+\n+\tbuf[-4] \u003d LWSSSS_LLM_MUX;\n+\tbuf[-3] \u003d tmc-\u003ech_idx;\n+\tbuf[-2] \u003d (len \u003e\u003e 8) \u0026 0xff;\n+\tbuf[-1] \u003d len \u0026 0xff;\n+\n+\ttm-\u003einfo.txp_cpath.ops_onw-\u003e_write(tm-\u003einfo.txp_cpath.priv_onw,\n+\t\t\t\t\t buf - 4, len + 4);\n+\n+\treturn 0;\n+}\n+static void\n+lws_transport_mux_close(lws_transport_priv_t priv)\n+{\n+\n+}\n+static void\n+lws_transport_mux_stream_up(lws_transport_priv_t priv)\n+{\n+\n+}\n+\n+/* incoming parsed channel cbs */\n+\n+static int\n+ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len)\n+{\n+\tlws_ss_state_return_t r;\n+\n+//\tlwsl_notice(\u0022%s: len %d\u005cn\u0022, __func__, (int)len);\n+\n+\tassert_is_tmch(tmc);\n+\n+//\tlwsl_hexdump_notice(buf, len);\n+\n+\tr \u003d lws_txp_inside_sspc.event_read(tmc-\u003epriv, buf, len);\n+\tif (r) {\n+\t\t/*\n+\t\t * Basically the sspc parser rejected it as malformed... we\n+\t\t * lost something somewhere\n+\t\t *\n+\t\t */\n+\t\tlwsl_notice(\u0022%s: r %d\u005cn\u0022, __func__, r);\n+\n+\t\treturn 1;\n+\t}\n+\n+//\treturn tm-\u003einfo.txp_cpath.ops_in-\u003eevent_read(tm-\u003einfo.txp_cpath.priv_in,\n+//\t\t\t\t\t\t\tbuf, len);\n+\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_opens(lws_transport_mux_ch_t *tmc, int determination)\n+{\n+\tstruct lws_sspc_handle *h \u003d (struct lws_sspc_handle *)tmc-\u003epriv;\n+\n+//\tlws_transport_path_client_dump(\u0026tm-\u003einfo.txp_cpath, __func__);\n+\n+\tlwsl_sspc_err(h, \u0022%d\u0022, determination);\n+\n+ \tif (lws_txp_inside_sspc.event_connect_disposition(h, determination))\n+ \t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_closes(lws_transport_mux_ch_t *tmc)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn 0;\n+}\n+\n+static void\n+ltm_txp_req_write(lws_transport_mux_t *tm)\n+{\n+\tlws_transport_mux_client_request_tx(tm);\n+}\n+\n+static int\n+ltm_txp_can_write(lws_transport_mux_ch_t *tmc)\n+{\n+\tassert_is_tmch(tmc);\n+\treturn lws_txp_inside_sspc.event_can_write(\n+\t\t\t(struct lws_sspc_handle *)tmc-\u003epriv, 2048);\n+}\n+\n+static const lws_txp_mux_parse_cbs_t cbs \u003d {\n+\t.payload\t\t\u003d ltm_ch_payload,\n+\t.ch_opens\t\t\u003d ltm_ch_opens,\n+\t.ch_closes\t\t\u003d ltm_ch_closes,\n+\t.txp_req_write\t\t\u003d ltm_txp_req_write,\n+\t.txp_can_write\t\t\u003d ltm_txp_can_write,\n+};\n+\n+lws_ss_state_return_t\n+lws_transport_mux_event_read(lws_transport_priv_t priv,\n+\t\t\t const uint8_t *buf, size_t len)\n+{\n+\tlws_transport_mux_t *tm \u003d (lws_transport_mux_t *)priv;\n+\tlws_ss_state_return_t r;\n+\n+\tassert_is_tm(tm);\n+\tr \u003d lws_transport_mux_rx_parse(tm, buf, len, \u0026cbs);\n+\n+\treturn r;\n+}\n+\n+lws_ss_state_return_t\n+lws_transport_mux_event_can_write(struct lws_sspc_handle *h,\n+\t\t\t\t size_t metadata_limit)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn lws_txp_inside_sspc.event_can_write(h, metadata_limit);\n+}\n+\n+void\n+lws_transport_mux_lost_coherence(lws_transport_priv_t priv)\n+{\n+\tlws_transport_mux_t *tm \u003d (lws_transport_mux_t *)priv;\n+\n+\tif (!tm)\n+\t\treturn;\n+\tassert_is_tm(tm);\n+\n+\tlwsl_warn(\u0022%s: entering link LOST_SYNC\u005cn\u0022, __func__);\n+\n+\tlws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN);\n+}\n+\n+lws_ss_state_return_t\n+lws_transport_mux_event_closed(lws_transport_priv_t priv)\n+{\n+\tlws_transport_mux_ch_t *tmc \u003d (lws_transport_mux_ch_t *)priv;\n+#if defined(_DEBUG)\n+\tlws_transport_mux_t *tm \u003d lws_container_of(tmc-\u003elist.owner,\n+\t\t\t\t lws_transport_mux_t, owner);\n+#endif\n+\tif (!tmc)\n+\t\treturn 0;\n+\tassert_is_tmch(tmc);\n+\tassert_is_tm(tm);\n+\n+\tif (tmc-\u003epriv) {\n+\t\tlwsl_notice(\u0022%s: calling sspc event closed\u005cn\u0022, __func__);\n+\t\tlws_txp_inside_sspc.event_closed(tmc-\u003epriv);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+const lws_transport_client_ops_t lws_transport_mux_client_ops \u003d {\n+\t.name\t\t\t\u003d \u0022txpmuxc\u0022,\n+ \t.event_retry_connect\t\u003d lws_transport_mux_retry_connect,\n+ \t.req_write\t\t\u003d lws_transport_mux_ch_req_write,\n+ \t._write\t\t\t\u003d lws_transport_mux_write,\n+ \t._close\t\t\t\u003d lws_transport_mux_close,\n+ \t.event_stream_up\t\u003d lws_transport_mux_stream_up,\n+\t.event_read\t\t\u003d lws_transport_mux_event_read,\n+\t.lost_coherence\t\t\u003d lws_transport_mux_lost_coherence,\n+\t.event_can_write\t\u003d lws_transport_mux_event_can_write,\n+\t.event_closed\t\t\u003d lws_transport_mux_event_closed,\n+\t.flags\t\t\t\u003d LWS_DSHFLAG_ENABLE_COALESCE |\n+\t\t\t\t LWS_DSHFLAG_ENABLE_SPLIT\n+};\n+\n+\n+\n+#if defined(STANDALONE)\n+#undef lws_context\n+#endif\ndiff --git a/lib/core-net/transport-mux-common.c b/lib/core-net/transport-mux-common.c\nnew file mode 100644\nindex 0000000..5844a32\n--- /dev/null\n+++ b/lib/core-net/transport-mux-common.c\n@@ -0,0 +1,812 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * Transport mux / demux\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+#if defined(STANDALONE)\n+struct lws_context_standalone;\n+#define lws_context lws_context_standalone\n+\n+#if defined(_DEBUG)\n+void\n+lws_assert_fourcc(uint32_t fourcc, uint32_t expected)\n+{\n+\tif (fourcc \u003d\u003d expected)\n+\t\treturn;\n+\n+\tlwsl_err(\u0022%s: fourcc mismatch, expected %c%c%c%c, saw %c%c%c%c\u005cn\u0022,\n+\t\t\t__func__, (int)(expected \u003e\u003e 24), (int)((expected \u003e\u003e 16) \u0026 0xff),\n+\t\t\t(int)((expected \u003e\u003e 8) \u0026 0xff), (int)(expected \u0026 0xff),\n+\t\t\t(int)(fourcc \u003e\u003e 24), (int)((fourcc \u003e\u003e 16) \u0026 0xff),\n+\t\t\t(int)((fourcc \u003e\u003e 8) \u0026 0xff), (int)(fourcc \u0026 0xff));\n+\n+\tassert(0);\n+}\n+#endif\n+#endif\n+\n+lws_transport_mux_ch_t *\n+lws_transport_mux_get_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i)\n+{\n+\tlws_transport_mux_ch_t *mc;\n+\n+\tlws_start_foreach_dll(struct lws_dll2 *, d,\n+\t\t\t lws_dll2_get_head(\u0026tm-\u003eowner)) {\n+\t\tmc \u003d lws_container_of(d, lws_transport_mux_ch_t,\n+\t\t\t\t\tlist);\n+\t\tif (mc-\u003ech_idx \u003d\u003d i)\n+\t\t\treturn mc;\n+\t} lws_end_foreach_dll(d);\n+\n+\treturn NULL;\n+}\n+\n+int\n+lws_transport_mux_next_free(lws_transport_mux_t *tm, lws_mux_ch_idx_t *result)\n+{\n+\tint n \u003d tm-\u003einfo.flags \u0026 LWSTMINFO_SERVER ? 1 : LWS_MUCH_RANGE - 1;\n+\n+\tif (tm-\u003eowner.count \u003e\u003d LWS_MUCH_RANGE - 3)\n+\t\t/* too full to be safe against new muc ch selection collision */\n+\t\treturn 1;\n+\n+\tdo {\n+\t\tif (!(tm-\u003e_open[n \u003e\u003e 5] \u0026 (1u \u003c\u003c (n \u0026 31)))) {\n+\t\t\t/*\n+\t\t\t * Additionally check if any placeholders for this\n+\t\t\t * channel, that did not reach open yet\n+\t\t\t */\n+\t\t\tif (lws_transport_mux_get_channel(tm, (lws_mux_ch_idx_t)n))\n+\t\t\t\tgoto go_on;\n+\n+\t\t\t/*\n+\t\t\t * No it seems good to try it\n+\t\t\t */\n+\t\t\t*result \u003d (lws_mux_ch_idx_t)n;\n+\n+\t\t\treturn 0;\n+\t\t}\n+go_on:\n+\t\tn +\u003d tm-\u003einfo.flags \u0026 LWSTMINFO_SERVER ? 1 : -1;\n+\t} while (n \u003e\u003d 0 \u0026\u0026 n \u003c LWS_MUCH_RANGE);\n+\n+\treturn 1;\n+}\n+\n+void\n+lws_transport_set_link(lws_transport_mux_t *tm, int link_state)\n+{\n+\tif (tm-\u003elink_state \u0026\u0026 !link_state) {\n+\t\tlws_transport_mux_ch_t *mc;\n+\n+\t\tlwsl_user(\u0022%s: ******* transport mux link is DOWN\u005cn\u0022, __func__);\n+\t\t/* destroy any mux channels that were using the link */\n+\t\twhile (tm-\u003eowner.head) {\n+\t\t\tmc \u003d lws_container_of(tm-\u003eowner.head,\n+\t\t\t\t\t lws_transport_mux_ch_t, list);\n+\t\t\tlws_transport_mux_destroy_channel(\u0026mc);\n+\t\t}\n+\t\tmemset(tm-\u003e_open, 0, sizeof(tm-\u003e_open));\n+\t\ttm-\u003eissue_ping \u003d 1;\n+\t\ttm-\u003eawaiting_pong \u003d 0;\n+\t\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t\t sul_ping_cb, 2 * LWS_US_PER_SEC);\n+\t} else if (!tm-\u003elink_state \u0026\u0026 link_state) {\n+\t\tlwsl_user(\u0022%s: ******* transport mux link is UP\u005cn\u0022, __func__);\n+\t}\n+\ttm-\u003elink_state \u003d (uint8_t)link_state;\n+}\n+\n+void\n+sul_ping_cb(lws_sorted_usec_list_t *sul)\n+{\n+\tlws_transport_mux_t *tm \u003d lws_container_of(sul, lws_transport_mux_t,\n+\t\t\t\t\t\t sul_ping);\n+\n+\t/*\n+\t * Some interval expired on the transport...\n+\t *\n+\t * ...because we need to send a ping now?\n+\t */\n+\n+\tif (!tm-\u003eawaiting_pong) {\n+\t\t/*\n+\t\t * We start the pong timer when we decided we wanted to send\n+\t\t * it, not when we sent it, so we can catch unable to send\n+\t\t */\n+\t\tlwsl_notice(\u0022%s: issuing ping\u005cn\u0022, __func__);\n+\t\ttm-\u003eissue_ping \u003d 1;\n+\t\ttm-\u003eawaiting_pong \u003d 1;\n+\n+\t\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t\t sul_ping_cb, tm-\u003einfo.pong_grace_us);\n+\n+\t\tif (tm-\u003einfo.txp_ppath.ops_onw)\n+\t\t\ttm-\u003einfo.txp_ppath.ops_onw-\u003eproxy_req_write(\n+\t\t\t\t\t\ttm-\u003einfo.txp_ppath.priv_onw);\n+\t\telse\n+\t\t\ttm-\u003einfo.txp_cpath.ops_onw-\u003ereq_write(\n+\t\t\t\t\t\ttm-\u003einfo.txp_cpath.priv_onw);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * ... hm it's because our PONG never arrived in the grace period...\n+\t * it means we take it that the transport is no longer passing data\n+\t */\n+\n+\tlwsl_notice(\u0022%s: no PONG came\u005cn\u0022, __func__);\n+\ttm-\u003eissue_ping \u003d 1;\n+\ttm-\u003eawaiting_pong \u003d 0;\n+\tlws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN);\n+\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t sul_ping_cb, 2 * LWS_US_PER_SEC);\n+}\n+\n+#if defined(PICO_SDK_PATH)\n+#if 0\n+struct stv {\n+\tuint32_t tv_sec;\n+\tuint32_t tv_usec;\n+};\n+\n+static uint64_t\n+get_us_timeofday(void)\n+{\n+\tstruct stv tv;\n+\n+\tgettimeofday((struct timeval *)\u0026tv, NULL);\n+\n+\treturn ((uint64_t)((lws_usec_t)tv.tv_sec * LWS_US_PER_SEC) +\n+\t\t\t (uint64_t)tv.tv_usec);\n+}\n+#else\n+static uint64_t\n+get_us_timeofday(void)\n+{\n+\treturn (uint64_t)lws_now_usecs();\n+}\n+#endif\n+#else\n+static\n+uint64_t\n+get_us_timeofday(void)\n+{\n+\tstruct timeval tv;\n+\n+\tgettimeofday(\u0026tv, NULL);\n+\n+\treturn ((uint64_t)((lws_usec_t)tv.tv_sec * LWS_US_PER_SEC) +\n+\t\t\t (uint64_t)tv.tv_usec);\n+}\n+#endif\n+\n+/*\n+ * If the mux channel wants to do something, pack together as much as will\n+ * fit and return nonzero to announce that the mux layer has commandeered this\n+ * write opportunity\n+ *\n+ * Caution, this is called by both client and proxy mux sides\n+ */\n+\n+// !!! response timeouts\n+\n+int\n+lws_transport_mux_pending(lws_transport_mux_t *tm, uint8_t *buf, size_t *len,\n+\t\t\t const lws_txp_mux_parse_cbs_t *cbs)\n+{\n+\tuint8_t *p \u003d buf, *end \u003d buf + (*len) - 1u;\n+\tlws_transport_mux_ch_t *mc;\n+\tint n;\n+\n+\t/* pings and pongs go first */\n+\n+\tif (tm-\u003eissue_ping) {\n+\t\tif (tm-\u003elink_state \u003d\u003d LWSTM_TRANSPORT_DOWN) {\n+\t\t\tlwsl_info(\u0022%s: send RESET_TRANSPORT\u005cn\u0022, __func__);\n+\t\t\t*p++ \u003d LWSSSS_LLM_RESET_TRANSPORT;\n+\t\t}\n+\t\tlwsl_info(\u0022%s: issuing PING\u005cn\u0022, __func__);\n+\t\t*p++ \u003d LWSSSS_LLM_PING;\n+\t\ttm-\u003eus_ping_out \u003d (uint64_t)lws_now_usecs();\n+\t\tlws_ser_wu64be(p, tm-\u003eus_ping_out);\n+\t\tp +\u003d 8;\n+\t\ttm-\u003eissue_ping \u003d 0;\n+\t\tcbs-\u003etxp_req_write(tm);\n+\t}\n+\n+\tif (lws_ptr_diff_size_t(end, p) \u003c 18)\n+\t\tgoto issue;\n+\n+\tif (tm-\u003eissue_pong) {\n+\t\tlwsl_info(\u0022%s: issuing PONG\u005cn\u0022, __func__);\n+\t\t*p++ \u003d LWSSSS_LLM_PONG;\n+\t\tlws_ser_wu64be(p, tm-\u003eus_ping_in);\n+\t\tp +\u003d 8;\n+\t\tlws_ser_wu64be(p, (uint64_t)lws_now_usecs());\n+\t\tp +\u003d 8;\n+\t\ttm-\u003eissue_pong \u003d 0;\n+\t\tcbs-\u003etxp_req_write(tm);\n+\t}\n+\n+\tif (lws_ptr_diff_size_t(end, p) \u003c 18)\n+\t\tgoto issue;\n+\n+\tif (tm-\u003eissue_pongack) {\n+\t\tlwsl_info(\u0022%s: issuing PONGACK\u005cn\u0022, __func__);\n+\t\t*p++ \u003d LWSSSS_LLM_PONGACK;\n+\t\tlws_ser_wu64be(p, (uint64_t)get_us_timeofday());\n+\t\tp +\u003d 8;\n+\t\ttm-\u003eissue_pongack \u003d 0;\n+\t\tlws_sul_cancel(\u0026tm-\u003esul_ping);\n+\t\ttm-\u003eawaiting_pong \u003d 0;\n+\t\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t\t sul_ping_cb, tm-\u003einfo.ping_interval_us);\n+\n+\t\tlws_transport_set_link(tm, LWSTM_OPERATIONAL);\n+\t\tcbs-\u003etxp_req_write(tm);\n+\t}\n+\n+\tfor (n \u003d 0; n \u003c LWS_MUCH_RANGE / 32; n++)\n+\t\tif (tm-\u003efin[n] \u0026\u0026 lws_ptr_diff_size_t(end, p) \u003e 2) {\n+\t\t\tint m;\n+\t\t\tfor (m \u003d 0; m \u003c 32 \u0026\u0026 lws_ptr_diff_size_t(end, p) \u003e 2; m++)\n+\t\t\t\tif (tm-\u003efin[n] \u0026 (1u \u003c\u003c m)) {\n+\t\t\t\t\tlwsl_notice(\u0022%s: FIN on closed ch %d\u005cn\u0022, __func__, (n \u003c\u003c 5) |m);\n+\t\t\t\t\ttm-\u003efin[n] \u0026\u003d (uint32_t)~(1 \u003c\u003c m);\n+\t\t\t\t\t*p++ \u003d LWSSSS_LLM_CHANNEL_NACK;\n+\t\t\t\t\t*p++ \u003d (uint8_t)((n \u003c\u003c 5) | m);\n+\t\t\t\t\tcbs-\u003etxp_req_write(tm);\n+\t\t\t\t}\n+\t\t}\n+\n+\tif (lws_ptr_diff_size_t(end, p) \u003c 18)\n+\t\tgoto issue;\n+\n+\n+\tif (tm-\u003elink_state \u003d\u003d LWSTM_TRANSPORT_DOWN)\n+\t\t/*\n+\t\t * We can't do anything except PING / PONG probes if the\n+\t\t * transport state is down\n+\t\t */\n+\t\tgoto issue;\n+\n+\t/* let's do any mux control packets first */\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\t\t tm-\u003epending_tx.head) {\n+\t\tmc \u003d lws_container_of(d, lws_transport_mux_ch_t,\n+\t\t\t\t list_pending_tx);\n+\n+\t\tif (lws_ptr_diff_size_t(end, p) \u003c 18)\n+\t\t\tbreak;\n+\n+\t\tif (mc-\u003estate !\u003d LWSTMC_OPERATIONAL)\n+\t\t\tlws_dll2_remove(\u0026mc-\u003elist_pending_tx);\n+\n+\t\t/* he wants to write something... let's see how he is */\n+\n+\t\tswitch (mc-\u003estate) {\n+\t\tcase LWSTMC_PENDING_CREATE_CHANNEL:\n+\t\t\t*p++ \u003d LWSSSS_LLM_CHANNEL_REQ;\n+\t\t\t*p++ \u003d mc-\u003ech_idx;\n+\t\t\tmc-\u003estate \u003d LWSTMC_AWAITING_CREATE_CHANNEL_ACK;\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMC_PENDING_CREATE_CHANNEL_ACK:\n+\t\t\t*p++ \u003d LWSSSS_LLM_CHANNEL_ACK;\n+\t\t\t*p++ \u003d mc-\u003ech_idx;\n+\t\t\ttm-\u003e_open[mc-\u003ech_idx \u003e\u003e 5] \u003d (uint32_t)(\n+\t\t\t\t\ttm-\u003e_open[mc-\u003ech_idx \u003e\u003e 5] |\n+\t\t\t\t\t\t(1u \u003c\u003c (mc-\u003ech_idx \u0026 31)));\n+\t\t\tcbs-\u003ech_opens(mc, 0);\n+\t\t\tmc-\u003estate \u003d LPCSPROX_OPERATIONAL;\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMC_PENDING_CREATE_CHANNEL_NACK:\n+\t\t\t*p++ \u003d LWSSSS_LLM_CHANNEL_NACK;\n+\t\t\t*p++ \u003d mc-\u003ech_idx;\n+\t\t\t/*\n+\t\t\t * We're not on board with creating the proposed\n+\t\t\t * channel, so let's reply with that and then delete the\n+\t\t\t * placeholder channel we speculatively created\n+\t\t\t */\n+\t\t\tcbs-\u003ech_closes(mc);\n+\t\t\tlws_transport_mux_destroy_channel(\u0026mc);\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMC_PENDING_CLOSE_CHANNEL:\n+\t\t\t*p++ \u003d LWSSSS_LLM_CHANNEL_CLOSE;\n+\t\t\t*p++ \u003d mc-\u003ech_idx;\n+\t\t\tmc-\u003estate \u003d LWSTMC_AWAITING_CLOSE_CHANNEL_ACK;\n+\t\t\tbreak;\n+\n+\t\tcase LWSSSS_LLM_CHANNEL_CLOSE_ACK:\n+\t\t\t/*\n+\t\t\t * We're telling the peer we saw and actioned his\n+\t\t\t * close request. Then we can remove our side.\n+\t\t\t */\n+\t\t\t*p++ \u003d LWSSSS_LLM_CHANNEL_CLOSE;\n+\t\t\t*p++ \u003d mc-\u003ech_idx;\n+\n+\t\t\tcbs-\u003ech_closes(mc);\n+\t\t\tlws_transport_mux_destroy_channel(\u0026mc);\n+\t\t\tbreak;\n+\t\t}\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\t/* if none, do the first OPERATIONAL that wants to write */\n+\n+\tif (buf \u003d\u003d p) {\n+\t\t//lwsl_notice(\u0022%s: looking for OPERATIONAL\u005cn\u0022, __func__);\n+\t\tlws_start_foreach_dll(struct lws_dll2 *, d, tm-\u003epending_tx.head) {\n+\t\t\tmc \u003d lws_container_of(d, lws_transport_mux_ch_t,\n+\t\t\t\t\t list_pending_tx);\n+\n+\t\t\tif (mc-\u003estate \u003d\u003d LWSTMC_OPERATIONAL) {\n+\t\t\t\tlws_dll2_remove(\u0026mc-\u003elist_pending_tx);\n+\t\t\t\t// lwsl_notice(\u0022%s: passing up event_can_write\u005cn\u0022,\n+\t\t\t\t//\t\t__func__);\n+\n+\t\t\t\tif (cbs-\u003etxp_can_write(mc))\n+\t\t\t\t\treturn -1;\n+\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t} lws_end_foreach_dll(d);\n+\t}\n+\n+\tif (tm-\u003epending_tx.head || buf !\u003d p)\n+\t\tcbs-\u003etxp_req_write(tm);\n+\n+issue:\n+\t*len \u003d lws_ptr_diff_size_t(p, buf);\n+\n+\treturn p !\u003d buf;\n+}\n+\n+int\n+lws_transport_mux_rx_parse(lws_transport_mux_t *tm,\n+\t\t\t const uint8_t *buf, size_t len,\n+\t\t\t const lws_txp_mux_parse_cbs_t *cbs)\n+{\n+\tconst uint8_t *end \u003d buf + len;\n+\tlws_transport_mux_ch_t *mc;\n+\tsize_t av;\n+\n+\t//lwsl_hexdump_notice(buf, len);\n+\n+\twhile (buf \u003c end) {\n+\t\t// lwsl_user(\u0022%s: state %d\u005cn\u0022, __func__, tm-\u003emp_state);\n+\t\tswitch (tm-\u003emp_state) {\n+\t\tcase LWSTMCPAR_CMD:\n+\t\t\ttm-\u003emp_cmd \u003d *buf++;\n+\n+\t\t\tswitch (tm-\u003emp_cmd) {\n+\t\t\tcase LWSSSS_LLM_CHANNEL_REQ:\n+\t\t\tcase LWSSSS_LLM_CHANNEL_ACK:\n+\t\t\tcase LWSSSS_LLM_CHANNEL_NACK:\n+\t\t\tcase LWSSSS_LLM_CHANNEL_CLOSE:\n+\t\t\tcase LWSSSS_LLM_CHANNEL_CLOSE_ACK:\n+\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CHIDX_DONE;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSSSS_LLM_MUX:\n+\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CHIDX;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSSSS_LLM_PING:\n+\t\t\tcase LWSSSS_LLM_PONG:\n+\t\t\tcase LWSSSS_LLM_PONGACK:\n+\t\t\t\ttm-\u003emp_ctr \u003d 8;\n+\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_T64_1;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSSSS_LLM_RESET_TRANSPORT:\n+\t\t\t\t/*\n+\t\t\t\t * The other side is telling us he lost\n+\t\t\t\t * framing coherence, the transport must be\n+\t\t\t\t * reset\n+\t\t\t\t */\n+\t\t\t\tlws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\t/* uhhh... */\n+\t\t\t\tlwsl_warn(\u0022%s: unknown mux cmd 0x%x\u005cn\u0022,\n+\t\t\t\t\t\t__func__, tm-\u003emp_cmd);\n+\t\t\t\t// assert(0); /* temp */\n+\t\t\t\tgoto fail_transport;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMCPAR_CHIDX_DONE:\n+\t\t\ttm-\u003emp_idx \u003d *buf++;\n+\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\t\t\tswitch (tm-\u003emp_cmd) {\n+\t\t\tcase LWSSSS_LLM_CHANNEL_REQ:\n+\t\t\t\t/*\n+\t\t\t\t * peer wants to open a specific channel, how\n+\t\t\t\t * do we feel about that?\n+\t\t\t\t */\n+\t\t\t\tmc \u003d lws_transport_mux_create_channel(tm,\n+\t\t\t\t\t\t\t\ttm-\u003emp_idx);\n+\t\t\t\tif (mc) {\n+\t\t\t\t\t/* We want to try it... */\n+\t\t\t\t\tmc-\u003estate \u003d LWSTMC_PENDING_CREATE_CHANNEL_ACK;\n+\t\t\t\t\tgoto ask_to_send;\n+\t\t\t\t}\n+\t\t\t\t\t/*\n+\t\t\t\t\t * else already pending or open for that\n+\t\t\t\t\t * channel, just ignore and let timeout\n+\t\t\t\t\t */\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSSS_LLM_CHANNEL_NACK:\n+\t\t\tcase LWSSSS_LLM_CHANNEL_ACK:\n+\t\t\t\t/* peer says we can open this channel, but did\n+\t\t\t\t * we ask to open it? */\n+\t\t\t\tmc \u003d lws_transport_mux_get_channel(tm, tm-\u003emp_idx);\n+\t\t\t\tif (!mc) {\n+\t\t\t\t\tlwsl_warn(\u0022%s: (N)ACK for open %u we don't \u0022\n+\t\t\t\t\t\t \u0022remember asking for\u005cn\u0022,\n+\t\t\t\t\t\t __func__, tm-\u003emp_idx);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tif (tm-\u003e_open[tm-\u003emp_idx \u003e\u003e 5] \u0026\n+\t\t\t\t\t\t1u \u003c\u003c (tm-\u003emp_idx \u0026 31)) {\n+\t\t\t\t\tlwsl_warn(\u0022%s: (N)ACK for channel \u0022\n+\t\t\t\t\t\t \u0022already fully open\u005cn\u0022,\n+\t\t\t\t\t\t __func__);\n+\t\t\t\t\tif (tm-\u003emp_cmd \u003d\u003d LWSSSS_LLM_CHANNEL_NACK) {\n+\t\t\t\t\t\tlwsl_warn(\u0022%s: taking as FIN ch %d\u005cn\u0022,\n+\t\t\t\t\t\t\t\t__func__, tm-\u003emp_idx);\n+\t\t\t\t\t\ttm-\u003e_open[tm-\u003emp_idx \u003e\u003e 5] \u0026\u003d (uint32_t)~(\n+\t\t\t\t\t\t\t\t1 \u003c\u003c (tm-\u003emp_idx \u0026 31));\n+\t\t\t\t\t\tcbs-\u003ech_closes(mc);\n+\t\t\t\t\t}\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\n+\t\t\t\tif (tm-\u003emp_cmd \u003d\u003d LWSSSS_LLM_CHANNEL_ACK) {\n+\t\t\t\t\t/* peer said 'yes' to the channel\n+\t\t\t\t\t * we wanted */\n+\t\t\t\t\ttm-\u003e_open[tm-\u003emp_idx \u003e\u003e 5] \u003d\n+\t\t\t\t\t\t(uint32_t)(tm-\u003e_open[tm-\u003emp_idx \u003e\u003e 5] |\n+\t\t\t\t\t\t(1u \u003c\u003c (tm-\u003emp_idx \u0026 31)));\n+\n+\t\t\t\t\tlwsl_notice(\u0022%s: ch %d fully open\u005cn\u0022,\n+\t\t\t\t\t\t\t__func__, tm-\u003emp_idx);\n+\n+\t\t\t\t\tmc-\u003estate \u003d LWSTMC_OPERATIONAL;\n+\t\t\t\t\tcbs-\u003ech_opens(mc, 0);\n+\t\t\t\t\tgoto ask_to_send;\n+\t\t\t\t}\n+\n+\t\t\t\t/* peer said 'no' to the channel we wanted */\n+\n+\t\t\t\tcbs-\u003ech_opens(mc, 1);\n+\t\t\t\tlws_transport_mux_destroy_channel(\u0026mc);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSSS_LLM_CHANNEL_CLOSE:\n+\t\t\t\tmc \u003d lws_transport_mux_get_channel(tm, tm-\u003emp_idx);\n+\t\t\t\tif (!mc) {\n+\t\t\t\t\tlwsl_warn(\u0022%s: CLOSE for unknown ch\u005cn\u0022,\n+\t\t\t\t\t\t __func__);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tif (!(tm-\u003e_open[tm-\u003emp_idx \u003e\u003e 5] \u0026\n+\t\t\t\t\t\t1u \u003c\u003c (tm-\u003emp_idx \u0026 31))) {\n+\t\t\t\t\tlwsl_warn(\u0022%s: CLOSE for channel \u0022\n+\t\t\t\t\t\t \u0022not fully open\u005cn\u0022,\n+\t\t\t\t\t\t __func__);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tmc-\u003estate \u003d LWSTMC_PENDING_CLOSE_CHANNEL_ACK;\n+\t\t\t\tgoto ask_to_send;\n+\n+\t\t\tcase LWSSSS_LLM_CHANNEL_CLOSE_ACK:\n+\t\t\t\t/* ok... so we did ask to close that channel? */\n+\t\t\t\tmc \u003d lws_transport_mux_get_channel(tm, tm-\u003emp_idx);\n+\t\t\t\tif (!mc) {\n+\t\t\t\t\tlwsl_warn(\u0022%s: CLOSE_ACK for unknown ch\u005cn\u0022,\n+\t\t\t\t\t\t __func__);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tif (mc-\u003estate !\u003d LWSTMC_AWAITING_CLOSE_CHANNEL_ACK) {\n+\t\t\t\t\tlwsl_warn(\u0022%s: CLOSE_ACK on ch not waiting for it\u005cn\u0022, __func__);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\t/* nothing more should come on this channel */\n+\t\t\t\tlws_transport_mux_destroy_channel(\u0026mc);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\t/* mux payload encapsulation */\n+\n+\t\tcase LWSTMCPAR_CHIDX:\n+\t\t\ttm-\u003emp_idx \u003d *buf++;\n+\t\t\ttm-\u003emp_state++;\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMCPAR_PLENH:\n+\t\t\ttm-\u003emp_pay \u003d (uint32_t)((*buf++) \u003c\u003c 8);\n+\t\t\ttm-\u003emp_state++;\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMCPAR_PLENL:\n+\t\t\ttm-\u003emp_pay |\u003d *buf++;\n+\t\t\tmc \u003d lws_transport_mux_get_channel(tm, tm-\u003emp_idx);\n+\t\t\tif (!mc) {\n+\t\t\t\tlwsl_warn(\u0022%s: DATA for unknown ch\u005cn\u0022,\n+\t\t\t\t\t __func__);\n+\t\t\t\t/* assertively NAK the channel */\n+\t\t\t\ttm-\u003efin[tm-\u003emp_idx \u003e\u003e 5] |\u003d 1u \u003c\u003c (tm-\u003emp_idx \u0026 31);\n+\t\t\t\tav \u003d lws_ptr_diff_size_t(end, buf);\n+\t\t\t\tif (av \u003e tm-\u003emp_pay)\n+\t\t\t\t\tav \u003d tm-\u003emp_pay;\n+\t\t\t\tbuf +\u003d av;\n+\t\t\t\ttm-\u003emp_pay \u003d (uint32_t)(tm-\u003emp_pay - av);\n+\t\t\t\tif (!tm-\u003emp_pay)\n+\t\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\t\t\t\telse\n+\t\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_PAY;\n+\t\t\t\tgoto ask_to_send;\n+\t\t\t}\n+\t\t//\tlwsl_notice(\u0022%s: mux data frame len %d\u005cn\u0022, __func__, (int)tm-\u003emp_pay);\n+\t\t\tassert(tm-\u003e_open[tm-\u003emp_idx \u003e\u003e 5] \u0026 (1u \u003c\u003c (tm-\u003emp_idx \u0026 31)));\n+\t\t\tif (!tm-\u003emp_pay)\n+\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\t\t\telse\n+\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_PAY;\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMCPAR_PAY:\n+\t\t\tav \u003d lws_ptr_diff_size_t(end, buf);\n+\t\t\tif (av \u003e tm-\u003emp_pay)\n+\t\t\t\tav \u003d tm-\u003emp_pay;\n+\t\t\tmc \u003d lws_transport_mux_get_channel(tm, tm-\u003emp_idx);\n+\t\t\tif (mc) {\n+\t\t\t\tif (cbs-\u003epayload(mc, buf, av)) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * indication of broken framing...\n+\t\t\t\t\t * other outcomes handled at SSPC layer\n+\t\t\t\t\t */\n+\n+\t\t\t\t\tgoto fail_transport;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbuf +\u003d av;\n+\t\t\t// lwsl_notice(\u0022%s: mp_pay %d -\u003e %d\u005cn\u0022, __func__,\n+\t\t\t// (int)tm-\u003emp_pay, (int)(tm-\u003emp_pay - av));\n+\t\t\ttm-\u003emp_pay -\u003d (uint32_t)av;\n+\t\t\tif (!tm-\u003emp_pay)\n+\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\t\t\tbreak;\n+\n+\t\tcase LWSTMCPAR_T64_1:\n+\t\t\ttm-\u003emp_time \u003d (tm-\u003emp_time \u003c\u003c 8) | *buf++;\n+\t\t\tif (!--tm-\u003emp_ctr) {\n+\t\t\t\ttm-\u003emp_ctr \u003d 8;\n+\t\t\t\tif (tm-\u003emp_cmd \u003d\u003d LWSSSS_LLM_PING) {\n+\t\t\t\t\tlwsl_user(\u0022%s: got PING\u005cn\u0022, __func__);\n+\t\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\t\t\t\t\ttm-\u003eus_ping_in \u003d tm-\u003emp_time;\n+\t\t\t\t\ttm-\u003eissue_pong \u003d 1;\n+\t\t\t\t\tcbs-\u003etxp_req_write(tm);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tif (tm-\u003emp_cmd \u003d\u003d LWSSSS_LLM_PONGACK) {\n+\t\t\t\t\tlwsl_user(\u0022%s: got PONGACK: ustime %llu\u005cn\u0022,\n+\t\t\t\t\t\t\t__func__,\n+\t\t\t\t\t\t\t(unsigned long long)tm-\u003emp_time);\n+\t\t\t\t\ttm-\u003eus_unixtime_peer \u003d tm-\u003emp_time - get_us_timeofday();\n+\t\t\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\t\t\t\t\tlws_transport_set_link(tm, LWSTM_OPERATIONAL);\n+\t\t\t\t\tlws_sul_cancel(\u0026tm-\u003esul_ping);\n+\t\t\t\t\ttm-\u003eawaiting_pong \u003d 0;\n+\t\t\t\t\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t\t\t\t\t sul_ping_cb, tm-\u003einfo.ping_interval_us);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\n+\t\t\t\ttm-\u003emp_state++;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase LWSTMCPAR_T64_2:\n+\t\t\ttm-\u003emp_time1 \u003d (tm-\u003emp_time1 \u003c\u003c 8) | *buf++;\n+\t\t\tif (--tm-\u003emp_ctr)\n+\t\t\t\t\tbreak;\n+\n+\t\t\ttm-\u003emp_state \u003d LWSTMCPAR_CMD;\n+\n+\t\t\tif (tm-\u003emp_time !\u003d tm-\u003eus_ping_out) {\n+\t\t\t\tlwsl_warn(\u0022%s: PONG payload mismatch 0x%llx 0x%llx\u005cn\u0022,\n+\t\t\t\t\t __func__, (unsigned long long)tm-\u003emp_time,\n+\t\t\t\t\t (unsigned long long)tm-\u003eus_ping_out);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tlwsl_user(\u0022%s: got PONG\u005cn\u0022, __func__);\n+\t\t\ttm-\u003eawaiting_pong \u003d 0;\n+\t\t\tlws_sul_cancel(\u0026tm-\u003esul_ping);\n+\t\t\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t\t\t sul_ping_cb, tm-\u003einfo.ping_interval_us);\n+\t\t\ttm-\u003eissue_pongack \u003d 1;\n+\t\t\tcbs-\u003etxp_req_write(tm);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcontinue;\n+\n+ask_to_send:\n+\t\tif (mc \u0026\u0026 lws_dll2_is_detached(\u0026mc-\u003elist_pending_tx))\n+\t\t\tlws_dll2_add_tail(\u0026mc-\u003elist_pending_tx, \u0026tm-\u003epending_tx);\n+\n+\t\tcbs-\u003etxp_req_write(tm);\n+\t}\n+\n+\treturn 0;\n+\n+fail_transport:\n+\n+\tlws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN);\n+\n+\treturn -1;\n+}\n+\n+lws_transport_mux_ch_t *\n+lws_transport_mux_create_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i)\n+{\n+\tlws_transport_mux_ch_t *mc;\n+\n+\tif (tm-\u003e_open[i \u003e\u003e 5] \u0026 (1u \u003c\u003c (i \u0026 31)))\n+\t\treturn NULL;\n+\n+\tif (lws_transport_mux_get_channel(tm, i))\n+\t\treturn NULL;\n+\n+\tmc \u003d malloc(sizeof(*mc));\n+\tif (!mc)\n+\t\treturn NULL;\n+\n+\tmemset(mc, 0, sizeof(*mc));\n+\n+#if defined(_DEBUG)\n+\tmc-\u003emagic \u003d LWS_TRANSPORT_MUXCH_MAGIC;\n+#endif\n+\tmc-\u003ech_idx \u003d i;\n+\n+\tlws_dll2_add_tail(\u0026mc-\u003elist, \u0026tm-\u003eowner);\n+\n+\treturn mc;\n+}\n+\n+lws_transport_mux_ch_t *\n+lws_transport_mux_add_channel(lws_transport_mux_t *tm, lws_transport_priv_t priv)\n+{\n+\tlws_transport_mux_ch_t *mc;\n+\tlws_mux_ch_idx_t i;\n+\n+\tif (lws_transport_mux_next_free(tm, \u0026i)) {\n+\t\tlwsl_err(\u0022%s: unable to add new mux channel\u005cn\u0022, __func__);\n+\t\treturn NULL;\n+\t}\n+\n+\tmc \u003d lws_transport_mux_create_channel(tm, i);\n+\tif (mc)\n+\t\tmc-\u003epriv \u003d priv;\n+\n+\treturn mc;\n+}\n+\n+void\n+lws_transport_mux_destroy_channel(lws_transport_mux_ch_t **_mc)\n+{\n+\tlws_transport_mux_ch_t *mc \u003d *_mc;\n+\tlws_transport_mux_t *tm \u003d lws_container_of(mc-\u003elist.owner,\n+\t\t\t\t\t\tlws_transport_mux_t, owner);\n+\n+\tlwsl_notice(\u0022%s: mux ch %u\u005cn\u0022, __func__, mc-\u003ech_idx);\n+\n+\tif (mc-\u003estate \u003e\u003d LWSTMC_PENDING_CREATE_CHANNEL_ACK)\n+\t\t/* he only sets the open bit on receipt of the ACK */\n+\t\ttm-\u003e_open[mc-\u003ech_idx \u003e\u003e 5] \u0026\u003d (lws_mux_ch_idx_t)\n+\t\t\t\t\t\t~(1 \u003c\u003c (mc-\u003ech_idx \u0026 31));\n+\n+\t/*\n+\t * We must report channel closure... client side\n+\t */\n+\n+\tif (tm-\u003einfo.txp_cpath.ops_in \u0026\u0026\n+\t tm-\u003einfo.txp_cpath.ops_in-\u003eevent_closed) {\n+\t\tlwsl_notice(\u0022%s: calling %s event closed\u005cn\u0022, __func__,\n+\t\t\t\ttm-\u003einfo.txp_cpath.ops_in-\u003ename);\n+\t\ttm-\u003einfo.txp_cpath.ops_in-\u003eevent_closed((lws_transport_priv_t)mc);\n+\t}\n+\n+\t/*\n+\t * We must report channel closure... proxy side\n+\t */\n+\n+\tif (tm-\u003einfo.txp_ppath.ops_in \u0026\u0026\n+\t tm-\u003einfo.txp_ppath.ops_in-\u003eevent_close_conn) {\n+\t\tlwsl_notice(\u0022%s: calling %s event_close_conn\u005cn\u0022, __func__,\n+\t\t\t\ttm-\u003einfo.txp_ppath.ops_in-\u003ename);\n+\t\ttm-\u003einfo.txp_ppath.ops_in-\u003eevent_close_conn(\n+\t\t\t\t(lws_transport_priv_t)mc-\u003epriv);\n+\t}\n+\n+\tlws_sul_cancel(\u0026mc-\u003esul);\n+\tlws_dll2_remove(\u0026mc-\u003elist_pending_tx);\n+\tlws_dll2_remove(\u0026mc-\u003elist);\n+\n+\tfree(mc);\n+\t*_mc \u003d NULL;\n+}\n+\n+lws_transport_mux_t *\n+lws_transport_mux_create(struct lws_context *cx, lws_transport_info_t *info,\n+\t\tvoid *txp_handle)\n+{\n+\tlws_transport_mux_t *tm \u003d malloc(sizeof(*tm));\n+\n+\tif (tm) {\n+\t\tmemset(tm, 0, sizeof(*tm));\n+\n+#if defined(_DEBUG)\n+\t\ttm-\u003emagic \u003d LWS_TRANSPORT_MUX_MAGIC;\n+#endif\n+\n+\t\ttm-\u003ecx\t\t\u003d cx;\n+\t\ttm-\u003einfo\t\u003d *info;\n+\t\ttm-\u003etxp_handle\t\u003d txp_handle;\n+\t\ttm-\u003elink_state\t\u003d LWSTM_TRANSPORT_DOWN;\n+\n+\t\tassert_is_tm(tm);\n+\n+\t\t/* let's try a ping straight off */\n+\t\tif (tm-\u003ecx)\n+\t\t\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0,\n+\t\t\t\t\t \u0026tm-\u003esul_ping, sul_ping_cb, 1);\n+\t}\n+\n+\treturn tm;\n+}\n+\n+void\n+lws_transport_mux_destroy(lws_transport_mux_t **tm)\n+{\n+\tlws_transport_mux_ch_t *mc;\n+\n+\twhile ((*tm)-\u003eowner.head) {\n+\t\tmc \u003d lws_container_of((*tm)-\u003eowner.head,\n+\t\t\t\t lws_transport_mux_ch_t, list);\n+\t\tlws_transport_mux_destroy_channel(\u0026mc);\n+\t}\n+\tfree(*tm);\n+\t*tm \u003d NULL;\n+}\ndiff --git a/lib/core-net/transport-mux-proxy.c b/lib/core-net/transport-mux-proxy.c\nnew file mode 100644\nindex 0000000..da7c601\n--- /dev/null\n+++ b/lib/core-net/transport-mux-proxy.c\n@@ -0,0 +1,404 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * Transport mux / demux\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+#if defined(_DEBUG)\n+void\n+lws_transport_path_proxy_dump(lws_txp_path_proxy_t *path, const char *ctx)\n+{\n+\tchar buf[128], *p \u003d buf, *end \u003d buf + sizeof(buf) - 1;\n+\tuint32_t magic;\n+\n+\tp +\u003d lws_snprintf(p, lws_ptr_diff_size_t(end, p),\n+\t\t\t\u0022MUX: %p, IN: ops %s, priv %p\u0022,\n+\t\t\tpath-\u003emux, path-\u003eops_in ? path-\u003eops_in-\u003ename : \u0022null\u0022,\n+\t\t\tpath-\u003epriv_in);\n+\tif (path-\u003epriv_in) {\n+\t\tmagic \u003d *(uint32_t *)path-\u003epriv_in;\n+\t\tp +\u003d lws_snprintf(p, lws_ptr_diff_size_t(end, p), \u0022 (%c%c%c%c)\u0022,\n+\t\t\t\t(int)(magic \u003e\u003e 24), (int)((magic \u003e\u003e 16) \u0026 0xff),\n+\t\t\t\t(int)((magic \u003e\u003e 8) \u0026 0xff), (int)(magic \u0026 0xff));\n+\t}\n+\tp +\u003d lws_snprintf(p, lws_ptr_diff_size_t(end, p), \u0022, ONW: ops %s, priv %p\u0022,\n+\t\t\tpath-\u003eops_in ? path-\u003eops_in-\u003ename : \u0022null\u0022, path-\u003epriv_in);\n+\tif (path-\u003epriv_in) {\n+\t\tmagic \u003d *(uint32_t *)path-\u003epriv_in;\n+\t\tp +\u003d lws_snprintf(p, lws_ptr_diff_size_t(end, p), \u0022 (%c%c%c%c)\u0022,\n+\t\t\t\t(int)(magic \u003e\u003e 24), (int)((magic \u003e\u003e 16) \u0026 0xff),\n+\t\t\t\t(int)((magic \u003e\u003e 8) \u0026 0xff), (int)(magic \u0026 0xff));\n+\t}\n+\n+\t*end \u003d '\u005c0';\n+\tlwsl_notice(\u0022%s: %s: %s\u005cn\u0022, __func__, ctx, buf);\n+}\n+#endif\n+\n+\n+void\n+lws_transport_mux_proxy_request_tx(lws_transport_mux_t *tm)\n+{\n+\ttm-\u003einfo.txp_ppath.ops_onw-\u003eproxy_req_write(tm-\u003einfo.txp_ppath.priv_onw);\n+}\n+\n+\n+\n+/*\n+ * We're the outer, mux server creation, we should instantiate the mux and\n+ * onward transport\n+ *\n+ * Our transport_priv is the mux object itself.\n+ */\n+\n+static int\n+lws_transport_mux_init_proxy_server(struct lws_context *cx,\n+\t\t\t const struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t lws_transport_priv_t txp_priv_inward,\n+\t\t\t lws_txp_path_proxy_t *txp_ppath,\n+\t\t\t const void *txp_info,\n+\t\t\t const char *bind, int port)\n+{\n+\tlws_transport_info_t *info \u003d (lws_transport_info_t *)txp_info;\n+\tlws_txp_path_proxy_t txp_ppath_temp;\n+\tlws_transport_mux_t *tm;\n+\n+\tlwsl_user(\u0022%s: priv_inward %p\u005cn\u0022, __func__, txp_priv_inward);\n+\tassert(info);\n+\tassert(info-\u003etxp_ppath.ops_onw);\n+\n+\t/* let's create the mux... */\n+\n+\ttm \u003d malloc(sizeof(*tm));\n+\tif (!tm)\n+\t\treturn 1;\n+\n+\tmemset(tm, 0, sizeof(*tm));\n+\ttxp_ppath-\u003emux \u003d tm;\n+\n+#if defined(_DEBUG)\n+\ttm-\u003emagic\t\t\t\u003d LWS_TRANSPORT_MUX_MAGIC;\n+#endif\n+\ttm-\u003ecx\t\t\t\t\u003d cx;\n+\ttm-\u003einfo\t\t\t\u003d *info;\n+\ttm-\u003einfo.txp_ppath.ops_in\t\u003d txp_ops_inward;\n+\ttm-\u003einfo.txp_ppath.priv_in\t\u003d txp_priv_inward;\n+\ttm-\u003einfo.txp_ppath.mux\t\t\u003d tm;\n+\n+\t/* Let's see about creating the onward transport instance after...\n+\t * This is creating the transport-serial instance or whatever.\n+\t *\n+\t * For channels, priv is a conn. For the proxy itself, it's NULL here.\n+\t */\n+\n+\tif (info-\u003etxp_ppath.ops_onw-\u003einit_proxy_server(cx,\n+\t\t\t\t\t\t\u0026lws_transport_mux_proxy_ops,\n+\t\t\t\t\t\t(lws_transport_priv_t)tm,\n+\t\t\t\t\t\t\u0026txp_ppath_temp,\n+\t\t\t\t\t\tinfo-\u003eonward_txp_info,\n+\t\t\t\t\t\tbind, port)) {\n+\t\tlwsl_err(\u0022%s: onward %s server int fail\u005cn\u0022, __func__,\n+\t\t\t\tinfo-\u003etxp_ppath.ops_onw-\u003ename);\n+\t\treturn 1;\n+\t}\n+\n+\ttm-\u003einfo.txp_ppath.ops_onw\t\u003d info-\u003etxp_ppath.ops_onw;\n+\ttm-\u003einfo.txp_ppath.priv_onw\t\u003d txp_ppath_temp.priv_onw;\n+\n+\t/* ...let's schedule a ping straight off at the mux layer */\n+\n+\tlws_sul_schedule((struct lws_context *)tm-\u003ecx, 0, \u0026tm-\u003esul_ping,\n+\t\t\t sul_ping_cb, 1);\n+\n+\tlwsl_user(\u0022%s: OK\u005cn\u0022, __func__);\n+\n+\treturn 0;\n+}\n+\n+static int\n+lws_transport_mux_destroy_proxy_server(struct lws_context *cx)\n+{\n+\tif (!cx-\u003etxp_ppath.mux)\n+\t\treturn 0;\n+\n+\tlws_transport_mux_destroy(\u0026cx-\u003etxp_ppath.mux);\n+\n+\treturn 0;\n+}\n+\n+lws_ss_state_return_t\n+lws_transport_mux_proxy_new_conn(struct lws_context *cx,\n+\t\t\t\t const struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t\t lws_transport_priv_t txp_priv_inward,\n+\t#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t const lws_fi_ctx_t *fic,\n+\t#endif\n+\t\t\t\tstruct lws_sss_proxy_conn **conn,\n+\t\t\t\tlws_transport_priv_t txp_priv)\n+{\n+\treturn 0;\n+}\n+\n+lws_ss_state_return_t\n+lws_transport_mux_proxy_close_conn(struct lws_sss_proxy_conn *conn)\n+{\n+\treturn 0;\n+}\n+\n+/* incoming parsed channel cbs */\n+\n+static int\n+ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len)\n+{\n+#if defined(_DEBUG)\n+\tlws_transport_mux_t *tm;\n+#endif\n+\n+\tassert_is_tmch(tmc);\n+\n+#if defined(_DEBUG)\n+\ttm \u003d lws_container_of(tmc-\u003elist.owner, lws_transport_mux_t, owner);\n+\tassert_is_tm(tm);\n+#endif\n+\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+//\tlwsl_hexdump_err(buf, len);\n+#if defined(_DEBUG)\n+\tlws_transport_path_proxy_dump(\u0026tm-\u003einfo.txp_ppath, __func__);\n+#endif\n+\n+\tlws_txp_inside_proxy.proxy_read(tmc-\u003epriv, buf, len);\n+\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_opens(lws_transport_mux_ch_t *tmc, int determination)\n+{\n+\tlws_transport_mux_t *tm;\n+\tstruct lws_sss_proxy_conn *conn;\n+\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\n+\tassert_is_tmch(tmc);\n+\ttm \u003d lws_container_of(tmc-\u003elist.owner, lws_transport_mux_t, owner);\n+\t\tassert_is_tm(tm);\n+\n+\tif (lws_txp_inside_proxy.event_new_conn(\n+\t\t\ttm-\u003ecx, \u0026lws_txp_inside_proxy,\n+\t\t\t(lws_transport_priv_t)NULL,\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\tNULL,\n+#endif\n+\t\t\t\u0026conn,\n+\t\t\t(lws_transport_priv_t)tmc)) {\n+\t\tlwsl_err(\u0022%s: hangup from new_conn\u005cn\u0022, __func__);\n+\t\treturn -1;\n+\t}\n+\n+\ttmc-\u003epriv \u003d (lws_transport_priv_t)conn;\n+\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_closes(lws_transport_mux_ch_t *tmc)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn 0;\n+}\n+\n+static void\n+ltm_txp_req_write(lws_transport_mux_t *tm)\n+{\n+//\tlws_transport_mux_proxy_request_tx(tm);\n+\tif (tm-\u003einfo.txp_ppath.priv_onw)\n+\t\ttm-\u003einfo.txp_ppath.ops_onw-\u003eproxy_req_write(tm-\u003einfo.txp_ppath.priv_onw);\n+}\n+\n+static int\n+ltm_txp_can_write(lws_transport_mux_ch_t *tmc)\n+{\n+\tassert_is_tmch(tmc);\n+\treturn lws_txp_inside_proxy.event_proxy_can_write(tmc-\u003epriv\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t, NULL\n+#endif\n+\t\t\t);\n+}\n+\n+static const lws_txp_mux_parse_cbs_t cbs \u003d {\n+\t.payload\t\t\u003d ltm_ch_payload ,\n+\t.ch_opens\t\t\u003d ltm_ch_opens,\n+\t.ch_closes\t\t\u003d ltm_ch_closes,\n+\t.txp_req_write\t\t\u003d ltm_txp_req_write,\n+\t.txp_can_write\t\t\u003d ltm_txp_can_write,\n+};\n+\n+lws_ss_state_return_t\n+lws_transport_mux_proxy_event_proxy_can_write(\n+\t\tlws_transport_priv_t priv\n+\t\t//struct lws_sss_proxy_conn *conn\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t, const lws_fi_ctx_t *fic\n+#endif\n+\t\t)\n+{\n+\tlws_transport_mux_t *tm \u003d (lws_transport_mux_t *)priv;\n+\tstruct lws_sss_proxy_conn *conn;\n+\tuint8_t buf[2048];\n+\tsize_t r \u003d sizeof(buf), r1;\n+\n+\tassert_is_tm(tm);\n+\n+\tif (lws_transport_mux_pending(tm, buf, \u0026r, \u0026cbs)) {\n+\t\tr1 \u003d r;\n+\t\ttm-\u003einfo.txp_ppath.ops_onw-\u003eproxy_write(tm-\u003einfo.txp_ppath.priv_onw, buf, \u0026r);\n+\t\tif (r !\u003d r1)\n+\t\t\tassert(0);\n+\t\treturn 0;\n+\t}\n+\n+\tconn \u003d (struct lws_sss_proxy_conn *)tm-\u003einfo.txp_ppath.priv_in;\n+\tif (conn) {\n+\n+\t\tassert_is_conn(conn);\n+\n+\t\ttm-\u003einfo.txp_ppath.ops_in-\u003eevent_proxy_can_write(conn\n+\t#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t\t, fic\n+\t#endif\n+\t\t\t\t);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+lws_transport_mux_onward_bind(lws_transport_priv_t priv, struct lws_ss_handle *h)\n+{\n+\n+}\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+static const lws_fi_ctx_t *\n+lws_transport_mux_fault_context(lws_transport_priv_t priv)\n+{\n+\treturn NULL;\n+}\n+#endif\n+static void\n+lws_transport_mux_client_up(lws_transport_priv_t priv)\n+{\n+\n+}\n+\n+static void\n+lws_transport_mux_proxy_req_write(lws_transport_priv_t priv)\n+{\n+\tlws_transport_mux_ch_t *tmc \u003d (lws_transport_mux_ch_t *)priv;\n+\tlws_transport_mux_t *tm;\n+\n+\tassert_is_tmch(tmc);\n+\n+\ttm \u003d lws_container_of(tmc-\u003elist.owner, lws_transport_mux_t, owner);\n+\tassert_is_tm(tm);\n+\n+\tif (!tm-\u003einfo.txp_ppath.priv_onw)\n+\t\treturn;\n+\n+\tif (lws_dll2_is_detached(\u0026tmc-\u003elist_pending_tx))\n+\t\tlws_dll2_add_tail(\u0026tmc-\u003elist_pending_tx, \u0026tm-\u003epending_tx);\n+\n+\ttm-\u003einfo.txp_ppath.ops_onw-\u003eproxy_req_write(tm-\u003einfo.txp_ppath.priv_onw);\n+}\n+/**\u003c Get the proxy to write to out on the onward (back to client) transport on this channel */\n+int\n+lws_transport_mux_proxy_write(lws_transport_priv_t priv, uint8_t *buf, size_t *len)\n+{\n+\tlws_transport_mux_ch_t *tmc \u003d (lws_transport_mux_ch_t *)priv;\n+\tlws_transport_mux_t *tm;\n+\tsize_t olen;\n+\n+\t//lwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\n+\tassert_is_tmch(tmc);\n+\n+\ttm \u003d lws_container_of(tmc-\u003elist.owner, lws_transport_mux_t, owner);\n+\tassert_is_tm(tm);\n+\n+\tassert(*len \u003c 0xffff);\n+\n+\t/* use the LWS_PRE area to encapsulate the SSS inside the mux protocol */\n+\n+\tbuf[-4] \u003d LWSSSS_LLM_MUX;\n+\tbuf[-3] \u003d tmc-\u003ech_idx;\n+\tbuf[-2] \u003d (*len \u003e\u003e 8) \u0026 0xff;\n+\tbuf[-1] \u003d *len \u0026 0xff;\n+\n+\tolen \u003d (*len) + 4;\n+\ttm-\u003einfo.txp_ppath.ops_onw-\u003eproxy_write(tm-\u003einfo.txp_ppath.priv_onw,\n+\t\t\t\t\t\tbuf - 4, \u0026olen);\n+\n+\tassert(olen \u003d\u003d (*len) + 4);\n+\n+\treturn 0;\n+}\n+\n+lws_ss_state_return_t\n+lws_transport_mux_proxy_read(lws_transport_priv_t priv,\n+\t\t\t const uint8_t *buf, size_t len)\n+{\n+\tlws_transport_mux_t *tm \u003d (lws_transport_mux_t *)priv;\n+\tlws_ss_state_return_t r;\n+\n+\tassert_is_tm(tm);\n+\n+\n+\n+\tr \u003d lws_transport_mux_rx_parse(tm, buf, len, \u0026cbs);\n+\n+\treturn r;\n+}\n+\n+\n+const lws_transport_proxy_ops_t lws_transport_mux_proxy_ops \u003d {\n+\t.name\t\t\t\u003d \u0022txpmuxp\u0022,\n+\t.init_proxy_server\t\u003d lws_transport_mux_init_proxy_server,\n+\t.destroy_proxy_server\t\u003d lws_transport_mux_destroy_proxy_server,\n+\t.proxy_read\t\t\u003d lws_transport_mux_proxy_read,\n+ \t.proxy_req_write\t\u003d lws_transport_mux_proxy_req_write,\n+ \t.proxy_write\t\t\u003d lws_transport_mux_proxy_write,\n+\t.event_onward_bind\t\u003d lws_transport_mux_onward_bind,\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t.fault_context\t\t\u003d lws_transport_mux_fault_context,\n+#endif\n+\t.event_close_conn\t\u003d lws_transport_mux_proxy_close_conn,\n+\t.event_proxy_can_write\t\u003d lws_transport_mux_proxy_event_proxy_can_write,\n+\t.event_new_conn\t\t\u003d lws_transport_mux_proxy_new_conn,\n+\t.event_client_up\t\u003d lws_transport_mux_client_up,\n+\t.flags\t\t\t\u003d LWS_DSHFLAG_ENABLE_COALESCE |\n+\t\t\t\t LWS_DSHFLAG_ENABLE_SPLIT\n+};\ndiff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c\nindex 298bf01..562607c 100644\n--- a/lib/core-net/vhost.c\n+++ b/lib/core-net/vhost.c\n@@ -200,7 +200,7 @@ lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot)\n \n #if defined(LWS_ROLE_RAW_FILE)\n \n-\tlwsl_wsi_notice(wsi, \u0022falling back to raw file role bind\u0022);\n+\tlwsl_wsi_info(wsi, \u0022falling back to raw file role bind\u0022);\n \n \t/* fall back to raw file role if, eg, h1 not configured */\n \n@@ -402,6 +402,7 @@ lws_protocol_init_vhost(struct lws_vhost *vh, int *any)\n \t\tlwsa-\u003eprotocol \u003d \u0026vh-\u003eprotocols[n];\n \t\tif (!vh-\u003eprotocols[n].name)\n \t\t\tcontinue;\n+\n \t\tpvo \u003d lws_vhost_protocol_options(vh, vh-\u003eprotocols[n].name);\n \t\tif (pvo) {\n \t\t\t/*\n@@ -590,6 +591,9 @@ lws_create_vhost(struct lws_context *context,\n #endif\n \tint n;\n \n+\tif (!pcols \u0026\u0026 context-\u003eprotocols_copy)\n+\t\tpcols \u003d context-\u003eprotocols_copy;\n+\n \tif (info-\u003evhost_name)\n \t\tname \u003d info-\u003evhost_name;\n \n@@ -683,12 +687,14 @@ lws_create_vhost(struct lws_context *context,\n \t * old or new way depending on what he gave us\n \t */\n \n-\tif (!pcols)\n+\tif (!pcols) {\n \t\tfor (vh-\u003ecount_protocols \u003d 0;\n \t\t\tinfo-\u003epprotocols[vh-\u003ecount_protocols];\n-\t\t\tvh-\u003ecount_protocols++)\n-\t\t\t;\n-\telse\n+\t\t\tvh-\u003ecount_protocols++) {\n+\t\t\t\tlwsl_user(\u0022%s: ppcols: %s\u005cn\u0022, __func__,\n+\t\t\t\t\t\tinfo-\u003epprotocols[vh-\u003ecount_protocols]-\u003ename);\n+\t\t}\n+\t} else\n \t\tfor (vh-\u003ecount_protocols \u003d 0;\n \t\t\tpcols[vh-\u003ecount_protocols].callback;\n \t\t\tvh-\u003ecount_protocols++)\ndiff --git a/lib/core/CMakeLists.txt b/lib/core/CMakeLists.txt\nindex dc64794..bd0b05d 100644\n--- a/lib/core/CMakeLists.txt\n+++ b/lib/core/CMakeLists.txt\n@@ -25,18 +25,39 @@\n include_directories(.)\n \n list(APPEND SOURCES\n-\tcore/alloc.c\n-\tcore/buflist.c\n-\tcore/context.c\n \tcore/lws_dll2.c\n-\tcore/lws_map.c\n-\tcore/libwebsockets.c\n-\tcore/logs.c\n )\n \n-if (LWS_WITH_FILE_OPS)\n-\tlist(APPEND SOURCES core/vfs.c)\n+if (NOT LWS_ONLY_SSPC)\n+\n+\tlist(APPEND SOURCES\n+\t\tcore/alloc.c\n+\t\tcore/buflist.c\n+\t\tcore/context.c\n+\t\tcore/lws_map.c\n+\t\tcore/libwebsockets.c\n+\t\tcore/logs.c\n+\t)\n+\t\n+\tif (LWS_WITH_FILE_OPS)\n+\t\tlist(APPEND SOURCES core/vfs.c)\n+\tendif()\n+\n+else()\n+\n+\t#\n+\t# libwebsockets.a with only SSPC pieces in\n+\t#\n+\t\n+\tlist(APPEND SOURCES\n+\t\tsecure-streams/serialized/client/sspc.c\n+\t\tsecure-streams/serialized/client/sspc-transport.c\n+\t\tsecure-streams/serialized/client/sspc-deserialize.c\n+\t\tcore-net/lws-dsh.c\n+\t\tcore-net/transport-mux-client.c\n+\t\tcore-net/transport-mux-common.c\n+\t)\n+\t\n endif()\n \n exports_to_parent_scope()\n-\ndiff --git a/lib/core/context.c b/lib/core/context.c\nindex 0656581..de0504c 100644\n--- a/lib/core/context.c\n+++ b/lib/core/context.c\n@@ -403,7 +403,7 @@ lws_create_context(const struct lws_context_creation_info *info)\n #endif\n \t\tsize \u003d sizeof(struct lws_context);\n #endif\n-#if !defined(LWS_PLAT_BAREMETAL)\n+#if !defined(LWS_PLAT_BAREMETAL) \u0026\u0026 defined(LWS_WITH_NETWORK)\n \tint n;\n #endif\n \tunsigned int lpf \u003d info-\u003efd_limit_per_thread;\n@@ -817,6 +817,18 @@ lws_create_context(const struct lws_context_creation_info *info)\n \tcontext-\u003ess_proxy_bind \u003d info-\u003ess_proxy_bind;\n \tcontext-\u003ess_proxy_port \u003d info-\u003ess_proxy_port;\n \tcontext-\u003ess_proxy_address \u003d info-\u003ess_proxy_address;\n+\n+\tif (info-\u003etxp_ops_ssproxy)\n+\t\tcontext-\u003etxp_ppath.ops_onw \u003d info-\u003etxp_ops_ssproxy;\n+\telse\n+\t\tcontext-\u003etxp_ppath.ops_onw \u003d \u0026txp_ops_ssproxy_wsi;\n+\tif (info-\u003etxp_ops_sspc)\n+\t\tcontext-\u003etxp_cpath.ops_onw \u003d info-\u003etxp_ops_sspc;\n+\telse\n+\t\tcontext-\u003etxp_cpath.ops_onw \u003d \u0026txp_ops_sspc_wsi;\n+\n+\tcontext-\u003etxp_ssproxy_info \u003d info-\u003etxp_ssproxy_info;\n+\n \tif (context-\u003ess_proxy_bind \u0026\u0026 context-\u003ess_proxy_address)\n \t\tlwsl_cx_notice(context, \u0022ss proxy bind '%s', port %d, ads '%s'\u0022,\n \t\t\tcontext-\u003ess_proxy_bind, context-\u003ess_proxy_port,\n@@ -1073,7 +1085,7 @@ lws_create_context(const struct lws_context_creation_info *info)\n \t}\n #endif\n \n-#if !defined(LWS_PLAT_BAREMETAL)\n+#if !defined(LWS_PLAT_BAREMETAL) \u0026\u0026 defined(LWS_WITH_NETWORK)\n \tn \u003d 0;\n #endif\n #if defined(LWS_WITH_NETWORK)\n@@ -1179,13 +1191,13 @@ lws_create_context(const struct lws_context_creation_info *info)\n #endif\n \n \tlwsl_cx_info(context, \u0022ctx: %5luB (%ld ctx + pt(%ld thr x %d)), \u0022\n-\t\t \u0022pt-fds: %d, fdmap: %d\u0022,\n+\t\t \u0022pt-fds: %d\u0022,\n \t\t (long)sizeof(struct lws_context) +\n \t\t (context-\u003ecount_threads * context-\u003ept_serv_buf_size),\n \t\t (long)sizeof(struct lws_context),\n \t\t (long)context-\u003ecount_threads,\n \t\t context-\u003ept_serv_buf_size,\n-\t\t context-\u003efd_limit_per_thread, n);\n+\t\t context-\u003efd_limit_per_thread);\n \n #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n \tlwsl_cx_info(context, \u0022 http: ah_data: %u, ah: %lu, max count %u\u0022,\n@@ -1371,7 +1383,8 @@ lws_create_context(const struct lws_context_creation_info *info)\n \t * if he's not saying he'll make his own vhosts later then act\n \t * compatibly and make a default vhost using the data in the info\n \t */\n-\tif (!lws_check_opt(info-\u003eoptions, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {\n+\tif (!lws_check_opt(info-\u003eoptions, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)\n+\t\t\t|| info-\u003epprotocols) {\n \t\tif (!lws_create_vhost(context, info) ||\n \t\t lws_fi(\u0026context-\u003efic, \u0022ctx_createfail_def_vh\u0022)) {\n \t\t\tlwsl_cx_err(context, \u0022Failed to create default vhost\u0022);\n@@ -1919,6 +1932,11 @@ next:\n \t\t */\n \n #if defined(LWS_WITH_NETWORK)\n+\n+#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)\n+\t\tlws_ss_proxy_destroy(context);\n+#endif\n+\n \t\tif (context-\u003eevent_loop_ops-\u003edestroy_context1) {\n \t\t\tlwsl_cx_info(context, \u0022do evlib destroy_context1 and wait\u0022);\n \t\t\tcontext-\u003eevent_loop_ops-\u003edestroy_context1(context);\ndiff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c\nindex 713066f..958d843 100644\n--- a/lib/core/libwebsockets.c\n+++ b/lib/core/libwebsockets.c\n@@ -195,6 +195,23 @@ lws_hex_random(struct lws_context *context, char *dest, size_t len)\n \treturn 0;\n }\n \n+#if defined(_DEBUG)\n+void\n+lws_assert_fourcc(uint32_t fourcc, uint32_t expected)\n+{\n+\tif (fourcc \u003d\u003d expected)\n+\t\treturn;\n+\n+\tlwsl_err(\u0022%s: fourcc mismatch, expected %c%c%c%c, saw %c%c%c%c\u005cn\u0022,\n+\t\t\t__func__, (int)(expected \u003e\u003e 24), (int)((expected \u003e\u003e 16) \u0026 0xff),\n+\t\t\t(int)((expected \u003e\u003e 8) \u0026 0xff),(int)( expected \u0026 0xff),\n+\t\t\t(int)(fourcc \u003e\u003e 24), (int)((fourcc \u003e\u003e 16) \u0026 0xff),\n+\t\t\t(int)((fourcc \u003e\u003e 8) \u0026 0xff), (int)(fourcc \u0026 0xff));\n+\n+\tassert(0);\n+}\n+#endif\n+\n #if !defined(LWS_PLAT_OPTEE) \u0026\u0026 !defined(LWS_PLAT_BAREMETAL)\n \n #if defined(LWS_WITH_FILE_OPS)\ndiff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h\nindex 07c29c4..71ed780 100644\n--- a/lib/core/private-lib-core.h\n+++ b/lib/core/private-lib-core.h\n@@ -120,22 +120,22 @@\n *\n */\n \n-#if defined(LWS_PLAT_BAREMETAL)\n-#else\n #if defined(LWS_PLAT_FREERTOS)\n #include \u0022private-lib-plat-freertos.h\u0022\n #else\n #if defined(WIN32) || defined(_WIN32)\n #include \u0022private-lib-plat-windows.h\u0022\n #else\n- #if defined(LWS_PLAT_OPTEE)\n- #include \u0022private-lib-plat.h\u0022\n+ #if defined(LWS_PLAT_BAREMETAL)\n #else\n- #include \u0022private-lib-plat-unix.h\u0022\n+ #if defined(LWS_PLAT_OPTEE)\n+ #include \u0022private-lib-plat.h\u0022\n+ #else\n+ #include \u0022private-lib-plat-unix.h\u0022\n+ #endif\n #endif\n #endif\n #endif\n-#endif\n \n /*\n *\n@@ -317,17 +317,11 @@ struct lws_ring {\n struct lws_protocols;\n struct lws;\n \n-#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)\n #include \u0022private-lib-secure-streams.h\u0022\n-#endif\n \n #if defined(LWS_WITH_NETWORK) /* network */\n #include \u0022private-lib-event-libs.h\u0022\n \n-#if defined(LWS_WITH_SECURE_STREAMS) || defined(LWS_WITH_SECURE_STREAMS_PROXY_API)\n-#include \u0022private-lib-secure-streams.h\u0022\n-#endif\n-\n #if defined(LWS_WITH_SYS_SMD)\n #include \u0022private-lib-system-smd.h\u0022\n #endif\n@@ -338,18 +332,15 @@ struct lws;\n \n #include \u0022private-lib-system-metrics.h\u0022\n \n-\n struct lws_foreign_thread_pollfd {\n \tstruct lws_foreign_thread_pollfd *next;\n \tint fd_index;\n \tint _and;\n \tint _or;\n };\n-#endif /* network */\n \n-#if defined(LWS_WITH_NETWORK)\n #include \u0022private-lib-core-net.h\u0022\n-#endif\n+#endif /* network */\n \n struct lws_system_blob {\n \tunion {\n@@ -616,12 +607,18 @@ struct lws_context {\n \n #endif /* NETWORK */\n \n-\tlws_log_cx_t\t\t\t*log_cx;\n-\tconst char\t\t\t*name;\n+\tlws_log_cx_t\t\t\t\t*log_cx;\n+\tconst char\t\t\t\t*name;\n \n #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)\n-\tconst char\t*ss_proxy_bind;\n-\tconst char\t*ss_proxy_address;\n+\tconst char\t\t\t\t*ss_proxy_bind;\n+\tconst char\t\t\t\t*ss_proxy_address;\n+\n+\tlws_txp_path_proxy_t\t\t\ttxp_ppath;\n+\tlws_txp_path_client_t\t\t\ttxp_cpath;\n+\n+\tconst void\t\t\t\t*txp_ssproxy_info;\n+\n #endif\n \n #if defined(LWS_WITH_FILE_OPS)\n@@ -731,7 +728,7 @@ struct lws_context {\n \tuint16_t smd_queue_depth;\n #endif\n \n-#if defined(LWS_WITH_NETLINK)\n+#if defined(LWS_WITH_NETLINK) \u0026\u0026 defined(LWS_WITH_NETWORK)\n \tlws_route_uidx_t\t\t\troute_uidx;\n #endif\n \n@@ -980,6 +977,27 @@ void lws_msleep(unsigned int);\n void\n lws_context_destroy2(struct lws_context *context);\n \n+/* it's public extern const lws_transport_client_ops_t lws_txp_inside_sspc; */\n+\n+lws_transport_mux_ch_t *\n+lws_transport_mux_create_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i);\n+\n+lws_transport_mux_ch_t *\n+lws_transport_mux_add_channel(lws_transport_mux_t *tm, lws_transport_priv_t priv);\n+\n+void\n+lws_transport_mux_destroy_channel(lws_transport_mux_ch_t **_mc);\n+\n+lws_transport_mux_ch_t *\n+lws_transport_mux_get_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i);\n+\n+int\n+lws_transport_mux_next_free(lws_transport_mux_t *tm, lws_mux_ch_idx_t *result);\n+\n+\n+void\n+sul_ping_cb(lws_sorted_usec_list_t *sul);\n+\n #if !defined(PRIu64)\n #define PRIu64 \u0022llu\u0022\n #endif\ndiff --git a/lib/event-libs/libuv/CMakeLists.txt b/lib/event-libs/libuv/CMakeLists.txt\nindex fb810a8..28460b3 100644\n--- a/lib/event-libs/libuv/CMakeLists.txt\n+++ b/lib/event-libs/libuv/CMakeLists.txt\n@@ -83,3 +83,4 @@ CHECK_INCLUDE_FILE(uv-version.h LWS_HAVE_UV_VERSION_H)\n exports_to_parent_scope()\n set(LWS_HAVE_UV_VERSION_H ${LWS_HAVE_UV_VERSION_H} PARENT_SCOPE)\n set(LWS_HAVE_NEW_UV_VERSION_H ${LWS_HAVE_NEW_UV_VERSION_H} PARENT_SCOPE)\n+set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIRS} PARENT_SCOPE)\ndiff --git a/lib/misc/CMakeLists.txt b/lib/misc/CMakeLists.txt\nindex a305381..c4d0c3b 100644\n--- a/lib/misc/CMakeLists.txt\n+++ b/lib/misc/CMakeLists.txt\n@@ -30,7 +30,7 @@\n # and keep everything else private\n \n include_directories(.)\n-\n+if (NOT LWS_ONLY_SSPC)\n list(APPEND SOURCES\n \tmisc/base64-decode.c\n \tmisc/prng.c\n@@ -129,6 +129,7 @@ if (NOT WIN32 AND NOT LWS_WITHOUT_DAEMONIZE)\n \t\tmisc/daemonize.c)\n endif()\n \n+endif()\n #\n # Keep explicit parent scope exports at end\n #\ndiff --git a/lib/misc/lecp.c b/lib/misc/lecp.c\nindex 4783241..df9cf12 100644\n--- a/lib/misc/lecp.c\n+++ b/lib/misc/lecp.c\n@@ -1242,7 +1242,7 @@ enum lws_lec_pctx_ret\n lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *fmt, va_list args)\n {\n \tsize_t fl \u003d strlen(fmt);\n-\tuint64_t u64;\n+\tuint64_t u64 \u003d 0;\n \tint64_t i64;\n #if defined(LWS_WITH_CBOR_FLOAT)\n \tdouble dbl;\n@@ -1450,6 +1450,9 @@ lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *fmt, va_list args)\n \t\t\t\t\ti64 \u003d (int64_t)va_arg(args, long long);\n \t\t\t\t\tctx-\u003evaa[ctx-\u003evaa_pos++] \u003d NATTYPE_LONG_LONG;\n \t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\ti64 \u003d 0;\n+\t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t\tif (i64 \u003c 0)\n \t\t\t\t\tlws_lec_int(ctx,\n@@ -1474,6 +1477,9 @@ lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *fmt, va_list args)\n \t\t\t\t\tu64 \u003d (uint64_t)va_arg(args, unsigned long long);\n \t\t\t\t\tctx-\u003evaa[ctx-\u003evaa_pos++] \u003d NATTYPE_LONG_LONG;\n \t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\ti64 \u003d 0;\n+\t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t\tlws_lec_int(ctx, LWS_CBOR_MAJTYP_UINT, 0, u64);\n \t\t\t\tbreak;\n@@ -1518,6 +1524,9 @@ lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *fmt, va_list args)\n \t\t\t\t\tctx-\u003eitem.u.u64 \u003d (uint64_t)va_arg(args, long long);\n \t\t\t\t\tctx-\u003evaa[ctx-\u003evaa_pos++] \u003d NATTYPE_LONG_LONG;\n \t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\ti64 \u003d 0;\n+\t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t\tctx-\u003eitem.opcode \u003d LWS_CBOR_MAJTYP_UINT;\n \t\t\t\tctx-\u003efmt_pos++;\ndiff --git a/lib/secure-streams/CMakeLists.txt b/lib/secure-streams/CMakeLists.txt\nindex 2513e3b..66237c3 100644\n--- a/lib/secure-streams/CMakeLists.txt\n+++ b/lib/secure-streams/CMakeLists.txt\n@@ -60,14 +60,17 @@ if (LWS_WITH_CLIENT)\n \n \t\tif (LWS_WITH_SECURE_STREAMS_PROXY_API)\n \t\t\tlist(APPEND SOURCES\n-\t\t\t\tsecure-streams/secure-streams-serialize.c\n-\t\t\t\tsecure-streams/secure-streams-client.c\n-\t\t\t)\n-\t\tendif()\n-\n-\t\tif (LWS_WITH_SECURE_STREAMS_PROXY_API)\n-\t\t\tlist(APPEND SOURCES\n-\t\t\t\tsecure-streams/secure-streams-process.c\n+\t\t\t\tsecure-streams/serialized/proxy/proxy.c\n+\t\t\t\tsecure-streams/serialized/proxy/proxy-transport.c\n+\t\t\t\tsecure-streams/serialized/proxy/proxy-transport-wsi.c\n+\t\t\t\tsecure-streams/serialized/proxy/proxy-deserialize.c\n+\t\t\t\tsecure-streams/serialized/client/sspc.c\n+\t\t\t\tsecure-streams/serialized/client/sspc-transport.c\n+\t\t\t\tsecure-streams/serialized/client/sspc-transport-wsi.c\n+\t\t\t\tsecure-streams/serialized/client/sspc-deserialize.c\n+\t\t\t\tcore-net/transport-mux-client.c\n+\t\t\t\tcore-net/transport-mux-common.c\n+\t\t\t\tcore-net/transport-mux-proxy.c\n \t\t\t)\n \t\tendif()\n \ndiff --git a/lib/secure-streams/private-lib-secure-streams.h b/lib/secure-streams/private-lib-secure-streams.h\nindex 9c0ac53..dd01bdb 100644\n--- a/lib/secure-streams/private-lib-secure-streams.h\n+++ b/lib/secure-streams/private-lib-secure-streams.h\n@@ -28,6 +28,11 @@\n /* current SS Serialization protocol version */\n #define LWS_SSS_CLIENT_PROTOCOL_VERSION 1\n \n+#if defined(STANDALONE)\n+#define lws_context lws_context_standalone\n+struct lws_context_standalone;\n+#endif\n+\n /*\n * Secure Stream state\n */\n@@ -42,7 +47,7 @@ typedef enum {\n \tSSSEQ_CONNECTED,\n } lws_ss_seq_state_t;\n \n-struct conn;\n+struct lws_sss_proxy_conn;\n \n /**\n * lws_ss_handle_t: publicly-opaque secure stream object implementation\n@@ -74,7 +79,7 @@ typedef struct lws_ss_handle {\n \tstruct lws_sequencer\t*seq;\t /**\u003c owning sequencer if any */\n \tstruct lws\t\t*wsi;\t /**\u003c the stream wsi if any */\n \n-\tstruct conn\t\t*conn_if_sspc_onw;\n+\tstruct lws_sss_proxy_conn *conn_if_sspc_onw;\n \n #if defined(LWS_WITH_SSPLUGINS)\n \tvoid\t\t\t*nauthi; /**\u003c the nauth plugin instance data */\n@@ -235,6 +240,66 @@ enum {\n \tKIND_SS_TO_P,\n };\n \n+typedef enum {\n+\tRPAR_TYPE,\n+\tRPAR_LEN_MSB,\n+\tRPAR_LEN_LSB,\n+\n+\tRPAR_FLAG_B3,\n+\tRPAR_FLAG_B2,\n+\tRPAR_FLAG_B1,\n+\tRPAR_FLAG_B0,\n+\n+\tRPAR_LATA3,\n+\tRPAR_LATA2,\n+\tRPAR_LATA1,\n+\tRPAR_LATA0,\n+\n+\tRPAR_LATB7,\n+\tRPAR_LATB6,\n+\tRPAR_LATB5,\n+\tRPAR_LATB4,\n+\tRPAR_LATB3,\n+\tRPAR_LATB2,\n+\tRPAR_LATB1,\n+\tRPAR_LATB0,\n+\n+\tRPAR_RIDESHARE_LEN,\n+\tRPAR_RIDESHARE,\n+\n+\tRPAR_PERF,\n+\n+\tRPAR_RESULT_CREATION_DSH,\n+\tRPAR_RESULT_CREATION_RIDESHARE,\n+\n+\tRPAR_METADATA_NAMELEN,\n+\tRPAR_METADATA_NAME,\n+\tRPAR_METADATA_VALUE,\n+\n+\tRPAR_PAYLOAD,\n+\n+\tRPAR_RX_TXCR_UPDATE,\n+\n+\tRPAR_STREAMTYPE,\n+\tRPAR_INIT_PROVERS,\n+\tRPAR_INIT_PID,\n+\tRPAR_INITTXC0,\n+\n+\tRPAR_TXCR0,\n+\n+\tRPAR_TIMEOUT0,\n+\n+\tRPAR_PAYLEN0,\n+\n+\tRPAR_RESULT_CREATION,\n+\n+\tRPAR_STATEINDEX,\n+\tRPAR_ORD3,\n+\tRPAR_ORD2,\n+\tRPAR_ORD1,\n+\tRPAR_ORD0,\n+} rx_parser_t;\n+\n struct lws_ss_serialization_parser {\n \tchar\t\t\tstreamtype[32];\n \tchar\t\t\trideshare[32];\n@@ -297,6 +362,14 @@ enum {\n \tLWSSSPC_ONW_CONN,\n };\n \n+typedef struct ss_proxy_onward {\n+\tlws_ss_handle_t \t *ss;\n+\tstruct lws_sss_proxy_conn *conn;\n+} ss_proxy_t;\n+\n+extern const lws_transport_client_ops_t txp_ops_sspc_wsi;\n+extern const lws_transport_proxy_ops_t txp_ops_ssproxy_wsi;\n+\n typedef struct lws_sspc_handle {\n \tchar\t\t\trideshare_list[128];\n \n@@ -305,6 +378,8 @@ typedef struct lws_sspc_handle {\n \tlws_ss_info_t\t\tssi;\n \tlws_sorted_usec_list_t\tsul_retry;\n \n+\tlws_txp_path_client_t\ttxp_path;\n+\n \tstruct lws_ss_serialization_parser parser;\n \n #if defined(LWS_WITH_SYS_FAULT_INJECTION)\n@@ -321,8 +396,6 @@ typedef struct lws_sspc_handle {\n \tlws_metrics_caliper_compose(cal_txn)\n #endif\n \n-\tstruct lws\t\t*cwsi;\n-\n \tstruct lws_dsh\t\t*dsh;\n \tstruct lws_context\t*context;\n \n@@ -419,21 +492,23 @@ extern const lws_ss_policy_t pol_smd;\n *\tLWSSSSRET_DESTROY_ME\n */\n int\n-lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,\n-\t\t\t struct lws_context *context,\n-\t\t\t struct lws_dsh *dsh, const uint8_t *cp, size_t len,\n-\t\t\t lws_ss_conn_states_t *state, void *parconn,\n-\t\t\t lws_ss_handle_t **pss, lws_ss_info_t *ssi, char client);\n+lws_ss_proxy_deserialize_parse(struct lws_ss_serialization_parser *par,\n+\t\t\t struct lws_context *context,\n+\t\t\t struct lws_dsh *dsh, const uint8_t *cp,\n+\t\t\t size_t len, lws_ss_conn_states_t *state,\n+\t\t\t void *parconn, lws_ss_handle_t **pss,\n+\t\t\t lws_ss_info_t *ssi);\n int\n-lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf,\n-\t\t\t size_t len, int flags, const char *rsp);\n+lws_sspc_deserialize_parse(lws_sspc_handle_t *hh, const uint8_t *cp, size_t len,\n+\t\t\t lws_ss_handle_t **pss);\n+\n int\n lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,\n \t\t\t lws_ss_tx_ordinal_t ord, uint8_t *buf,\n \t\t\t size_t *len, int *flags);\n-int\n-lws_ss_serialize_state(struct lws *wsi, struct lws_dsh *dsh, lws_ss_constate_t state,\n-\t\t lws_ss_tx_ordinal_t ack);\n+\n+void\n+lws_sspc_sul_retry_cb(lws_sorted_usec_list_t *sul);\n \n const lws_ss_policy_t *\n lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype);\n@@ -474,9 +549,6 @@ void\n ss_proxy_onward_txcr(void *userobj, int bump);\n \n int\n-lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr);\n-\n-int\n lws_ss_sys_auth_api_amazon_com(struct lws_context *context);\n \n lws_ss_metadata_t *\n@@ -601,18 +673,47 @@ lws_conmon_ss_json(lws_ss_handle_t *h);\n void\n ss_proxy_onward_link_req_writeable(lws_ss_handle_t *h_onward);\n \n-struct conn {\n+#define LWS_PROXY_CONN_MAGIC LWS_FOURCC('C', 'o', 'N', 'N')\n+#define assert_is_conn(_conn) lws_assert_fourcc(_conn-\u003emagic, LWS_PROXY_CONN_MAGIC)\n+\n+struct lws_sss_proxy_conn {\n+#if defined(_DEBUG)\n+\tuint32_t\t\tmagic;\n+#endif\n \tstruct lws_ss_serialization_parser parser;\n \n \tlws_dsh_t\t\t*dsh;\t/* unified buffer for both sides */\n-\tstruct lws\t\t*wsi;\t/* the proxy's client side */\n+\tlws_txp_path_proxy_t\ttxp_path;\n \tlws_ss_handle_t\t\t*ss;\t/* the onward, ss side */\n \n \tlws_ss_conn_states_t\tstate;\n+\tstruct lws_context\t*cx;\n \n \tchar\t\t\tonward_in_flow_control;\n };\n \n+/*\n+ * Handlers for onward SS that divert the events and data into serialized\n+ * secure streams proxy.\n+ */\n+\n+lws_ss_state_return_t\n+lws_sss_proxy_onward_state(void *userobj, void *sh, lws_ss_constate_t state,\n+\t\t\t lws_ss_tx_ordinal_t ack);\n+\n+lws_ss_state_return_t\n+lws_sss_proxy_onward_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,\n+\t\t\tsize_t *len, int *flags);\n+\n+lws_ss_state_return_t\n+lws_sss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags);\n+\n+void\n+lws_transport_set_link(lws_transport_mux_t *tm, int link_state);\n+\n+lws_ss_state_return_t\n+lws_ss_proxy_destroy(struct lws_context *cx);\n+\n extern const struct ss_pcols ss_pcol_h1;\n extern const struct ss_pcols ss_pcol_h2;\n extern const struct ss_pcols ss_pcol_ws;\n@@ -625,3 +726,7 @@ extern const struct lws_protocols protocol_secstream_ws;\n extern const struct lws_protocols protocol_secstream_mqtt;\n extern const struct lws_protocols protocol_secstream_raw;\n \n+#if defined(STANDALONE)\n+#undef lws_context\n+#endif\n+#endif\ndiff --git a/lib/secure-streams/secure-streams-client.c b/lib/secure-streams/secure-streams-client.c\ndeleted file mode 100644\nindex 76ea952..0000000\n--- a/lib/secure-streams/secure-streams-client.c\n+++ /dev/null\n@@ -1,1109 +0,0 @@\n-/*\n- * lws-minimal-secure-streams-client\n- *\n- * Written in 2010-2020 by 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- *\n- * This client does not perform any INET networking... instead it opens a unix\n- * domain socket on a proxy that is listening for it, and that creates the\n- * actual secure stream connection.\n- *\n- * We are able to use the usual secure streams api in the client process, with\n- * payloads and connection state information proxied over the unix domain\n- * socket and fulfilled in the proxy process.\n- *\n- * The public client helper pieces are built as part of lws\n- */\n-#include \u003cprivate-lib-core.h\u003e\n-\n-extern const uint32_t ss_state_txn_validity[17];\n-\n-int\n-lws_ss_check_next_state_sspc(lws_sspc_handle_t *ss, uint8_t *prevstate,\n-\t\t\t lws_ss_constate_t cs)\n-{\n-\tif (cs \u003e\u003d LWSSSCS_USER_BASE || cs \u003d\u003d LWSSSCS_EVENT_WAIT_CANCELLED)\n-\t\t/*\n-\t\t * we can't judge user or transient states, leave the old state\n-\t\t * and just wave them through\n-\t\t */\n-\t\treturn 0;\n-\n-\tif (cs \u003e\u003d LWS_ARRAY_SIZE(ss_state_txn_validity)) {\n-\t\t/* we don't recognize this state as usable */\n-\t\tlwsl_sspc_err(ss, \u0022bad new state %u\u0022, cs);\n-\t\tassert(0);\n-\t\treturn 1;\n-\t}\n-\n-\tif (*prevstate \u003e\u003d LWS_ARRAY_SIZE(ss_state_txn_validity)) {\n-\t\t/* existing state is broken */\n-\t\tlwsl_sspc_err(ss, \u0022bad existing state %u\u0022,\n-\t\t\t\t(unsigned int)*prevstate);\n-\t\tassert(0);\n-\t\treturn 1;\n-\t}\n-\n-\tif (ss_state_txn_validity[*prevstate] \u0026 (1u \u003c\u003c cs)) {\n-\n-\t\tlwsl_sspc_notice(ss, \u0022%s -\u003e %s\u0022,\n-\t\t\t lws_ss_state_name((int)*prevstate),\n-\t\t\t lws_ss_state_name((int)cs));\n-\n-\t\t/* this is explicitly allowed, update old state to new */\n-\t\t*prevstate \u003d (uint8_t)cs;\n-\n-\t\treturn 0;\n-\t}\n-\n-\tlwsl_sspc_err(ss, \u0022transition from %s -\u003e %s is illegal\u0022,\n-\t\t lws_ss_state_name((int)*prevstate),\n-\t\t lws_ss_state_name((int)cs));\n-\n-\tassert(0);\n-\n-\treturn 1;\n-}\n-\n-lws_ss_state_return_t\n-lws_sspc_event_helper(lws_sspc_handle_t *h, lws_ss_constate_t cs,\n-\t\t lws_ss_tx_ordinal_t flags)\n-{\n-\tlws_ss_state_return_t ret;\n-\n-\tif (!h)\n-\t\treturn LWSSSSRET_OK;\n-\n-\tif (lws_ss_check_next_state_sspc(h, \u0026h-\u003eprev_ss_state, cs))\n-\t\treturn LWSSSSRET_DESTROY_ME;\n-\n-\tif (!h-\u003essi.state)\n-\t\treturn LWSSSSRET_OK;\n-\n-\th-\u003eh_in_svc \u003d h;\n-\tret \u003d h-\u003essi.state((void *)((uint8_t *)\u0026h[1]), NULL, cs, flags);\n-\th-\u003eh_in_svc \u003d NULL;\n-\n-\treturn ret;\n-}\n-\n-static void\n-lws_sspc_sul_retry_cb(lws_sorted_usec_list_t *sul)\n-{\n-\tlws_sspc_handle_t *h \u003d lws_container_of(sul, lws_sspc_handle_t, sul_retry);\n-\tstatic struct lws_client_connect_info i;\n-\n-\t/*\n-\t * We may have started up before the system proxy, so be prepared with\n-\t * a sul to retry at 1Hz\n-\t */\n-\n-\tmemset(\u0026i, 0, sizeof i);\n-\ti.context \u003d h-\u003econtext;\n-\tif (h-\u003econtext-\u003ess_proxy_port) { /* tcp */\n-\t\ti.address \u003d h-\u003econtext-\u003ess_proxy_address;\n-\t\ti.port \u003d h-\u003econtext-\u003ess_proxy_port;\n-\t\ti.iface \u003d h-\u003econtext-\u003ess_proxy_bind;\n-\t} else {\n-\t\tif (h-\u003econtext-\u003ess_proxy_bind)\n-\t\t\ti.address \u003d h-\u003econtext-\u003ess_proxy_bind;\n-\t\telse\n-#if defined(__linux__)\n-\t\t\ti.address \u003d \u0022+@proxy.ss.lws\u0022;\n-#else\n-\t\t\ti.address \u003d \u0022+/tmp/proxy.ss.lws\u0022;\n-#endif\n-\t}\n-\ti.host \u003d i.address;\n-\ti.origin \u003d i.address;\n-\ti.method \u003d \u0022RAW\u0022;\n-\ti.protocol \u003d lws_sspc_protocols[0].name;\n-\ti.local_protocol_name \u003d lws_sspc_protocols[0].name;\n-\ti.path \u003d \u0022\u0022;\n-\ti.pwsi \u003d \u0026h-\u003ecwsi;\n-\ti.opaque_user_data \u003d (void *)h;\n-\ti.ssl_connection \u003d LCCSCF_SECSTREAM_PROXY_LINK;\n-\n-\tlws_metrics_caliper_bind(h-\u003ecal_txn, h-\u003econtext-\u003emt_ss_cliprox_conn);\n-#if defined(LWS_WITH_SYS_METRICS)\n-\tlws_metrics_tag_add(\u0026h-\u003ecal_txn.mtags_owner, \u0022ss\u0022, h-\u003essi.streamtype);\n-#endif\n-\n-\t/* this wsi is the link to the proxy */\n-\n-\tif (!lws_client_connect_via_info(\u0026i)) {\n-\n-#if defined(LWS_WITH_SYS_METRICS)\n-\t\t/*\n-\t\t * If any hanging caliper measurement, dump it, and free any tags\n-\t\t */\n-\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n-#endif\n-\n-\t\tlws_sul_schedule(h-\u003econtext, 0, \u0026h-\u003esul_retry,\n-\t\t\t\t lws_sspc_sul_retry_cb, LWS_US_PER_SEC);\n-\n-\t\treturn;\n-\t}\n-\n-\tlwsl_sspc_notice(h, \u0022%s\u0022, h-\u003ecwsi-\u003elc.gutag);\n-}\n-\n-static int\n-lws_sspc_serialize_metadata(lws_sspc_handle_t *h, lws_sspc_metadata_t *md,\n-\t\t\t\tuint8_t *p, uint8_t *end)\n-{\n-\tint n, txc;\n-\n-\tif (md-\u003ename[0] \u003d\u003d '\u005c0') {\n-\n-\t\tlwsl_info(\u0022sending tx credit update %d\u005cn\u0022,\n-\t\t\t\tmd-\u003etx_cr_adjust);\n-\n-\t\tp[0] \u003d LWSSS_SER_TXPRE_TXCR_UPDATE;\n-\t\tlws_ser_wu16be(\u0026p[1], 4);\n-\t\tlws_ser_wu32be(\u0026p[3], (uint32_t)md-\u003etx_cr_adjust);\n-\n-\t\tn \u003d 7;\n-\n-\t} else {\n-\n-\t\tlwsl_sspc_info(h, \u0022sending metadata\u0022);\n-\n-\t\tp[0] \u003d LWSSS_SER_TXPRE_METADATA;\n-\t\ttxc \u003d (int)strlen(md-\u003ename);\n-\t\tn \u003d txc + 1 + (int)md-\u003elen;\n-\t\tif (n \u003e 0xffff)\n-\t\t\t/* we can't serialize this metadata in 16b length */\n-\t\t\treturn -1;\n-\t\tif (n \u003e lws_ptr_diff(end, \u0026p[4]))\n-\t\t\t/* we don't have space for this metadata */\n-\t\t\treturn -1;\n-\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)n);\n-\t\tp[3] \u003d (uint8_t)txc;\n-\t\tmemcpy(\u0026p[4], md-\u003ename, (unsigned int)txc);\n-\t\tmemcpy(\u0026p[4 + txc], \u0026md[1], md-\u003elen);\n-\t\tn \u003d 4 + txc + (int)md-\u003elen;\n-\t}\n-\n-\tlws_dll2_remove(\u0026md-\u003elist);\n-\tlws_free(md);\n-\n-\treturn n;\n-}\n-\n-static int\n-callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,\n-\t\t void *user, void *in, size_t len)\n-{\n-\tlws_sspc_handle_t *h \u003d (lws_sspc_handle_t *)lws_get_opaque_user_data(wsi);\n-\tsize_t pktsize \u003d wsi-\u003ea.context-\u003emax_http_header_data;\n-\tvoid *m \u003d (void *)((uint8_t *)\u0026h[1]);\n-\tuint8_t *pkt \u003d NULL, *p \u003d NULL, *end \u003d NULL;\n-\tlws_ss_state_return_t r;\n-\tuint64_t interval;\n-\tconst uint8_t *cp;\n-\tuint8_t s[64];\n-\tlws_usec_t us;\n-\tint flags, n;\n-\n-\tswitch (reason) {\n-\n-\tcase LWS_CALLBACK_CONNECTING:\n-\t\t/*\n-\t\t * In our particular case, we want CCEs even inside the\n-\t\t * initial connect loop time\n-\t\t */\n-\t\twsi-\u003eclient_suppress_CONNECTION_ERROR \u003d 0;\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_CLIENT_CONNECTION_ERROR:\n-\t\tlwsl_warn(\u0022%s: CCE: %s\u005cn\u0022, __func__,\n-\t\t\t in ? (const char *)in : \u0022null\u0022);\n-#if defined(LWS_WITH_SYS_METRICS)\n-\t\t/*\n-\t\t * If any hanging caliper measurement, dump it, and free any tags\n-\t\t */\n-\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n-#endif\n-\t\tlws_set_opaque_user_data(wsi, NULL);\n-\t\th-\u003ecwsi \u003d NULL;\n-\t\tlws_sul_schedule(h-\u003econtext, 0, \u0026h-\u003esul_retry,\n-\t\t\t\t lws_sspc_sul_retry_cb, LWS_US_PER_SEC);\n-\t\tif (h-\u003essi.state) {\n-\t\t\tinterval \u003d (uint64_t)(lws_now_usecs() - h-\u003eus_start_upstream) /\n-\t\t\t\t\t\t\t\tLWS_US_PER_MS;\n-\t\t\tif (interval \u003e 0xffffffffull)\n-\t\t\t\tinterval \u003d 0xffffffffull;\n-\t\t\tr \u003d h-\u003essi.state(lws_sspc_to_user_object(h), NULL,\n-\t\t\t\t\t LWSSSCS_UPSTREAM_LINK_RETRY,\n-\t\t\t\t\t (uint32_t)interval);\n-\t\t\tif (r \u003d\u003d LWSSSSRET_DESTROY_ME)\n-\t\t\t\tlws_sspc_destroy(\u0026h);\n-\t\t}\n-\t\tbreak;\n-\n- case LWS_CALLBACK_RAW_CONNECTED:\n-\t\tif (!h || lws_fi(\u0026h-\u003efic, \u0022sspc_fail_on_linkup\u0022))\n-\t\t\treturn -1;\n-\t\tlwsl_sspc_info(h, \u0022CONNECTED (%s)\u0022, h-\u003essi.streamtype);\n-\n-\t\th-\u003estate \u003d LPCSCLI_SENDING_INITIAL_TX;\n-\t\t/*\n-\t\t * We create the dsh at the response to the initial tx, which\n-\t\t * will let us know the policy's max size for it... let's\n-\t\t * protect the connection with a promise to complete the\n-\t\t * SS serialization streamtype negotation within a short period,\n-\t\t * we will cancel this timeout when we have the proxy's ack\n-\t\t * of the streamtype serialization, eg, it exists in the proxy\n-\t\t * policy etc\n-\t\t */\n-\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);\n-\t\tlws_callback_on_writable(wsi);\n-\t\th-\u003eus_start_upstream \u003d 0;\n- break;\n-\n-\tcase LWS_CALLBACK_RAW_CLOSE:\n-\t\t/*\n-\t\t * our ss proxy Unix Domain socket has closed...\n-\t\t */\n-\t\tif (!h) {\n-\t\t\tlwsl_info(\u0022%s: no sspc on client proxy link close\u0022, __func__);\n-\t\t\tbreak;\n-\t\t}\n-\t\tlwsl_sspc_info(h, \u0022LWS_CALLBACK_RAW_CLOSE: proxy conn down, wsi %s\u0022,\n-\t\t\t\tlws_wsi_tag(wsi));\n-\n-\t\tlws_dsh_destroy(\u0026h-\u003edsh);\n-\t\tif (h-\u003ess_dangling_connected \u0026\u0026 h-\u003essi.state) {\n-\n-\t\t\tlwsl_sspc_notice(h, \u0022setting _DISCONNECTED\u0022);\n-\t\t\th-\u003ess_dangling_connected \u003d 0;\n-\t\t\th-\u003eprev_ss_state \u003d LWSSSCS_DISCONNECTED;\n-\t\t\tr \u003d h-\u003essi.state(ss_to_userobj(h), NULL,\n-\t\t\t\t\t\t LWSSSCS_DISCONNECTED, 0);\n-\t\t\tif (r \u003d\u003d LWSSSSRET_DESTROY_ME) {\n-\t\t\t\th-\u003ecwsi \u003d NULL;\n-\t\t\t\tlws_set_opaque_user_data(wsi, NULL);\n-\t\t\t\tlws_sspc_destroy(\u0026h);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\n-\t\th-\u003ecwsi \u003d NULL;\n-\t\t/*\n-\t\t * schedule a reconnect in 1s\n-\t\t */\n-\t\tlws_sul_schedule(h-\u003econtext, 0, \u0026h-\u003esul_retry,\n-\t\t\t\t lws_sspc_sul_retry_cb, LWS_US_PER_SEC);\n-\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_RAW_RX:\n-\t\t/*\n-\t\t * ie, the proxy has sent us something\n-\t\t */\n-\n-\t\tif (!h || !h-\u003ecwsi) {\n-\t\t\tlwsl_info(\u0022%s: rx when client ss destroyed\u005cn\u0022, __func__);\n-\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\tlwsl_sspc_info(h, \u0022%s: RAW_RX: rx %d\u005cn\u0022, __func__, (int)len);\n-\n-\t\tif (!len) {\n-\t\t\tlwsl_sspc_notice(h, \u0022RAW_RX: zero len\u0022);\n-\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_fake_rxparse_disconnect_me\u0022))\n-\t\t\tn \u003d LWSSSSRET_DISCONNECT_ME;\n-\t\telse\n-\t\t\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_fake_rxparse_destroy_me\u0022))\n-\t\t\t\tn \u003d LWSSSSRET_DESTROY_ME;\n-\t\t\telse\n-\t\t\t\tn \u003d lws_ss_deserialize_parse(\u0026h-\u003eparser,\n-\t\t\t\t\t\t\t lws_get_context(wsi),\n-\t\t\t\t\t\t\t h-\u003edsh, in, len,\n-\t\t\t\t\t\t\t \u0026h-\u003estate, h,\n-\t\t\t\t\t\t\t (lws_ss_handle_t **)m,\n-\t\t\t\t\t\t\t \u0026h-\u003essi, 1);\n-\t\tswitch (n) {\n-\t\tcase LWSSSSRET_OK:\n-\t\t\tbreak;\n-\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\tlwsl_info(\u0022%s: proxlicent RX ended with DISCONNECT_ME\u005cn\u0022,\n-\t\t\t\t\t__func__);\n-\t\t\treturn -1;\n-\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\tlwsl_info(\u0022%s: proxlicent RX ended with DESTROY_ME\u005cn\u0022,\n-\t\t\t\t\t__func__);\n-\t\t\tlws_set_opaque_user_data(wsi, NULL);\n-\t\t\tlws_sspc_destroy(\u0026h);\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED ||\n-\t\t h-\u003estate \u003d\u003d LPCSCLI_ONWARD_CONNECT)\n-\t\t\tlws_set_timeout(wsi, 0, 0);\n-\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_RAW_WRITEABLE:\n-\n-\t\t/*\n-\t\t * We can transmit something to the proxy...\n-\t\t */\n-\n-\t\tif (!h)\n-\t\t\tbreak;\n-\n-\t\tlwsl_sspc_debug(h, \u0022WRITEABLE %s, state %d\u0022,\n-\t\t\t\twsi-\u003elc.gutag, h-\u003estate);\n-\n-\t\t/*\n-\t\t * Management of ss timeout can happen any time and doesn't\n-\t\t * depend on wsi existence or state\n-\t\t */\n-\n-\t\tn \u003d 0;\n-\t\tcp \u003d s;\n-\n-\t\tif (h-\u003epending_timeout_update) {\n-\t\t\ts[0] \u003d LWSSS_SER_TXPRE_TIMEOUT_UPDATE;\n-\t\t\ts[1] \u003d 0;\n-\t\t\ts[2] \u003d 4;\n-\t\t\t/*\n-\t\t\t * 0: use policy timeout value\n-\t\t\t * 0xffffffff: cancel the timeout\n-\t\t\t */\n-\t\t\tlws_ser_wu32be(\u0026s[3], h-\u003etimeout_ms);\n-\t\t\t/* in case anything else to write */\n-\t\t\tlws_callback_on_writable(h-\u003ecwsi);\n-\t\t\th-\u003epending_timeout_update \u003d 0;\n-\t\t\tn \u003d 7;\n-\t\t\tgoto do_write;\n-\t\t}\n-\n-\t\ts[1] \u003d 0;\n-\t\t/*\n-\t\t * This is the state of the link that connects us to the onward\n-\t\t * proxy\n-\t\t */\n-\t\tswitch (h-\u003estate) {\n-\t\tcase LPCSCLI_SENDING_INITIAL_TX:\n-\t\t\t/*\n-\t\t\t * We are negotating the opening of a particular\n-\t\t\t * streamtype\n-\t\t\t */\n-\t\t\tn \u003d (int)strlen(h-\u003essi.streamtype) + 1 + 4 + 4;\n-\n-\t\t\ts[0] \u003d LWSSS_SER_TXPRE_STREAMTYPE;\n-\t\t\tlws_ser_wu16be(\u0026s[1], (uint16_t)n);\n-\t\t\t/* SSSv1: add protocol version byte (initially 1) */\n-\t\t\ts[3] \u003d (uint8_t)LWS_SSS_CLIENT_PROTOCOL_VERSION;\n-\t\t\tlws_ser_wu32be(\u0026s[4], (uint32_t)getpid());\n-\t\t\tlws_ser_wu32be(\u0026s[8], (uint32_t)h-\u003etxc.peer_tx_cr_est);\n-\t\t\t//h-\u003etxcr_out \u003d txc;\n-\t\t\tlws_strncpy((char *)\u0026s[12], h-\u003essi.streamtype, sizeof(s) - 12);\n-\t\t\tn +\u003d 3;\n-\t\t\th-\u003estate \u003d LPCSCLI_WAITING_CREATE_RESULT;\n-\n-\t\t\tbreak;\n-\n-\t\tcase LPCSCLI_LOCAL_CONNECTED:\n-\n-\t\t\t// lwsl_notice(\u0022%s: LPCSCLI_LOCAL_CONNECTED\u005cn\u0022, __func__);\n-\n-\t\t\t/*\n-\t\t\t * Do we need to prioritize sending any metadata\n-\t\t\t * changes?\n-\t\t\t */\n-\n-\t\t\tif (h-\u003emetadata_owner.count) {\n-\t\t\t\tlws_sspc_metadata_t *md \u003d lws_container_of(\n-\t\t\t\t\tlws_dll2_get_tail(\u0026h-\u003emetadata_owner),\n-\t\t\t\t\tlws_sspc_metadata_t, list);\n-\n-\t\t\t\tpkt \u003d lws_malloc(pktsize + LWS_PRE, __func__);\n-\t\t\t\tif (!pkt)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tcp \u003d p \u003d pkt + LWS_PRE;\n-\t\t\t\tend \u003d p + pktsize;\n-\n-\t\t\t\tn \u003d lws_sspc_serialize_metadata(h, md, p, end);\n-\t\t\t\tif (n \u003c 0)\n-\t\t\t\t\tgoto metadata_hangup;\n-\n-\t\t\t\tlwsl_sspc_debug(h, \u0022(local_conn) metadata\u0022);\n-\n-\t\t\t\tgoto req_write_and_issue;\n-\t\t\t}\n-\n-\t\t\tif (h-\u003epending_writeable_len) {\n-\t\t\t\tlwsl_sspc_debug(h, \u0022(local_conn) PAYLOAD_LENGTH_HINT %u\u0022,\n-\t\t\t\t\t (unsigned int)h-\u003ewriteable_len);\n-\t\t\t\ts[0] \u003d LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT;\n-\t\t\t\tlws_ser_wu16be(\u0026s[1], 4);\n-\t\t\t\tlws_ser_wu32be(\u0026s[3], (uint32_t)h-\u003ewriteable_len);\n-\t\t\t\th-\u003epending_writeable_len \u003d 0;\n-\t\t\t\tn \u003d 7;\n-\t\t\t\tgoto req_write_and_issue;\n-\t\t\t}\n-\n-\t\t\tif (h-\u003econn_req_state \u003e\u003d LWSSSPC_ONW_ONGOING) {\n-\t\t\t\tlwsl_sspc_info(h, \u0022conn_req_state %d\u0022,\n-\t\t\t\t\t\th-\u003econn_req_state);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tlwsl_sspc_info(h, \u0022(local_conn) onward connect\u0022);\n-\n-\t\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_ONGOING;\n-\n-\t\t\ts[0] \u003d LWSSS_SER_TXPRE_ONWARD_CONNECT;\n-\t\t\ts[1] \u003d 0;\n-\t\t\ts[2] \u003d 0;\n-\t\t\tn \u003d 3;\n-\t\t\tbreak;\n-\n-\t\tcase LPCSCLI_OPERATIONAL:\n-\n-\t\t\t/*\n-\t\t\t *\n-\t\t\t * - Do we need to prioritize sending any metadata\n-\t\t\t * changes? (includes txcr updates)\n-\t\t\t *\n-\t\t\t * - Do we need to forward a hint about the payload\n-\t\t\t * length?\n-\t\t\t */\n-\n-\t\t\tpkt \u003d lws_malloc(pktsize + LWS_PRE, __func__);\n-\t\t\tif (!pkt)\n-\t\t\t\tgoto hangup;\n-\t\t\tcp \u003d p \u003d pkt + LWS_PRE;\n-\t\t\tend \u003d p + pktsize;\n-\n-\t\t\tif (h-\u003emetadata_owner.count) {\n-\t\t\t\tlws_sspc_metadata_t *md \u003d lws_container_of(\n-\t\t\t\t\tlws_dll2_get_tail(\u0026h-\u003emetadata_owner),\n-\t\t\t\t\tlws_sspc_metadata_t, list);\n-\n-\t\t\t\tn \u003d lws_sspc_serialize_metadata(h, md, p, end);\n-\t\t\t\tif (n \u003c 0)\n-\t\t\t\t\tgoto metadata_hangup;\n-\n-\t\t\t\tgoto req_write_and_issue;\n-\t\t\t}\n-\n-\t\t\tif (h-\u003epending_writeable_len) {\n-\t\t\t\tlwsl_sspc_info(h, \u0022PAYLOAD_LENGTH_HINT %u\u0022,\n-\t\t\t\t\t (unsigned int)h-\u003ewriteable_len);\n-\t\t\t\ts[0] \u003d LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT;\n-\t\t\t\tlws_ser_wu16be(\u0026s[1], 4);\n-\t\t\t\tlws_ser_wu32be(\u0026s[3], (uint32_t)h-\u003ewriteable_len);\n-\t\t\t\th-\u003epending_writeable_len \u003d 0;\n-\t\t\t\tn \u003d 7;\n-\t\t\t\tgoto req_write_and_issue;\n-\t\t\t}\n-\n-\t\t\t/* we can't write anything if we don't have credit */\n-\t\t\tif (!h-\u003eignore_txc \u0026\u0026 h-\u003etxc.tx_cr \u003c\u003d 0) {\n-\t\t\t\tlwsl_sspc_info(h, \u0022WRITEABLE / OPERATIONAL:\u0022\n-\t\t\t\t\t \u0022 lack credit (%d)\u0022,\n-\t\t\t\t\t h-\u003etxc.tx_cr);\n-\t\t\t\t// break;\n-\t\t\t}\n-\n-\t\t\tlen \u003d pktsize - LWS_PRE - 19;\n-\t\t\tflags \u003d 0;\n-\t\t\tif (!h-\u003essi.tx) {\n-\t\t\t\tn \u003d 0;\n-\t\t\t\tgoto do_write_nz;\n-\t\t\t}\n-\n-\t\t\tn \u003d h-\u003essi.tx(m, h-\u003eord++, pkt + LWS_PRE + 19, \u0026len,\n-\t\t\t\t \u0026flags);\n-\t\t\tswitch (n) {\n-\t\t\tcase LWSSSSRET_TX_DONT_SEND:\n-\t\t\t\tn \u003d 0;\n-\t\t\t\tgoto do_write_nz;\n-\t\n-\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\t\tlwsl_notice(\u0022%s: sspc tx DISCONNECT/DESTROY unimplemented\u005cn\u0022, __func__);\n-\t\t\t\tbreak;\n-\t\t\tdefault:\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\th-\u003etxc.tx_cr \u003d h-\u003etxc.tx_cr - (int)len;\n-\n-\t\t\tcp \u003d p;\n-\t\t\tn \u003d (int)(len + 19);\n-\t\t\tus \u003d lws_now_usecs();\n-\t\t\tp[0] \u003d LWSSS_SER_TXPRE_TX_PAYLOAD;\n-\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)(len + 19 - 3));\n-\t\t\tlws_ser_wu32be(\u0026p[3], (uint32_t)flags);\n-\t\t\t/* time spent here waiting to send this */\n-\t\t\tlws_ser_wu32be(\u0026p[7], (uint32_t)(us - h-\u003eus_earliest_write_req));\n-\t\t\t/* ust that the client write happened */\n-\t\t\tlws_ser_wu64be(\u0026p[11], (uint64_t)us);\n-\t\t\th-\u003eus_earliest_write_req \u003d 0;\n-\n-\t\t\tif (flags \u0026 LWSSS_FLAG_EOM)\n-\t\t\t\tif (h-\u003ersidx + 1 \u003c (int)LWS_ARRAY_SIZE(h-\u003erideshare_ofs) \u0026\u0026\n-\t\t\t\t h-\u003erideshare_ofs[h-\u003ersidx + 1])\n-\t\t\t\t\th-\u003ersidx++;\n-\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tbreak;\n-\t\t}\n-\n-do_write_nz:\n-\n-\t\tif (!n)\n-\t\t\tbreak;\n-\n-do_write:\n-\t\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_link_write_fail\u0022))\n-\t\t\tn \u003d -1;\n-\t\telse\n-\t\t\tn \u003d lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);\n-\t\tif (n \u003c 0) {\n-\t\t\tlwsl_sspc_notice(h, \u0022WRITEABLE: %d\u0022, n);\n-\n-\t\t\tgoto hangup;\n-\t\t}\n-\t\tbreak;\n-\n-\tdefault:\n-\t\tbreak;\n-\t}\n-\n-\tlws_free(pkt);\n-\n-\treturn lws_callback_http_dummy(wsi, reason, user, in, len);\n-\n-metadata_hangup:\n-\tlwsl_sspc_err(h, \u0022metadata too large\u0022);\n-\n-hangup:\n-\tlws_free(pkt);\n-\tlwsl_warn(\u0022hangup\u005cn\u0022);\n-\t/* hang up on him */\n-\treturn -1;\n-\n-req_write_and_issue:\n-\t/* in case anything else to write */\n-\tlws_callback_on_writable(h-\u003ecwsi);\n-\tgoto do_write_nz;\n-}\n-\n-const struct lws_protocols lws_sspc_protocols[] \u003d {\n-\t{\n-\t\t\u0022ssproxy-protocol\u0022,\n-\t\tcallback_sspc_client,\n-\t\t0,\n-\t\t2048, 2048, NULL, 0\n-\t},\n-\t{ NULL, NULL, 0, 0, 0, NULL, 0 }\n-};\n-\n-int\n-lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,\n-\t void *opaque_user_data, lws_sspc_handle_t **ppss,\n-\t struct lws_sequencer *seq_owner, const char **ppayload_fmt)\n-{\n-\tlws_sspc_handle_t *h;\n-\tuint8_t *ua;\n-\tchar *p;\n-\n-\tlws_service_assert_loop_thread(context, tsi);\n-\n-\t/* allocate the handle (including ssi), the user alloc,\n-\t * and the streamname */\n-\n-\th \u003d malloc(sizeof(lws_sspc_handle_t) + ssi-\u003euser_alloc +\n-\t\t\t\tstrlen(ssi-\u003estreamtype) + 1);\n-\tif (!h)\n-\t\treturn 1;\n-\tmemset(h, 0, sizeof(*h));\n-\n-\th-\u003elc.log_cx \u003d context-\u003elog_cx;\n-\n-#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n-\th-\u003efic.name \u003d \u0022sspc\u0022;\n-\tlws_xos_init(\u0026h-\u003efic.xos, lws_xos(\u0026context-\u003efic.xos));\n-\tif (ssi-\u003efic.fi_owner.count)\n-\t\tlws_fi_import(\u0026h-\u003efic, \u0026ssi-\u003efic);\n-\n-\tlws_fi_inherit_copy(\u0026h-\u003efic, \u0026context-\u003efic, \u0022ss\u0022, ssi-\u003estreamtype);\n-#endif\n-\n-\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_create_oom\u0022)) {\n-\t\t/*\n-\t\t * We have to do this a litte later, so we can cleanly inherit\n-\t\t * the OOM pieces and drain the info fic\n-\t\t */\n-\t\tlws_fi_destroy(\u0026h-\u003efic);\n-\t\tfree(h);\n-\t\treturn 1;\n-\t}\n-\n-\t__lws_lc_tag(context, \u0026context-\u003elcg[LWSLCG_SSP_CLIENT], \u0026h-\u003elc,\n-\t\t\tssi-\u003estreamtype);\n-\n-\tmemcpy(\u0026h-\u003essi, ssi, sizeof(*ssi));\n-\tua \u003d (uint8_t *)\u0026h[1];\n-\tmemset(ua, 0, ssi-\u003euser_alloc);\n-\tp \u003d (char *)ua + ssi-\u003euser_alloc;\n-\tmemcpy(p, ssi-\u003estreamtype, strlen(ssi-\u003estreamtype) + 1);\n-\th-\u003essi.streamtype \u003d (const char *)p;\n-\th-\u003econtext \u003d context;\n-\th-\u003eus_start_upstream \u003d lws_now_usecs();\n-\n-\tif (!ssi-\u003emanual_initial_tx_credit)\n-\t\th-\u003etxc.peer_tx_cr_est \u003d 500000000;\n-\telse\n-\t\th-\u003etxc.peer_tx_cr_est \u003d ssi-\u003emanual_initial_tx_credit;\n-\n-\tif (!strcmp(ssi-\u003estreamtype, LWS_SMD_STREAMTYPENAME))\n-\t\th-\u003eignore_txc \u003d 1;\n-\n-\tlws_dll2_add_head(\u0026h-\u003eclient_list, \u0026context-\u003ept[tsi].ss_client_owner);\n-\n-\t/* fill in the things the real api does for the caller */\n-\n-\t*((void **)(ua + ssi-\u003eopaque_user_data_offset)) \u003d opaque_user_data;\n-\t*((void **)(ua + ssi-\u003ehandle_offset)) \u003d h;\n-\n-\tif (ppss)\n-\t\t*ppss \u003d h;\n-\n-\t/* try the actual connect */\n-\n-\tlws_sspc_sul_retry_cb(\u0026h-\u003esul_retry);\n-\n-\treturn 0;\n-}\n-\n-/* used on context destroy when iterating listed lws_ss on a pt */\n-\n-int\n-lws_sspc_destroy_dll(struct lws_dll2 *d, void *user)\n-{\n-\tlws_sspc_handle_t *h \u003d lws_container_of(d, lws_sspc_handle_t, client_list);\n-\n-\tlws_sspc_destroy(\u0026h);\n-\n-\treturn 0;\n-}\n-\n-void\n-lws_sspc_rxmetadata_destroy(lws_sspc_handle_t *h)\n-{\n-\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n-\t\t\tlws_dll2_get_head(\u0026h-\u003emetadata_owner_rx)) {\n-\t\tlws_sspc_metadata_t *md \u003d\n-\t\t\t\tlws_container_of(d, lws_sspc_metadata_t, list);\n-\n-\t\tlws_dll2_remove(\u0026md-\u003elist);\n-\t\tlws_free(md);\n-\n-\t} lws_end_foreach_dll_safe(d, d1);\n-}\n-\n-void\n-lws_sspc_destroy(lws_sspc_handle_t **ph)\n-{\n-\tlws_sspc_handle_t *h;\n-\n-\tif (!*ph)\n-\t\treturn;\n-\n-\th \u003d *ph;\n-\tif (h \u003d\u003d h-\u003eh_in_svc) {\n-\t\tlwsl_err(\u0022%s: illegal destroy, return LWSSSSRET_DESTROY_ME instead\u005cn\u0022,\n-\t\t\t\t__func__);\n-\t\tassert(0);\n-\t\treturn;\n-\t}\n-\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\n-\tif (h-\u003edestroying)\n-\t\treturn;\n-\n-\th-\u003edestroying \u003d 1;\n-\n-\t/* if this caliper is still dangling at destroy, we failed */\n-#if defined(LWS_WITH_SYS_METRICS)\n-\t/*\n-\t * If any hanging caliper measurement, dump it, and free any tags\n-\t */\n-\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n-#endif\n-\tif (h-\u003ess_dangling_connected \u0026\u0026 h-\u003essi.state) {\n-\t\tlws_sspc_event_helper(h, LWSSSCS_DISCONNECTED, 0);\n-\t\th-\u003ess_dangling_connected \u003d 0;\n-\t}\n-\n-#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n-\tlws_fi_destroy(\u0026h-\u003efic);\n-#endif\n-\n-\tlws_sul_cancel(\u0026h-\u003esul_retry);\n-\tlws_dll2_remove(\u0026h-\u003eclient_list);\n-\n-\tif (h-\u003edsh)\n-\t\tlws_dsh_destroy(\u0026h-\u003edsh);\n-\tif (h-\u003ecwsi) {\n-\t\tlws_set_opaque_user_data(h-\u003ecwsi, NULL);\n-\t\tlws_wsi_close(h-\u003ecwsi, LWS_TO_KILL_ASYNC);\n-\t\th-\u003ecwsi \u003d NULL;\n-\t}\n-\n-\t/* clean out any pending metadata changes that didn't make it */\n-\n-\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n-\t\t\tlws_dll2_get_head(\u0026(*ph)-\u003emetadata_owner)) {\n-\t\tlws_sspc_metadata_t *md \u003d\n-\t\t\t\tlws_container_of(d, lws_sspc_metadata_t, list);\n-\n-\t\tlws_dll2_remove(\u0026md-\u003elist);\n-\t\tlws_free(md);\n-\n-\t} lws_end_foreach_dll_safe(d, d1);\n-\n-\tlws_sspc_rxmetadata_destroy(h);\n-\n-\tlws_sspc_event_helper(h, LWSSSCS_DESTROYING, 0);\n-\t*ph \u003d NULL;\n-\n-\tlws_sul_cancel(\u0026h-\u003esul_retry);\n-\n-\n-\t/* confirm no sul left scheduled in handle or user allocation object */\n-\tlws_sul_debug_zombies(h-\u003econtext, h, sizeof(*h) + h-\u003essi.user_alloc,\n-\t\t\t __func__);\n-\n-\t__lws_lc_untag(h-\u003econtext, \u0026h-\u003elc);\n-\n-\tfree(h);\n-}\n-\n-lws_ss_state_return_t\n-lws_sspc_request_tx(lws_sspc_handle_t *h)\n-{\n-\tif (!h || !h-\u003ecwsi)\n-\t\treturn LWSSSSRET_OK;\n-\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\n-\tif (!h-\u003eus_earliest_write_req)\n-\t\th-\u003eus_earliest_write_req \u003d lws_now_usecs();\n-\n-\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n-\t h-\u003econn_req_state \u003d\u003d LWSSSPC_ONW_NONE)\n-\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_REQ;\n-\n-\tlws_callback_on_writable(h-\u003ecwsi);\n-\n-\treturn LWSSSSRET_OK;\n-}\n-\n-/*\n- * Currently we fulfil the writeable part locally by just enabling POLLOUT on\n- * the UDS link, without serialization footprint, which is reasonable as far as\n- * it goes.\n- *\n- * But for the ..._len() variant, the expected payload length hint we are being\n- * told is something that must be serialized to the onward peer, since either\n- * that guy or someone upstream of him is the guy who will compose the framing\n- * with it that actually goes out.\n- *\n- * This information is needed at the upstream guy before we have sent any\n- * payload, eg, for http POST, he has to prepare the content-length in the\n- * headers, before any payload. So we have to issue a serialization of the\n- * length at this point.\n- */\n-\n-lws_ss_state_return_t\n-lws_sspc_request_tx_len(lws_sspc_handle_t *h, unsigned long len)\n-{\n-\t/*\n-\t * for client conns, they cannot even complete creation of the handle\n-\t * without the onwared connection to the proxy, it's not legal to start\n-\t * using it until it's operation and has the onward connection (and the\n-\t * link has called CREATED state)\n-\t */\n-\n-\tif (!h)\n-\t\treturn LWSSSSRET_OK;\n-\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\n-\tlwsl_sspc_notice(h, \u0022setting writeable_len %u\u0022, (unsigned int)len);\n-\th-\u003ewriteable_len \u003d len;\n-\th-\u003epending_writeable_len \u003d 1;\n-\n-\tif (!h-\u003eus_earliest_write_req)\n-\t\th-\u003eus_earliest_write_req \u003d lws_now_usecs();\n-\n-\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n-\t h-\u003econn_req_state \u003d\u003d LWSSSPC_ONW_NONE)\n-\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_REQ;\n-\n-\t/*\n-\t * We're going to use this up with serializing h-\u003ewriteable_len... that\n-\t * will request again.\n-\t */\n-\n-\tif (h-\u003ecwsi)\n-\t\tlws_callback_on_writable(h-\u003ecwsi);\n-\n-\treturn LWSSSSRET_OK;\n-}\n-\n-int\n-lws_sspc_client_connect(lws_sspc_handle_t *h)\n-{\n-\tif (!h || h-\u003estate \u003d\u003d LPCSCLI_OPERATIONAL)\n-\t\treturn 0;\n-\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\n-\tassert(h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED);\n-\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n-\t h-\u003econn_req_state \u003d\u003d LWSSSPC_ONW_NONE)\n-\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_REQ;\n-\tif (h-\u003ecwsi)\n-\t\tlws_callback_on_writable(h-\u003ecwsi);\n-\n-\treturn 0;\n-}\n-\n-struct lws_context *\n-lws_sspc_get_context(struct lws_sspc_handle *h)\n-{\n-\treturn h-\u003econtext;\n-}\n-\n-const char *\n-lws_sspc_rideshare(struct lws_sspc_handle *h)\n-{\n-\t/*\n-\t * ...the serialized RX rideshare name if any...\n-\t */\n-\n-\tif (h-\u003eparser.rideshare[0]) {\n-\t\tlwsl_sspc_info(h, \u0022parser %s\u0022, h-\u003eparser.rideshare);\n-\n-\t\treturn h-\u003eparser.rideshare;\n-\t}\n-\n-\t/*\n-\t * The tx rideshare index\n-\t */\n-\n-\tif (h-\u003erideshare_list[0]) {\n-\t\tlwsl_sspc_info(h, \u0022tx list %s\u0022,\n-\t\t\t \u0026h-\u003erideshare_list[h-\u003erideshare_ofs[h-\u003ersidx]]);\n-\t\treturn \u0026h-\u003erideshare_list[h-\u003erideshare_ofs[h-\u003ersidx]];\n-\t}\n-\n-\t/*\n-\t * ... otherwise default to our stream type name\n-\t */\n-\n-\tlwsl_sspc_info(h, \u0022def %s\u005cn\u0022, h-\u003essi.streamtype);\n-\n-\treturn h-\u003essi.streamtype;\n-}\n-\n-static int\n-_lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,\n-\t\t const void *value, size_t len, int tx_cr_adjust)\n-{\n-\tlws_sspc_metadata_t *md;\n-\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\n-\t/*\n-\t * Are we replacing a pending metadata of the same name? It's not\n-\t * efficient to do this but user code can do what it likes... let's\n-\t * optimize away the old one.\n-\t *\n-\t * Tx credit adjust always has name \u0022\u0022\n-\t */\n-\n-\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n-\t\t\t\t lws_dll2_get_head(\u0026h-\u003emetadata_owner)) {\n-\t\tmd \u003d lws_container_of(d, lws_sspc_metadata_t, list);\n-\n-\t\tif (!strcmp(name, md-\u003ename)) {\n-\t\t\tlws_dll2_remove(\u0026md-\u003elist);\n-\t\t\tlws_free(md);\n-\t\t\tbreak;\n-\t\t}\n-\n-\t} lws_end_foreach_dll_safe(d, d1);\n-\n-\t/*\n-\t * We have to stash the metadata and pass it to the proxy\n-\t */\n-\n-\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_fail_metadata_set\u0022))\n-\t\tmd \u003d NULL;\n-\telse\n-\t\tmd \u003d lws_malloc(sizeof(*md) + len, \u0022set metadata\u0022);\n-\tif (!md) {\n-\t\tlwsl_sspc_err(h, \u0022OOM\u0022);\n-\n-\t\treturn 1;\n-\t}\n-\n-\tmemset(md, 0, sizeof(*md));\n-\n-\tmd-\u003etx_cr_adjust \u003d tx_cr_adjust;\n-\th-\u003etxc.peer_tx_cr_est +\u003d tx_cr_adjust;\n-\n-\tlws_strncpy(md-\u003ename, name, sizeof(md-\u003ename));\n-\tmd-\u003elen \u003d len;\n-\tif (len)\n-\t\tmemcpy(\u0026md[1], value, len);\n-\n-\tlws_dll2_add_tail(\u0026md-\u003elist, \u0026h-\u003emetadata_owner);\n-\n-\tif (len) {\n-\t\tlwsl_sspc_info(h, \u0022set metadata %s\u0022, name);\n-\t\tlwsl_hexdump_sspc_info(h, value, len);\n-\t} else\n-\t\tlwsl_sspc_info(h, \u0022serializing tx cr adj %d\u0022,\n-\t\t\t (int)tx_cr_adjust);\n-\n-\tif (h-\u003ecwsi)\n-\t\tlws_callback_on_writable(h-\u003ecwsi);\n-\n-\treturn 0;\n-}\n-\n-int\n-lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,\n-\t\t const void *value, size_t len)\n-{\n-\treturn _lws_sspc_set_metadata(h, name, value, len, 0);\n-}\n-\n-int\n-lws_sspc_get_metadata(struct lws_sspc_handle *h, const char *name,\n-\t\t const void **value, size_t *len)\n-{\n-\tlws_sspc_metadata_t *md;\n-\n-\t/*\n-\t * client side does not have access to policy\n-\t * and any metadata are new to it each time,\n-\t * we allocate them, removing any existing with\n-\t * the same name first\n-\t */\n-\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\n-\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n-\t\t\tlws_dll2_get_head(\u0026h-\u003emetadata_owner_rx)) {\n-\t\tmd \u003d lws_container_of(d,\n-\t\t\t lws_sspc_metadata_t, list);\n-\n-\t\tif (!strcmp(md-\u003ename, name)) {\n-\t\t\t*len \u003d md-\u003elen;\n-\t\t\t*value \u003d \u0026md[1];\n-\n-\t\t\treturn 0;\n-\t\t}\n-\n-\t} lws_end_foreach_dll_safe(d, d1);\n-\n-\treturn 1;\n-}\n-\n-int\n-lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t bump)\n-{\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\tlwsl_sspc_notice(h, \u0022%d\u005cn\u0022, bump);\n-\treturn _lws_sspc_set_metadata(h, \u0022\u0022, NULL, 0, (int)bump);\n-}\n-\n-int\n-lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h)\n-{\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\treturn h-\u003etxc.peer_tx_cr_est;\n-}\n-\n-void\n-lws_sspc_start_timeout(struct lws_sspc_handle *h, unsigned int timeout_ms)\n-{\n-\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n-\tif (!h-\u003ecwsi)\n-\t\t/* we can't fulfil it */\n-\t\treturn;\n-\th-\u003etimeout_ms \u003d (uint32_t)timeout_ms;\n-\th-\u003epending_timeout_update \u003d 1;\n-\tlws_callback_on_writable(h-\u003ecwsi);\n-}\n-\n-void\n-lws_sspc_cancel_timeout(struct lws_sspc_handle *h)\n-{\n-\tlws_sspc_start_timeout(h, (unsigned int)-1);\n-}\n-\n-void *\n-lws_sspc_to_user_object(struct lws_sspc_handle *h)\n-{\n-\treturn (void *)\u0026h[1];\n-}\n-\n-void\n-lws_sspc_change_handlers(struct lws_sspc_handle *h,\n-\tlws_ss_state_return_t (*rx)(void *userobj, const uint8_t *buf, size_t len, int flags),\n-\tlws_ss_state_return_t (*tx)(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,\n-\t\t size_t *len, int *flags),\n-\tlws_ss_state_return_t (*state)(void *userobj, void *h_src /* ss handle type */,\n-\t\t lws_ss_constate_t state, lws_ss_tx_ordinal_t ack))\n-{\n-\tif (rx)\n-\t\th-\u003essi.rx \u003d rx;\n-\tif (tx)\n-\t\th-\u003essi.tx \u003d tx;\n-\tif (state)\n-\t\th-\u003essi.state \u003d state;\n-}\n-\n-const char *\n-lws_sspc_tag(struct lws_sspc_handle *h)\n-{\n-\tif (!h)\n-\t\treturn \u0022[null sspc]\u0022;\n-\treturn lws_lc_tag(\u0026h-\u003elc);\n-}\n-\n-int\n-lws_sspc_cancel_notify_dll(struct lws_dll2 *d, void *user)\n-{\n-\tlws_sspc_handle_t *h \u003d lws_container_of(d, lws_sspc_handle_t, client_list);\n-\n-\tlws_sspc_event_helper(h, LWSSSCS_EVENT_WAIT_CANCELLED, 0);\n-\n-\treturn 0;\n-}\n-\ndiff --git a/lib/secure-streams/secure-streams-process.c b/lib/secure-streams/secure-streams-process.c\ndeleted file mode 100644\nindex d5822b6..0000000\n--- a/lib/secure-streams/secure-streams-process.c\n+++ /dev/null\n@@ -1,801 +0,0 @@\n-/*\n- * libwebsockets - small server side websockets and web server implementation\n- *\n- * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n- *\n- * Permission is hereby granted, free of charge, to any person obtaining a copy\n- * of this software and associated documentation files (the \u0022Software\u0022), to\n- * deal in the Software without restriction, including without limitation the\n- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n- * sell copies of the Software, and to permit persons to whom the Software is\n- * furnished to do so, subject to the following conditions:\n- *\n- * The above copyright notice and this permission notice shall be included in\n- * all copies or substantial portions of the Software.\n- *\n- * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n- * IN THE SOFTWARE.\n- *\n- *\n- * When the user code is in a different process, a non-tls unix domain socket\n- * proxy is used to asynchronusly transfer buffers in each direction via the\n- * network stack, without explicit IPC\n- *\n- * user_process{ [user code] | shim | socket-}------ lws_process{ lws }\n- *\n- * Lws exposes a listening unix domain socket in this case, the user processes\n- * connect to it and pass just info.streamtype in an initial tx packet. All\n- * packets are prepended by a 1-byte type field when used in this mode. See\n- * lws-secure-streams.h for documentation and definitions.\n- *\n- * Proxying in either direction can face the situation it cannot send the onward\n- * packet immediately and is subject to separating the write request from the\n- * write action. To make the best use of memory, a single preallocated buffer\n- * stashes pending packets in all four directions (c-\u003ep, p-\u003ec, p-\u003ess, ss-\u003ep).\n- * This allows it to adapt to different traffic patterns without wasted areas\n- * dedicated to traffic that isn't coming in a particular application.\n- *\n- * A shim is provided to monitor the process' unix domain socket and regenerate\n- * the secure sockets api there with callbacks happening in the process thread\n- * context.\n- *\n- * This file implements the listening unix domain socket proxy... this code is\n- * only going to run on a Linux-class device with its implications about memory\n- * availability.\n- */\n-\n-#include \u003cprivate-lib-core.h\u003e\n-\n-struct raw_pss {\n-\tstruct conn\t\t*conn;\n-};\n-\n-/*\n- * Proxy - onward secure-stream handler\n- */\n-\n-typedef struct ss_proxy_onward {\n-\tlws_ss_handle_t \t*ss;\n-\tstruct conn\t\t*conn;\n-} ss_proxy_t;\n-\n-void\n-lws_proxy_clean_conn_ss(struct lws *wsi)\n-{\n-#if 0\n-\tlws_ss_handle_t *h \u003d (lws_ss_handle_t *)wsi-\u003ea.opaque_user_data;\n-\tstruct conn *conn \u003d h-\u003econn_if_sspc_onw;\n-\n-\tif (!wsi)\n-\t\treturn;\n-\n-\tif (conn \u0026\u0026 conn-\u003ess)\n-\t\tconn-\u003ess-\u003ewsi \u003d NULL;\n-#endif\n-}\n-\n-\n-void\n-ss_proxy_onward_link_req_writeable(lws_ss_handle_t *h_onward)\n-{\n-\tss_proxy_t *m \u003d (ss_proxy_t *)\u0026h_onward[1];\n-\n-\tif (m-\u003econn-\u003ewsi) /* if possible, request client conn write */\n-\t\tlws_callback_on_writable(m-\u003econn-\u003ewsi);\n-}\n-\n-int\n-__lws_ss_proxy_bind_ss_to_conn_wsi(void *parconn, size_t dsh_size)\n-{\n-\tstruct conn *conn \u003d (struct conn *)parconn;\n-\tstruct lws_context_per_thread *pt;\n-\n-\tif (!conn || !conn-\u003ewsi || !conn-\u003ess)\n-\t\treturn -1;\n-\n-\tpt \u003d \u0026conn-\u003ewsi-\u003ea.context-\u003ept[(int)conn-\u003ewsi-\u003etsi];\n-\n-\tif (lws_fi(\u0026conn-\u003ess-\u003efic, \u0022ssproxy_dsh_create_oom\u0022))\n-\t\treturn -1;\n-\tconn-\u003edsh \u003d lws_dsh_create(\u0026pt-\u003ess_dsh_owner, dsh_size, 2);\n-\tif (!conn-\u003edsh)\n-\t\treturn -1;\n-\n-\t__lws_lc_tag_append(\u0026conn-\u003ewsi-\u003elc, lws_ss_tag(conn-\u003ess));\n-\n-\treturn 0;\n-}\n-\n-/* Onward secure streams payload interface */\n-\n-static lws_ss_state_return_t\n-ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)\n-{\n-\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n-\tconst char *rsp \u003d NULL;\n-\tint n;\n-\n-\t// lwsl_notice(\u0022%s: len %d\u005cn\u0022, __func__, (int)len);\n-\n-\t/*\n-\t * The onward secure stream connection has received something.\n-\t */\n-\n-\tif (m-\u003ess-\u003erideshare !\u003d m-\u003ess-\u003epolicy \u0026\u0026 m-\u003ess-\u003erideshare) {\n-\t\trsp \u003d m-\u003ess-\u003erideshare-\u003estreamtype;\n-\t\tflags |\u003d LWSSS_FLAG_RIDESHARE;\n-\t}\n-\n-\t/*\n-\t * Apply SSS framing around this chunk of RX and stash it in the dsh\n-\t * in ss -\u003e proxy [ -\u003e client] direction. This can fail...\n-\t */\n-\n-\tif (lws_fi(\u0026m-\u003ess-\u003efic, \u0022ssproxy_dsh_rx_queue_oom\u0022))\n-\t\tn \u003d 1;\n-\telse\n-\t\tn \u003d lws_ss_serialize_rx_payload(m-\u003econn-\u003edsh, buf, len,\n-\t\t\t\t\t\tflags, rsp);\n-\tif (n)\n-\t\t/*\n-\t\t * We couldn't buffer this rx, eg due to OOM, let's escalate it\n-\t\t * to be a \u0022loss of connection\u0022, which it basically is...\n-\t\t */\n-\t\treturn LWSSSSRET_DISCONNECT_ME;\n-\n-\t/*\n-\t * Manage rx flow on the SS (onward) side according to our situation\n-\t * in the dsh holding proxy-\u003eclient serialized forwarding rx\n-\t */\n-\n-\tif (!m-\u003econn-\u003eonward_in_flow_control \u0026\u0026 m-\u003ess-\u003ewsi \u0026\u0026\n-\t m-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above \u0026\u0026\n-\t lws_dsh_get_size(m-\u003econn-\u003edsh, KIND_SS_TO_P) \u003e\u003d\n-\t\t\t\tm-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above) {\n-\t\tlwsl_info(\u0022%s: %s: rxflow disabling rx (%lu / %lu, hwm %lu)\u005cn\u0022, __func__,\n-\t\t\t\tlws_wsi_tag(m-\u003ess-\u003ewsi),\n-\t\t\t\t(unsigned long)lws_dsh_get_size(m-\u003econn-\u003edsh, KIND_SS_TO_P),\n-\t\t\t\t(unsigned long)m-\u003ess-\u003epolicy-\u003eproxy_buflen,\n-\t\t\t\t(unsigned long)m-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above);\n-\t\t/*\n-\t\t * stop taking in rx once the onward wsi rx is above the\n-\t\t * high water mark\n-\t\t */\n-\t\tlws_rx_flow_control(m-\u003ess-\u003ewsi, 0);\n-\t\tm-\u003econn-\u003eonward_in_flow_control \u003d 1;\n-\t}\n-\n-\tif (m-\u003econn-\u003ewsi) /* if possible, request client conn write */\n-\t\tlws_callback_on_writable(m-\u003econn-\u003ewsi);\n-\n-\treturn LWSSSSRET_OK;\n-}\n-\n-/*\n- * we are transmitting buffered payload originally from the client on to the ss\n- */\n-\n-static lws_ss_state_return_t\n-ss_proxy_onward_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,\n-\t\t size_t *len, int *flags)\n-{\n-\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n-\tvoid *p;\n-\tsize_t si;\n-\n-\tif (!m-\u003econn-\u003ess || m-\u003econn-\u003estate !\u003d LPCSPROX_OPERATIONAL) {\n-\t\tlwsl_notice(\u0022%s: ss not ready\u005cn\u0022, __func__);\n-\t\t*len \u003d 0;\n-\n-\t\treturn LWSSSSRET_TX_DONT_SEND;\n-\t}\n-\n-\t/*\n-\t * The onward secure stream says that we could send something to it\n-\t * (by putting it in buf, and setting *len and *flags)... dredge the\n-\t * next thing out of the dsh\n-\t */\n-\n-\tif (lws_ss_deserialize_tx_payload(m-\u003econn-\u003edsh, m-\u003ess-\u003ewsi,\n-\t\t\t\t\t ord, buf, len, flags))\n-\t\treturn LWSSSSRET_TX_DONT_SEND;\n-\n-\t/* ... there's more we want to send? */\n-\tif (!lws_dsh_get_head(m-\u003econn-\u003edsh, KIND_C_TO_P, (void **)\u0026p, \u0026si))\n-\t\t_lws_ss_request_tx(m-\u003econn-\u003ess);\n-\n-\tif (!*len \u0026\u0026 !*flags)\n-\t\t/* we don't actually want to send anything */\n-\t\treturn LWSSSSRET_TX_DONT_SEND;\n-\n-\tlwsl_info(\u0022%s: onward tx %d fl 0x%x\u005cn\u0022, __func__, (int)*len, *flags);\n-\n-#if 0\n-\t{\n-\t\tint ff \u003d open(\u0022/tmp/z\u0022, O_RDWR | O_CREAT | O_APPEND, 0666);\n-\t\tif (ff \u003d\u003d -1)\n-\t\t\tlwsl_err(\u0022%s: errno %d\u005cn\u0022, __func__, errno);\n-\t\twrite(ff, buf, *len);\n-\t\tclose(ff);\n-\t}\n-#endif\n-\n-\treturn LWSSSSRET_OK;\n-}\n-\n-static lws_ss_state_return_t\n-ss_proxy_onward_state(void *userobj, void *sh,\n-\t\t lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)\n-{\n-\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n-\tsize_t dsh_size;\n-\n-\tswitch (state) {\n-\tcase LWSSSCS_CREATING:\n-\n-\t\t/*\n-\t\t * conn is private to -process.c, call thru to a) adjust\n-\t\t * the accepted incoming proxy link wsi tag name to be\n-\t\t * appended with the onward ss tag information now we\n-\t\t * have it, and b) allocate the dsh buffer now we\n-\t\t * can find out the policy about it for the streamtype.\n-\t\t */\n-\n-\t\tdsh_size \u003d m-\u003ess-\u003epolicy-\u003eproxy_buflen ?\n-\t\t\t\tm-\u003ess-\u003epolicy-\u003eproxy_buflen : 32768;\n-\n-\t\tlwsl_notice(\u0022%s: %s: initializing dsh max len %lu\u005cn\u0022,\n-\t\t\t\t__func__, lws_ss_tag(m-\u003ess),\n-\t\t\t\t(unsigned long)dsh_size);\n-\n-\t\t/* this includes ssproxy_dsh_create_oom fault generation */\n-\n-\t\tif (__lws_ss_proxy_bind_ss_to_conn_wsi(m-\u003econn, dsh_size)) {\n-\n-\t\t\t/* failed to allocate the dsh */\n-\n-\t\t\tlwsl_notice(\u0022%s: dsh init failed\u005cn\u0022, __func__);\n-\n-\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\t\t}\n-\t\tbreak;\n-\n-\tcase LWSSSCS_DESTROYING:\n-\t\tif (!m-\u003econn)\n-\t\t\tbreak;\n-\t\tif (!m-\u003econn-\u003ewsi) {\n-\t\t\t/*\n-\t\t\t * Our onward secure stream is closing and our client\n-\t\t\t * connection has already gone away... destroy the conn.\n-\t\t\t */\n-\t\t\tlwsl_info(\u0022%s: Destroying conn\u005cn\u0022, __func__);\n-\t\t\tlws_dsh_destroy(\u0026m-\u003econn-\u003edsh);\n-\t\t\tfree(m-\u003econn);\n-\t\t\tm-\u003econn \u003d NULL;\n-\t\t\treturn 0;\n-\t\t} else\n-\t\t\tlwsl_info(\u0022%s: ss DESTROYING, wsi up\u005cn\u0022, __func__);\n-\t\tbreak;\n-\n-\tdefault:\n-\t\tbreak;\n-\t}\n-\tif (!m-\u003econn) {\n-\t\tlwsl_warn(\u0022%s: dropping state due to conn not up\u005cn\u0022, __func__);\n-\n-\t\treturn LWSSSSRET_OK;\n-\t}\n-\n-\tif (lws_ss_serialize_state(m-\u003econn-\u003ewsi, m-\u003econn-\u003edsh, state, ack))\n-\t\t/*\n-\t\t * Failed to alloc state packet that we want to send in dsh,\n-\t\t * we will lose coherence and have to disconnect the link\n-\t\t */\n-\t\treturn LWSSSSRET_DISCONNECT_ME;\n-\n-\tif (m-\u003econn-\u003ewsi) /* if possible, request client conn write */\n-\t\tlws_callback_on_writable(m-\u003econn-\u003ewsi);\n-\n-\treturn LWSSSSRET_OK;\n-}\n-\n-void\n-ss_proxy_onward_txcr(void *userobj, int bump)\n-{\n-\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n-\n-\tif (!m-\u003econn)\n-\t\treturn;\n-\n-\tlws_ss_serialize_txcr(m-\u003econn-\u003edsh, bump);\n-\n-\tif (m-\u003econn-\u003ewsi) /* if possible, request client conn write */\n-\t\tlws_callback_on_writable(m-\u003econn-\u003ewsi);\n-}\n-\n-/*\n- * Client \u003c-\u003e Proxy connection, usually on Unix Domain Socket\n- */\n-\n-static int\n-callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,\n-\t\t void *user, void *in, size_t len)\n-{\n-\tstruct raw_pss *pss \u003d (struct raw_pss *)user;\n-\tconst lws_ss_policy_t *rsp;\n-\tstruct conn *conn \u003d NULL;\n-\tlws_ss_metadata_t *md;\n-\tlws_ss_info_t ssi;\n-\tconst uint8_t *cp;\n-\tchar s[512];\n-\tuint8_t *p;\n-\tsize_t si;\n-\tchar pay;\n-\tint n;\n-\n-\tif (pss)\n-\t\tconn \u003d pss-\u003econn;\n-\n-\tswitch (reason) {\n-\tcase LWS_CALLBACK_PROTOCOL_INIT:\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_PROTOCOL_DESTROY:\n-\t\tbreak;\n-\n-\t/* callbacks related to raw socket descriptor \u0022accepted side\u0022 */\n-\n- case LWS_CALLBACK_RAW_ADOPT:\n-\t\tlwsl_info(\u0022LWS_CALLBACK_RAW_ADOPT\u005cn\u0022);\n-\t\tif (!pss)\n-\t\t\treturn -1;\n-\n-\t\tif (lws_fi(\u0026wsi-\u003efic, \u0022ssproxy_client_adopt_oom\u0022))\n-\t\t\tpss-\u003econn \u003d NULL;\n-\t\telse\n-\t\t\tpss-\u003econn \u003d malloc(sizeof(struct conn));\n-\t\tif (!pss-\u003econn)\n-\t\t\treturn -1;\n-\n-\t\tmemset(pss-\u003econn, 0, sizeof(*pss-\u003econn));\n-\n-\t\t/* dsh is allocated when the onward ss is done */\n-\n-\t\tpss-\u003econn-\u003ewsi \u003d wsi;\n-\t\twsi-\u003ebound_ss_proxy_conn \u003d 1; /* opaque is conn */\n-\n-\t\tpss-\u003econn-\u003estate \u003d LPCSPROX_WAIT_INITIAL_TX;\n-\n-\t\t/*\n-\t\t * Client is expected to follow the unix domain socket\n-\t\t * acceptance up rapidly with an initial tx containing the\n-\t\t * streamtype name. We can't create the stream until then.\n-\t\t */\n-\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);\n- break;\n-\n-\tcase LWS_CALLBACK_RAW_CLOSE:\n-\t\tlwsl_info(\u0022LWS_CALLBACK_RAW_CLOSE:\u005cn\u0022);\n-\n-\t\tif (!conn)\n-\t\t\tbreak;\n-\n-\t\t/*\n-\t\t * the client unix domain socket connection (wsi / conn-\u003ewsi)\n-\t\t * has closed... eg, client has exited or otherwise has\n-\t\t * definitively finished with the proxying and onward connection\n-\t\t *\n-\t\t * But right now, the SS and possibly the SS onward wsi are\n-\t\t * still live...\n-\t\t */\n-\n-\t\tassert(conn-\u003ewsi \u003d\u003d wsi);\n-\t\tconn-\u003ewsi \u003d NULL;\n-\n-\t\tlwsl_notice(\u0022%s: cli-\u003eprox link %s closing\u005cn\u0022, __func__,\n-\t\t\t\tlws_wsi_tag(wsi));\n-\n-\t\t/* sever relationship with conn */\n-\t\tlws_set_opaque_user_data(wsi, NULL);\n-\n-\t\t/*\n-\t\t * The current wsi is decoupled from the pss / conn and\n-\t\t * the conn no longer has a pointer on it.\n-\t\t *\n-\t\t * If there's an outgoing, proxied SS conn on our behalf, we\n-\t\t * have to destroy those\n-\t\t */\n-\n-\t\tif (conn-\u003ess) {\n-\t\t\tstruct lws *cw \u003d conn-\u003ess-\u003ewsi;\n-\t\t\t/*\n-\t\t\t * conn-\u003ess is the onward connection SS\n-\t\t\t */\n-\n-\t\t\tlwsl_info(\u0022%s: destroying %s, wsi %s\u005cn\u0022,\n-\t\t\t\t\t__func__, lws_ss_tag(conn-\u003ess),\n-\t\t\t\t\tlws_wsi_tag(conn-\u003ess-\u003ewsi));\n-\n-\t\t\t/* sever conn relationship with ss about to be deleted */\n-\n-\t\t\tconn-\u003ess-\u003ewsi \u003d NULL;\n-\n-\t\t\tif (cw \u0026\u0026 wsi !\u003d cw) {\n-\n-\t\t\t\t/* disconnect onward SS from its wsi */\n-\n-\t\t\t\tlws_set_opaque_user_data(cw, NULL);\n-\n-\t\t\t\t/*\n-\t\t\t\t * The wsi doing the onward connection can no\n-\t\t\t\t * longer relate to the conn... otherwise when\n-\t\t\t\t * he gets callbacks he wants to bind to\n-\t\t\t\t * the ss we are about to delete\n-\t\t\t\t */\n-\t\t\t\tlws_wsi_close(cw, LWS_TO_KILL_ASYNC);\n-\t\t\t}\n-\n-\t\t\tlws_ss_destroy(\u0026conn-\u003ess);\n-\t\t\t/*\n-\t\t\t * Conn may have gone, at ss destroy handler in\n-\t\t\t * ssi.state for proxied ss\n-\t\t\t */\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\tif (conn-\u003estate \u003d\u003d LPCSPROX_DESTROYED || !conn-\u003ess) {\n-\t\t\t/*\n-\t\t\t * There's no onward secure stream and our client\n-\t\t\t * connection is closing. Destroy the conn.\n-\t\t\t */\n-\t\t\tlws_dsh_destroy(\u0026conn-\u003edsh);\n-\t\t\tfree(conn);\n-\t\t\tpss-\u003econn \u003d NULL;\n-\t\t} else\n-\t\t\tlwsl_debug(\u0022%s: CLOSE; %s\u005cn\u0022, __func__, lws_ss_tag(conn-\u003ess));\n-\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_RAW_RX:\n-\t\t/*\n-\t\t * ie, the proxy is receiving something from a client\n-\t\t */\n-\t\tlwsl_info(\u0022%s: RX: rx %d\u005cn\u0022, __func__, (int)len);\n-\n-\t\tif (!conn || !conn-\u003ewsi) {\n-\t\t\tlwsl_err(\u0022%s: rx with bad conn state\u005cn\u0022, __func__);\n-\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\t// lwsl_hexdump_info(in, len);\n-\n-\t\tif (conn-\u003estate \u003d\u003d LPCSPROX_WAIT_INITIAL_TX) {\n-\t\t\tmemset(\u0026ssi, 0, sizeof(ssi));\n-\t\t\tssi.user_alloc \u003d sizeof(ss_proxy_t);\n-\t\t\tssi.handle_offset \u003d offsetof(ss_proxy_t, ss);\n-\t\t\tssi.opaque_user_data_offset \u003d\n-\t\t\t\t\toffsetof(ss_proxy_t, conn);\n-\t\t\tssi.rx \u003d ss_proxy_onward_rx;\n-\t\t\tssi.tx \u003d ss_proxy_onward_tx;\n-\t\t}\n-\t\tssi.state \u003d ss_proxy_onward_state;\n-\t\tssi.flags \u003d 0;\n-\n-\t\tn \u003d lws_ss_deserialize_parse(\u0026conn-\u003eparser,\n-\t\t\t\tlws_get_context(wsi), conn-\u003edsh, in, len,\n-\t\t\t\t\u0026conn-\u003estate, conn, \u0026conn-\u003ess, \u0026ssi, 0);\n-\t\tswitch (n) {\n-\t\tcase LWSSSSRET_OK:\n-\t\t\tbreak;\n-\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\treturn -1;\n-\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\tif (conn-\u003ess)\n-\t\t\t\tlws_ss_destroy(\u0026conn-\u003ess);\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\tif (conn-\u003estate \u003d\u003d LPCSPROX_REPORTING_FAIL ||\n-\t\t conn-\u003estate \u003d\u003d LPCSPROX_REPORTING_OK)\n-\t\t\tlws_callback_on_writable(conn-\u003ewsi);\n-\n-\t\tbreak;\n-\n-\tcase LWS_CALLBACK_RAW_WRITEABLE:\n-\n-\t\tlwsl_debug(\u0022%s: %s: LWS_CALLBACK_RAW_WRITEABLE, state 0x%x\u005cn\u0022,\n-\t\t\t\t__func__, lws_wsi_tag(wsi), lwsi_state(wsi));\n-\n-\t\t/*\n-\t\t * We can transmit something back to the client from the dsh\n-\t\t * of stuff we received on its behalf from the ss\n-\t\t */\n-\n-\t\tif (!conn || !conn-\u003ewsi)\n-\t\t\tbreak;\n-\n-\t\tn \u003d 0;\n-\t\tpay \u003d 0;\n-\n-\t\ts[3] \u003d 0;\n-\t\tcp \u003d (const uint8_t *)s;\n-\t\tswitch (conn-\u003estate) {\n-\t\tcase LPCSPROX_REPORTING_FAIL:\n-\t\t\ts[3] \u003d 1;\n-\t\t\t/* fallthru */\n-\t\tcase LPCSPROX_REPORTING_OK:\n-\t\t\ts[0] \u003d LWSSS_SER_RXPRE_CREATE_RESULT;\n-\t\t\ts[1] \u003d 0;\n-\t\t\ts[2] \u003d 1;\n-\n-\t\t\tn \u003d 8;\n-\n-\t\t\tlws_ser_wu32be((uint8_t *)\u0026s[4], conn-\u003ess \u0026\u0026\n-\t\t\t\t\t\t\t conn-\u003ess-\u003epolicy ?\n-\t\t\t\t\tconn-\u003ess-\u003epolicy-\u003eclient_buflen : 0);\n-\n-\t\t\t/*\n-\t\t\t * If there's rideshare sequencing, it's added after the\n-\t\t\t * first 4 bytes or the create result, comma-separated\n-\t\t\t */\n-\n-\t\t\tif (conn-\u003ess) {\n-\t\t\t\trsp \u003d conn-\u003ess-\u003epolicy;\n-\n-\t\t\t\twhile (rsp) {\n-\t\t\t\t\tif (n !\u003d 4 \u0026\u0026 n \u003c (int)sizeof(s) - 2)\n-\t\t\t\t\t\ts[n++] \u003d ',';\n-\t\t\t\t\tn +\u003d lws_snprintf(\u0026s[n], sizeof(s) - (unsigned int)n,\n-\t\t\t\t\t\t\t\u0022%s\u0022, rsp-\u003estreamtype);\n-\t\t\t\t\trsp \u003d lws_ss_policy_lookup(wsi-\u003ea.context,\n-\t\t\t\t\t\trsp-\u003erideshare_streamtype);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\ts[2] \u003d (char)(n - 3);\n-\t\t\tconn-\u003estate \u003d LPCSPROX_OPERATIONAL;\n-\t\t\tlws_set_timeout(wsi, 0, 0);\n-\t\t\tbreak;\n-\n-\t\tcase LPCSPROX_OPERATIONAL:\n-\n-\t\t\t/*\n-\t\t\t * returning [onward -\u003e ] proxy]-\u003e client\n-\t\t\t * rx metadata has priority 1\n-\t\t\t */\n-\n-\t\t\tmd \u003d conn-\u003ess-\u003emetadata;\n-\t\t\twhile (md) {\n-\t\t\t\t// lwsl_notice(\u0022%s: check %s: %d\u005cn\u0022, __func__,\n-\t\t\t\t// md-\u003ename, md-\u003epending_onward);\n-\t\t\t\tif (md-\u003epending_onward) {\n-\t\t\t\t\tsize_t naml \u003d strlen(md-\u003ename);\n-\n-\t\t\t\t\t// lwsl_notice(\u0022%s: proxy issuing rxmd\u005cn\u0022, __func__);\n-\n-\t\t\t\t\tif (4 + naml + md-\u003elength \u003e sizeof(s)) {\n-\t\t\t\t\t\tlwsl_err(\u0022%s: rxmdata too big\u005cn\u0022,\n-\t\t\t\t\t\t\t\t__func__);\n-\t\t\t\t\t\tgoto hangup;\n-\t\t\t\t\t}\n-\t\t\t\t\tmd-\u003epending_onward \u003d 0;\n-\t\t\t\t\tp \u003d (uint8_t *)s;\n-\t\t\t\t\tp[0] \u003d LWSSS_SER_RXPRE_METADATA;\n-\t\t\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)(1 + naml +\n-\t\t\t\t\t\t\t md-\u003elength));\n-\t\t\t\t\tp[3] \u003d (uint8_t)naml;\n-\t\t\t\t\tmemcpy(\u0026p[4], md-\u003ename, naml);\n-\t\t\t\t\tp +\u003d 4 + naml;\n-\t\t\t\t\tmemcpy(p, md-\u003evalue__may_own_heap,\n-\t\t\t\t\t md-\u003elength);\n-\t\t\t\t\tp +\u003d md-\u003elength;\n-\n-\t\t\t\t\tn \u003d lws_ptr_diff(p, cp);\n-\t\t\t\t\tgoto again;\n-\t\t\t\t}\n-\n-\t\t\t\tmd \u003d md-\u003enext;\n-\t\t\t}\n-\n-\t\t\t/*\n-\t\t\t * If we have performance data, render it in JSON\n-\t\t\t * and send that in LWSSS_SER_RXPRE_PERF has\n-\t\t\t * priority 2\n-\t\t\t */\n-\n-#if defined(LWS_WITH_CONMON)\n-\t\t\tif (conn-\u003ess-\u003econmon_json) {\n-\t\t\t\tunsigned int xlen \u003d conn-\u003ess-\u003econmon_len;\n-\n-\t\t\t\tif (xlen \u003e sizeof(s) - 3)\n-\t\t\t\t\txlen \u003d sizeof(s) - 3;\n-\t\t\t\tcp \u003d (uint8_t *)s;\n-\t\t\t\tp \u003d (uint8_t *)s;\n-\t\t\t\tp[0] \u003d LWSSS_SER_RXPRE_PERF;\n-\t\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)xlen);\n-\t\t\t\tmemcpy(\u0026p[3], conn-\u003ess-\u003econmon_json, xlen);\n-\n-\t\t\t\tlws_free_set_NULL(conn-\u003ess-\u003econmon_json);\n-\t\t\t\tn \u003d (int)(xlen + 3);\n-\n-\t\t\t\tpay \u003d 0;\n-\t\t\t\tgoto again;\n-\t\t\t}\n-#endif\n-\t\t\t/*\n-\t\t\t * if no fresh rx metadata, just pass through incoming\n-\t\t\t * dsh\n-\t\t\t */\n-\n-\t\t\tif (lws_dsh_get_head(conn-\u003edsh, KIND_SS_TO_P,\n-\t\t\t\t\t (void **)\u0026p, \u0026si))\n-\t\t\t\tbreak;\n-\n-\t\t\tcp \u003d p;\n-\n-#if 0\n-\t\t\tif (cp[0] \u003d\u003d LWSSS_SER_RXPRE_RX_PAYLOAD \u0026\u0026\n-\t\t\t wsi-\u003ea.context-\u003edetailed_latency_cb) {\n-\n-\t\t\t\t/*\n-\t\t\t\t * we're fulfilling rx that came in on ss\n-\t\t\t\t * by sending it back out to the client on\n-\t\t\t\t * the Unix Domain Socket\n-\t\t\t\t *\n-\t\t\t\t * + 7 u32 write will compute latency here...\n-\t\t\t\t * + 11 u32 ust we received from ss\n-\t\t\t\t *\n-\t\t\t\t * lws_write will report it and fill in\n-\t\t\t\t * LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE\n-\t\t\t\t */\n-\n-\t\t\t\tus \u003d lws_now_usecs();\n-\t\t\t\tlws_ser_wu32be(\u0026p[7], us -\n-\t\t\t\t\t\t lws_ser_ru64be(\u0026p[11]));\n-\t\t\t\tlws_ser_wu64be(\u0026p[11], us);\n-\n-\t\t\t\twsi-\u003edetlat.acc_size \u003d\n-\t\t\t\t\twsi-\u003edetlat.req_size \u003d si - 19;\n-\t\t\t\t/* time proxy held it */\n-\t\t\t\twsi-\u003edetlat.latencies[\n-\t\t\t\t LAT_DUR_PROXY_RX_TO_ONWARD_TX] \u003d\n-\t\t\t\t\t\t\tlws_ser_ru32be(\u0026p[7]);\n-\t\t\t}\n-#endif\n-\t\t\tpay \u003d 1;\n-\t\t\tn \u003d (int)si;\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tbreak;\n-\t\t}\n-again:\n-\t\tif (!n)\n-\t\t\tbreak;\n-\n-\t\tif (lws_fi(\u0026wsi-\u003efic, \u0022ssproxy_client_write_fail\u0022))\n-\t\t\tn \u003d -1;\n-\t\telse\n-\t\t\tn \u003d lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);\n-\t\tif (n \u003c 0) {\n-\t\t\tlwsl_info(\u0022%s: WRITEABLE: %d\u005cn\u0022, __func__, n);\n-\n-\t\t\tgoto hangup;\n-\t\t}\n-\n-\t\tswitch (conn-\u003estate) {\n-\t\tcase LPCSPROX_REPORTING_FAIL:\n-\t\t\tgoto hangup;\n-\t\tcase LPCSPROX_OPERATIONAL:\n-\t\t\tif (!conn)\n-\t\t\t\tbreak;\n-\t\t\tif (pay) {\n-\t\t\t\tlws_dsh_free((void **)\u0026p);\n-\n-\t\t\t\t/*\n-\t\t\t\t * Did we go below the rx flow threshold for\n-\t\t\t\t * this dsh?\n-\t\t\t\t */\n-\n-\t\t\t\tif (conn-\u003eonward_in_flow_control \u0026\u0026\n-\t\t\t\t conn-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above \u0026\u0026\n-\t\t\t\t conn-\u003ess-\u003ewsi \u0026\u0026\n-\t\t\t\t lws_dsh_get_size(conn-\u003edsh, KIND_SS_TO_P) \u003c\n-\t\t\t\t conn-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_off_below) {\n-\t\t\t\t\tlwsl_info(\u0022%s: %s: rxflow enabling rx (%lu / %lu, lwm %lu)\u005cn\u0022, __func__,\n-\t\t\t\t\t\t\tlws_wsi_tag(conn-\u003ess-\u003ewsi),\n-\t\t\t\t\t\t\t(unsigned long)lws_dsh_get_size(conn-\u003edsh, KIND_SS_TO_P),\n-\t\t\t\t\t\t\t(unsigned long)conn-\u003ess-\u003epolicy-\u003eproxy_buflen,\n-\t\t\t\t\t\t\t(unsigned long)conn-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_off_below);\n-\t\t\t\t\t/*\n-\t\t\t\t\t * Resume receiving taking in rx once\n-\t\t\t\t\t * below the low threshold\n-\t\t\t\t\t */\n-\t\t\t\t\tlws_rx_flow_control(conn-\u003ess-\u003ewsi,\n-\t\t\t\t\t\t\t LWS_RXFLOW_ALLOW);\n-\t\t\t\t\tconn-\u003eonward_in_flow_control \u003d 0;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif (!lws_dsh_get_head(conn-\u003edsh, KIND_SS_TO_P,\n-\t\t\t\t\t (void **)\u0026p, \u0026si)) {\n-\t\t\t\tif (!lws_send_pipe_choked(wsi)) {\n-\t\t\t\t\tcp \u003d p;\n-\t\t\t\t\tpay \u003d 1;\n-\t\t\t\t\tn \u003d (int)si;\n-\t\t\t\t\tgoto again;\n-\t\t\t\t}\n-\t\t\t\tlws_callback_on_writable(wsi);\n-\t\t\t}\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tbreak;\n-\t\t}\n-\t\tbreak;\n-\n-\tdefault:\n-\t\tbreak;\n-\t}\n-\n-\treturn lws_callback_http_dummy(wsi, reason, user, in, len);\n-\n-hangup:\n-\t/* hang up on him */\n-\n-\treturn -1;\n-}\n-\n-static const struct lws_protocols protocols[] \u003d {\n-\t{\n-\t\t\u0022ssproxy-protocol\u0022,\n-\t\tcallback_ss_proxy,\n-\t\tsizeof(struct raw_pss),\n-\t\t2048, 2048, NULL, 0\n-\t},\n-\t{ NULL, NULL, 0, 0, 0, NULL, 0 }\n-};\n-\n-/*\n- * called from create_context()\n- */\n-\n-int\n-lws_ss_proxy_create(struct lws_context *context, const char *bind, int port)\n-{\n-\tstruct lws_context_creation_info info;\n-\n-\tmemset(\u0026info, 0, sizeof(info));\n-\n-\tinfo.vhost_name\t\t\t\u003d \u0022ssproxy\u0022;\n-\tinfo.options \u003d LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG |\n-\t\t\tLWS_SERVER_OPTION_SS_PROXY;\n-\tinfo.port \u003d port;\n-\tif (!port) {\n-\t\tif (!bind)\n-#if defined(__linux__)\n-\t\t\tbind \u003d \u0022@proxy.ss.lws\u0022;\n-#else\n-\t\t\tbind \u003d \u0022/tmp/proxy.ss.lws\u0022;\n-#endif\n-\t\tinfo.options |\u003d LWS_SERVER_OPTION_UNIX_SOCK;\n-\t}\n-\tinfo.iface\t\t\t\u003d bind;\n-#if defined(__linux__)\n-\tinfo.unix_socket_perms\t\t\u003d \u0022root:root\u0022;\n-#else\n-#endif\n-\tinfo.listen_accept_role\t\t\u003d \u0022raw-skt\u0022;\n-\tinfo.listen_accept_protocol\t\u003d \u0022ssproxy-protocol\u0022;\n-\tinfo.protocols\t\t\t\u003d protocols;\n-\n-\tif (!lws_create_vhost(context, \u0026info)) {\n-\t\tlwsl_err(\u0022%s: Failed to create ss proxy vhost\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\treturn 0;\n-}\ndiff --git a/lib/secure-streams/secure-streams-serialize.c b/lib/secure-streams/secure-streams-serialize.c\ndeleted file mode 100644\nindex 09969fe..0000000\n--- a/lib/secure-streams/secure-streams-serialize.c\n+++ /dev/null\n@@ -1,1523 +0,0 @@\n-/*\n- * libwebsockets - small server side websockets and web server implementation\n- *\n- * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n- *\n- * Permission is hereby granted, free of charge, to any person obtaining a copy\n- * of this software and associated documentation files (the \u0022Software\u0022), to\n- * deal in the Software without restriction, including without limitation the\n- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n- * sell copies of the Software, and to permit persons to whom the Software is\n- * furnished to do so, subject to the following conditions:\n- *\n- * The above copyright notice and this permission notice shall be included in\n- * all copies or substantial portions of the Software.\n- *\n- * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n- * IN THE SOFTWARE.\n- *\n- *\n- * In the case Secure Streams protocol needs to pass through a buffer,\n- * or a streamed connection, the protocol metadata must be serialized. This\n- * file provides internal apis to perform the serialization and deserialization\n- * in and out of an lws_dsh fifo-type buffer.\n- */\n-\n-#include \u003cprivate-lib-core.h\u003e\n-\n-typedef enum {\n-\tRPAR_TYPE,\n-\tRPAR_LEN_MSB,\n-\tRPAR_LEN_LSB,\n-\n-\tRPAR_FLAG_B3,\n-\tRPAR_FLAG_B2,\n-\tRPAR_FLAG_B1,\n-\tRPAR_FLAG_B0,\n-\n-\tRPAR_LATA3,\n-\tRPAR_LATA2,\n-\tRPAR_LATA1,\n-\tRPAR_LATA0,\n-\n-\tRPAR_LATB7,\n-\tRPAR_LATB6,\n-\tRPAR_LATB5,\n-\tRPAR_LATB4,\n-\tRPAR_LATB3,\n-\tRPAR_LATB2,\n-\tRPAR_LATB1,\n-\tRPAR_LATB0,\n-\n-\tRPAR_RIDESHARE_LEN,\n-\tRPAR_RIDESHARE,\n-\n-\tRPAR_PERF,\n-\n-\tRPAR_RESULT_CREATION_DSH,\n-\tRPAR_RESULT_CREATION_RIDESHARE,\n-\n-\tRPAR_METADATA_NAMELEN,\n-\tRPAR_METADATA_NAME,\n-\tRPAR_METADATA_VALUE,\n-\n-\tRPAR_PAYLOAD,\n-\n-\tRPAR_RX_TXCR_UPDATE,\n-\n-\tRPAR_STREAMTYPE,\n-\tRPAR_INIT_PROVERS,\n-\tRPAR_INIT_PID,\n-\tRPAR_INITTXC0,\n-\n-\tRPAR_TXCR0,\n-\n-\tRPAR_TIMEOUT0,\n-\n-\tRPAR_PAYLEN0,\n-\n-\tRPAR_RESULT_CREATION,\n-\n-\tRPAR_STATEINDEX,\n-\tRPAR_ORD3,\n-\tRPAR_ORD2,\n-\tRPAR_ORD1,\n-\tRPAR_ORD0,\n-} rx_parser_t;\n-\n-#if defined(_DEBUG) \u0026\u0026 !defined(LWS_WITH_NO_LOGS)\n-static const char *sn[] \u003d {\n-\t\u0022unset\u0022,\n-\n-\t\u0022LPCSPROX_WAIT_INITIAL_TX\u0022,\n-\t\u0022LPCSPROX_REPORTING_FAIL\u0022,\n-\t\u0022LPCSPROX_REPORTING_OK\u0022,\n-\t\u0022LPCSPROX_OPERATIONAL\u0022,\n-\t\u0022LPCSPROX_DESTROYED\u0022,\n-\n-\t\u0022LPCSCLI_SENDING_INITIAL_TX\u0022,\n-\t\u0022LPCSCLI_WAITING_CREATE_RESULT\u0022,\n-\t\u0022LPCSCLI_LOCAL_CONNECTED\u0022,\n-\t\u0022LPCSCLI_ONWARD_CONNECT\u0022,\n-\t\u0022LPCSCLI_OPERATIONAL\u0022,\n-};\n-#endif\n-\n-struct lws_log_cx *\n-lwsl_sspc_get_cx(struct lws_sspc_handle *sspc)\n-{\n-\tif (!sspc)\n-\t\treturn NULL;\n-\n-\treturn sspc-\u003elc.log_cx;\n-}\n-\n-\n-void\n-lws_log_prepend_sspc(struct lws_log_cx *cx, void *obj, char **p, char *e)\n-{\n-\tstruct lws_sspc_handle *h \u003d (struct lws_sspc_handle *)obj;\n-\n-\t*p +\u003d lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), \u0022%s: \u0022,\n-\t\t\tlws_sspc_tag(h));\n-}\n-\n-static void\n-lws_ss_serialize_state_transition(lws_sspc_handle_t *h,\n-\t\t\t\t lws_ss_conn_states_t *state, int new_state)\n-{\n-#if defined(_DEBUG)\n-\tlwsl_sspc_info(h, \u0022%s -\u003e %s\u0022, sn[*state], sn[new_state]);\n-#endif\n-\t*state \u003d (lws_ss_conn_states_t)new_state;\n-}\n-\n-\n-/*\n- * event loop received something and is queueing it for the foreign side of\n- * the dsh to consume later as serialized rx\n- */\n-\n-int\n-lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf,\n-\t\t\t size_t len, int flags, const char *rsp)\n-{\n-\tlws_usec_t us \u003d lws_now_usecs();\n-\tuint8_t pre[128];\n-\tint est \u003d 19, l \u003d 0;\n-\n-\tif (flags \u0026 LWSSS_FLAG_RIDESHARE) {\n-\t\t/*\n-\t\t * We should have the rideshare name if we have been told it's\n-\t\t * on a non-default rideshare\n-\t\t */\n-\t\tassert(rsp);\n-\t\tif (!rsp)\n-\t\t\treturn 1;\n-\t\tl \u003d (int)strlen(rsp);\n-\t\test +\u003d 1 + l;\n-\t} else\n-\t\tassert(!rsp);\n-\n-\t// lwsl_user(\u0022%s: len %d, flags: %d\u005cn\u0022, __func__, (int)len, flags);\n-\t// lwsl_hexdump_info(buf, len);\n-\n-\tpre[0] \u003d LWSSS_SER_RXPRE_RX_PAYLOAD;\n-\tlws_ser_wu16be(\u0026pre[1], (uint16_t)(len + (size_t)est - 3));\n-\tlws_ser_wu32be(\u0026pre[3], (uint32_t)flags);\n-\tlws_ser_wu32be(\u0026pre[7], 0);\t/* write will compute latency here... */\n-\tlws_ser_wu64be(\u0026pre[11], (uint64_t)us);\t/* ... and set this to the write time */\n-\n-\t/*\n-\t * If we are on a non-default rideshare, append the non-default name to\n-\t * the headers of the payload part, 1-byte length first\n-\t */\n-\n-\tif (flags \u0026 LWSSS_FLAG_RIDESHARE) {\n-\t\tpre[19] \u003d (uint8_t)l;\n-\t\tmemcpy(\u0026pre[20], rsp, (unsigned int)l);\n-\t}\n-\n-\tif (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, (unsigned int)est, buf, len)) {\n-\t\tlwsl_err(\u0022%s: unable to alloc in dsh 1\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-/*\n- * event loop is consuming dsh-buffered, already-serialized tx from the\n- * foreign side\n- */\n-\n-int\n-lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,\n-\t\t\t lws_ss_tx_ordinal_t ord, uint8_t *buf,\n-\t\t\t size_t *len, int *flags)\n-{\n-\tuint8_t *p;\n-\tsize_t si;\n-\n-\tif (lws_dsh_get_head(dsh, KIND_C_TO_P, (void **)\u0026p, \u0026si)) {\n-\t\t*len \u003d 0;\n-\t\treturn 0;\n-\t}\n-\n-\t/*\n-\t * The packet in the dsh has a proxying serialization header, process\n-\t * and strip it so we just forward the payload\n-\t */\n-\n-\tif (*len \u003c\u003d si - 23 || si \u003c 23) {\n-\t\t/*\n-\t\t * What comes out of the dsh needs to fit in the tx buffer...\n-\t\t * we have arrangements at the proxy rx of the client UDS to\n-\t\t * chop chunks larger than 1380 into seuqential lumps of 1380\n-\t\t */\n-\t\tlwsl_err(\u0022%s: *len \u003d %d, si \u003d %d\u005cn\u0022, __func__, (int)*len, (int)si);\n-\t\tassert(0);\n-\t\treturn 1;\n-\t}\n-\tif (p[0] !\u003d LWSSS_SER_TXPRE_TX_PAYLOAD) {\n-\t\tassert(0);\n-\t\treturn 1;\n-\t}\n-\n-\t*len \u003d (size_t)(lws_ser_ru16be(\u0026p[1]) - (23 - 3));\n-\tif (*len !\u003d si - 23) {\n-\t\t/*\n-\t\t * We cannot accept any length that doesn't reflect the actual\n-\t\t * length of what came in from the dsh, either something nasty\n-\t\t * happened with truncation or we are being attacked\n-\t\t */\n-\t\tassert(0);\n-\n-\t\treturn 1;\n-\t}\n-\n-\tmemcpy(buf, p + 23, si - 23);\n-\n-\t*flags \u003d (int)lws_ser_ru32be(\u0026p[3]);\n-\n-\tlws_dsh_free((void **)\u0026p);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * event loop side is issuing state, serialize and put it in the dbuf for\n- * the foreign side to consume later\n- */\n-\n-int\n-lws_ss_serialize_state(struct lws *wsi, struct lws_dsh *dsh, lws_ss_constate_t state,\n-\t\t lws_ss_tx_ordinal_t ack)\n-{\n-\tuint8_t pre[12];\n-\tint n \u003d 4;\n-\n-\tif (state \u003d\u003d LWSSSCS_EVENT_WAIT_CANCELLED)\n-\t\treturn 0;\n-\n-\tlwsl_info(\u0022%s: %s, ord 0x%x\u005cn\u0022, __func__, lws_ss_state_name((int)state),\n-\t\t (unsigned int)ack);\n-\n-\tpre[0] \u003d LWSSS_SER_RXPRE_CONNSTATE;\n-\tpre[1] \u003d 0;\n-\n-\tif (state \u003e 255) {\n-\t\tpre[2] \u003d 8;\n-\t\tlws_ser_wu32be(\u0026pre[3], state);\n-\t\tn \u003d 7;\n-\t} else {\n-\t\tpre[2] \u003d 5;\n-\t\tpre[3] \u003d (uint8_t)state;\n-\t}\n-\n-\tlws_ser_wu32be(\u0026pre[n], ack);\n-\n-\tif (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, (unsigned int)n + 4, NULL, 0) ||\n-\t (wsi \u0026\u0026 lws_fi(\u0026wsi-\u003efic, \u0022sspc_dsh_ss2p_oom\u0022))) {\n-\t\tlwsl_err(\u0022%s: unable to alloc in dsh 2\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-/*\n- * event loop side was told about remote peer tx credit window update, serialize\n- * and put it in the dbuf for the foreign side to consume later\n- */\n-\n-int\n-lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr)\n-{\n-\tuint8_t pre[7];\n-\n-\tlwsl_info(\u0022%s: %d\u005cn\u0022, __func__, txcr);\n-\n-\tpre[0] \u003d LWSSS_SER_RXPRE_TXCR_UPDATE;\n-\tpre[1] \u003d 0;\n-\tpre[2] \u003d 4;\n-\tlws_ser_wu32be(\u0026pre[3], (uint32_t)txcr);\n-\n-\tif (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, 7, NULL, 0)) {\n-\t\tlwsl_err(\u0022%s: unable to alloc in dsh 2\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-/*\n- * event loop side is consuming serialized data from the client via dsh, parse\n- * it using a bytewise parser for the serialization header(s)...\n- * it's possibly coalesced\n- *\n- * client: pss is pointing to the start of userdata. We can use\n- * pss_to_sspc_h(_pss, _ssi) to convert that to a pointer to the sspc\n- * handle\n- *\n- * proxy: pss is pointing to \u0026conn-\u003ess, a pointer to the ss handle\n- *\n- * Returns one of\n- *\n- * \tLWSSSSRET_OK\n- *\tLWSSSSRET_DISCONNECT_ME\n- *\tLWSSSSRET_DESTROY_ME\n- */\n-\n-/* convert userdata ptr _pss to handle pointer, allowing for any layout in\n- * userdata */\n-#define client_pss_to_sspc_h(_pss, _ssi) (*((lws_sspc_handle_t **) \u005c\n-\t\t\t\t ((uint8_t *)_pss) + _ssi-\u003ehandle_offset))\n-/* client pss to sspc userdata */\n-#define client_pss_to_userdata(_pss) ((void *)_pss)\n-/* proxy convert pss to ss handle */\n-#define proxy_pss_to_ss_h(_pss) (*_pss)\n-\n-/* convert userdata ptr _pss to handle pointer, allowing for any layout in\n- * userdata */\n-#define client_pss_to_sspc_h(_pss, _ssi) (*((lws_sspc_handle_t **) \u005c\n-\t\t\t\t ((uint8_t *)_pss) + _ssi-\u003ehandle_offset))\n-/* client pss to sspc userdata */\n-#define client_pss_to_userdata(_pss) ((void *)_pss)\n-/* proxy convert pss to ss handle */\n-#define proxy_pss_to_ss_h(_pss) (*_pss)\n-\n-int\n-lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,\n-\t\t\t struct lws_context *context,\n-\t\t\t struct lws_dsh *dsh, const uint8_t *cp, size_t len,\n-\t\t\t lws_ss_conn_states_t *state, void *parconn,\n-\t\t\t lws_ss_handle_t **pss, lws_ss_info_t *ssi, char client)\n-{\n-\tlws_ss_state_return_t r;\n-\tlws_ss_metadata_t *pm;\n-\tlws_sspc_handle_t *h;\n-\tuint8_t pre[23];\n-\tuint32_t flags;\n-\tlws_usec_t us;\n-\tuint8_t *p;\n-\tint n;\n-\n-\twhile (len--) {\n-\n-\t\tswitch (par-\u003eps) {\n-\t\tcase RPAR_TYPE:\n-\t\t\tpar-\u003etype \u003d *cp++;\n-\t\t\tpar-\u003eps++;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_LEN_MSB: /* this is remaining frame length */\n-\t\t\tpar-\u003erem \u003d (uint16_t)((*cp++) \u003c\u003c 8);\n-\t\t\tpar-\u003eps++;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_LEN_LSB:\n-\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem | *cp++);\n-\t\t\tswitch (par-\u003etype) {\n-\n-\t\t\t/* event loop side */\n-\n-\t\t\tcase LWSSS_SER_TXPRE_TX_PAYLOAD:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (*state !\u003d LPCSPROX_OPERATIONAL)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tpar-\u003eps \u003d RPAR_FLAG_B3;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_DESTROYING:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\t\tlwsl_cx_notice(context, \u0022DESTROYING\u0022);\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_ONWARD_CONNECT:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tif (*state !\u003d LPCSPROX_OPERATIONAL)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\t\tlwsl_cx_notice(context, \u0022ONWARD_CONNECT\u0022);\n-\n-\t\t\t\t/*\n-\t\t\t\t * Shrug it off if we are already connecting or\n-\t\t\t\t * connected\n-\t\t\t\t */\n-\n-\t\t\t\tif (!proxy_pss_to_ss_h(pss) ||\n-\t\t\t\t proxy_pss_to_ss_h(pss)-\u003ewsi)\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\t/*\n-\t\t\t\t * We're going to try to do the onward connect\n-\t\t\t\t */\n-\n-\t\t\t\tif ((proxy_pss_to_ss_h(pss) \u0026\u0026\n-\t\t\t\t lws_fi(\u0026proxy_pss_to_ss_h(pss)-\u003efic, \u0022ssproxy_onward_conn_fail\u0022)) ||\n-\t\t\t\t _lws_ss_client_connect(proxy_pss_to_ss_h(pss),\n-\t\t\t\t\t\t\t 0, parconn) \u003d\u003d\n-\t\t\t\t\t\t\t LWSSSSRET_DESTROY_ME)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_STREAMTYPE:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (*state !\u003d LPCSPROX_WAIT_INITIAL_TX)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (par-\u003erem \u003c 1 + 4 + 1)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003eps \u003d RPAR_INIT_PROVERS;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_METADATA:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (par-\u003erem \u003c 3)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tpar-\u003eps \u003d RPAR_METADATA_NAMELEN;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_TXCR_UPDATE:\n-\t\t\t\tpar-\u003eps \u003d RPAR_TXCR0;\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_TIMEOUT_UPDATE:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (par-\u003erem !\u003d 4)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003eps \u003d RPAR_TIMEOUT0;\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT:\n-\t\t\t\tif (client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (par-\u003erem !\u003d 4)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003eps \u003d RPAR_PAYLEN0;\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\t/* client side */\n-\n-\t\t\tcase LWSSS_SER_RXPRE_RX_PAYLOAD:\n-\t\t\t\tif (!client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (*state !\u003d LPCSCLI_OPERATIONAL \u0026\u0026\n-\t\t\t\t *state !\u003d LPCSCLI_LOCAL_CONNECTED)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tpar-\u003erideshare[0] \u003d '\u005c0';\n-\t\t\t\tpar-\u003eps \u003d RPAR_FLAG_B3;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_RXPRE_CREATE_RESULT:\n-\t\t\t\tif (!client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (*state !\u003d LPCSCLI_WAITING_CREATE_RESULT)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tif (par-\u003erem \u003c 1)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tpar-\u003eps \u003d RPAR_RESULT_CREATION;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_RXPRE_CONNSTATE:\n-\t\t\t\tif (!client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (*state !\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n-\t\t\t\t *state !\u003d LPCSCLI_OPERATIONAL)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tif (par-\u003erem \u003c 5 || par-\u003erem \u003e 8)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tpar-\u003eps \u003d RPAR_STATEINDEX;\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_RXPRE_METADATA:\n-\t\t\t\tif (!client)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tif (par-\u003erem \u003c 3)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tpar-\u003eps \u003d RPAR_METADATA_NAMELEN;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_RXPRE_TXCR_UPDATE:\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tpar-\u003eps \u003d RPAR_RX_TXCR_UPDATE;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSS_SER_RXPRE_PERF:\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tif (!par-\u003erem)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tpar-\u003eps \u003d RPAR_PERF;\n-\t\t\t\tbreak;\n-\n-\t\t\tdefault:\n-\t\t\t\tlwsl_cx_notice(context, \u0022bad type 0x%x\u0022,\n-\t\t\t\t\t par-\u003etype);\n-\t\t\t\tgoto hangup;\n-\t\t\t}\n-\t\t\tbreak;\n-\n-\t\t\tcase RPAR_FLAG_B3:\n-\t\t\tcase RPAR_FLAG_B2:\n-\t\t\tcase RPAR_FLAG_B1:\n-\t\t\tcase RPAR_FLAG_B0:\n-\t\t\t\tpar-\u003eflags \u003c\u003c\u003d 8;\n-\t\t\t\tpar-\u003eflags |\u003d *cp++;\n-\t\t\t\tpar-\u003eps++;\n-\t\t\t\tif (!par-\u003erem--)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase RPAR_LATA3:\n-\t\t\tcase RPAR_LATA2:\n-\t\t\tcase RPAR_LATA1:\n-\t\t\tcase RPAR_LATA0:\n-\t\t\t\tpar-\u003eusd_phandling \u003c\u003c\u003d 8;\n-\t\t\t\tpar-\u003eusd_phandling |\u003d *cp++;\n-\t\t\t\tpar-\u003eps++;\n-\t\t\t\tif (!par-\u003erem--)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase RPAR_LATB7:\n-\t\t\tcase RPAR_LATB6:\n-\t\t\tcase RPAR_LATB5:\n-\t\t\tcase RPAR_LATB4:\n-\t\t\tcase RPAR_LATB3:\n-\t\t\tcase RPAR_LATB2:\n-\t\t\tcase RPAR_LATB1:\n-\t\t\tcase RPAR_LATB0:\n-\t\t\t\tpar-\u003eust_pwait \u003c\u003c\u003d 8;\n-\t\t\t\tpar-\u003eust_pwait |\u003d *cp++;\n-\t\t\t\tpar-\u003eps++;\n-\t\t\t\tpar-\u003efrag1 \u003d 1;\n-\t\t\t\tif (!par-\u003erem--)\n-\t\t\t\t\tgoto hangup;\n-\n-\t\t\t\tif (par-\u003eps \u003d\u003d RPAR_RIDESHARE_LEN \u0026\u0026\n-\t\t\t\t !(par-\u003eflags \u0026 LWSSS_FLAG_RIDESHARE))\n-\t\t\t\t\tpar-\u003eps \u003d RPAR_PAYLOAD;\n-\n-\t\t\t\tif (par-\u003erem)\n-\t\t\t\t\tbreak;\n-\n-\t\t\t\t/* fallthru - handle 0-length payload */\n-\n-\t\t\t\tif (!(par-\u003eflags \u0026 LWSSS_FLAG_RIDESHARE))\n-\t\t\t\t\tgoto payload_ff;\n-\t\t\t\tgoto hangup;\n-\n-\t\t\t/*\n-\t\t\t * Inbound rideshare info is provided on the RX packet\n-\t\t\t * itself\n-\t\t\t */\n-\n-\t\tcase RPAR_RIDESHARE_LEN:\n-\t\t\tpar-\u003eslen \u003d *cp++;\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tpar-\u003eps++;\n-\t\t\tif (par-\u003erem-- \u003c par-\u003eslen)\n-\t\t\t\tgoto hangup;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_PERF:\n-\t\t\tn \u003d (int)len + 1;\n-\t\t\tif (n \u003e par-\u003erem)\n-\t\t\t\tn \u003d par-\u003erem;\n-\n-\t\t\tif (client \u0026\u0026\n-\t\t\t client_pss_to_sspc_h(pss, ssi) \u0026\u0026\n-\t\t\t ssi-\u003erx) {\n-\t\t\t\tint ret;\n-\n-\t\t\t\t/* we still have an sspc handle */\n-\t\t\t\tret \u003d ssi-\u003erx(client_pss_to_userdata(pss),\n-\t\t\t\t\t(uint8_t *)cp, (unsigned int)n,\n-\t\t\t\t\t(int)(LWSSS_FLAG_SOM | LWSSS_FLAG_EOM |\n-\t\t\t\t\t\t\tLWSSS_FLAG_PERF_JSON));\n-\n-\t\t\t\tif (lws_fi(\u0026client_pss_to_sspc_h(pss, ssi)-\u003efic,\n-\t\t\t\t\t\t \u0022sspc_perf_rx_fake_destroy_me\u0022))\n-\t\t\t\t\tret \u003d LWSSSSRET_DESTROY_ME;\n-\n-\t\t\t\tswitch (ret) {\n-\t\t\t\tcase LWSSSSRET_OK:\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif (n) {\n-\t\t\t\tcp +\u003d n;\n-\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem - (uint16_t)(unsigned int)n);\n-\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n-\t\t\t}\n-\t\t\tif (!par-\u003erem)\n-\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_RIDESHARE:\n-\t\t\tpar-\u003erideshare[par-\u003ectr++] \u003d (char)*cp++;\n-\t\t\tif (!par-\u003erem--)\n-\t\t\t\tgoto hangup;\n-\t\t\tif (par-\u003ectr !\u003d par-\u003eslen)\n-\t\t\t\tbreak;\n-\t\t\tpar-\u003eps \u003d RPAR_PAYLOAD;\n-\t\t\tif (par-\u003erem)\n-\t\t\t\tbreak;\n-\n-\t\t\t/* fallthru - handle 0-length payload */\n-\n-\t\tcase RPAR_PAYLOAD:\n-payload_ff:\n-\t\t\tn \u003d (int)len + 1;\n-\t\t\tif (n \u003e par-\u003erem)\n-\t\t\t\tn \u003d par-\u003erem;\n-\t\t\t/*\n-\t\t\t * We get called with a serialized buffer of a size\n-\t\t\t * chosen by the client. We can only create dsh entries\n-\t\t\t * with up to 1380 payload, to guarantee we can emit\n-\t\t\t * them on the onward connection atomically.\n-\t\t\t *\n-\t\t\t * If 1380 isn't enough to cover what was handed to us,\n-\t\t\t * we'll stop at 1380 and go around again and create\n-\t\t\t * more dsh entries for the rest, with their own\n-\t\t\t * headers.\n-\t\t\t */\n-\n-\t\t\tif (n \u003e 1380)\n-\t\t\t\tn \u003d 1380;\n-\n-\t\t\t/*\n-\t\t\t * Since we're in the business of fragmenting client\n-\t\t\t * serialized payloads at 1380, we have to deal with\n-\t\t\t * refragmenting the SOM / EOM flags that covered the\n-\t\t\t * whole client serialized packet, so they apply to\n-\t\t\t * each dsh entry we split it into correctly\n-\t\t\t */\n-\n-\t\t\tflags \u003d par-\u003eflags \u0026 LWSSS_FLAG_RELATED_START;\n-\t\t\tif (par-\u003efrag1)\n-\t\t\t\t/*\n-\t\t\t\t * Only set the first time we came to this\n-\t\t\t\t * state after deserialization of the header\n-\t\t\t\t */\n-\t\t\t\tflags |\u003d par-\u003eflags \u0026\n-\t\t\t\t (LWSSS_FLAG_SOM | LWSSS_FLAG_POLL);\n-\n-\t\t\tif (par-\u003erem \u003d\u003d n)\n-\t\t\t\t/*\n-\t\t\t\t * We are going to complete the advertised\n-\t\t\t\t * payload length from the client on this dsh,\n-\t\t\t\t * so give him the EOM type flags if any\n-\t\t\t\t */\n-\t\t\t\tflags |\u003d par-\u003eflags \u0026 (LWSSS_FLAG_EOM |\n-\t\t\t\t\t\tLWSSS_FLAG_RELATED_END);\n-\n-\t\t\tpar-\u003efrag1 \u003d 0;\n-\t\t\tus \u003d lws_now_usecs();\n-\n-\t\t\tif (!client) {\n-\t\t\t\tlws_ss_handle_t *hss;\n-\n-\t\t\t\t/*\n-\t\t\t\t * Proxy - we received some serialized tx from\n-\t\t\t\t * the client.\n-\t\t\t\t *\n-\t\t\t\t * The header for buffering private to the\n-\t\t\t\t * proxy is 23 bytes vs 19, so we can hold the\n-\t\t\t\t * current time when it was buffered\n-\t\t\t\t * additionally\n-\t\t\t\t */\n-\n-\t\t\t\thss \u003d proxy_pss_to_ss_h(pss);\n-\t\t\t\tif (hss)\n-\t\t\t\t\tlwsl_ss_info(hss, \u0022C2P RX: len %d\u0022, (int)n);\n-\n-\t\t\t\tp \u003d pre;\n-\t\t\t\tpre[0] \u003d LWSSS_SER_TXPRE_TX_PAYLOAD;\n-\t\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)((unsigned int)n + 23 - 3));\n-\t\t\t\tlws_ser_wu32be(\u0026p[3], flags);\n-\t\t\t\t/* us held at client before written */\n-\t\t\t\tlws_ser_wu32be(\u0026p[7], par-\u003eusd_phandling);\n-\t\t\t\t/* us taken for transit to proxy */\n-\t\t\t\tlws_ser_wu32be(\u0026p[11], (uint32_t)(us - (lws_usec_t)par-\u003eust_pwait));\n-\t\t\t\t/* time used later to find proxy hold time */\n-\t\t\t\tlws_ser_wu64be(\u0026p[15], (uint64_t)us);\n-\n-\t\t\t\tif ((hss \u0026\u0026\n-\t\t\t\t lws_fi(\u0026hss-\u003efic, \u0022ssproxy_dsh_c2p_pay_oom\u0022)) ||\n-\t\t\t\t lws_dsh_alloc_tail(dsh, KIND_C_TO_P, pre,\n-\t\t\t\t\t\t 23, cp, (unsigned int)n)) {\n-\t\t\t\t\tlwsl_ss_err(hss, \u0022unable to alloc in dsh 3\u0022);\n-\n-\t\t\t\t\treturn LWSSSSRET_DISCONNECT_ME;\n-\t\t\t\t}\n-\n-\t\t\t\tif (hss)\n-\t\t\t\t\t_lws_ss_request_tx(hss);\n-\t\t\t} else {\n-\n-\t\t\t\t/*\n-\t\t\t\t * Client receives some RX from proxy\n-\t\t\t\t *\n-\t\t\t\t * Pass whatever payload we have to ss user\n-\t\t\t\t */\n-\n-\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t,\n-\t\t\t\t\t\t parser);\n-\t\t\t\th-\u003etxc.peer_tx_cr_est -\u003d n;\n-\n-\t\t\t\tlwsl_sspc_info(h, \u0022P2C RX: len %d\u0022, (int)n);\n-\n-\t\t\t\tif (ssi-\u003erx \u0026\u0026 client_pss_to_sspc_h(pss, ssi)) {\n-\t\t\t\t\t/* we still have an sspc handle */\n-\t\t\t\t\tint ret;\n-\n-\t\t\t\t\tret \u003d ssi-\u003erx(client_pss_to_userdata(pss),\n-\t\t\t\t\t\t(uint8_t *)cp, (unsigned int)n, (int)flags);\n-\n-\t\t\t\t\tif (client_pss_to_sspc_h(pss, ssi) \u0026\u0026\n-\t\t\t\t\t lws_fi(\u0026client_pss_to_sspc_h(pss, ssi)-\u003efic, \u0022sspc_rx_fake_destroy_me\u0022))\n-\t\t\t\t\t\tret \u003d LWSSSSRET_DESTROY_ME;\n-\n-\t\t\t\t\tswitch (ret) {\n-\t\t\t\t\tcase LWSSSSRET_OK:\n-\t\t\t\t\t\tbreak;\n-\t\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\t\t\t\tgoto hangup;\n-\t\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\n-#if 0\n-\t\t\t\tif (lws_det_lat_active(context)) {\n-\t\t\t\t\tlws_detlat_t d;\n-\n-\t\t\t\t\td.type \u003d LDLT_READ;\n-\t\t\t\t\td.acc_size \u003d d.req_size \u003d n;\n-\t\t\t\t\td.latencies[LAT_DUR_USERCB] \u003d\n-\t\t\t\t\t\t\tlws_now_usecs() - us;\n-\t\t\t\t\td.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] \u003d\n-\t\t\t\t\t\t\tpar-\u003eusd_phandling;\n-\t\t\t\t\td.latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX] \u003d\n-\t\t\t\t\t\tus - par-\u003eust_pwait;\n-\n-\t\t\t\t\tlws_det_lat_cb(context, \u0026d);\n-\t\t\t\t}\n-#endif\n-\t\t\t}\n-\n-\t\t\tif (n) {\n-\t\t\t\tcp +\u003d n;\n-\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem - (uint16_t)(unsigned int)n);\n-\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n-\t\t\t\t/*\n-\t\t\t\t * if we didn't consume it all, we'll come\n-\t\t\t\t * around again and produce more dsh entries up\n-\t\t\t\t * to 1380 each until it is gone\n-\t\t\t\t */\n-\t\t\t}\n-\t\t\tif (!par-\u003erem)\n-\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_RX_TXCR_UPDATE:\n-\t\t\tif (!--par-\u003erem \u0026\u0026 par-\u003ectr !\u003d 3)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n-\t\t\tif (++par-\u003ectr \u003c 4)\n-\t\t\t\tbreak;\n-\n-\t\t\t/*\n-\t\t\t * Proxy is telling us remote endpoint is allowing us\n-\t\t\t * par-\u003etemp32 more bytes tx credit to write to it\n-\t\t\t */\n-\n-\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n-\t\t\th-\u003etxc.tx_cr +\u003d par-\u003etemp32;\n-\t\t\tlwsl_info(\u0022%s: RX_PEER_TXCR: %d\u005cn\u0022, __func__, par-\u003etemp32);\n-\t\t\tlws_sspc_request_tx(h); /* in case something waiting */\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_INIT_PROVERS:\n-\t\t\t/* Protocol version byte for this connection */\n-\t\t\tpar-\u003eprotocol_version \u003d *cp++;\n-\n-\t\t\t/*\n-\t\t\t * So we have to know what versions of the serialization\n-\t\t\t * protocol we can support at the proxy side, and\n-\t\t\t * reject anythng we don't know how to deal with\n-\t\t\t * noisily in the logs.\n-\t\t\t */\n-\n-\t\t\tif (par-\u003eprotocol_version !\u003d 1) {\n-\t\t\t\tlwsl_err(\u0022%s: Rejecting client with \u0022\n-\t\t\t\t\t \u0022unsupported SSv%d protocol\u005cn\u0022,\n-\t\t\t\t\t __func__, par-\u003eprotocol_version);\n-\n-\t\t\t\tgoto hangup;\n-\t\t\t}\n-\n-\t\t\tif (!--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tpar-\u003eps \u003d RPAR_INIT_PID;\n-\t\t\tbreak;\n-\n-\n-\t\tcase RPAR_INIT_PID:\n-\t\t\tif (!--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n-\t\t\tif (++par-\u003ectr \u003c 4)\n-\t\t\t\tbreak;\n-\n-\t\t\tpar-\u003eclient_pid \u003d (uint32_t)par-\u003etemp32;\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tpar-\u003eps \u003d RPAR_INITTXC0;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_INITTXC0:\n-\t\t\tif (!--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n-\t\t\tif (++par-\u003ectr \u003c 4)\n-\t\t\t\tbreak;\n-\n-\t\t\tpar-\u003etxcr_out \u003d par-\u003etemp32;\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tpar-\u003eps \u003d RPAR_STREAMTYPE;\n-\t\t\tbreak;\n-\n-\t\t/*\n-\t\t * These are the client adjusting our / the remote peer ability\n-\t\t * to send back to him. He's sending a signed u32 BE\n-\t\t */\n-\n-\t\tcase RPAR_TXCR0:\n-\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n-\t\t\tif (++par-\u003ectr \u003c 4) {\n-\t\t\t\tif (!--par-\u003erem)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tif (--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tif (!client) {\n-\t\t\t\t/*\n-\t\t\t\t * We're the proxy, being told by the client\n-\t\t\t\t * that it wants to allow more tx from the peer\n-\t\t\t\t * on the onward connection towards it.\n-\t\t\t\t */\n-#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT)\n-\t\t\t\tif (proxy_pss_to_ss_h(pss) \u0026\u0026\n-\t\t\t\t proxy_pss_to_ss_h(pss)-\u003ewsi) {\n-\t\t\t\t\tlws_wsi_tx_credit(\n-\t\t\t\t\t\tproxy_pss_to_ss_h(pss)-\u003ewsi,\n-\t\t\t\t\t\t\t LWSTXCR_PEER_TO_US,\n-\t\t\t\t\t\t\t par-\u003etemp32);\n-\t\t\t\t\tlwsl_notice(\u0022%s: proxy RX_PEER_TXCR: +%d (est %d)\u005cn\u0022,\n-\t\t\t\t\t\t __func__, par-\u003etemp32,\n-\t\t\t\t\t\t proxy_pss_to_ss_h(pss)-\u003ewsi-\u003e\n-\t\t\t\t\t\t\t txc.peer_tx_cr_est);\n-\t\t\t\t\t_lws_ss_request_tx(proxy_pss_to_ss_h(pss));\n-\t\t\t\t} else\n-#endif\n-\t\t\t\t\tlwsl_info(\u0022%s: dropping TXCR\u005cn\u0022, __func__);\n-\t\t\t} else {\n-\t\t\t\t/*\n-\t\t\t\t * We're the client, being told by the proxy\n-\t\t\t\t * about tx credit being given to us from the\n-\t\t\t\t * remote peer, allowing the client to write to\n-\t\t\t\t * it.\n-\t\t\t\t */\n-\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t,\n-\t\t\t\t\t\t parser);\n-\t\t\t\th-\u003etxc.tx_cr +\u003d par-\u003etemp32;\n-\t\t\t\tlwsl_info(\u0022%s: client RX_PEER_TXCR: %d\u005cn\u0022,\n-\t\t\t\t\t\t\t__func__, par-\u003etemp32);\n-\t\t\t\tlws_sspc_request_tx(h); /* in case something waiting */\n-\t\t\t}\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_TIMEOUT0:\n-\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n-\t\t\tif (++par-\u003ectr \u003c 4) {\n-\t\t\t\tif (!--par-\u003erem)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tif (--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\t/*\n-\t\t\t * Proxy...\n-\t\t\t *\n-\t\t\t * *pss may have gone away asynchronously inbetweentimes\n-\t\t\t */\n-\n-\t\t\tif (proxy_pss_to_ss_h(pss)) {\n-\n-\t\t\t\tif ((unsigned int)par-\u003etemp32 \u003d\u003d 0xffffffff) {\n-\t\t\t\t\tlwsl_notice(\u0022%s: cancel ss timeout\u005cn\u0022,\n-\t\t\t\t\t\t\t__func__);\n-\t\t\t\t\tlws_ss_cancel_timeout(\n-\t\t\t\t\t\tproxy_pss_to_ss_h(pss));\n-\t\t\t\t} else {\n-\n-\t\t\t\t\tif (!par-\u003etemp32)\n-\t\t\t\t\t\tpar-\u003etemp32 \u003d (int)\n-\t\t\t\t\t\t proxy_pss_to_ss_h(pss)-\u003e\n-\t\t\t\t\t\t\t policy-\u003etimeout_ms;\n-\n-\t\t\t\t\tlwsl_notice(\u0022%s: set ss timeout for +%ums\u005cn\u0022,\n-\t\t\t\t\t\t__func__, par-\u003etemp32);\n-\n-\t\t\t\t\tlws_ss_start_timeout(\n-\t\t\t\t\t\tproxy_pss_to_ss_h(pss), (unsigned int)\n-\t\t\t\t\t\t\t\tpar-\u003etemp32);\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_PAYLEN0:\n-\t\t\t/*\n-\t\t\t * It's the length from lws_ss_request_tx_len() being\n-\t\t\t * passed up to the proxy\n-\t\t\t */\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n-\t\t\tif (++par-\u003ectr \u003c 4) {\n-\t\t\t\tif (!--par-\u003erem)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tif (--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tlwsl_notice(\u0022%s: set payload len %u\u005cn\u0022, __func__,\n-\t\t\t\t par-\u003etemp32);\n-\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\n-\t\t\tif (proxy_pss_to_ss_h(pss)) {\n-\t\t\t\tr \u003d lws_ss_request_tx_len(proxy_pss_to_ss_h(pss),\n-\t\t\t\t\t\t\t(unsigned long)par-\u003etemp32);\n-\t\t\t\tif (r \u003d\u003d LWSSSSRET_DESTROY_ME)\n-\t\t\t\t\tgoto hangup;\n-\t\t\t}\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_METADATA_NAMELEN:\n-\t\t\t/* both client and proxy */\n-\t\t\tif (!--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\t\t\tpar-\u003eslen \u003d *cp++;\n-\t\t\tif (par-\u003eslen \u003e\u003d sizeof(par-\u003emetadata_name) - 1)\n-\t\t\t\tgoto hangup;\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tpar-\u003eps++;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_METADATA_NAME:\n-\t\t\t/* both client and proxy */\n-\t\t\tif (!--par-\u003erem)\n-\t\t\t\tgoto hangup;\n-\t\t\tpar-\u003emetadata_name[par-\u003ectr++] \u003d (char)*cp++;\n-\t\t\tif (par-\u003ectr !\u003d par-\u003eslen)\n-\t\t\t\tbreak;\n-\t\t\tpar-\u003emetadata_name[par-\u003ectr] \u003d '\u005c0';\n-\t\t\tpar-\u003eps \u003d RPAR_METADATA_VALUE;\n-\n-\t\t\tif (client) {\n-\t\t\t\tlws_sspc_metadata_t *md;\n-\t\t\t\tlws_sspc_handle_t *h \u003d\n-\t\t\t\t\t\tclient_pss_to_sspc_h(pss, ssi);\n-\n-\t\t\t\t/*\n-\t\t\t\t * client side does not have access to policy\n-\t\t\t\t * and any metadata are new to it each time,\n-\t\t\t\t * we allocate them, removing any existing with\n-\t\t\t\t * the same name first\n-\t\t\t\t */\n-\n-\t\t\t\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n-\t\t\t\t\t\tlws_dll2_get_head(\n-\t\t\t\t\t\t\t\u0026h-\u003emetadata_owner_rx)) {\n-\t\t\t\t\tmd \u003d lws_container_of(d,\n-\t\t\t\t\t\t lws_sspc_metadata_t, list);\n-\n-\t\t\t\t\tif (!strcmp(md-\u003ename,\n-\t\t\t\t\t\t par-\u003emetadata_name)) {\n-\t\t\t\t\t\tlws_dll2_remove(\u0026md-\u003elist);\n-\t\t\t\t\t\tlws_free(md);\n-\t\t\t\t\t}\n-\n-\t\t\t\t} lws_end_foreach_dll_safe(d, d1);\n-\n-\t\t\t\t/*\n-\t\t\t\t * Create the client's rx metadata entry\n-\t\t\t\t */\n-\n-\t\t\t\tif (h \u0026\u0026 lws_fi(\u0026h-\u003efic, \u0022sspc_rx_metadata_oom\u0022))\n-\t\t\t\t\tmd \u003d NULL;\n-\t\t\t\telse\n-\t\t\t\t\tmd \u003d lws_malloc(sizeof(lws_sspc_metadata_t) +\n-\t\t\t\t\t\tpar-\u003erem + 1, \u0022rxmeta\u0022);\n-\t\t\t\tif (!md) {\n-\t\t\t\t\tlwsl_err(\u0022%s: OOM\u005cn\u0022, __func__);\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\t}\n-\t\t\t\tmemset(md, 0, sizeof(lws_sspc_metadata_t));\n-\n-\t\t\t\tlws_strncpy(md-\u003ename, par-\u003emetadata_name,\n-\t\t\t\t\t\tsizeof(md-\u003ename));\n-\t\t\t\tmd-\u003elen \u003d par-\u003erem;\n-\t\t\t\tpar-\u003erxmetaval \u003d (uint8_t *)\u0026md[1];\n-\t\t\t\t/*\n-\t\t\t\t * Overallocate by 1 and put a NUL just beyond\n-\t\t\t\t * the official md-\u003elen, so value can be easily\n-\t\t\t\t * dereferenced safely for NUL-terminated string\n-\t\t\t\t * apis that's the most common usage\n-\t\t\t\t */\n-\t\t\t\tpar-\u003erxmetaval[md-\u003elen] \u003d '\u005c0';\n-\t\t\t\tlws_dll2_add_tail(\u0026md-\u003elist,\n-\t\t\t\t\t\t \u0026h-\u003emetadata_owner_rx);\n-\t\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\t/* proxy side is receiving it */\n-\n-\t\t\tif (!proxy_pss_to_ss_h(pss))\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tif (!proxy_pss_to_ss_h(pss)-\u003epolicy) {\n-\t\t\t\tlwsl_err(\u0022%s: null policy\u005cn\u0022, __func__);\n-\t\t\t\tgoto hangup;\n-\t\t\t}\n-\n-\t\t\t/*\n-\t\t\t * This is the policy's metadata list for the given\n-\t\t\t * name\n-\t\t\t */\n-\t\t\tpm \u003d lws_ss_policy_metadata(\n-\t\t\t\t\tproxy_pss_to_ss_h(pss)-\u003epolicy,\n-\t\t\t\t\tpar-\u003emetadata_name);\n-\t\t\tif (!pm) {\n-\t\t\t\tlwsl_err(\u0022%s: metadata %s not in proxy policy\u005cn\u0022,\n-\t\t\t\t\t __func__, par-\u003emetadata_name);\n-\n-\t\t\t\tgoto hangup;\n-\t\t\t}\n-\n-\t\t\tpar-\u003essmd \u003d lws_ss_get_handle_metadata(\n-\t\t\t\t\tproxy_pss_to_ss_h(pss),\n-\t\t\t\t\tpar-\u003emetadata_name);\n-\n-\t\t\tif (par-\u003essmd) {\n-\n-\t\t\t\tif (par-\u003essmd-\u003evalue_on_lws_heap)\n-\t\t\t\t\tlws_free_set_NULL(par-\u003essmd-\u003evalue__may_own_heap);\n-\t\t\t\tpar-\u003essmd-\u003evalue_on_lws_heap \u003d 0;\n-\n-\t\t\t\tif (proxy_pss_to_ss_h(pss) \u0026\u0026\n-\t\t\t\t lws_fi(\u0026proxy_pss_to_ss_h(pss)-\u003efic, \u0022ssproxy_rx_metadata_oom\u0022))\n-\t\t\t\t\tpar-\u003essmd-\u003evalue__may_own_heap \u003d NULL;\n-\t\t\t\telse\n-\t\t\t\t\tpar-\u003essmd-\u003evalue__may_own_heap \u003d\n-\t\t\t\t\t\tlws_malloc((unsigned int)par-\u003erem + 1, \u0022metadata\u0022);\n-\n-\t\t\t\tif (!par-\u003essmd-\u003evalue__may_own_heap) {\n-\t\t\t\t\tlwsl_err(\u0022%s: OOM mdv\u005cn\u0022, __func__);\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\t}\n-\t\t\t\tpar-\u003essmd-\u003elength \u003d par-\u003erem;\n-\t\t\t\t((uint8_t *)par-\u003essmd-\u003evalue__may_own_heap)[par-\u003erem] \u003d '\u005c0';\n-\t\t\t\t/* mark it as needing cleanup */\n-\t\t\t\tpar-\u003essmd-\u003evalue_on_lws_heap \u003d 1;\n-\t\t\t}\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_METADATA_VALUE:\n-\t\t\t/* both client and proxy */\n-\n-\t\t\tif (client) {\n-\t\t\t\t*par-\u003erxmetaval++ \u003d *cp++;\n-\t\t\t} else\n-\t\t\t\t((uint8_t *)(par-\u003essmd-\u003evalue__may_own_heap))[par-\u003ectr++] \u003d *cp++;\n-\n-\t\t\tif (--par-\u003erem)\n-\t\t\t\tbreak;\n-\n-\t\t\t/* we think we got all the value */\n-\t\t\tif (client) {\n-\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n-\t\t\t\tlwsl_sspc_notice(h, \u0022RX METADATA %s\u0022,\n-\t\t\t\t\t\t\tpar-\u003emetadata_name);\n-\t\t\t} else {\n-\t\t\t\tlwsl_ss_info(proxy_pss_to_ss_h(pss),\n-\t\t\t\t\t \u0022RPAR_METADATA_VALUE for %s (len %d)\u0022,\n-\t\t\t\t\t par-\u003essmd-\u003ename,\n-\t\t\t\t\t (int)par-\u003essmd-\u003elength);\n-\t\t\t\tlwsl_hexdump_ss_info(proxy_pss_to_ss_h(pss),\n-\t\t\t\t\t\tpar-\u003essmd-\u003evalue__may_own_heap,\n-\t\t\t\t\t\tpar-\u003essmd-\u003elength);\n-\t\t\t}\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_STREAMTYPE:\n-\n-\t\t\t/* only the proxy can get these */\n-\n-\t\t\tif (client)\n-\t\t\t\tgoto hangup;\n-\t\t\tif (par-\u003ectr \u003d\u003d sizeof(par-\u003estreamtype) - 1)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\t/*\n-\t\t\t * We can only expect to get this if we ourselves are\n-\t\t\t * in the state that we're waiting for it. If it comes\n-\t\t\t * later it's a protocol error.\n-\t\t\t */\n-\n-\t\t\tif (*state !\u003d LPCSPROX_WAIT_INITIAL_TX)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\t/*\n-\t\t\t * We're the proxy, creating an SS on behalf of a\n-\t\t\t * client\n-\t\t\t */\n-\n-\t\t\tpar-\u003estreamtype[par-\u003ectr++] \u003d (char)*cp++;\n-\t\t\tif (--par-\u003erem)\n-\t\t\t\tbreak;\n-\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tpar-\u003estreamtype[par-\u003ectr] \u003d '\u005c0';\n-\t\t\tlwsl_info(\u0022%s: proxy ss '%s', sssv%d, txcr %d\u005cn\u0022,\n-\t\t\t\t __func__, par-\u003estreamtype,\n-\t\t\t\t par-\u003eprotocol_version, par-\u003etxcr_out);\n-\n-\t\t\tssi-\u003estreamtype \u003d par-\u003estreamtype;\n-\t\t\tif (par-\u003etxcr_out) // !!!\n-\t\t\t\tssi-\u003emanual_initial_tx_credit \u003d par-\u003etxcr_out;\n-\n-\t\t\t/*\n-\t\t\t * Even for a synthetic SS proxing action like _lws_smd,\n-\t\t\t * we create an actual SS in the proxy representing the\n-\t\t\t * connection\n-\t\t\t */\n-\n-\t\t\tssi-\u003eflags |\u003d LWSSSINFLAGS_PROXIED;\n-\t\t\tssi-\u003esss_protocol_version \u003d par-\u003eprotocol_version;\n-\t\t\tssi-\u003eclient_pid \u003d par-\u003eclient_pid;\n-\n-\t\t\tif (lws_ss_create(context, 0, ssi, parconn, pss,\n-\t\t\t\t\t NULL, NULL)) {\n-\t\t\t\t/*\n-\t\t\t\t * We're unable to create the onward secure\n-\t\t\t\t * stream he asked for... schedule a chance to\n-\t\t\t\t * inform him\n-\t\t\t\t */\n-\t\t\t\tlwsl_err(\u0022%s: create '%s' fail\u005cn\u0022, __func__,\n-\t\t\t\t\t par-\u003estreamtype);\n-\t\t\t\t*state \u003d LPCSPROX_REPORTING_FAIL;\n-\t\t\t\tbreak;\n-\t\t\t} else {\n-\t\t\t\tlwsl_debug(\u0022%s: create '%s' OK\u005cn\u0022,\n-\t\t\t\t\t__func__, par-\u003estreamtype);\n-\t\t\t\t*state \u003d LPCSPROX_REPORTING_OK;\n-\t\t\t}\n-\n-\t\t\tif (*pss) {\n-\t\t\t\t(*pss)-\u003ebeing_serialized \u003d 1;\n-#if defined(LWS_WITH_SYS_SMD)\n-\t\t\t\tif ((*pss)-\u003epolicy !\u003d \u0026pol_smd)\n-\t\t\t\t\t/*\n-\t\t\t\t\t * In SMD case we overloaded the\n-\t\t\t\t\t * initial credit to be the class mask\n-\t\t\t\t\t */\n-#endif\n-\t\t\t\t{\n-\t\t\t\t\tlwsl_info(\u0022%s: Created SS initial credit %d\u005cn\u0022,\n-\t\t\t\t\t\t__func__, par-\u003etxcr_out);\n-\n-\t\t\t\t\t(*pss)-\u003einfo.manual_initial_tx_credit \u003d par-\u003etxcr_out;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\t/* parent needs to schedule write on client conn */\n-\t\t\tbreak;\n-\n-\t\t/* clientside states */\n-\n-\t\tcase RPAR_RESULT_CREATION:\n-\t\t\tif (*cp++) {\n-\t\t\t\tlwsl_err(\u0022%s: stream creation failed\u005cn\u0022,\n-\t\t\t\t\t __func__);\n-\t\t\t\tgoto hangup;\n-\t\t\t}\n-\n-\t\t\tif (--par-\u003erem \u003c 4)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tpar-\u003eps \u003d RPAR_RESULT_CREATION_DSH;\n-\t\t\tpar-\u003ectr \u003d 0;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_RESULT_CREATION_DSH:\n-\n-\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | (*cp++);\n-\t\t\tif (!par-\u003erem--)\n-\t\t\t\tgoto hangup;\n-\t\t\tif (++par-\u003ectr \u003c 4)\n-\t\t\t\tbreak;\n-\n-\t\t\t/*\n-\t\t\t * Client (par-\u003etemp32 \u003d\u003d dsh alloc)\n-\t\t\t */\n-\n-\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n-\n-\t\t\tlws_ss_serialize_state_transition(h, state,\n-\t\t\t\t\t\t\t LPCSCLI_LOCAL_CONNECTED);\n-\n-\t\t\tlws_set_timeout(h-\u003ecwsi, NO_PENDING_TIMEOUT, 0);\n-\n-\t\t\tif (h-\u003edsh)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\t/*\n-\t\t\t * This is telling us that the streamtype could be (and\n-\t\t\t * was) created at the proxy. It's not telling us that\n-\t\t\t * the onward peer connection could be connected.\n-\t\t\t *\n-\t\t\t * We'll get a proxied state() coming later that informs\n-\t\t\t * us about the situation with that.\n-\t\t\t *\n-\t\t\t * However at this point, we should choose to inform\n-\t\t\t * the client that his stream was created... we will\n-\t\t\t * later get a proxied CREATING state from the peer\n-\t\t\t * but we should do it now and suppress the later one.\n-\t\t\t *\n-\t\t\t * The reason is he may set metadata in CREATING, and\n-\t\t\t * we will try to do writeables to sync the stream to\n-\t\t\t * proxy and ultimately bring up the onward connection\n-\t\t\t * now we are in LOCAL_CONNECTED. We need to do the\n-\t\t\t * CREATING now so we'll know the metadata to sync.\n-\t\t\t */\n-\n-#if defined(LWS_WITH_SYS_METRICS)\n-\t\t\t/*\n-\t\t\t * If any hanging caliper measurement, dump it, and free any tags\n-\t\t\t */\n-\t\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n-#endif\n-\n-\t\t\tif (!h-\u003ecreating_cb_done) {\n-\t\t\t\tif (lws_ss_check_next_state_sspc(h,\n-\t\t\t\t\t\t\t \u0026h-\u003eprev_ss_state,\n-\t\t\t\t\t\t\t LWSSSCS_CREATING))\n-\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\t\t\t\th-\u003eprev_ss_state \u003d (uint8_t)LWSSSCS_CREATING;\n-\t\t\t\th-\u003ecreating_cb_done \u003d 1;\n-\t\t\t} else\n-\t\t\t\th-\u003eprev_ss_state \u003d LWSSSCS_DISCONNECTED;\n-\n-\t\t\tif (ssi-\u003estate) {\n-\t\t\t\tn \u003d ssi-\u003estate(client_pss_to_userdata(pss),\n-\t\t\t\t\t NULL, h-\u003eprev_ss_state, 0);\n-\t\t\t\tswitch (n) {\n-\t\t\t\tcase LWSSSSRET_OK:\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\th-\u003edsh \u003d lws_dsh_create(NULL, (size_t)(par-\u003etemp32 ?\n-\t\t\t\t\t\tpar-\u003etemp32 : 32768), 1);\n-\t\t\tif (!h-\u003edsh)\n-\t\t\t\tgoto hangup;\n-\n-\t\t\tlws_callback_on_writable(h-\u003ecwsi);\n-\n-\t\t\tpar-\u003ersl_pos \u003d 0;\n-\t\t\tpar-\u003ersl_idx \u003d 0;\n-\n-\t\t\tmemset(\u0026h-\u003erideshare_ofs[0], 0, sizeof(h-\u003erideshare_ofs[0]));\n-\t\t\th-\u003erideshare_list[0] \u003d '\u005c0';\n-\t\t\th-\u003ersidx \u003d 0;\n-\n-\t\t\t/* no rideshare data is OK */\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\n-\t\t\tif (par-\u003erem) {\n-\t\t\t\tpar-\u003eps \u003d RPAR_RESULT_CREATION_RIDESHARE;\n-\t\t\t\tif (par-\u003erem \u003e\u003d sizeof(h-\u003erideshare_list))\n-\t\t\t\t\tgoto hangup;\n-\t\t\t}\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_RESULT_CREATION_RIDESHARE:\n-\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n-\t\t\tif (*cp \u003d\u003d ',') {\n-\t\t\t\tcp++;\n-\t\t\t\th-\u003erideshare_list[par-\u003ersl_pos++] \u003d '\u005c0';\n-\t\t\t\tif (par-\u003ersl_idx \u003d\u003d LWS_ARRAY_SIZE(h-\u003erideshare_ofs))\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\th-\u003erideshare_ofs[++par-\u003ersl_idx] \u003d par-\u003ersl_pos;\n-\t\t\t} else\n-\t\t\t\th-\u003erideshare_list[par-\u003ersl_pos++] \u003d (char)*cp++;\n-\t\t\tif (!--par-\u003erem)\n-\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_STATEINDEX:\n-\t\t\tpar-\u003ectr \u003d (par-\u003ectr \u003c\u003c 8) | (*cp++);\n-\t\t\tif (--par-\u003erem \u003d\u003d 4)\n-\t\t\t\tpar-\u003eps \u003d RPAR_ORD3;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_ORD3:\n-\t\t\tpar-\u003eflags \u003d (uint32_t)((*cp++) \u003c\u003c 24);\n-\t\t\tpar-\u003eps++;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_ORD2:\n-\t\t\tpar-\u003eflags |\u003d (uint32_t)((*cp++) \u003c\u003c 16);\n-\t\t\tpar-\u003eps++;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_ORD1:\n-\t\t\tpar-\u003eflags |\u003d (uint32_t)((*cp++) \u003c\u003c 8);\n-\t\t\tpar-\u003eps++;\n-\t\t\tbreak;\n-\n-\t\tcase RPAR_ORD0:\n-\t\t\tpar-\u003eflags |\u003d (uint32_t)(*cp++);\n-\t\t\tpar-\u003eps++;\n-\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n-\n-\t\t\t/*\n-\t\t\t * Client received a proxied state change\n-\t\t\t */\n-\n-\t\t\th \u003d client_pss_to_sspc_h(pss, ssi);\n-\t\t\tif (!h)\n-\t\t\t\t/*\n-\t\t\t\t * Since we're being informed we need to have\n-\t\t\t\t * a stream to inform. Assume whatever set this\n-\t\t\t\t * to NULL has started to close it.\n-\t\t\t\t */\n-\t\t\t\tbreak;\n-\n-\t\t\tswitch (par-\u003ectr) {\n-\t\t\tcase LWSSSCS_DISCONNECTED:\n-\t\t\tcase LWSSSCS_UNREACHABLE:\n-\t\t\tcase LWSSSCS_AUTH_FAILED:\n-\t\t\t\tlws_ss_serialize_state_transition(h, state,\n-\t\t\t\t\t\tLPCSCLI_LOCAL_CONNECTED);\n-\t\t\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_NONE;\n-\t\t\t\tbreak;\n-\n-\t\t\tcase LWSSSCS_CONNECTED:\n-\t\t\t\tlwsl_sspc_info(h, \u0022CONNECTED %s\u0022,\n-\t\t\t\t\t\t\tssi-\u003estreamtype);\n-\t\t\t\tif (*state \u003d\u003d LPCSCLI_OPERATIONAL)\n-\t\t\t\t\t/*\n-\t\t\t\t\t * Don't allow to see connected more\n-\t\t\t\t\t * than once for one connection\n-\t\t\t\t\t */\n-\t\t\t\t\tgoto swallow;\n-\n-\t\t\t\tlws_ss_serialize_state_transition(h, state,\n-\t\t\t\t\t\t\tLPCSCLI_OPERATIONAL);\n-\n-\t\t\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_CONN;\n-\t\t\t\tbreak;\n-\t\t\tcase LWSSSCS_TIMEOUT:\n-\t\t\t\tbreak;\n-\t\t\tdefault:\n-\t\t\t\tbreak;\n-\t\t\t}\n-\n-\t\t\tif (par-\u003ectr \u003c 0)\n-\t\t\t\tgoto hangup;\n-\n-#if defined(_DEBUG)\n-\t\t\tlwsl_sspc_info(h, \u0022forwarding proxied state %s\u0022,\n-\t\t\t\t\tlws_ss_state_name(par-\u003ectr));\n-#endif\n-\n-\t\t\tif (par-\u003ectr \u003d\u003d LWSSSCS_CREATING) {\n-\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n-\t\t\t\tif (h-\u003ecreating_cb_done)\n-\t\t\t\t\t/*\n-\t\t\t\t\t * We have told him he's CREATING when\n-\t\t\t\t\t * we heard we had linked up to the\n-\t\t\t\t\t * proxy, so suppress the remote\n-\t\t\t\t\t * CREATING so that he only sees it once\n-\t\t\t\t\t */\n-\t\t\t\tbreak;\n-\n-\t\t\t\th-\u003ecreating_cb_done \u003d 1;\n-\t\t\t}\n-\n-\t\t\tif (ssi-\u003estate) {\n-\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n-\t\t\t\tlws_ss_constate_t cs \u003d (lws_ss_constate_t)par-\u003ectr;\n-\n-\t\t\t\tif (cs \u003d\u003d LWSSSCS_CONNECTED)\n-\t\t\t\t\th-\u003ess_dangling_connected \u003d 1;\n-\t\t\t\tif (cs \u003d\u003d LWSSSCS_DISCONNECTED)\n-\t\t\t\t\th-\u003ess_dangling_connected \u003d 0;\n-\n-\t\t\t\tif (lws_ss_check_next_state_sspc(h,\n-\t\t\t\t\t\t\t \u0026h-\u003eprev_ss_state, cs))\n-\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\n-\t\t\t\tif (cs \u003c LWSSSCS_USER_BASE)\n-\t\t\t\t\th-\u003eprev_ss_state \u003d (uint8_t)cs;\n-\n-\t\t\t\th-\u003eh_in_svc \u003d h;\n-\t\t\t\tn \u003d ssi-\u003estate(client_pss_to_userdata(pss),\n-\t\t\t\t\tNULL, cs, par-\u003eflags);\n-\t\t\t\th-\u003eh_in_svc \u003d NULL;\n-\t\t\t\tswitch (n) {\n-\t\t\t\tcase LWSSSSRET_OK:\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n-\t\t\t\t\tgoto hangup;\n-\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n-\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n-\t\t\t\t}\n-\t\t\t}\n-\n-swallow:\n-\t\t\tbreak;\n-\n-\t\tdefault:\n-\t\t\tgoto hangup;\n-\t\t}\n-\t}\n-\n-\treturn LWSSSSRET_OK;\n-\n-hangup:\n-\n-\tlwsl_cx_notice(context, \u0022hangup\u0022);\n-\n-\treturn LWSSSSRET_DISCONNECT_ME;\n-}\ndiff --git a/lib/secure-streams/secure-streams.c b/lib/secure-streams/secure-streams.c\nindex 4f56bcd..4f25a8a 100644\n--- a/lib/secure-streams/secure-streams.c\n+++ b/lib/secure-streams/secure-streams.c\n@@ -267,7 +267,9 @@ lws_conmon_ss_json(lws_ss_handle_t *h)\n \t\t * ask to forward it on the proxy link\n \t\t */\n \n-\t\tss_proxy_onward_link_req_writeable(h);\n+\t\th-\u003econn_if_sspc_onw-\u003etxp_path.ops_onw-\u003eproxy_req_write(\n+\t\t\t\th-\u003econn_if_sspc_onw-\u003etxp_path.priv_onw);\n+\n \t\treturn LWSSSSRET_OK;\n \t}\n #endif\n@@ -1378,6 +1380,17 @@ lws_ss_destroy(lws_ss_handle_t **ppss)\n #endif\n \n \tif (h-\u003ewsi) {\n+\n+\t\tlwsl_warn(\u0022%s: conn-\u003ess-\u003ewsi %d %d\u005cn\u0022, __func__,\n+\t\t\t\th-\u003ewsi-\u003ebound_ss_proxy_conn, h-\u003ewsi-\u003eclient_proxy_onward);\n+\n+\t\tif (h-\u003ewsi-\u003ebound_ss_proxy_conn) {\n+\t\t\tstruct lws_sss_proxy_conn *conn \u003d (struct lws_sss_proxy_conn *)\n+\t\t\t\tlws_get_opaque_user_data(h-\u003ewsi);\n+\n+\t\t\tconn-\u003ess \u003d NULL;\n+\t\t}\n+\n \t\t/*\n \t\t * Don't let the wsi point to us any more,\n \t\t * we (the ss object bound to the wsi) are going away now\ndiff --git a/lib/secure-streams/serialized/client/CMakeLists.txt b/lib/secure-streams/serialized/client/CMakeLists.txt\nnew file mode 100644\nindex 0000000..a16433e\n--- /dev/null\n+++ b/lib/secure-streams/serialized/client/CMakeLists.txt\n@@ -0,0 +1,27 @@\n+include_directories(../../../core\n+\t\t ../../../core-net\n+\t\t ../../../plat/unix\n+\t\t ../../../plat/freertos\n+\t\t ../../../tls\n+\t\t ../../../event-libs\n+\t\t ../../../roles\n+\t\t ../../../roles/http\n+\t\t ../../../roles/h1\n+\t\t ../../../roles/h2\n+\t\t ../../../roles/ws\n+\t\t ../../../roles/mqtt\n+\t\t ../../../roles/raw\n+\n+\t\t ../../../system\n+\t\t ../../../system/smd\n+\t\t ../../../system/fault-injection\n+\t\t ../../../system/metrics\n+\n+\t\t ${LIBUV_INCLUDE_DIRS}\n+\t\t )\n+\n+\n+foreach(libpath ${LWS_LIB_BUILD_INC_PATHS})\n+\t\tinclude_directories(${libpath})\n+endforeach()\n+\ndiff --git a/lib/secure-streams/serialized/client/README.md b/lib/secure-streams/serialized/client/README.md\nnew file mode 100644\nindex 0000000..6d090c8\n--- /dev/null\n+++ b/lib/secure-streams/serialized/client/README.md\n@@ -0,0 +1,89 @@\n+# SSPC client support\n+\n+## Full vs LWS_ONLY_SSPC lws builds\n+\n+SSPC (Secure Stream Proxy Client) apis are built into libwebsockets as you would\n+expect if built with `LWS_WITH_SECURE_STREAMS_PROXY_API`. These are the client\n+SS api implementation, the serialization and deserialization of SS protocol, and\n+generic transport interface.\n+\n+You can also choose to build lws so it ONLY contains the SSPC pieces suitable\n+for the client side, with the libwebsockets build option `LWS_ONLY_SSPC`.\n+\n+\n+\n+Identical sources are used in both cases, but with `LWS_ONLY_SSPC` only the\n+parts related to mux and SSPC are included, coming to less than 25KB .text on\n+armv7 while providing the full proxied SS API. Also excluded are the normal\n+`lws_context` and event loop facilities, the provided apis expect to work with\n+an existing event loop.\n+\n+`LWS_ONLY_SSPC` facilitates using full libwebsockets SSPC client support on very\n+small devices that don't need the rest of lws. Such devices do not need to have\n+tls, an IP stack or any network, yet can use full Secure Streams capabilities\n+via the proxy (eg, h2, including stream binding, or ws over tls).\n+\n+\n+\n+`LWS_ONLY_SSPC` does not need a normal `struct lws_context` nor the lws event\n+loop or other apis. To maintain compatibility with the SS apis the user code\n+provides a simple, static stub lws_context with a couple of members and no\n+creation or destruction.\n+\n+You can find a minimal example that builds with `LWS_ONLY_SSPC` in\n+at `minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport`,\n+implementing full SS apis over a UART link, where the client does not link to\n+lws nor need any network stack, tls library or internet protocol implementation.\n+\n+It expects to use two USB serial adapters in loopback, one for this example and\n+one for the related proxy, minimal-secure-streams-custom-proxy-transport.\n+\n+These two examples need different build dirs (but use the same libwebsockets\n+source) since the client requires lws built with `LWS_ONLY_SSPC`.\n+\n+## Communication layers\n+\n+Different transports have quite different semantics for connectivity. Serialized\n+SS communication has three abstract layers:\n+\n+ - link: connectivity is established. The serial cable is plugged in, the\n+ RF link is established, or the Posix Socket has connected.\n+\n+ - mux: On some transports, eg, UART, there is a single logical link that we\n+ want to be able to carry multiple SS connections, so the transport must\n+ provide a way to negotiate multiplexing over the link.\n+\n+ - Serialized SS: the SS activity serialized into a stream in both directions\n+\n+How these look depend on the nature of the underlying transport, some examples:\n+\n+|layer|RF|UART|Unix Domain Socket|\n+|---|---|---|---|\n+|link|managed at RF stack|detect by periodic PING/PONG|Socket Connection semantics|\n+|mux|extra framing|extra framing|not needed, one UDS client socket for each SS|\n+|Serialized SS|bytestream|bytestream|Datagram stream|\n+\n+SSPC can directly hook up to an lws_transport_ops structure, this is used in the\n+case where multiplexing is handled by the transport like Unix Domain Socket link\n+to the proxy. In that case, each SS makes its own personal Unix Domain Socket\n+link to the proxy.\n+\n+The lws_transport_mux support is designed to interpose between SSPC layter and\n+the lws_transport itself, to provide its own multiplexing layer. This is needed\n+in the case where there is just a reliable bytestream link to the proxy, and no\n+suitable channelization or link detection, for example a simple UART transport.\n+\n+lws_transport_mux provides 250 mux channels over the transport, with link\n+detection by three-way PING handshakes at the mux layer. \n+\n+## LWS_ONLY_SSPC imports\n+\n+Four system integration imports are needed by the library.\n+\n+|prototype|function|\n+|---|---|\n+|lws_usec_t **lws_now_usecs**(void)|get us-resolution monotonic time|\n+|void **__lws_logv**(lws_log_cx_t *ignore1, lws_log_prepend_cx_t ignore2, void *ignore3, int filter, const char *_fun, const char *format, va_list ap)|log emit|\n+|void **lws_sul_schedule**(struct lws_context_standalone *ctx, int tsi, lws_sorted_usec_list_t *sul, sul_cb_t _cb, lws_usec_t _us)|schedule sul callback|\n+|void **lws_sul_cancel**(lws_sorted_usec_list_t *sul)|Cancel scheduled callback|\n+\ndiff --git a/lib/secure-streams/serialized/client/sspc-deserialize.c b/lib/secure-streams/serialized/client/sspc-deserialize.c\nnew file mode 100644\nindex 0000000..c37d9c2\n--- /dev/null\n+++ b/lib/secure-streams/serialized/client/sspc-deserialize.c\n@@ -0,0 +1,1022 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ * Serialized Secure Streams deserializer for Client / SSPC side\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+#if defined(STANDALONE)\n+\n+#define lws_context lws_context_standalone\n+\n+#undef lws_malloc\n+#define lws_malloc(a, b) malloc(a)\n+#undef lws_free\n+#define lws_free(a) free(a)\n+#endif\n+\n+#if defined(_DEBUG) \u0026\u0026 !defined(LWS_WITH_NO_LOGS)\n+static const char *sn[] \u003d {\n+\t\u0022unset\u0022,\n+\n+\t\u0022LPCSPROX_WAIT_INITIAL_TX\u0022,\n+\t\u0022LPCSPROX_REPORTING_FAIL\u0022,\n+\t\u0022LPCSPROX_REPORTING_OK\u0022,\n+\t\u0022LPCSPROX_OPERATIONAL\u0022,\n+\t\u0022LPCSPROX_DESTROYED\u0022,\n+\n+\t\u0022LPCSCLI_SENDING_INITIAL_TX\u0022,\n+\t\u0022LPCSCLI_WAITING_CREATE_RESULT\u0022,\n+\t\u0022LPCSCLI_LOCAL_CONNECTED\u0022,\n+\t\u0022LPCSCLI_ONWARD_CONNECT\u0022,\n+\t\u0022LPCSCLI_OPERATIONAL\u0022,\n+};\n+#endif\n+\n+static void\n+lws_ss_serialize_state_transition(lws_sspc_handle_t *h,\n+\t\t\t\t lws_ss_conn_states_t *state, int new_state)\n+{\n+#if defined(_DEBUG)\n+\tlwsl_sspc_info(h, \u0022%s -\u003e %s\u0022, sn[*state], sn[new_state]);\n+#endif\n+\t*state \u003d (lws_ss_conn_states_t)new_state;\n+}\n+\n+/*\n+ * event loop side is consuming serialized data from the client via dsh, parse\n+ * it using a bytewise parser for the serialization header(s)...\n+ * it's possibly coalesced\n+ *\n+ * client: pss is pointing to the start of userdata. We can use\n+ * pss_to_sspc_h(_pss, _ssi) to convert that to a pointer to the sspc\n+ * handle\n+ *\n+ * proxy: pss is pointing to \u0026conn-\u003ess, a pointer to the ss handle\n+ *\n+ * Returns one of\n+ *\n+ * \tLWSSSSRET_OK\n+ *\tLWSSSSRET_DISCONNECT_ME\n+ *\tLWSSSSRET_DESTROY_ME\n+ */\n+\n+/* convert userdata ptr _pss to handle pointer, allowing for any layout in\n+ * userdata */\n+#define client_pss_to_sspc_h(_pss, _ssi) (*((lws_sspc_handle_t **) \u005c\n+\t\t\t\t ((uint8_t *)_pss) + _ssi-\u003ehandle_offset))\n+/* client pss to sspc userdata */\n+#define client_pss_to_userdata(_pss) ((void *)_pss)\n+\n+int\n+lws_sspc_deserialize_parse(lws_sspc_handle_t *hh, const uint8_t *cp, size_t len,\n+\t\t\t lws_ss_handle_t **pss)\n+{\n+\tstruct lws_ss_serialization_parser *par \u003d \u0026hh-\u003eparser;\n+\tlws_ss_conn_states_t *state \u003d \u0026hh-\u003estate;\n+\tlws_ss_info_t *ssi \u003d \u0026hh-\u003essi;\n+\tlws_sspc_metadata_t *md;\n+\tlws_sspc_handle_t *h;\n+\tuint32_t flags;\n+\tint n, r \u003d 0;\n+\n+//\tlwsl_notice(\u0022%s: len %u, par-\u003eps %d, par-\u003erem %d\u005cn\u0022, __func__, (unsigned int)len, (int)par-\u003eps, (int)par-\u003erem);\n+\n+\twhile (len--) {\n+\n+\t\tswitch (par-\u003eps) {\n+\t\tcase RPAR_TYPE:\n+\t\t\tpar-\u003etype \u003d *cp++;\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_LEN_MSB: /* this is remaining frame length */\n+\t\t\tpar-\u003erem \u003d (uint16_t)((*cp++) \u003c\u003c 8);\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_LEN_LSB:\n+\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem | *cp++);\n+\t\t\tswitch (par-\u003etype) {\n+\n+\t\t\t/* event loop side */\n+\n+\t\t\tcase LWSSS_SER_TXPRE_TX_PAYLOAD:\n+\t\t\tcase LWSSS_SER_TXPRE_STREAMTYPE:\n+\t\t\tcase LWSSS_SER_TXPRE_DESTROYING:\n+\t\t\tcase LWSSS_SER_TXPRE_ONWARD_CONNECT:\n+\t\t\tcase LWSSS_SER_TXPRE_METADATA:\n+\t\t\tcase LWSSS_SER_TXPRE_TIMEOUT_UPDATE:\n+\t\t\tcase LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT:\n+\t\t\t\tlwsl_info(\u0022RPAR_LEN_LSB\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_TXCR_UPDATE:\n+\t\t\t\tpar-\u003eps \u003d RPAR_TXCR0;\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tbreak;\n+\n+\t\t\t/* client side */\n+\n+\t\t\tcase LWSSS_SER_RXPRE_RX_PAYLOAD:\n+\n+\t\t\t\tif (*state !\u003d LPCSCLI_OPERATIONAL \u0026\u0026\n+\t\t\t\t *state !\u003d LPCSCLI_LOCAL_CONNECTED) {\n+\t\t\t\t\tlwsl_info(\u0022LWSSS_SER_RXPRE_RX_PAYLOAD\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\n+\t\t\t\tpar-\u003erideshare[0] \u003d '\u005c0';\n+\t\t\t\tpar-\u003eps \u003d RPAR_FLAG_B3;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_CREATE_RESULT:\n+\n+\t\t\t\tif (*state !\u003d LPCSCLI_WAITING_CREATE_RESULT) {\n+\t\t\t\t\tlwsl_info(\u0022CREATE_RESULT\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\n+\t\t\t\tif (par-\u003erem \u003c 1) {\n+\t\t\t\t\tlwsl_info(\u0022CREATE_RESULT 1\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\n+\t\t\t\tpar-\u003eps \u003d RPAR_RESULT_CREATION;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_CONNSTATE:\n+\n+\t\t\t\tif (*state !\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n+\t\t\t\t *state !\u003d LPCSCLI_OPERATIONAL) {\n+\t\t\t\t\tlwsl_info(\u0022CONNSTATE1\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\n+\t\t\t\tif (par-\u003erem \u003c 5 || par-\u003erem \u003e 8) {\n+\t\t\t\t\tlwsl_info(\u0022CONNSTATE2\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\n+\t\t\t\tpar-\u003eps \u003d RPAR_STATEINDEX;\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_METADATA:\n+\n+\t\t\t\tif (par-\u003erem \u003c 3) {\n+\t\t\t\t\tlwsl_info(\u0022METADATA1\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tpar-\u003eps \u003d RPAR_METADATA_NAMELEN;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_TXCR_UPDATE:\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tpar-\u003eps \u003d RPAR_RX_TXCR_UPDATE;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_PERF:\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tif (!par-\u003erem) {\n+\t\t\t\t\tlwsl_info(\u0022PERF1\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tpar-\u003eps \u003d RPAR_PERF;\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\n+\t\t\t\tlwsl_notice(\u0022bad type 0x%x\u005cn\u0022, par-\u003etype);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\t\tcase RPAR_FLAG_B3:\n+\t\t\tcase RPAR_FLAG_B2:\n+\t\t\tcase RPAR_FLAG_B1:\n+\t\t\tcase RPAR_FLAG_B0:\n+\t\t\t\tpar-\u003eflags \u003c\u003c\u003d 8;\n+\t\t\t\tpar-\u003eflags |\u003d *cp++;\n+\t\t\t\tpar-\u003eps++;\n+\t\t\t\tif (!par-\u003erem--) {\n+\t\t\t\t\tlwsl_info(\u0022RPAR_FLAG\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\n+\t\t\tcase RPAR_LATA3:\n+\t\t\tcase RPAR_LATA2:\n+\t\t\tcase RPAR_LATA1:\n+\t\t\tcase RPAR_LATA0:\n+\t\t\t\tpar-\u003eusd_phandling \u003c\u003c\u003d 8;\n+\t\t\t\tpar-\u003eusd_phandling |\u003d *cp++;\n+\t\t\t\tpar-\u003eps++;\n+\t\t\t\tif (!par-\u003erem--) {\n+\t\t\t\t\tlwsl_info(\u0022RPAR_LATA\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\n+\t\t\tcase RPAR_LATB7:\n+\t\t\tcase RPAR_LATB6:\n+\t\t\tcase RPAR_LATB5:\n+\t\t\tcase RPAR_LATB4:\n+\t\t\tcase RPAR_LATB3:\n+\t\t\tcase RPAR_LATB2:\n+\t\t\tcase RPAR_LATB1:\n+\t\t\tcase RPAR_LATB0:\n+\t\t\t\tpar-\u003eust_pwait \u003c\u003c\u003d 8;\n+\t\t\t\tpar-\u003eust_pwait |\u003d *cp++;\n+\t\t\t\tpar-\u003eps++;\n+\t\t\t\tpar-\u003efrag1 \u003d 1;\n+\t\t\t\tif (!par-\u003erem--) {\n+\t\t\t\t\tlwsl_info(\u0022RPAR_LATB\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\n+\t\t\t\tif (par-\u003eps \u003d\u003d RPAR_RIDESHARE_LEN \u0026\u0026\n+\t\t\t\t !(par-\u003eflags \u0026 LWSSS_FLAG_RIDESHARE))\n+\t\t\t\t\tpar-\u003eps \u003d RPAR_PAYLOAD;\n+\n+\t\t\t\tif (par-\u003erem)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\t/* fallthru - handle 0-length payload */\n+\n+\t\t\t\tif (!(par-\u003eflags \u0026 LWSSS_FLAG_RIDESHARE))\n+\t\t\t\t\tgoto payload_ff;\n+\n+\t\t\t\tlwsl_info(\u0022RPAR_LATB1\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\n+\t\t\t/*\n+\t\t\t * Inbound rideshare info is provided on the RX packet\n+\t\t\t * itself\n+\t\t\t */\n+\n+\t\tcase RPAR_RIDESHARE_LEN:\n+\t\t\tpar-\u003eslen \u003d *cp++;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps++;\n+\t\t\tif (par-\u003erem-- \u003c par-\u003eslen) {\n+\t\t\t\tlwsl_info(\u0022RPAR_RIDESHARE_LEN\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_PERF:\n+\t\t\tn \u003d (int)len + 1;\n+\t\t\tif (n \u003e par-\u003erem)\n+\t\t\t\tn \u003d par-\u003erem;\n+\n+\t\t\tif (client_pss_to_sspc_h(pss, ssi) \u0026\u0026\n+\t\t\t ssi-\u003erx) {\n+\t\t\t\tint ret;\n+\n+\t\t\t\t/* we still have an sspc handle */\n+\t\t\t\tret \u003d ssi-\u003erx(client_pss_to_userdata(pss),\n+\t\t\t\t\t(uint8_t *)cp, (unsigned int)n,\n+\t\t\t\t\t(int)(LWSSS_FLAG_SOM | LWSSS_FLAG_EOM |\n+\t\t\t\t\t\t\tLWSSS_FLAG_PERF_JSON));\n+#if !defined(STANDALONE)\n+\t\t\t\tif (lws_fi(\u0026client_pss_to_sspc_h(pss, ssi)-\u003efic,\n+\t\t\t\t\t \u0022sspc_perf_rx_fake_destroy_me\u0022))\n+\t\t\t\t\tret \u003d LWSSSSRET_DESTROY_ME;\n+#endif\n+\n+\t\t\t\tswitch (ret) {\n+\t\t\t\tcase LWSSSSRET_OK:\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\t\t\t\tlwsl_info(\u0022PERF_DME\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n+\t\t\t\t\tlwsl_user(\u0022%s: a\u005cn\u0022, __func__);\n+\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (n) {\n+\t\t\t\tcp +\u003d n;\n+\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem -\n+\t\t\t\t\t\t(uint16_t)(unsigned int)n);\n+\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n+\t\t\t}\n+\t\t\tif (!par-\u003erem)\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_RIDESHARE:\n+\t\t\tpar-\u003erideshare[par-\u003ectr++] \u003d (char)*cp++;\n+\t\t\tif (!par-\u003erem--) {\n+\t\t\t\tlwsl_info(\u0022RS\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tif (par-\u003ectr !\u003d par-\u003eslen)\n+\t\t\t\tbreak;\n+\t\t\tpar-\u003eps \u003d RPAR_PAYLOAD;\n+\t\t\tif (par-\u003erem)\n+\t\t\t\tbreak;\n+\n+\t\t\t/* fallthru - handle 0-length payload */\n+\n+\t\tcase RPAR_PAYLOAD:\n+payload_ff:\n+\t\t\tn \u003d (int)len + 1;\n+\t\t\tassert(n \u003e\u003d 0);\n+\t\t\tif (n \u003d\u003d 0)\n+\t\t\t\tbreak;\n+\n+\t\t\tif (n \u003e par-\u003erem)\n+\t\t\t\tn \u003d par-\u003erem;\n+\t\t\tif (n \u003e 1380)\n+\t\t\t\tn \u003d 1380;\n+\n+\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n+\n+\t\t\t/*\n+\t\t\t * If the transport is passing up little pieces, use the\n+\t\t\t * dsh to coalesce them to whole datagrams before giving\n+\t\t\t * them to the application.\n+\t\t\t */\n+\n+\t\t\tif (par-\u003efrag1 || n !\u003d par-\u003erem) {\n+//\t\t\t\tlwsl_notice(\u0022%s: coalescing %d (par-\u003erem %d)\u005cn\u0022,\n+//\t\t\t\t\t__func__, n, (int)par-\u003erem);\n+\t\t\t\tr \u003d lws_dsh_alloc_tail(h-\u003edsh, 0, cp, (size_t)n,\n+\t\t\t\t\t\t NULL, 0);\n+\n+\t\t\t\tif (!r \u0026\u0026 n !\u003d par-\u003erem) {\n+//\t\t\t\t\tlwsl_notice(\u0022%s: coalesced and waiting... len %u, n %u\u005cn\u0022, __func__, (unsigned int)len, (unsigned int)n);\n+\t\t\t\t\tcp +\u003d n;\n+\t\t\t\t\th-\u003etxc.peer_tx_cr_est -\u003d n;\n+\t\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem -\n+\t\t\t\t\t\t\t (uint16_t)(unsigned int)n);\n+\t\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n+\t\t\t\t\tassert((int)len \u003e\u003d 0);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\n+\t\t\t\t/*\n+\t\t\t\t * We have to flush the dsh,\n+\t\t\t\t * or it's the last bit...\n+\t\t\t\t */\n+\n+//\t\t\t\tlwsl_notice(\u0022%s: flushing %u (par-\u003erem %d)\u005cn\u0022, __func__, (unsigned int)n, (int)par-\u003erem);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * We have to deal with refragmenting the SOM / EOM\n+\t\t\t * flags that covered the whole client serialized\n+\t\t\t * packet, so they apply to each fragment correctly\n+\t\t\t */\n+\n+\t\t\tflags \u003d par-\u003eflags \u0026 LWSSS_FLAG_RELATED_START;\n+\t\t\tif (par-\u003efrag1)\n+\t\t\t\t/*\n+\t\t\t\t * Only set the first time we came to this\n+\t\t\t\t * state after deserialization of the header\n+\t\t\t\t */\n+\t\t\t\tflags |\u003d par-\u003eflags \u0026 (LWSSS_FLAG_SOM |\n+\t\t\t\t\t\t LWSSS_FLAG_POLL);\n+\n+\t\t\tif (par-\u003erem \u003d\u003d n)\n+\t\t\t\t/*\n+\t\t\t\t * We are going to complete the advertised\n+\t\t\t\t * payload length from the client on this dsh,\n+\t\t\t\t * so give him the EOM type flags if any\n+\t\t\t\t */\n+\t\t\t\tflags |\u003d par-\u003eflags \u0026 (LWSSS_FLAG_EOM |\n+\t\t\t\t\t\t LWSSS_FLAG_RELATED_END);\n+\n+\t\t\t/*\n+\t\t\t * Client receives some RX from proxy\n+\t\t\t *\n+\t\t\t * Pass whatever payload we have to ss user.\n+\t\t\t */\n+\n+\t\t\th-\u003etxc.peer_tx_cr_est -\u003d n;\n+\n+\t\t\t// lwsl_sspc_info(h, \u0022P2C RX: len %d\u0022, (int)n);\n+\n+\t\t\tif (ssi-\u003erx \u0026\u0026 client_pss_to_sspc_h(pss, ssi)) {\n+\t\t\t\t/* we still have an sspc handle */\n+\t\t\t\tvoid *vb \u003d NULL;\n+\t\t\t\tsize_t size;\n+\t\t\t\tint ret;\n+\n+\t\t\t\tif (lws_dsh_get_head(h-\u003edsh, 0, \u0026vb, \u0026size))\n+\t\t\t\t\tsize \u003d 0;\n+\t\t\t\t// lwsl_notice(\u0022%s: flush head says %d\u005cn\u0022, __func__, (int)size);\n+\n+\t\t\t\tif (!size)\n+\t\t\t\t\t/* did not go through dsh */\n+\t\t\t\t\tret \u003d ssi-\u003erx(client_pss_to_userdata(pss),\n+\t\t\t\t\t (uint8_t *)cp, (unsigned int)n,\n+\t\t\t\t\t (int)flags);\n+\t\t\t\telse {\n+\t\t\t\t//\tlwsl_notice(\u0022%s: drainning %u\u005cn\u0022,\n+\t\t\t\t//\t\t\t__func__, (int)size);\n+\n+\t\t\t\t\tdo {\n+\t\t\t\t\t\tret \u003d ssi-\u003erx(client_pss_to_userdata(pss),\n+\t\t\t\t\t\t\t\t(uint8_t *)vb, (unsigned int)size,\n+\t\t\t\t\t\t\t\t(int)flags);\n+\t\t\t\t\t\tlws_dsh_free(\u0026vb);\n+\t\t\t\t\t\tif (lws_dsh_get_head(h-\u003edsh, 0, \u0026vb, \u0026size))\n+\t\t\t\t\t\t\tsize \u003d 0;\n+\t\t\t\t\t} while (!ret \u0026\u0026 size);\n+\n+\t\t\t\t\tlws_dsh_empty(h-\u003edsh);\n+\n+\t\t\t\t\tif (r) {\n+\t\t\t\t\t\t/*\n+\t\t\t\t\t\t * Deal with stashing the new\n+\t\t\t\t\t\t * data we couldn't fit before,\n+\t\t\t\t\t\t * now we flushed the dsh\n+\t\t\t\t\t\t */\n+\t\t\t\t\t\tr \u003d lws_dsh_alloc_tail(h-\u003edsh, 0, cp, (size_t)n,\n+\t\t\t\t\t\t\t\t NULL, 0);\n+\t\t\t\t\t\tassert(!r);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tpar-\u003efrag1 \u003d 0;\n+\n+#if !defined(STANDALONE)\n+\t\t\t\tif (client_pss_to_sspc_h(pss, ssi) \u0026\u0026\n+\t\t\t\t lws_fi(\u0026client_pss_to_sspc_h(pss, ssi)-\u003efic,\n+\t\t\t\t\t \u0022sspc_rx_fake_destroy_me\u0022))\n+\t\t\t\t\tret \u003d LWSSSSRET_DESTROY_ME;\n+#endif\n+\n+\t\t\t\tswitch (ret) {\n+\t\t\t\tcase LWSSSSRET_OK:\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\t\t\t\tlwsl_info(\u0022PLDM\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n+\t\t\t\t\tlwsl_user(\u0022%s: b\u005cn\u0022, __func__);\n+\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tpar-\u003efrag1 \u003d 0;\n+\n+\t\t\tif (n) {\n+\t\t\t\tcp +\u003d n;\n+\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem -\n+\t\t\t\t\t\t (uint16_t)(unsigned int)n);\n+\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n+\t\t\t}\n+\t\t\tif (!par-\u003erem)\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_RX_TXCR_UPDATE:\n+\t\t\tif (!--par-\u003erem \u0026\u0026 par-\u003ectr !\u003d 3) {\n+\t\t\t\tlwsl_info(\u0022TXCU\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4)\n+\t\t\t\tbreak;\n+\n+\t\t\t/*\n+\t\t\t * Proxy is telling us remote endpoint is allowing us\n+\t\t\t * par-\u003etemp32 more bytes tx credit to write to it\n+\t\t\t */\n+\n+\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n+\t\t\th-\u003etxc.tx_cr +\u003d par-\u003etemp32;\n+\t\t\tlwsl_sspc_info(h, \u0022RX_PEER_TXCR: %d\u0022, (int)par-\u003etemp32);\n+\t\t\tlws_sspc_request_tx(h); /* in case something waiting */\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_INIT_PROVERS:\n+\t\t\t/* Protocol version byte for this connection */\n+\t\t\tpar-\u003eprotocol_version \u003d *cp++;\n+\n+\t\t\t/*\n+\t\t\t * So we have to know what versions of the serialization\n+\t\t\t * protocol we can support at the proxy side, and\n+\t\t\t * reject anythng we don't know how to deal with\n+\t\t\t * noisily in the logs.\n+\t\t\t */\n+\n+\t\t\tif (par-\u003eprotocol_version !\u003d 1) {\n+\t\t\t\tlwsl_err(\u0022%s: Rejecting client with \u0022\n+\t\t\t\t\t \u0022unsupported SSv%d protocol\u005cn\u0022,\n+\t\t\t\t\t __func__, par-\u003eprotocol_version);\n+\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tif (!--par-\u003erem) {\n+\t\t\t\tlwsl_info(\u0022PROVERS\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_INIT_PID;\n+\t\t\tbreak;\n+\n+\n+\t\tcase RPAR_INIT_PID:\n+\t\t\tif (!--par-\u003erem) {\n+\t\t\t\tlwsl_info(\u0022PID\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4)\n+\t\t\t\tbreak;\n+\n+\t\t\tpar-\u003eclient_pid \u003d (uint32_t)par-\u003etemp32;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_INITTXC0;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_INITTXC0:\n+\t\t\tif (!--par-\u003erem) {\n+\t\t\t\tlwsl_info(\u0022TXC0\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4)\n+\t\t\t\tbreak;\n+\n+\t\t\tpar-\u003etxcr_out \u003d par-\u003etemp32;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_STREAMTYPE;\n+\t\t\tbreak;\n+\n+\t\t/*\n+\t\t * These are the client adjusting our / the remote peer ability\n+\t\t * to send back to him. He's sending a signed u32 BE\n+\t\t */\n+\n+\t\tcase RPAR_TXCR0:\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4) {\n+\t\t\t\tif (!--par-\u003erem) {\n+\t\t\t\t\tlwsl_info(\u0022TXCR0\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (--par-\u003erem) {\n+\t\t\t\tlwsl_info(\u0022TXCR0b\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * We're the client, being told by the proxy\n+\t\t\t * about tx credit being given to us from the\n+\t\t\t * remote peer, allowing the client to write to\n+\t\t\t * it.\n+\t\t\t */\n+\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t,\n+\t\t\t\t\t parser);\n+\t\t\th-\u003etxc.tx_cr +\u003d par-\u003etemp32;\n+\t\t\tlwsl_sspc_info(h, \u0022client RX_PEER_TXCR: %d\u0022,\n+\t\t\t\t (int)par-\u003etemp32);\n+\t\t\t/* in case something waiting */\n+\t\t\tlws_sspc_request_tx(h);\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_PAYLEN0:\n+\t\tcase RPAR_TIMEOUT0:\n+\t\t\tlwsl_info(\u0022TIMEOUT0\u005cn\u0022);\n+\t\t\tgoto hangup;\n+\n+\t\tcase RPAR_METADATA_NAMELEN:\n+\t\t\t/* both client and proxy */\n+\t\t\tif (!--par-\u003erem) {\n+\t\t\t\tlwsl_info(\u0022NL1\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tpar-\u003eslen \u003d *cp++;\n+\t\t\tif (par-\u003eslen \u003e\u003d sizeof(par-\u003emetadata_name) - 1) {\n+\t\t\t\tlwsl_info(\u0022NL2\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_METADATA_NAME:\n+\t\t\t/* both client and proxy */\n+\t\t\tif (!--par-\u003erem) {\n+\t\t\t\tlwsl_info(\u0022MDN\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tpar-\u003emetadata_name[par-\u003ectr++] \u003d (char)*cp++;\n+\t\t\tif (par-\u003ectr !\u003d par-\u003eslen)\n+\t\t\t\tbreak;\n+\t\t\tpar-\u003emetadata_name[par-\u003ectr] \u003d '\u005c0';\n+\t\t\tpar-\u003eps \u003d RPAR_METADATA_VALUE;\n+\n+\t\t\th \u003d client_pss_to_sspc_h(pss, ssi);\n+\n+\t\t\t/*\n+\t\t\t * client side does not have access to policy\n+\t\t\t * and any metadata are new to it each time,\n+\t\t\t * we allocate them, removing any existing with\n+\t\t\t * the same name first\n+\t\t\t */\n+\n+\t\t\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\t\t\tlws_dll2_get_head(\n+\t\t\t\t\t\t\u0026h-\u003emetadata_owner_rx)) {\n+\t\t\t\tmd \u003d lws_container_of(d,\n+\t\t\t\t\t lws_sspc_metadata_t, list);\n+\n+\t\t\t\tif (!strcmp(md-\u003ename,\n+\t\t\t\t\t par-\u003emetadata_name)) {\n+\t\t\t\t\tlws_dll2_remove(\u0026md-\u003elist);\n+\t\t\t\t\tlws_free(md);\n+\t\t\t\t}\n+\n+\t\t\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\t\t\t/*\n+\t\t\t * Create the client's rx metadata entry\n+\t\t\t */\n+\n+#if !defined(STANDALONE)\n+\t\t\tif (h \u0026\u0026 lws_fi(\u0026h-\u003efic, \u0022sspc_rx_metadata_oom\u0022))\n+\t\t\t\tmd \u003d NULL;\n+\t\t\telse\n+#endif\n+\t\t\t\tmd \u003d lws_malloc(sizeof(lws_sspc_metadata_t) +\n+\t\t\t\t\tpar-\u003erem + 1, \u0022rxmeta\u0022);\n+\t\t\tif (!md) {\n+\t\t\t\tlwsl_err(\u0022%s: OOM\u005cn\u0022, __func__);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tmemset(md, 0, sizeof(lws_sspc_metadata_t));\n+\n+\t\t\tlws_strncpy(md-\u003ename, par-\u003emetadata_name,\n+\t\t\t\t\tsizeof(md-\u003ename));\n+\t\t\tmd-\u003elen \u003d par-\u003erem;\n+\t\t\tpar-\u003erxmetaval \u003d (uint8_t *)\u0026md[1];\n+\t\t\t/*\n+\t\t\t * Overallocate by 1 and put a NUL just beyond\n+\t\t\t * the official md-\u003elen, so value can be easily\n+\t\t\t * dereferenced safely for NUL-terminated string\n+\t\t\t * apis that's the most common usage\n+\t\t\t */\n+\t\t\tpar-\u003erxmetaval[md-\u003elen] \u003d '\u005c0';\n+\t\t\tlws_dll2_add_tail(\u0026md-\u003elist,\n+\t\t\t\t\t \u0026h-\u003emetadata_owner_rx);\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_METADATA_VALUE:\n+\t\t\t/* both client and proxy */\n+\n+\t\t\t*par-\u003erxmetaval++ \u003d *cp++;\n+\t\t\tif (--par-\u003erem)\n+\t\t\t\tbreak;\n+\n+\t\t\t/* we think we got all the value */\n+\n+\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n+\t\t\tlwsl_sspc_notice(h, \u0022RX METADATA %s\u0022, par-\u003emetadata_name);\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_STREAMTYPE:\n+\n+\t\t\t/* only the proxy can get these */\n+\n+\t\t\tlwsl_info(\u0022ST\u005cn\u0022);\n+\t\t\tgoto hangup;\n+\n+\t\t/* clientside states */\n+\n+\t\tcase RPAR_RESULT_CREATION:\n+\t\t\tif (*cp++) {\n+\t\t\t\tlwsl_err(\u0022%s: stream creation failed\u005cn\u0022,\n+\t\t\t\t\t __func__);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tif (--par-\u003erem \u003c 4) {\n+\t\t\t\tlwsl_info(\u0022RC1\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tpar-\u003eps \u003d RPAR_RESULT_CREATION_DSH;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_RESULT_CREATION_DSH:\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | (*cp++);\n+\t\t\tif (!par-\u003erem--) {\n+\t\t\t\tlwsl_info(\u0022CDSH\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tif (++par-\u003ectr \u003c 4)\n+\t\t\t\tbreak;\n+\n+\t\t\t/*\n+\t\t\t * Client (par-\u003etemp32 \u003d\u003d dsh alloc)\n+\t\t\t */\n+\n+\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n+\n+\t\t\tlws_ss_serialize_state_transition(h, state,\n+\t\t\t\t\t\t\t LPCSCLI_LOCAL_CONNECTED);\n+\n+\t\t\tassert(h-\u003etxp_path.ops_onw);\n+\t\t\tassert(h-\u003etxp_path.ops_onw-\u003eevent_stream_up);\n+\t\t\th-\u003etxp_path.ops_onw-\u003eevent_stream_up(h-\u003etxp_path.priv_onw);\n+\n+\t\t\tif (h-\u003edsh)\n+\t\t\t\tlws_dsh_empty(h-\u003edsh);\n+\n+\t\t\t/*\n+\t\t\t * This is telling us that the streamtype could be (and\n+\t\t\t * was) created at the proxy. It's not telling us that\n+\t\t\t * the onward peer connection could be connected.\n+\t\t\t *\n+\t\t\t * We'll get a proxied state() coming later that informs\n+\t\t\t * us about the situation with that.\n+\t\t\t *\n+\t\t\t * However at this point, we should choose to inform\n+\t\t\t * the client that his stream was created... we will\n+\t\t\t * later get a proxied CREATING state from the peer\n+\t\t\t * but we should do it now and suppress the later one.\n+\t\t\t *\n+\t\t\t * The reason is he may set metadata in CREATING, and\n+\t\t\t * we will try to do writeables to sync the stream to\n+\t\t\t * proxy and ultimately bring up the onward connection\n+\t\t\t * now we are in LOCAL_CONNECTED. We need to do the\n+\t\t\t * CREATING now so we'll know the metadata to sync.\n+\t\t\t */\n+\n+#if !defined(STANDALONE) \u0026\u0026 defined(LWS_WITH_SYS_METRICS)\n+\t\t\t/*\n+\t\t\t * If any hanging caliper measurement, dump it, and free any tags\n+\t\t\t */\n+\t\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn,\n+\t\t\t\t\t\t\t(struct lws *)NULL);\n+#endif\n+\n+\t\t\tif (!h-\u003ecreating_cb_done) {\n+\t\t\t\tif (lws_ss_check_next_state_sspc(h,\n+\t\t\t\t\t\t\u0026h-\u003eprev_ss_state,\n+\t\t\t\t\t\tLWSSSCS_CREATING))\n+\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\n+\t\t\t\th-\u003eprev_ss_state \u003d (uint8_t)LWSSSCS_CREATING;\n+\t\t\t\th-\u003ecreating_cb_done \u003d 1;\n+\t\t\t} else\n+\t\t\t\th-\u003eprev_ss_state \u003d LWSSSCS_DISCONNECTED;\n+\n+\t\t\tif (ssi-\u003estate) {\n+\t\t\t\tn \u003d ssi-\u003estate(client_pss_to_userdata(pss),\n+\t\t\t\t\t NULL, h-\u003eprev_ss_state, 0);\n+\t\t\t\tswitch (n) {\n+\t\t\t\tcase LWSSSSRET_OK:\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\t\t\t\tlwsl_info(\u0022CDSH2\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n+\t\t\t\t\tlwsl_sspc_warn(h, \u0022d\u0022);\n+\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (!h-\u003edsh)\n+\t\t\t\th-\u003edsh \u003d lws_dsh_create(NULL,\n+#if defined(STANDALONE)\n+\t\t\t\t\t2048,\n+#else\n+\t\t\t\t\t(size_t)(par-\u003etemp32 ?\n+\t\t\t\t\t\t par-\u003etemp32 : 32768),\n+#endif\n+\t\t\t\t\t(int)(hh-\u003etxp_path.ops_onw-\u003eflags | 1u));\n+\t\t\tif (!h-\u003edsh) {\n+\t\t\t\tlwsl_info(\u0022CDSH3\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\th-\u003edsh-\u003esplitat \u003d h-\u003etxp_path.ops_onw-\u003edsh_splitat;\n+\n+\t\t\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+\t\t\tpar-\u003ersl_pos \u003d 0;\n+\t\t\tpar-\u003ersl_idx \u003d 0;\n+\n+\t\t\tmemset(\u0026h-\u003erideshare_ofs[0], 0,\n+\t\t\t sizeof(h-\u003erideshare_ofs[0]));\n+\t\t\th-\u003erideshare_list[0] \u003d '\u005c0';\n+\t\t\th-\u003ersidx \u003d 0;\n+\n+\t\t\t/* no rideshare data is OK */\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\n+\t\t\tif (par-\u003erem) {\n+\t\t\t\tpar-\u003eps \u003d RPAR_RESULT_CREATION_RIDESHARE;\n+\t\t\t\tif (par-\u003erem \u003e\u003d sizeof(h-\u003erideshare_list)) {\n+\t\t\t\t\tlwsl_info(\u0022CDSH4\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_RESULT_CREATION_RIDESHARE:\n+\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t, parser);\n+\t\t\tif (*cp \u003d\u003d ',') {\n+\t\t\t\tcp++;\n+\t\t\t\th-\u003erideshare_list[par-\u003ersl_pos++] \u003d '\u005c0';\n+\t\t\t\tif (par-\u003ersl_idx \u003d\u003d LWS_ARRAY_SIZE(h-\u003erideshare_ofs)) {\n+\t\t\t\t\tlwsl_info(\u0022CDSH5\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\th-\u003erideshare_ofs[++par-\u003ersl_idx] \u003d par-\u003ersl_pos;\n+\t\t\t} else\n+\t\t\t\th-\u003erideshare_list[par-\u003ersl_pos++] \u003d (char)*cp++;\n+\t\t\tif (!--par-\u003erem)\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_STATEINDEX:\n+\t\t\tpar-\u003ectr \u003d (par-\u003ectr \u003c\u003c 8) | (*cp++);\n+\t\t\tif (--par-\u003erem \u003d\u003d 4)\n+\t\t\t\tpar-\u003eps \u003d RPAR_ORD3;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_ORD3:\n+\t\t\tpar-\u003eflags \u003d (uint32_t)((*cp++) \u003c\u003c 24);\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_ORD2:\n+\t\t\tpar-\u003eflags |\u003d (uint32_t)((*cp++) \u003c\u003c 16);\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_ORD1:\n+\t\t\tpar-\u003eflags |\u003d (uint32_t)((*cp++) \u003c\u003c 8);\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_ORD0:\n+\t\t\tpar-\u003eflags |\u003d (uint32_t)(*cp++);\n+\t\t\tpar-\u003eps++;\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\n+\t\t\t/*\n+\t\t\t * Client received a proxied state change\n+\t\t\t */\n+\n+\t\t\th \u003d client_pss_to_sspc_h(pss, ssi);\n+\t\t\tif (!h)\n+\t\t\t\t/*\n+\t\t\t\t * Since we're being informed we need to have\n+\t\t\t\t * a stream to inform. Assume whatever set this\n+\t\t\t\t * to NULL has started to close it.\n+\t\t\t\t */\n+\t\t\t\tbreak;\n+\n+\t\t\tswitch (par-\u003ectr) {\n+\t\t\tcase LWSSSCS_DISCONNECTED:\n+\t\t\tcase LWSSSCS_UNREACHABLE:\n+\t\t\tcase LWSSSCS_AUTH_FAILED:\n+\t\t\t\tlws_ss_serialize_state_transition(h, state,\n+\t\t\t\t\t\tLPCSCLI_LOCAL_CONNECTED);\n+\t\t\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_NONE;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSSCS_CONNECTED:\n+\t\t\t\tlwsl_sspc_info(h, \u0022CONNECTED %s\u0022,\n+\t\t\t\t\t\t\tssi-\u003estreamtype);\n+\t\t\t\tif (*state \u003d\u003d LPCSCLI_OPERATIONAL)\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Don't allow to see connected more\n+\t\t\t\t\t * than once for one connection\n+\t\t\t\t\t */\n+\t\t\t\t\tgoto swallow;\n+\n+\t\t\t\tlws_ss_serialize_state_transition(h, state,\n+\t\t\t\t\t\t\tLPCSCLI_OPERATIONAL);\n+\n+\t\t\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_CONN;\n+\t\t\t\tbreak;\n+\t\t\tcase LWSSSCS_TIMEOUT:\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (par-\u003ectr \u003c 0) {\n+\t\t\t\tlwsl_info(\u0022ORDA\u005cn\u0022);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+#if !defined(STANDALONE) \u0026\u0026 defined(_DEBUG)\n+\t\t\tlwsl_sspc_info(h, \u0022forwarding proxied state %s\u0022,\n+\t\t\t\t\tlws_ss_state_name(par-\u003ectr));\n+#endif\n+\n+\t\t\tif (par-\u003ectr \u003d\u003d LWSSSCS_CREATING) {\n+\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t,\n+\t\t\t\t\t\t parser);\n+\t\t\t\tif (h-\u003ecreating_cb_done)\n+\t\t\t\t\t/*\n+\t\t\t\t\t * We have told him he's CREATING when\n+\t\t\t\t\t * we heard we had linked up to the\n+\t\t\t\t\t * proxy, so suppress the remote\n+\t\t\t\t\t * CREATING so that he only sees it once\n+\t\t\t\t\t */\n+\t\t\t\tbreak;\n+\n+\t\t\t\th-\u003ecreating_cb_done \u003d 1;\n+\t\t\t}\n+\n+\t\t\tif (ssi-\u003estate) {\n+\t\t\t\th \u003d lws_container_of(par, lws_sspc_handle_t,\n+\t\t\t\t\t\t parser);\n+\t\t\t\tlws_ss_constate_t cs \u003d (lws_ss_constate_t)par-\u003ectr;\n+\n+\t\t\t\tif (cs \u003d\u003d LWSSSCS_CONNECTED)\n+\t\t\t\t\th-\u003ess_dangling_connected \u003d 1;\n+\t\t\t\tif (cs \u003d\u003d LWSSSCS_DISCONNECTED)\n+\t\t\t\t\th-\u003ess_dangling_connected \u003d 0;\n+\n+\t\t\t\tif (lws_ss_check_next_state_sspc(h,\n+\t\t\t\t\t\t\t \u0026h-\u003eprev_ss_state, cs)) {\n+\t\t\t\t\tlwsl_user(\u0022%s: e\u005cn\u0022, __func__);\n+\n+\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\t\t\t\t}\n+\t\t\t\tif (cs \u003c LWSSSCS_USER_BASE)\n+\t\t\t\t\th-\u003eprev_ss_state \u003d (uint8_t)cs;\n+\n+\t\t\t\th-\u003eh_in_svc \u003d h;\n+\t\t\t\tn \u003d ssi-\u003estate(client_pss_to_userdata(pss),\n+\t\t\t\t\tNULL, cs, par-\u003eflags);\n+\t\t\t\th-\u003eh_in_svc \u003d NULL;\n+\t\t\t\tswitch (n) {\n+\t\t\t\tcase LWSSSSRET_OK:\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\t\t\t\tlwsl_info(\u0022ORDB\u005cn\u0022);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tcase LWSSSSRET_DESTROY_ME:\n+\t\t\t\t\tlwsl_sspc_warn(h, \u0022f\u0022);\n+\t\t\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\t\t\t\t}\n+\t\t\t}\n+\n+swallow:\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tgoto hangup;\n+\t\t}\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+\n+hangup:\n+\n+\tlwsl_notice(\u0022%s: hangup\u005cn\u0022, __func__);\n+\n+\treturn LWSSSSRET_DISCONNECT_ME;\n+}\ndiff --git a/lib/secure-streams/serialized/client/sspc-transport-wsi.c b/lib/secure-streams/serialized/client/sspc-transport-wsi.c\nnew file mode 100644\nindex 0000000..b2bf7ae\n--- /dev/null\n+++ b/lib/secure-streams/serialized/client/sspc-transport-wsi.c\n@@ -0,0 +1,288 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * Client SSPC where the connectivity is implemented by a wsi\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+static int\n+lws_sss_transport_wsi_cb(struct lws *wsi, enum lws_callback_reasons reason,\n+\t\t\t void *user, void *in, size_t len)\n+{\n+\tlws_sspc_handle_t *h \u003d (lws_sspc_handle_t *)lws_get_opaque_user_data(wsi);\n+\tsize_t pktsize \u003d wsi-\u003ea.context-\u003emax_http_header_data;\n+\tlws_ss_state_return_t r;\n+\n+\tswitch (reason) {\n+\n+\tcase LWS_CALLBACK_CONNECTING:\n+\t\t/*\n+\t\t * In our particular case, we want CCEs even inside the\n+\t\t * initial connect loop time\n+\t\t */\n+\t\twsi-\u003eclient_suppress_CONNECTION_ERROR \u003d 0;\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_CLIENT_CONNECTION_ERROR:\n+\t\tlwsl_warn(\u0022%s: CCE: %s\u005cn\u0022, __func__,\n+\t\t\t in ? (const char *)in : \u0022null\u0022);\n+#if defined(LWS_WITH_SYS_METRICS)\n+\t\t/*\n+\t\t * If any hanging caliper measurement, dump it, and free\n+\t\t * any tags\n+\t\t */\n+\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n+#endif\n+\t\tlws_set_opaque_user_data(wsi, NULL);\n+\t\th-\u003etxp_path.ops_in-\u003eevent_connect_disposition(h, 1);\n+\t\tbreak;\n+\n+ case LWS_CALLBACK_RAW_CONNECTED:\n+ \tlwsl_user(\u0022%s: CONNECTED\u005cn\u0022, __func__);\n+ \tif (h-\u003etxp_path.ops_in-\u003eevent_connect_disposition(h, 0))\n+ \t\treturn -1;\n+\t\t/*\n+\t\t * We create the dsh at the response to the initial tx, which\n+\t\t * will let us know the policy's max size for it... let's\n+\t\t * protect the connection with a promise to complete the\n+\t\t * SS serialization streamtype negotation within a short period,\n+\t\t * we will cancel this timeout when we have the proxy's ack\n+\t\t * of the streamtype serialization, eg, it exists in the proxy\n+\t\t * policy etc\n+\t\t */\n+\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);\n+ break;\n+\n+\tcase LWS_CALLBACK_RAW_CLOSE:\n+\t\t/*\n+\t\t * our ss proxy Unix Domain socket has closed...\n+\t\t */\n+\t\tlwsl_sspc_info(h, \u0022LWS_CALLBACK_RAW_CLOSE: proxy conn down, wsi %s\u0022,\n+\t\t\t\tlws_wsi_tag(wsi));\n+\n+\t\tif (h) {\n+\t\t\tr \u003d h-\u003etxp_path.ops_in-\u003eevent_closed(h);\n+\t\t\th-\u003etxp_path.priv_in \u003d NULL;\n+\t\t\tif (r \u003d\u003d LWSSSSRET_DESTROY_ME) {\n+\t\t\t\tlws_set_opaque_user_data(wsi, NULL);\n+\t\t\t\tlws_sspc_destroy(\u0026h);\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_RX:\n+\t\t/*\n+\t\t * ie, the proxy has sent us something\n+\t\t */\n+\n+\t\tif (!h || !h-\u003etxp_path.priv_in) {\n+\t\t\tlwsl_info(\u0022%s: rx when client ss destroyed\u005cn\u0022, __func__);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tlwsl_sspc_info(h, \u0022%s: RAW_RX: rx %d\u005cn\u0022, __func__, (int)len);\n+\n+\t\tif (!len) {\n+\t\t\tlwsl_sspc_notice(h, \u0022RAW_RX: zero len\u0022);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tr \u003d h-\u003etxp_path.ops_in-\u003eevent_read((lws_transport_priv_t)h,\n+\t\t\t\t\t\t (const uint8_t *)in, len);\n+\n+\t\tswitch (r) {\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\t\tlwsl_info(\u0022%s: proxlicent RX ended with DISCONNECT_ME\u005cn\u0022,\n+\t\t\t\t\t__func__);\n+\t\t\treturn -1;\n+\t\tcase LWSSSSRET_DESTROY_ME:\n+\t\t\tlwsl_info(\u0022%s: proxlicent RX ended with DESTROY_ME\u005cn\u0022,\n+\t\t\t\t\t__func__);\n+\t\t\tlws_set_opaque_user_data(wsi, NULL);\n+\t\t\tlws_sspc_destroy(\u0026h);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED ||\n+\t\t h-\u003estate \u003d\u003d LPCSCLI_ONWARD_CONNECT)\n+\t\t\tlws_set_timeout(wsi, 0, 0);\n+\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_WRITEABLE:\n+\n+\t\t/*\n+\t\t * We can transmit something to the proxy...\n+\t\t */\n+\n+\t\tif (!h)\n+\t\t\tbreak;\n+\n+\t\tlwsl_sspc_debug(h, \u0022WRITEABLE %s, state %d\u0022,\n+\t\t\t\twsi-\u003elc.gutag, h-\u003estate);\n+\n+\t\tif (h-\u003etxp_path.ops_in-\u003eevent_can_write(h, pktsize))\n+\t\t\treturn -1;\n+\n+\t\treturn 0;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn lws_callback_http_dummy(wsi, reason, user, in, len);\n+}\n+\n+const struct lws_protocols lws_sspc_protocols[] \u003d {\n+\t{\n+\t\t\u0022ssproxy-protocol\u0022,\n+\t\tlws_sss_transport_wsi_cb,\n+\t\t0,\n+\t\t2048, 2048, NULL, 0\n+\t},\n+\t{ NULL, NULL, 0, 0, 0, NULL, 0 }\n+};\n+\n+/*\n+ * lws_sss_transport ops for wsi transport\n+ */\n+\n+static int\n+lws_sss_transport_wsi_retry_connect(lws_txp_path_client_t *path, lws_sspc_handle_t *h)\n+{\n+\tstruct lws_client_connect_info i;\n+\n+\t/*\n+\t * We may have started up before the system proxy, so be prepared with\n+\t * a sul to retry at 1Hz\n+\t */\n+\n+\tmemset(\u0026i, 0, sizeof i);\n+\ti.context \u003d h-\u003econtext;\n+\tif (h-\u003econtext-\u003ess_proxy_port) { /* tcp */\n+\t\ti.address \u003d h-\u003econtext-\u003ess_proxy_address;\n+\t\ti.port \u003d h-\u003econtext-\u003ess_proxy_port;\n+\t\ti.iface \u003d h-\u003econtext-\u003ess_proxy_bind;\n+\t} else {\n+\t\tif (h-\u003econtext-\u003ess_proxy_bind)\n+\t\t\ti.address \u003d h-\u003econtext-\u003ess_proxy_bind;\n+\t\telse\n+#if defined(__linux__)\n+\t\t\ti.address \u003d \u0022+@proxy.ss.lws\u0022;\n+#else\n+\t\t\ti.address \u003d \u0022+/tmp/proxy.ss.lws\u0022;\n+#endif\n+\t}\n+\n+\ti.host\t\t\t\u003d i.address;\n+\ti.origin\t\t\u003d i.address;\n+\ti.method\t\t\u003d \u0022RAW\u0022;\n+\ti.protocol\t\t\u003d lws_sspc_protocols[0].name;\n+\ti.local_protocol_name\t\u003d lws_sspc_protocols[0].name;\n+\ti.path\t\t\t\u003d \u0022\u0022;\n+\ti.pwsi\t\t\t\u003d (struct lws **)\u0026h-\u003etxp_path.priv_onw;\n+\ti.opaque_user_data\t\u003d (void *)h;\n+\ti.ssl_connection\t\u003d LCCSCF_SECSTREAM_PROXY_LINK;\n+\n+\tlws_metrics_caliper_bind(h-\u003ecal_txn, h-\u003econtext-\u003emt_ss_cliprox_conn);\n+#if defined(LWS_WITH_SYS_METRICS)\n+\tlws_metrics_tag_add(\u0026h-\u003ecal_txn.mtags_owner, \u0022ss\u0022, h-\u003essi.streamtype);\n+#endif\n+\n+\t/* this wsi is the link to the proxy */\n+\n+\tif (!lws_client_connect_via_info(\u0026i)) {\n+\n+#if defined(LWS_WITH_SYS_METRICS)\n+\t\t/*\n+\t\t * If any hanging caliper measurement, dump it, and free any tags\n+\t\t */\n+\t\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n+#endif\n+\n+\t\treturn 1; /* going to need to retry */\n+\t}\n+\n+\tlwsl_sspc_notice(h, \u0022%s\u0022, ((struct lws *)(h-\u003etxp_path.priv_onw))-\u003elc.gutag);\n+\n+\treturn 0; /* in progress */\n+}\n+\n+static void\n+lws_sss_transport_wsi_req_write(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (wsi)\n+\t\tlws_callback_on_writable(wsi);\n+}\n+\n+static int\n+lws_sss_transport_wsi_write(lws_transport_priv_t priv, uint8_t *buf, size_t len)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (lws_write(wsi, buf, len, LWS_WRITE_RAW) !\u003d (ssize_t)len) {\n+\t\tlwsl_wsi_notice(wsi, \u0022failed\u0022);\n+\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+lws_sss_transport_wsi_close(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (!wsi)\n+\t\treturn;\n+\n+\tlws_set_opaque_user_data(wsi, NULL);\n+\tlws_wsi_close(wsi, LWS_TO_KILL_ASYNC);\n+}\n+\n+static void\n+lws_sss_transport_wsi_stream_up(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n+}\n+\n+const lws_transport_client_ops_t txp_ops_sspc_wsi \u003d {\n+\t.name\t\t\t\u003d \u0022txp_sspc_wsi\u0022,\n+\t.event_retry_connect\t\u003d lws_sss_transport_wsi_retry_connect,\n+\t.req_write\t\t\u003d lws_sss_transport_wsi_req_write,\n+\t._write\t\t\t\u003d lws_sss_transport_wsi_write,\n+\t._close\t\t\t\u003d lws_sss_transport_wsi_close,\n+\t.event_stream_up\t\u003d lws_sss_transport_wsi_stream_up,\n+\t.dsh_splitat\t\t\u003d 1300,\n+};\ndiff --git a/lib/secure-streams/serialized/client/sspc-transport.c b/lib/secure-streams/serialized/client/sspc-transport.c\nnew file mode 100644\nindex 0000000..9fe4b34\n--- /dev/null\n+++ b/lib/secure-streams/serialized/client/sspc-transport.c\n@@ -0,0 +1,518 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ * These are helpers used by the transport implementation. They contain the\n+ * generic sspc actions to handle events that happen at the transport.\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+#if defined(STANDALONE)\n+\n+#define lws_context lws_context_standalone\n+\n+void\n+lws_ser_wu16be(uint8_t *b, uint16_t u)\n+{\n+\t*b++ \u003d (uint8_t)(u \u003e\u003e 8);\n+\t*b \u003d (uint8_t)u;\n+}\n+\n+void\n+lws_ser_wu32be(uint8_t *b, uint32_t u32)\n+{\n+\t*b++ \u003d (uint8_t)(u32 \u003e\u003e 24);\n+\t*b++ \u003d (uint8_t)(u32 \u003e\u003e 16);\n+\t*b++ \u003d (uint8_t)(u32 \u003e\u003e 8);\n+\t*b \u003d (uint8_t)u32;\n+}\n+\n+void\n+lws_ser_wu64be(uint8_t *b, uint64_t u64)\n+{\n+\tlws_ser_wu32be(b, (uint32_t)(u64 \u003e\u003e 32));\n+\tlws_ser_wu32be(b + 4, (uint32_t)u64);\n+}\n+\n+#undef lws_malloc\n+#define lws_malloc(a, b) malloc(a)\n+#undef lws_free\n+#define lws_free(a) free(a)\n+\n+#endif\n+\n+static size_t\n+lws_sspc_serialize_metadata(lws_sspc_handle_t *h, lws_sspc_metadata_t *md,\n+\t\t\t uint8_t *p, uint8_t *end)\n+{\n+\tsize_t n, txc;\n+\n+\tif (md-\u003ename[0] \u003d\u003d '\u005c0') {\n+\n+\t\tlwsl_sspc_info(h, \u0022sending tx credit update %d\u0022,\n+\t\t\t\tmd-\u003etx_cr_adjust);\n+\n+\t\tp[0] \u003d LWSSS_SER_TXPRE_TXCR_UPDATE;\n+\t\tlws_ser_wu16be(\u0026p[1], 4);\n+\t\tlws_ser_wu32be(\u0026p[3], (uint32_t)md-\u003etx_cr_adjust);\n+\n+\t\tn \u003d 7;\n+\n+\t} else {\n+\n+\t\tlwsl_sspc_info(h, \u0022sending metadata\u0022);\n+\n+\t\tp[0] \u003d LWSSS_SER_TXPRE_METADATA;\n+\t\ttxc \u003d strlen(md-\u003ename);\n+\t\tn \u003d txc + 1 + md-\u003elen;\n+\t\tif (n \u003e 0xffff)\n+\t\t\t/* we can't serialize this metadata in 16b length */\n+\t\t\treturn 0;\n+\t\tif (n \u003e lws_ptr_diff_size_t(end, \u0026p[4]))\n+\t\t\t/* we don't have space for this metadata */\n+\t\t\treturn 0;\n+\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)n);\n+\t\tp[3] \u003d (uint8_t)txc;\n+\t\tmemcpy(\u0026p[4], md-\u003ename, (unsigned int)txc);\n+\t\tmemcpy(\u0026p[4 + txc], \u0026md[1], md-\u003elen);\n+\t\tn \u003d 4 + txc + md-\u003elen;\n+\t}\n+\n+\tlws_dll2_remove(\u0026md-\u003elist);\n+\tlws_free(md);\n+\n+\treturn n;\n+}\n+\n+/*\n+ * An attempt to establish a link to the SS proxy has failed\n+ */\n+\n+lws_ss_state_return_t\n+lws_sspc_txp_connect_disposition(lws_sspc_handle_t *h, int disposition)\n+{\n+\tlws_ss_state_return_t r;\n+\tuint64_t i;\n+\n+\tif (!disposition) {\n+\t\tif (!h\n+\t#if !defined(STANDALONE)\n+\t\t\t\t|| lws_fi(\u0026h-\u003efic, \u0022sspc_fail_on_linkup\u0022)\n+\t#endif\n+\t\t)\n+\t\t\treturn 1;\n+\n+\t\tlwsl_sspc_info(h, \u0022CONNECTED (%s), %s\u0022, h-\u003essi.streamtype, h-\u003etxp_path.ops_onw-\u003ename);\n+\n+\t\th-\u003estate \u003d LPCSCLI_SENDING_INITIAL_TX;\n+\t\th-\u003eus_start_upstream \u003d 0;\n+\n+\t\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\th-\u003etxp_path.priv_onw \u003d NULL;\n+\tlws_sul_schedule(h-\u003econtext, 0, \u0026h-\u003esul_retry,\n+\t\t\t lws_sspc_sul_retry_cb, LWS_US_PER_SEC);\n+\n+\tif (!h-\u003essi.state)\n+\t\treturn LWSSSSRET_OK;\n+\n+\ti \u003d (uint64_t)(lws_now_usecs() - h-\u003eus_start_upstream) / LWS_US_PER_MS;\n+\tif (i \u003e 0xffffffffull)\n+\t\ti \u003d 0xffffffffull;\n+\n+\tr \u003d h-\u003essi.state(lws_sspc_to_user_object(h), NULL,\n+\t\t\t LWSSSCS_UPSTREAM_LINK_RETRY, (uint32_t)i);\n+\n+\tif (r \u003d\u003d LWSSSSRET_DESTROY_ME)\n+\t\tlws_sspc_destroy(\u0026h);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+void\n+lws_sspc_sul_retry_cb(lws_sorted_usec_list_t *sul)\n+{\n+\tlws_sspc_handle_t *h \u003d lws_container_of(sul, lws_sspc_handle_t,\n+\t\t\t\t\t\tsul_retry);\n+\n+\tif (h-\u003etxp_path.ops_onw-\u003eevent_retry_connect(\u0026h-\u003etxp_path, h))\n+\t\tlws_sul_schedule(h-\u003econtext, 0, \u0026h-\u003esul_retry,\n+\t\t\t\t lws_sspc_sul_retry_cb, LWS_US_PER_SEC);\n+}\n+\n+/*\n+ * The transport connection has closed\n+ */\n+\n+lws_ss_state_return_t\n+lws_sspc_txp_event_closed(lws_transport_priv_t priv)\n+{\n+\tlws_sspc_handle_t *h \u003d (lws_sspc_handle_t *)priv;\n+\tlws_ss_state_return_t r \u003d LWSSSSRET_OK;\n+\n+\n+\tlwsl_sspc_notice(h, \u0022entry\u0022);\n+\n+\tif (!h) {\n+\t\tlwsl_sspc_info(h, \u0022No sspc on client proxy link close\u0022);\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\th-\u003eparser.ps \u003d RPAR_TYPE;\n+\n+\tlws_dsh_empty(h-\u003edsh);\n+\th-\u003etxp_path.priv_onw \u003d NULL;\n+\th-\u003econn_req_state \u003d LWSSSPC_ONW_NONE;\n+\tif (h-\u003ess_dangling_connected \u0026\u0026 h-\u003essi.state) {\n+\n+\t\tlwsl_sspc_notice(h, \u0022setting _DISCONNECTED\u0022);\n+\t\th-\u003ess_dangling_connected \u003d 0;\n+\t\th-\u003eprev_ss_state \u003d LWSSSCS_DISCONNECTED;\n+\t\tr \u003d h-\u003essi.state(ss_to_userobj(h), NULL,\n+\t\t\t\t\t LWSSSCS_DISCONNECTED, 0);\n+\t}\n+\tif (r !\u003d LWSSSSRET_DESTROY_ME)\n+\t\t/*\n+\t\t * schedule a reconnect in 1s\n+\t\t */\n+\t\tlws_sul_schedule(h-\u003econtext, 0, \u0026h-\u003esul_retry,\n+\t\t\t\t lws_sspc_sul_retry_cb, LWS_US_PER_SEC);\n+\n+\treturn r;\n+}\n+\n+/*\n+ * We received rx from the proxy... caller must do destroy on DESTROY_ME\n+ */\n+\n+lws_ss_state_return_t\n+lws_sspc_txp_rx_from_proxy(lws_transport_priv_t txp_priv, const uint8_t *in,\n+\t\t\t\t size_t len)\n+{\n+\tlws_sspc_handle_t *h \u003d (lws_sspc_handle_t *)txp_priv;\n+\tvoid *m \u003d (void *)((uint8_t *)\u0026h[1]);\n+\n+\tassert(h);\n+\n+#if !defined(STANDALONE)\n+\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_fake_rxparse_disconnect_me\u0022))\n+\t\treturn LWSSSSRET_DISCONNECT_ME;\n+\n+\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_fake_rxparse_destroy_me\u0022))\n+\t\treturn LWSSSSRET_DESTROY_ME;\n+#endif\n+\n+\treturn lws_sspc_deserialize_parse(h, in, len, (lws_ss_handle_t **)m);\n+}\n+\n+lws_ss_state_return_t\n+lws_sspc_txp_tx(lws_sspc_handle_t *h, size_t metadata_limit)\n+{\n+\tuint8_t *pkt \u003d NULL, *p \u003d NULL, *end \u003d NULL;\n+\tvoid *m \u003d (void *)((uint8_t *)\u0026h[1]);\n+\tlws_ss_state_return_t r;\n+\tuint8_t _s[64 + LWS_PRE], *s \u003d _s + LWS_PRE, *cp \u003d s;\n+\tsize_t txl, len;\n+\tlws_usec_t us;\n+\tint flags;\n+\n+\t/*\n+\t * Management of ss timeout can happen any time and doesn't\n+\t * depend on wsi existence or state\n+\t */\n+\n+\tif (h-\u003epending_timeout_update) {\n+\t\tcp \u003d s;\n+\t\t*s \u003d LWSSS_SER_TXPRE_TIMEOUT_UPDATE;\n+\t\t*(s + 1) \u003d 0;\n+\t\t*(s + 2) \u003d 4;\n+\t\t/*\n+\t\t * 0: use policy timeout value\n+\t\t * 0xffffffff: cancel the timeout\n+\t\t */\n+\t\tlws_ser_wu32be(s + 3, h-\u003etimeout_ms);\n+\n+\t\t/* in case anything else to write */\n+\t\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\t\th-\u003epending_timeout_update \u003d 0;\n+\t\ttxl \u003d 7;\n+\n+\t\tgoto do_write;\n+\t}\n+\n+\t*(s + 1) \u003d 0;\n+\n+\t/*\n+\t * This is the state of the link that connects us to the onward\n+\t * proxy\n+\t */\n+\tswitch (h-\u003estate) {\n+\tcase LPCSCLI_SENDING_INITIAL_TX:\n+\t\t/*\n+\t\t * We are negotating the opening of a particular\n+\t\t * streamtype\n+\t\t */\n+\t\tlwsl_sspc_notice(h, \u0022LPCSCLI_SENDING_INITIAL_TX\u0022);\n+\t\ttxl \u003d strlen(h-\u003essi.streamtype) + 1 + 4 + 4;\n+\n+\t\tcp \u003d s;\n+\t\t*s \u003d LWSSS_SER_TXPRE_STREAMTYPE;\n+\t\tlws_ser_wu16be(s + 1, (uint16_t)txl);\n+\t\t/* SSSv1: add protocol version byte (initially 1) */\n+\t\t*(s + 3) \u003d (uint8_t)LWS_SSS_CLIENT_PROTOCOL_VERSION;\n+#if defined(WIN32)\n+\t\tlws_ser_wu32be(s + 4, (uint32_t)0);\n+#else\n+\t\tlws_ser_wu32be(s + 4, (uint32_t)getpid());\n+#endif\n+\t\tlws_ser_wu32be(s + 8, (uint32_t)h-\u003etxc.peer_tx_cr_est);\n+\t\tlws_strncpy((char *)(s + 12), h-\u003essi.streamtype,\n+\t\t\t\t(sizeof(_s) - LWS_PRE) - 12);\n+\t\ttxl +\u003d 3;\n+\t\th-\u003estate \u003d LPCSCLI_WAITING_CREATE_RESULT;\n+\t\tgoto do_write;\n+\n+\tcase LPCSCLI_LOCAL_CONNECTED:\n+\n+\t\tlwsl_sspc_notice(h, \u0022LPCSCLI_LOCAL_CONNECTED\u0022);\n+\n+\t\t/*\n+\t\t * Do we need to prioritize sending any metadata\n+\t\t * changes?\n+\t\t */\n+\n+\t\tif (h-\u003emetadata_owner.count) {\n+\t\t\tlws_sspc_metadata_t *md \u003d lws_container_of(\n+\t\t\t\tlws_dll2_get_tail(\u0026h-\u003emetadata_owner),\n+\t\t\t\tlws_sspc_metadata_t, list);\n+\t\t\tsize_t n;\n+\n+\t\t\tpkt \u003d lws_malloc(metadata_limit + LWS_PRE, __func__);\n+\t\t\tif (!pkt)\n+\t\t\t\tgoto hangup;\n+\t\t\tcp \u003d p \u003d pkt + LWS_PRE;\n+\t\t\tend \u003d p + metadata_limit;\n+\n+\t\t\tn \u003d lws_sspc_serialize_metadata(h, md, p, end);\n+\t\t\tif (!n)\n+\t\t\t\tgoto metadata_hangup;\n+\n+\t\t\ttxl \u003d (size_t)n;\n+\n+\t\t\tlwsl_sspc_debug(h, \u0022(local_conn) metadata\u0022);\n+\n+\t\t\tgoto req_write_and_issue;\n+\t\t}\n+\n+\t\tif (h-\u003epending_writeable_len) {\n+\t\t\tlwsl_sspc_debug(h, \u0022(local_conn) PAYLOAD_LENGTH_HINT %u\u0022,\n+\t\t\t\t (unsigned int)h-\u003ewriteable_len);\n+\t\t\tcp \u003d s;\n+\t\t\t*s \u003d LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT;\n+\t\t\tlws_ser_wu16be(s + 1, 4);\n+\t\t\tlws_ser_wu32be(s + 3, (uint32_t)h-\u003ewriteable_len);\n+\t\t\th-\u003epending_writeable_len \u003d 0;\n+\t\t\ttxl \u003d 7;\n+\t\t\tgoto req_write_and_issue;\n+\t\t}\n+\n+\t\tif (h-\u003econn_req_state \u003e\u003d LWSSSPC_ONW_ONGOING) {\n+\t\t\tlwsl_sspc_info(h, \u0022conn_req_state %d\u0022,\n+\t\t\t\t\th-\u003econn_req_state);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tlwsl_sspc_info(h, \u0022(local_conn) onward connect\u0022);\n+\n+\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_ONGOING;\n+\n+\t\tcp \u003d s;\n+\t\t*s \u003d LWSSS_SER_TXPRE_ONWARD_CONNECT;\n+\t\t*(s + 1) \u003d 0;\n+\t\t*(s + 2) \u003d 0;\n+\t\ttxl \u003d 3;\n+\n+\t\tgoto do_write;\n+\n+\tcase LPCSCLI_OPERATIONAL:\n+\n+\t\t/*\n+\t\t *\n+\t\t * - Do we need to prioritize sending any metadata\n+\t\t * changes? (includes txcr updates)\n+\t\t *\n+\t\t * - Do we need to forward a hint about the payload\n+\t\t * length?\n+\t\t */\n+\n+\t\tpkt \u003d lws_malloc(metadata_limit + LWS_PRE, __func__);\n+\t\tif (!pkt)\n+\t\t\tgoto hangup;\n+\t\tcp \u003d p \u003d pkt + LWS_PRE;\n+\t\tend \u003d p + metadata_limit;\n+\n+\t\tif (h-\u003emetadata_owner.count) {\n+\t\t\tlws_sspc_metadata_t *md \u003d lws_container_of(\n+\t\t\t\tlws_dll2_get_tail(\u0026h-\u003emetadata_owner),\n+\t\t\t\tlws_sspc_metadata_t, list);\n+\n+\t\t\ttxl \u003d lws_sspc_serialize_metadata(h, md, p, end);\n+\t\t\tif (!txl)\n+\t\t\t\tgoto metadata_hangup;\n+\n+\t\t\tgoto req_write_and_issue;\n+\t\t}\n+\n+\t\tif (h-\u003epending_writeable_len) {\n+\t\t\tlwsl_sspc_info(h, \u0022PAYLOAD_LENGTH_HINT %u\u0022,\n+\t\t\t\t (unsigned int)h-\u003ewriteable_len);\n+\t\t\tcp \u003d s;\n+\t\t\t*s \u003d LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT;\n+\t\t\tlws_ser_wu16be(s + 1, 4);\n+\t\t\tlws_ser_wu32be(s + 3, (uint32_t)h-\u003ewriteable_len);\n+\t\t\th-\u003epending_writeable_len \u003d 0;\n+\t\t\ttxl \u003d 7;\n+\t\t\tgoto req_write_and_issue;\n+\t\t}\n+\n+\t\t/* we can't write anything if we don't have credit */\n+\t\tif (!h-\u003eignore_txc \u0026\u0026 h-\u003etxc.tx_cr \u003c\u003d 0)\n+\t\t\tlwsl_sspc_info(h, \u0022WRITEABLE / OPERATIONAL:\u0022\n+\t\t\t\t \u0022 lack credit (%d)\u0022,\n+\t\t\t\t (int)h-\u003etxc.tx_cr);\n+\n+\t\tlen \u003d metadata_limit - LWS_PRE - 19;\n+\t\tflags \u003d 0;\n+\t\tif (!h-\u003essi.tx) {\n+\t\t\ttxl \u003d 0;\n+\t\t\tgoto do_write_nz;\n+\t\t}\n+\n+\t\tr \u003d h-\u003essi.tx(m, h-\u003eord++, pkt + LWS_PRE + 19, \u0026len, \u0026flags);\n+\t\tswitch (r) {\n+\t\tcase LWSSSSRET_TX_DONT_SEND:\n+\t\t\ttxl \u003d 0;\n+\t\t\tgoto do_write_nz;\n+\n+\t\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\tcase LWSSSSRET_DESTROY_ME:\n+\t\t\tlwsl_sspc_warn(h, \u0022sspc tx DISCONNECT/DESTROY TBD\u0022);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\th-\u003etxc.tx_cr \u003d h-\u003etxc.tx_cr - (int)len;\n+\n+\t\tcp \u003d p;\n+\t\ttxl \u003d len + 19;\n+\n+\t\tus \u003d lws_now_usecs();\n+\t\tp[0] \u003d LWSSS_SER_TXPRE_TX_PAYLOAD;\n+\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)(len + 19 - 3));\n+\t\tlws_ser_wu32be(\u0026p[3], (uint32_t)flags);\n+\t\t/* time spent here waiting to send this */\n+\t\tlws_ser_wu32be(\u0026p[7], (uint32_t)(us - h-\u003eus_earliest_write_req));\n+\t\t/* ust that the client write happened */\n+\t\tlws_ser_wu64be(\u0026p[11], (uint64_t)us);\n+\t\th-\u003eus_earliest_write_req \u003d 0;\n+\n+\t\tif (flags \u0026 LWSSS_FLAG_EOM)\n+\t\t\tif (h-\u003ersidx + 1 \u003c (int)LWS_ARRAY_SIZE(h-\u003erideshare_ofs) \u0026\u0026\n+\t\t\t h-\u003erideshare_ofs[h-\u003ersidx + 1])\n+\t\t\t\th-\u003ersidx++;\n+\n+\t\tgoto do_write;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+\n+req_write_and_issue:\n+\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+do_write_nz:\n+\tif (!txl) {\n+\t\tlws_free(pkt);\n+\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+do_write:\n+\tif (\n+#if !defined(STANDALONE)\n+\t\t\t!lws_fi(\u0026h-\u003efic, \u0022sspc_link_write_fail\u0022) \u0026\u0026\n+#endif\n+\t !h-\u003etxp_path.ops_onw-\u003e_write(h-\u003etxp_path.priv_onw, cp, txl)) {\n+\t\tif (pkt)\n+\t\t\tlws_free(pkt);\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\tgoto hangup;\n+\n+metadata_hangup:\n+\tlwsl_sspc_err(h, \u0022metadata too large\u0022);\n+\n+hangup:\n+\tlws_free(pkt);\n+\tlwsl_sspc_warn(h, \u0022hangup\u0022);\n+\n+\t/* hang up on the proxy link */\n+\treturn LWSSSSRET_DISCONNECT_ME;\n+}\n+\n+void\n+lws_sspc_txp_lost_coherence(lws_transport_priv_t txp_priv)\n+{\n+\tlws_sspc_handle_t *h \u003d (lws_sspc_handle_t *)txp_priv;\n+\n+\tlwsl_sspc_warn(h, \u0022Lost Coherence\u0022);\n+\n+\th-\u003econn_req_state \u003d LWSSSPC_ONW_NONE;\n+\n+\t/* pass thru to lower layer, eg, mux */\n+\n+\th-\u003etxp_path.ops_onw-\u003elost_coherence(h-\u003etxp_path.priv_onw);\n+}\n+\n+/*\n+ * The actual client transports bind to this transport ops for \u0022inside sspc\u0022.\n+ * It's like this so we can transparently interpose the mux.\n+ *\n+ * Only the apis the transport needs to call on the inside need timplementing\n+ * for this\n+ */\n+\n+const lws_transport_client_ops_t lws_txp_inside_sspc \u003d {\n+\t.name\t\t\t\t\u003d \u0022txp_inside_sspc\u0022,\n+\t.event_connect_disposition\t\u003d lws_sspc_txp_connect_disposition,\n+\t.event_read\t\t\t\u003d lws_sspc_txp_rx_from_proxy,\n+\t.event_can_write\t\t\u003d lws_sspc_txp_tx,\n+\t.event_closed\t\t\t\u003d lws_sspc_txp_event_closed,\n+\t.lost_coherence\t\t\t\u003d lws_sspc_txp_lost_coherence,\n+};\ndiff --git a/lib/secure-streams/serialized/client/sspc.c b/lib/secure-streams/serialized/client/sspc.c\nnew file mode 100644\nindex 0000000..1f454ec\n--- /dev/null\n+++ b/lib/secure-streams/serialized/client/sspc.c\n@@ -0,0 +1,843 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+extern const uint32_t ss_state_txn_validity[17];\n+\n+#if defined(STANDALONE)\n+\n+#define lws_context lws_context_standalone\n+\n+static const char *state_names[] \u003d {\n+\t\u0022(unset)\u0022,\n+\t\u0022LWSSSCS_CREATING\u0022,\n+\t\u0022LWSSSCS_DISCONNECTED\u0022,\n+\t\u0022LWSSSCS_UNREACHABLE\u0022,\n+\t\u0022LWSSSCS_AUTH_FAILED\u0022,\n+\t\u0022LWSSSCS_CONNECTED\u0022,\n+\t\u0022LWSSSCS_CONNECTING\u0022,\n+\t\u0022LWSSSCS_DESTROYING\u0022,\n+\t\u0022LWSSSCS_POLL\u0022,\n+\t\u0022LWSSSCS_ALL_RETRIES_FAILED\u0022,\n+\t\u0022LWSSSCS_QOS_ACK_REMOTE\u0022,\n+\t\u0022LWSSSCS_QOS_NACK_REMOTE\u0022,\n+\t\u0022LWSSSCS_QOS_ACK_LOCAL\u0022,\n+\t\u0022LWSSSCS_QOS_NACK_LOCAL\u0022,\n+\t\u0022LWSSSCS_TIMEOUT\u0022,\n+\t\u0022LWSSSCS_SERVER_TXN\u0022,\n+\t\u0022LWSSSCS_SERVER_UPGRADE\u0022,\n+\t\u0022LWSSSCS_EVENT_WAIT_CANCELLED\u0022,\n+\t\u0022LWSSSCS_UPSTREAM_LINK_RETRY\u0022,\n+};\n+\n+const char *\n+lws_ss_state_name(int state)\n+{\n+\tif (state \u003e\u003d LWSSSCS_USER_BASE)\n+\t\treturn \u0022user state\u0022;\n+\n+\tif (state \u003e\u003d (int)LWS_ARRAY_SIZE(state_names))\n+\t\treturn \u0022unknown\u0022;\n+\n+\treturn state_names[state];\n+}\n+\n+const uint32_t ss_state_txn_validity[] \u003d {\n+\n+\t/* if we was last in this state... we can legally go to these states */\n+\n+\t[0]\t\t\t\t\u003d (1 \u003c\u003c LWSSSCS_CREATING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_CREATING]\t\t\u003d (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_POLL) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_SERVER_UPGRADE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_DISCONNECTED]\t\t\u003d (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_POLL) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_UNREACHABLE]\t\t\u003d (1 \u003c\u003c LWSSSCS_ALL_RETRIES_FAILED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_POLL) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t /* win conn failure \u003e retry \u003e succ */\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_CONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_AUTH_FAILED]\t\t\u003d (1 \u003c\u003c LWSSSCS_ALL_RETRIES_FAILED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_CONNECTED]\t\t\u003d (1 \u003c\u003c LWSSSCS_SERVER_UPGRADE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_SERVER_TXN) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_AUTH_FAILED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_ACK_REMOTE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_NACK_REMOTE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_ACK_LOCAL) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_NACK_LOCAL) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_POLL) | /* proxy retry */\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_CONNECTING]\t\t\u003d (1 \u003c\u003c LWSSSCS_UNREACHABLE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_AUTH_FAILED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_CONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DISCONNECTED) | /* proxy retry */\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_DESTROYING]\t\t\u003d 0,\n+\n+\t[LWSSSCS_POLL]\t\t\t\u003d (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_ALL_RETRIES_FAILED]\t\u003d (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_QOS_ACK_REMOTE]\t\u003d (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+#if defined(LWS_ROLE_MQTT)\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_ACK_REMOTE) |\n+#endif\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_QOS_NACK_REMOTE]\t\u003d (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_QOS_ACK_LOCAL]\t\t\u003d (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_QOS_NACK_LOCAL]\t\u003d (1 \u003c\u003c LWSSSCS_DESTROYING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT),\n+\n+\t/* he can get the timeout at any point and take no action... */\n+\t[LWSSSCS_TIMEOUT]\t\t\u003d (1 \u003c\u003c LWSSSCS_CONNECTING) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_CONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_ACK_REMOTE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_QOS_NACK_REMOTE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_POLL) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_UNREACHABLE) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_SERVER_TXN]\t\t\u003d (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+\n+\t[LWSSSCS_SERVER_UPGRADE]\t\u003d (1 \u003c\u003c LWSSSCS_SERVER_TXN) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_TIMEOUT) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DISCONNECTED) |\n+\t\t\t\t\t (1 \u003c\u003c LWSSSCS_DESTROYING),\n+};\n+\n+char *\n+lws_strncpy(char *dest, const char *src, size_t size)\n+{\n+\tstrncpy(dest, src, size - 1);\n+\tdest[size - 1] \u003d '\u005c0';\n+\n+\treturn dest;\n+}\n+\n+#undef lws_malloc\n+#define lws_malloc(a, b) malloc(a)\n+#undef lws_free\n+#define lws_free(a) free(a)\n+\n+extern void\n+__lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,\n+\t int filter, const char *_fun, const char *format, va_list ap);\n+\n+void _lws_logv(int filter, const char *format, va_list ap)\n+{\n+\t__lws_logv(NULL, NULL, NULL, filter, NULL, format, ap);\n+}\n+\n+void\n+_lws_log(int filter, const char *format, ...)\n+{\n+\tva_list ap;\n+\n+\tva_start(ap, format);\n+\t_lws_logv(filter, format, ap);\n+\tva_end(ap);\n+}\n+\n+void\n+_lws_log_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,\n+\t int filter, const char *_fun, const char *format, ...)\n+{\n+\tva_list ap;\n+\n+\tva_start(ap, format);\n+\t__lws_logv(cx, prep, obj, filter, _fun, format, ap);\n+\tva_end(ap);\n+}\n+\n+#endif\n+\n+int\n+lws_ss_check_next_state_sspc(lws_sspc_handle_t *ss, uint8_t *prevstate,\n+\t\t\t lws_ss_constate_t cs)\n+{\n+\tif (cs \u003e\u003d LWSSSCS_USER_BASE || cs \u003d\u003d LWSSSCS_EVENT_WAIT_CANCELLED)\n+\t\t/*\n+\t\t * we can't judge user or transient states, leave the old state\n+\t\t * and just wave them through\n+\t\t */\n+\t\treturn 0;\n+\n+\tif (cs \u003e\u003d LWS_ARRAY_SIZE(ss_state_txn_validity)) {\n+\t\t/* we don't recognize this state as usable */\n+\t\tlwsl_sspc_err(ss, \u0022bad new state %u\u0022, cs);\n+\t\tassert(0);\n+\t\treturn 1;\n+\t}\n+\n+\tif (*prevstate \u003e\u003d LWS_ARRAY_SIZE(ss_state_txn_validity)) {\n+\t\t/* existing state is broken */\n+\t\tlwsl_sspc_err(ss, \u0022bad existing state %u\u0022,\n+\t\t\t\t(unsigned int)*prevstate);\n+\t\tassert(0);\n+\t\treturn 1;\n+\t}\n+\n+\tif (ss_state_txn_validity[*prevstate] \u0026 (1u \u003c\u003c cs)) {\n+\n+\t\tlwsl_sspc_notice(ss, \u0022%s -\u003e %s\u0022,\n+\t\t\t lws_ss_state_name((int)*prevstate),\n+\t\t\t lws_ss_state_name((int)cs));\n+\n+\t\t/* this is explicitly allowed, update old state to new */\n+\t\t*prevstate \u003d (uint8_t)cs;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tlwsl_sspc_err(ss, \u0022transition from %s -\u003e %s is illegal\u0022,\n+\t\t lws_ss_state_name((int)*prevstate),\n+\t\t lws_ss_state_name((int)cs));\n+\n+\tassert(0);\n+\n+\treturn 1;\n+}\n+\n+lws_ss_state_return_t\n+lws_sspc_event_helper(lws_sspc_handle_t *h, lws_ss_constate_t cs,\n+\t\t lws_ss_tx_ordinal_t flags)\n+{\n+\tlws_ss_state_return_t ret;\n+\n+\tif (!h)\n+\t\treturn LWSSSSRET_OK;\n+\n+\tif (lws_ss_check_next_state_sspc(h, \u0026h-\u003eprev_ss_state, cs))\n+\t\treturn LWSSSSRET_DESTROY_ME;\n+\n+\tif (!h-\u003essi.state)\n+\t\treturn LWSSSSRET_OK;\n+\n+\th-\u003eh_in_svc \u003d h;\n+\tret \u003d h-\u003essi.state((void *)((uint8_t *)\u0026h[1]), NULL, cs, flags);\n+\th-\u003eh_in_svc \u003d NULL;\n+\n+\treturn ret;\n+}\n+\n+int\n+lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,\n+\t void *opaque_user_data, lws_sspc_handle_t **ppss,\n+\t struct lws_sequencer *seq_owner, const char **ppayload_fmt)\n+{\n+\tlws_sspc_handle_t *h;\n+\tuint8_t *ua;\n+\tchar *p;\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(context, tsi);\n+#endif\n+\n+\t/* allocate the handle (including ssi), the user alloc,\n+\t * and the streamname */\n+\n+\th \u003d malloc(sizeof(lws_sspc_handle_t) + ssi-\u003euser_alloc +\n+\t\t\t\tstrlen(ssi-\u003estreamtype) + 1);\n+\tif (!h)\n+\t\treturn 1;\n+\tmemset(h, 0, sizeof(*h));\n+\n+#if !defined(STANDALONE)\n+\th-\u003elc.log_cx \u003d context-\u003elog_cx;\n+#endif\n+\n+#if !defined(STANDALONE) \u0026\u0026 defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\th-\u003efic.name \u003d \u0022sspc\u0022;\n+\tlws_xos_init(\u0026h-\u003efic.xos, lws_xos(\u0026context-\u003efic.xos));\n+\tif (ssi-\u003efic.fi_owner.count)\n+\t\tlws_fi_import(\u0026h-\u003efic, \u0026ssi-\u003efic);\n+\n+\tlws_fi_inherit_copy(\u0026h-\u003efic, \u0026context-\u003efic, \u0022ss\u0022, ssi-\u003estreamtype);\n+\n+\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_create_oom\u0022)) {\n+\t\t/*\n+\t\t * We have to do this a little later, so we can cleanly inherit\n+\t\t * the OOM pieces and drain the info fic\n+\t\t */\n+\t\tlws_fi_destroy(\u0026h-\u003efic);\n+\t\tfree(h);\n+\t\treturn 1;\n+\t}\n+#endif\n+#if !defined(STANDALONE)\n+\t__lws_lc_tag(context, \u0026context-\u003elcg[LWSLCG_SSP_CLIENT], \u0026h-\u003elc,\n+\t\t\tssi-\u003estreamtype);\n+#else\n+\tsnprintf(h-\u003elc.gutag, sizeof(h-\u003elc.gutag), \u0022[sspc|%s|%x]\u0022,\n+\t\t\t\tssi-\u003estreamtype,\n+\t\t\t\t(unsigned int)(context-\u003essidx++));\n+#endif\n+\n+\th-\u003etxp_path \u003d context-\u003etxp_cpath;\n+\n+\th-\u003etxp_path.ops_in \u003d \u0026lws_txp_inside_sspc;\n+\th-\u003etxp_path.priv_in \u003d (lws_transport_priv_t)h;\n+\n+\t/* priv_onw filled in by onw transport */\n+\n+\tlwsl_sspc_notice(h, \u0022txp path %s -\u003e %s\u0022, h-\u003etxp_path.ops_in-\u003ename,\n+\t\t\t\t\t\t h-\u003etxp_path.ops_onw-\u003ename);\n+\n+\tmemcpy(\u0026h-\u003essi, ssi, sizeof(*ssi));\n+\tua \u003d (uint8_t *)\u0026h[1];\n+\tmemset(ua, 0, ssi-\u003euser_alloc);\n+\tp \u003d (char *)ua + ssi-\u003euser_alloc;\n+\tmemcpy(p, ssi-\u003estreamtype, strlen(ssi-\u003estreamtype) + 1);\n+\th-\u003essi.streamtype \u003d (const char *)p;\n+\th-\u003econtext \u003d context;\n+\th-\u003eus_start_upstream \u003d lws_now_usecs();\n+\n+\tif (!ssi-\u003emanual_initial_tx_credit)\n+\t\th-\u003etxc.peer_tx_cr_est \u003d 500000000;\n+\telse\n+\t\th-\u003etxc.peer_tx_cr_est \u003d ssi-\u003emanual_initial_tx_credit;\n+\n+#if defined(LWS_WITH_NETWORK) \u0026\u0026 defined(LWS_WITH_SYS_SMD)\n+\tif (!strcmp(ssi-\u003estreamtype, LWS_SMD_STREAMTYPENAME))\n+\t\th-\u003eignore_txc \u003d 1;\n+#endif\n+\n+\tlws_dll2_add_head(\u0026h-\u003eclient_list, \u0026context-\u003e\n+#if !defined(STANDALONE)\n+\t\t\tpt[tsi].\n+#endif\n+\t\t\tss_client_owner);\n+\n+\t/* fill in the things the real api does for the caller */\n+\n+\t*((void **)(ua + ssi-\u003eopaque_user_data_offset)) \u003d opaque_user_data;\n+\t*((void **)(ua + ssi-\u003ehandle_offset)) \u003d h;\n+\n+\tif (ppss)\n+\t\t*ppss \u003d h;\n+\n+\t/* try the actual connect */\n+\n+\tlws_sspc_sul_retry_cb(\u0026h-\u003esul_retry);\n+\n+\treturn 0;\n+}\n+\n+/* used on context destroy when iterating listed lws_ss on a pt */\n+\n+int\n+lws_sspc_destroy_dll(struct lws_dll2 *d, void *user)\n+{\n+\tlws_sspc_handle_t *h \u003d lws_container_of(d, lws_sspc_handle_t,\n+\t\t\t\t\t\tclient_list);\n+\n+\tlws_sspc_destroy(\u0026h);\n+\n+\treturn 0;\n+}\n+\n+void\n+lws_sspc_rxmetadata_destroy(lws_sspc_handle_t *h)\n+{\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\tlws_dll2_get_head(\u0026h-\u003emetadata_owner_rx)) {\n+\t\tlws_sspc_metadata_t *md \u003d\n+\t\t\t\tlws_container_of(d, lws_sspc_metadata_t, list);\n+\n+\t\tlws_dll2_remove(\u0026md-\u003elist);\n+\t\tlws_free(md);\n+\n+\t} lws_end_foreach_dll_safe(d, d1);\n+}\n+\n+void\n+lws_sspc_destroy(lws_sspc_handle_t **ph)\n+{\n+\tlws_sspc_handle_t *h;\n+\n+\tif (!*ph)\n+\t\treturn;\n+\n+\th \u003d *ph;\n+\tif (h \u003d\u003d h-\u003eh_in_svc) {\n+\t\tlwsl_err(\u0022%s: illegal destroy, return LWSSSSRET_DESTROY_ME instead\u005cn\u0022,\n+\t\t\t\t__func__);\n+\t\tassert(0);\n+\t\treturn;\n+\t}\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\n+\tif (h-\u003edestroying)\n+\t\treturn;\n+\n+\th-\u003edestroying \u003d 1;\n+\n+\t/* if this caliper is still dangling at destroy, we failed */\n+#if !defined(STANDALONE) \u0026\u0026 defined(LWS_WITH_SYS_METRICS)\n+\t/*\n+\t * If any hanging caliper measurement, dump it, and free any tags\n+\t */\n+\tlws_metrics_caliper_report_hist(h-\u003ecal_txn, (struct lws *)NULL);\n+#endif\n+\tif (h-\u003ess_dangling_connected \u0026\u0026 h-\u003essi.state) {\n+\t\tlws_sspc_event_helper(h, LWSSSCS_DISCONNECTED, 0);\n+\t\th-\u003ess_dangling_connected \u003d 0;\n+\t}\n+\n+#if !defined(STANDALONE) \u0026\u0026 defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\tlws_fi_destroy(\u0026h-\u003efic);\n+#endif\n+\n+\tlws_sul_cancel(\u0026h-\u003esul_retry);\n+\tlws_dll2_remove(\u0026h-\u003eclient_list);\n+\n+\tif (h-\u003edsh)\n+\t\tlws_dsh_destroy(\u0026h-\u003edsh);\n+\n+\th-\u003etxp_path.ops_onw-\u003e_close(h-\u003etxp_path.priv_onw);\n+\n+\t/* clean out any pending metadata changes that didn't make it */\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\tlws_dll2_get_head(\u0026(*ph)-\u003emetadata_owner)) {\n+\t\tlws_sspc_metadata_t *md \u003d\n+\t\t\t\tlws_container_of(d, lws_sspc_metadata_t, list);\n+\n+\t\tlws_dll2_remove(\u0026md-\u003elist);\n+\t\tlws_free(md);\n+\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\tlws_sspc_rxmetadata_destroy(h);\n+\n+\tlws_sspc_event_helper(h, LWSSSCS_DESTROYING, 0);\n+\t*ph \u003d NULL;\n+\n+\tlws_sul_cancel(\u0026h-\u003esul_retry);\n+\n+#if !defined(STANDALONE)\n+\t/* confirm no sul left scheduled in handle or user allocation object */\n+\tlws_sul_debug_zombies(h-\u003econtext, h, sizeof(*h) + h-\u003essi.user_alloc,\n+\t\t\t __func__);\n+#endif\n+#if !defined(STANDALONE)\n+\t__lws_lc_untag(h-\u003econtext, \u0026h-\u003elc);\n+#endif\n+\n+\tfree(h);\n+}\n+\n+lws_ss_state_return_t\n+lws_sspc_request_tx(lws_sspc_handle_t *h)\n+{\n+\tif (!h || !h-\u003etxp_path.priv_onw)\n+\t\treturn LWSSSSRET_OK;\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\n+\tif (!h-\u003eus_earliest_write_req)\n+\t\th-\u003eus_earliest_write_req \u003d lws_now_usecs();\n+\n+\tlwsl_notice(\u0022%s: state %u, conn_req_state %u\u005cn\u0022, __func__,\n+\t\t\t(unsigned int)h-\u003estate,\n+\t\t\t(unsigned int)h-\u003econn_req_state);\n+\n+\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n+\t h-\u003econn_req_state \u003d\u003d LWSSSPC_ONW_NONE)\n+\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_REQ;\n+\n+\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+/*\n+ * Currently we fulfil the writeable part locally by just enabling POLLOUT on\n+ * the UDS link, without serialization footprint, which is reasonable as far as\n+ * it goes.\n+ *\n+ * But for the ..._len() variant, the expected payload length hint we are being\n+ * told is something that must be serialized to the onward peer, since either\n+ * that guy or someone upstream of him is the guy who will compose the framing\n+ * with it that actually goes out.\n+ *\n+ * This information is needed at the upstream guy before we have sent any\n+ * payload, eg, for http POST, he has to prepare the content-length in the\n+ * headers, before any payload. So we have to issue a serialization of the\n+ * length at this point.\n+ */\n+\n+lws_ss_state_return_t\n+lws_sspc_request_tx_len(lws_sspc_handle_t *h, unsigned long len)\n+{\n+\t/*\n+\t * for client conns, they cannot even complete creation of the handle\n+\t * without the onwared connection to the proxy, it's not legal to start\n+\t * using it until it's operation and has the onward connection (and the\n+\t * link has called CREATED state)\n+\t */\n+\n+\tif (!h)\n+\t\treturn LWSSSSRET_OK;\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\n+\tlwsl_sspc_notice(h, \u0022setting writeable_len %u\u0022, (unsigned int)len);\n+\th-\u003ewriteable_len \u003d len;\n+\th-\u003epending_writeable_len \u003d 1;\n+\n+\tif (!h-\u003eus_earliest_write_req)\n+\t\th-\u003eus_earliest_write_req \u003d lws_now_usecs();\n+\n+\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n+\t h-\u003econn_req_state \u003d\u003d LWSSSPC_ONW_NONE)\n+\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_REQ;\n+\n+\t/*\n+\t * We're going to use this up with serializing h-\u003ewriteable_len... that\n+\t * will request again.\n+\t */\n+\n+\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+lws_ss_state_return_t\n+lws_sspc_client_connect(struct lws_sspc_handle *h)\n+{\n+\tif (!h || h-\u003estate \u003d\u003d LPCSCLI_OPERATIONAL)\n+\t\treturn 0;\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\n+\tassert(h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED);\n+\tif (h-\u003estate \u003d\u003d LPCSCLI_LOCAL_CONNECTED \u0026\u0026\n+\t h-\u003econn_req_state \u003d\u003d LWSSSPC_ONW_NONE)\n+\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_REQ;\n+\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+\treturn 0;\n+}\n+\n+struct lws_context *\n+lws_sspc_get_context(struct lws_sspc_handle *h)\n+{\n+\treturn h-\u003econtext;\n+}\n+\n+const char *\n+lws_sspc_rideshare(struct lws_sspc_handle *h)\n+{\n+\t/*\n+\t * ...the serialized RX rideshare name if any...\n+\t */\n+\n+\tif (h-\u003eparser.rideshare[0]) {\n+\t\tlwsl_sspc_info(h, \u0022parser %s\u0022, h-\u003eparser.rideshare);\n+\n+\t\treturn h-\u003eparser.rideshare;\n+\t}\n+\n+\t/*\n+\t * The tx rideshare index\n+\t */\n+\n+\tif (h-\u003erideshare_list[0]) {\n+\t\tlwsl_sspc_info(h, \u0022tx list %s\u0022,\n+\t\t\t \u0026h-\u003erideshare_list[h-\u003erideshare_ofs[h-\u003ersidx]]);\n+\t\treturn \u0026h-\u003erideshare_list[h-\u003erideshare_ofs[h-\u003ersidx]];\n+\t}\n+\n+\t/*\n+\t * ... otherwise default to our stream type name\n+\t */\n+\n+\tlwsl_sspc_info(h, \u0022def %s\u005cn\u0022, h-\u003essi.streamtype);\n+\n+\treturn h-\u003essi.streamtype;\n+}\n+\n+static int\n+_lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,\n+\t\t const void *value, size_t len, int tx_cr_adjust)\n+{\n+\tlws_sspc_metadata_t *md;\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\n+\t/*\n+\t * Are we replacing a pending metadata of the same name? It's not\n+\t * efficient to do this but user code can do what it likes... let's\n+\t * optimize away the old one.\n+\t *\n+\t * Tx credit adjust always has name \u0022\u0022\n+\t */\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\t\t lws_dll2_get_head(\u0026h-\u003emetadata_owner)) {\n+\t\tmd \u003d lws_container_of(d, lws_sspc_metadata_t, list);\n+\n+\t\tif (!strcmp(name, md-\u003ename)) {\n+\t\t\tlws_dll2_remove(\u0026md-\u003elist);\n+\t\t\tlws_free(md);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\t/*\n+\t * We have to stash the metadata and pass it to the proxy\n+\t */\n+#if !defined(STANDALONE)\n+\tif (lws_fi(\u0026h-\u003efic, \u0022sspc_fail_metadata_set\u0022))\n+\t\tmd \u003d NULL;\n+\telse\n+#endif\n+\t\tmd \u003d lws_malloc(sizeof(*md) + len, \u0022set metadata\u0022);\n+\tif (!md) {\n+\t\tlwsl_sspc_err(h, \u0022OOM\u0022);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tmemset(md, 0, sizeof(*md));\n+\n+\tmd-\u003etx_cr_adjust \u003d tx_cr_adjust;\n+\th-\u003etxc.peer_tx_cr_est +\u003d tx_cr_adjust;\n+\n+\tlws_strncpy(md-\u003ename, name, sizeof(md-\u003ename));\n+\tmd-\u003elen \u003d len;\n+\tif (len)\n+\t\tmemcpy(\u0026md[1], value, len);\n+\n+\tlws_dll2_add_tail(\u0026md-\u003elist, \u0026h-\u003emetadata_owner);\n+\n+\tif (len) {\n+#if !defined(STANDALONE)\n+\t\tlwsl_sspc_info(h, \u0022set metadata %s\u0022, name);\n+\t\tlwsl_hexdump_sspc_info(h, value, len);\n+#endif\n+\t} else\n+\t\tlwsl_sspc_info(h, \u0022serializing tx cr adj %d\u0022,\n+\t\t\t (int)tx_cr_adjust);\n+\n+\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+\n+\treturn 0;\n+}\n+\n+int\n+lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,\n+\t\t const void *value, size_t len)\n+{\n+\treturn _lws_sspc_set_metadata(h, name, value, len, 0);\n+}\n+\n+int\n+lws_sspc_get_metadata(struct lws_sspc_handle *h, const char *name,\n+\t\t const void **value, size_t *len)\n+{\n+\tlws_sspc_metadata_t *md;\n+\n+\t/*\n+\t * client side does not have access to policy\n+\t * and any metadata are new to it each time,\n+\t * we allocate them, removing any existing with\n+\t * the same name first\n+\t */\n+\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\tlws_dll2_get_head(\u0026h-\u003emetadata_owner_rx)) {\n+\t\tmd \u003d lws_container_of(d,\n+\t\t\t lws_sspc_metadata_t, list);\n+\n+\t\tif (!strcmp(md-\u003ename, name)) {\n+\t\t\t*len \u003d md-\u003elen;\n+\t\t\t*value \u003d \u0026md[1];\n+\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\treturn 1;\n+}\n+\n+int\n+lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t bump)\n+{\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\tlwsl_sspc_notice(h, \u0022%d\u005cn\u0022, (int)bump);\n+\treturn _lws_sspc_set_metadata(h, \u0022\u0022, NULL, 0, (int)bump);\n+}\n+\n+int\n+lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h)\n+{\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\treturn h-\u003etxc.peer_tx_cr_est;\n+}\n+\n+void\n+lws_sspc_start_timeout(struct lws_sspc_handle *h, unsigned int timeout_ms)\n+{\n+#if !defined(STANDALONE)\n+\tlws_service_assert_loop_thread(h-\u003econtext, 0);\n+#endif\n+\tif (!h-\u003etxp_path.priv_onw)\n+\t\t/* we can't fulfil it */\n+\t\treturn;\n+\th-\u003etimeout_ms \u003d (uint32_t)timeout_ms;\n+\th-\u003epending_timeout_update \u003d 1;\n+\th-\u003etxp_path.ops_onw-\u003ereq_write(h-\u003etxp_path.priv_onw);\n+}\n+\n+void\n+lws_sspc_cancel_timeout(struct lws_sspc_handle *h)\n+{\n+\tlws_sspc_start_timeout(h, (unsigned int)-1);\n+}\n+\n+void *\n+lws_sspc_to_user_object(struct lws_sspc_handle *h)\n+{\n+\treturn (void *)\u0026h[1];\n+}\n+\n+struct lws_log_cx *\n+lwsl_sspc_get_cx(struct lws_sspc_handle *sspc)\n+{\n+\tif (!sspc)\n+\t\treturn NULL;\n+\n+\treturn sspc-\u003elc.log_cx;\n+}\n+\n+void\n+lws_log_prepend_sspc(struct lws_log_cx *cx, void *obj, char **p, char *e)\n+{\n+\tstruct lws_sspc_handle *h \u003d (struct lws_sspc_handle *)obj;\n+\n+#if defined(STANDALONE)\n+\tsnprintf(*p, lws_ptr_diff_size_t(e, (*p)), \u0022%s: \u0022, h-\u003elc.gutag);\n+#else\n+\t*p +\u003d lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), \u0022%s: \u0022,\n+\t\t\tlws_sspc_tag(h));\n+#endif\n+}\n+\n+void\n+lws_sspc_change_handlers(struct lws_sspc_handle *h, lws_sscb_rx rx,\n+\t\t\t lws_sscb_tx tx, lws_sscb_state state)\n+{\n+\tif (rx)\n+\t\th-\u003essi.rx \u003d rx;\n+\tif (tx)\n+\t\th-\u003essi.tx \u003d tx;\n+\tif (state)\n+\t\th-\u003essi.state \u003d state;\n+}\n+\n+const char *\n+lws_sspc_tag(struct lws_sspc_handle *h)\n+{\n+\tif (!h)\n+\t\treturn \u0022[null sspc]\u0022;\n+#if defined(STANDALONE)\n+\treturn h-\u003elc.gutag;\n+#else\n+\treturn lws_lc_tag(\u0026h-\u003elc);\n+#endif\n+}\n+\n+int\n+lws_sspc_cancel_notify_dll(struct lws_dll2 *d, void *user)\n+{\n+\tlws_sspc_handle_t *h \u003d lws_container_of(d, lws_sspc_handle_t,\n+\t\t\t\t\t\tclient_list);\n+\n+\tlws_sspc_event_helper(h, LWSSSCS_EVENT_WAIT_CANCELLED, 0);\n+\n+\treturn 0;\n+}\n+\n+\ndiff --git a/lib/secure-streams/serialized/proxy/proxy-deserialize.c b/lib/secure-streams/serialized/proxy/proxy-deserialize.c\nnew file mode 100644\nindex 0000000..112b8b2\n--- /dev/null\n+++ b/lib/secure-streams/serialized/proxy/proxy-deserialize.c\n@@ -0,0 +1,841 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * Serialized Secure Streams deserializer for Proxy side\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+/*\n+ * event loop is consuming dsh-buffered, already-serialized tx from the\n+ * foreign side\n+ */\n+\n+int\n+lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,\n+\t\t\t lws_ss_tx_ordinal_t ord, uint8_t *buf,\n+\t\t\t size_t *len, int *flags)\n+{\n+\tuint8_t *p;\n+\tsize_t si;\n+\n+\tif (lws_dsh_get_head(dsh, KIND_C_TO_P, (void **)\u0026p, \u0026si)) {\n+\t\t*len \u003d 0;\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * The packet in the dsh has a proxying serialization header, process\n+\t * and strip it so we just forward the payload\n+\t */\n+\n+\tif (*len \u003c\u003d si - 23 || si \u003c 23) {\n+\t\t/*\n+\t\t * What comes out of the dsh needs to fit in the tx buffer...\n+\t\t * we have arrangements at the proxy rx of the client UDS to\n+\t\t * chop chunks larger than 1380 into seuqential lumps of 1380\n+\t\t */\n+\t\tlwsl_err(\u0022%s: *len \u003d %d, si \u003d %d\u005cn\u0022, __func__, (int)*len, (int)si);\n+\t\tassert(0);\n+\t\treturn 1;\n+\t}\n+\tif (p[0] !\u003d LWSSS_SER_TXPRE_TX_PAYLOAD) {\n+\t\tassert(0);\n+\t\treturn 1;\n+\t}\n+\n+\t*len \u003d (size_t)(lws_ser_ru16be(\u0026p[1]) - (23 - 3));\n+\tif (*len !\u003d si - 23) {\n+\t\t/*\n+\t\t * We cannot accept any length that doesn't reflect the actual\n+\t\t * length of what came in from the dsh, either something nasty\n+\t\t * happened with truncation or we are being attacked\n+\t\t */\n+\t\tassert(0);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tmemcpy(buf, p + 23, si - 23);\n+\n+\t*flags \u003d (int)lws_ser_ru32be(\u0026p[3]);\n+\n+\tlws_dsh_free((void **)\u0026p);\n+\n+\treturn 0;\n+}\n+\n+\n+\n+/*\n+ * event loop side is consuming serialized data from the client via dsh, parse\n+ * it using a bytewise parser for the serialization header(s)...\n+ * it's possibly coalesced\n+ *\n+ * client: pss is pointing to the start of userdata. We can use\n+ * pss_to_sspc_h(_pss, _ssi) to convert that to a pointer to the sspc\n+ * handle\n+ *\n+ * proxy: pss is pointing to \u0026conn-\u003ess, a pointer to the ss handle\n+ *\n+ * Returns one of\n+ *\n+ * \tLWSSSSRET_OK\n+ *\tLWSSSSRET_DISCONNECT_ME\n+ *\tLWSSSSRET_DESTROY_ME\n+ */\n+\n+/* convert userdata ptr _pss to handle pointer, allowing for any layout in\n+ * userdata */\n+#define client_pss_to_sspc_h(_pss, _ssi) (*((lws_sspc_handle_t **) \u005c\n+\t\t\t\t ((uint8_t *)_pss) + _ssi-\u003ehandle_offset))\n+/* client pss to sspc userdata */\n+#define client_pss_to_userdata(_pss) ((void *)_pss)\n+/* proxy convert pss to ss handle */\n+#define proxy_pss_to_ss_h(_pss) (*_pss)\n+\n+/* convert userdata ptr _pss to handle pointer, allowing for any layout in\n+ * userdata */\n+#define client_pss_to_sspc_h(_pss, _ssi) (*((lws_sspc_handle_t **) \u005c\n+\t\t\t\t ((uint8_t *)_pss) + _ssi-\u003ehandle_offset))\n+/* client pss to sspc userdata */\n+#define client_pss_to_userdata(_pss) ((void *)_pss)\n+/* proxy convert pss to ss handle */\n+#define proxy_pss_to_ss_h(_pss) (*_pss)\n+\n+int\n+lws_ss_proxy_deserialize_parse(struct lws_ss_serialization_parser *par,\n+\t\t\t struct lws_context *context,\n+\t\t\t struct lws_dsh *dsh, const uint8_t *cp,\n+\t\t\t size_t len, lws_ss_conn_states_t *state,\n+\t\t\t void *parconn, lws_ss_handle_t **pss,\n+\t\t\t lws_ss_info_t *ssi)\n+{\n+\tlws_ss_state_return_t r;\n+\tlws_ss_metadata_t *pm;\n+\tuint8_t pre[23];\n+\tuint32_t flags;\n+\tlws_usec_t us;\n+\tuint8_t *p;\n+\tint n;\n+\n+\twhile (len--) {\n+\n+\t\tswitch (par-\u003eps) {\n+\t\tcase RPAR_TYPE:\n+\t\t\tpar-\u003etype \u003d *cp++;\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_LEN_MSB: /* this is remaining frame length */\n+\t\t\tpar-\u003erem \u003d (uint16_t)((*cp++) \u003c\u003c 8);\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_LEN_LSB:\n+\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem | *cp++);\n+\t\t\tswitch (par-\u003etype) {\n+\n+\t\t\t/* event loop side */\n+\n+\t\t\tcase LWSSS_SER_TXPRE_TX_PAYLOAD:\n+\n+\t\t\t\tif (*state !\u003d LPCSPROX_OPERATIONAL)\n+\t\t\t\t\tgoto hangup;\n+\n+\t\t\t\tpar-\u003eps \u003d RPAR_FLAG_B3;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_DESTROYING:\n+\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\t\tlwsl_cx_notice(context, \u0022DESTROYING\u0022);\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_ONWARD_CONNECT:\n+\n+\n+\t\t\t\tif (*state !\u003d LPCSPROX_OPERATIONAL)\n+\t\t\t\t\tgoto hangup;\n+\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\t\tlwsl_cx_notice(context, \u0022ONWARD_CONNECT\u0022);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Shrug it off if we are already connecting or\n+\t\t\t\t * connected\n+\t\t\t\t */\n+\n+\t\t\t\tif (!proxy_pss_to_ss_h(pss) ||\n+\t\t\t\t proxy_pss_to_ss_h(pss)-\u003ewsi)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\t/*\n+\t\t\t\t * We're going to try to do the onward connect\n+\t\t\t\t */\n+\n+\t\t\t\tif ((proxy_pss_to_ss_h(pss) \u0026\u0026\n+\t\t\t\t lws_fi(\u0026proxy_pss_to_ss_h(pss)-\u003efic,\n+\t\t\t\t\t\t \u0022ssproxy_onward_conn_fail\u0022)) ||\n+\t\t\t\t _lws_ss_client_connect(proxy_pss_to_ss_h(pss),\n+\t\t\t\t\t\t\t 0, parconn) \u003d\u003d\n+\t\t\t\t\t\t\t LWSSSSRET_DESTROY_ME)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_STREAMTYPE:\n+\n+\t\t\t\tif (*state !\u003d LPCSPROX_WAIT_INITIAL_TX)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tif (par-\u003erem \u003c 1 + 4 + 1)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tpar-\u003eps \u003d RPAR_INIT_PROVERS;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_METADATA:\n+\n+\t\t\t\tif (par-\u003erem \u003c 3)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tpar-\u003eps \u003d RPAR_METADATA_NAMELEN;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_TXCR_UPDATE:\n+\t\t\t\tpar-\u003eps \u003d RPAR_TXCR0;\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_TIMEOUT_UPDATE:\n+\n+\t\t\t\tif (par-\u003erem !\u003d 4)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tpar-\u003eps \u003d RPAR_TIMEOUT0;\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT:\n+\n+\t\t\t\tif (par-\u003erem !\u003d 4)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tpar-\u003eps \u003d RPAR_PAYLEN0;\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tbreak;\n+\n+\t\t\t/* client side */\n+\n+\t\t\tcase LWSSS_SER_RXPRE_RX_PAYLOAD:\n+\t\t\tcase LWSSS_SER_RXPRE_CREATE_RESULT:\n+\t\t\tcase LWSSS_SER_RXPRE_CONNSTATE:\n+\t\t\tcase LWSSS_SER_RXPRE_METADATA:\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_TXCR_UPDATE:\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tpar-\u003eps \u003d RPAR_RX_TXCR_UPDATE;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase LWSSS_SER_RXPRE_PERF:\n+\t\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\t\tif (!par-\u003erem)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tpar-\u003eps \u003d RPAR_PERF;\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\n+\t\t\t\tlwsl_cx_notice(context, \u0022bad type 0x%x\u0022,\n+\t\t\t\t\t par-\u003etype);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\t\tcase RPAR_FLAG_B3:\n+\t\t\tcase RPAR_FLAG_B2:\n+\t\t\tcase RPAR_FLAG_B1:\n+\t\t\tcase RPAR_FLAG_B0:\n+\t\t\t\tpar-\u003eflags \u003c\u003c\u003d 8;\n+\t\t\t\tpar-\u003eflags |\u003d *cp++;\n+\t\t\t\tpar-\u003eps++;\n+\t\t\t\tif (!par-\u003erem--)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase RPAR_LATA3:\n+\t\t\tcase RPAR_LATA2:\n+\t\t\tcase RPAR_LATA1:\n+\t\t\tcase RPAR_LATA0:\n+\t\t\t\tpar-\u003eusd_phandling \u003c\u003c\u003d 8;\n+\t\t\t\tpar-\u003eusd_phandling |\u003d *cp++;\n+\t\t\t\tpar-\u003eps++;\n+\t\t\t\tif (!par-\u003erem--)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase RPAR_LATB7:\n+\t\t\tcase RPAR_LATB6:\n+\t\t\tcase RPAR_LATB5:\n+\t\t\tcase RPAR_LATB4:\n+\t\t\tcase RPAR_LATB3:\n+\t\t\tcase RPAR_LATB2:\n+\t\t\tcase RPAR_LATB1:\n+\t\t\tcase RPAR_LATB0:\n+\t\t\t\tpar-\u003eust_pwait \u003c\u003c\u003d 8;\n+\t\t\t\tpar-\u003eust_pwait |\u003d *cp++;\n+\t\t\t\tpar-\u003eps++;\n+\t\t\t\tpar-\u003efrag1 \u003d 1;\n+\t\t\t\tif (!par-\u003erem--)\n+\t\t\t\t\tgoto hangup;\n+\n+\t\t\t\tif (par-\u003eps \u003d\u003d RPAR_RIDESHARE_LEN \u0026\u0026\n+\t\t\t\t !(par-\u003eflags \u0026 LWSSS_FLAG_RIDESHARE))\n+\t\t\t\t\tpar-\u003eps \u003d RPAR_PAYLOAD;\n+\n+\t\t\t\tif (par-\u003erem)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\t/* fallthru - handle 0-length payload */\n+\n+\t\t\t\tif (!(par-\u003eflags \u0026 LWSSS_FLAG_RIDESHARE))\n+\t\t\t\t\tgoto payload_ff;\n+\t\t\t\tgoto hangup;\n+\n+\t\t\t/*\n+\t\t\t * Inbound rideshare info is provided on the RX packet\n+\t\t\t * itself\n+\t\t\t */\n+\n+\t\tcase RPAR_RIDESHARE_LEN:\n+\t\t\tpar-\u003eslen \u003d *cp++;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps++;\n+\t\t\tif (par-\u003erem-- \u003c par-\u003eslen)\n+\t\t\t\tgoto hangup;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_PERF:\n+\t\t\tn \u003d (int)len + 1;\n+\t\t\tif (n \u003e par-\u003erem)\n+\t\t\t\tn \u003d par-\u003erem;\n+\n+\t\t\tif (n) {\n+\t\t\t\tcp +\u003d n;\n+\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem -\n+\t\t\t\t\t\t(uint16_t)(unsigned int)n);\n+\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n+\t\t\t}\n+\t\t\tif (!par-\u003erem)\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_RIDESHARE:\n+\t\t\tpar-\u003erideshare[par-\u003ectr++] \u003d (char)*cp++;\n+\t\t\tif (!par-\u003erem--)\n+\t\t\t\tgoto hangup;\n+\t\t\tif (par-\u003ectr !\u003d par-\u003eslen)\n+\t\t\t\tbreak;\n+\t\t\tpar-\u003eps \u003d RPAR_PAYLOAD;\n+\t\t\tif (par-\u003erem)\n+\t\t\t\tbreak;\n+\n+\t\t\t/* fallthru - handle 0-length payload */\n+\n+\t\tcase RPAR_PAYLOAD:\n+payload_ff:\n+\t\t\tn \u003d (int)len + 1;\n+\t\t\tif (n \u003e par-\u003erem)\n+\t\t\t\tn \u003d par-\u003erem;\n+\t\t\t/*\n+\t\t\t * We get called with a serialized buffer of a size\n+\t\t\t * chosen by the client. We can only create dsh entries\n+\t\t\t * with up to 1380 payload, to guarantee we can emit\n+\t\t\t * them on the onward connection atomically.\n+\t\t\t *\n+\t\t\t * If 1380 isn't enough to cover what was handed to us,\n+\t\t\t * we'll stop at 1380 and go around again and create\n+\t\t\t * more dsh entries for the rest, with their own\n+\t\t\t * headers.\n+\t\t\t */\n+\n+\t\t\tif (n \u003e 1380)\n+\t\t\t\tn \u003d 1380;\n+\n+\t\t\t/*\n+\t\t\t * Since we're in the business of fragmenting client\n+\t\t\t * serialized payloads at 1380, we have to deal with\n+\t\t\t * refragmenting the SOM / EOM flags that covered the\n+\t\t\t * whole client serialized packet, so they apply to\n+\t\t\t * each dsh entry we split it into correctly\n+\t\t\t */\n+\n+\t\t\tflags \u003d par-\u003eflags \u0026 LWSSS_FLAG_RELATED_START;\n+\t\t\tif (par-\u003efrag1)\n+\t\t\t\t/*\n+\t\t\t\t * Only set the first time we came to this\n+\t\t\t\t * state after deserialization of the header\n+\t\t\t\t */\n+\t\t\t\tflags |\u003d par-\u003eflags \u0026\n+\t\t\t\t (LWSSS_FLAG_SOM | LWSSS_FLAG_POLL);\n+\n+\t\t\tif (par-\u003erem \u003d\u003d n)\n+\t\t\t\t/*\n+\t\t\t\t * We are going to complete the advertised\n+\t\t\t\t * payload length from the client on this dsh,\n+\t\t\t\t * so give him the EOM type flags if any\n+\t\t\t\t */\n+\t\t\t\tflags |\u003d par-\u003eflags \u0026 (LWSSS_FLAG_EOM |\n+\t\t\t\t\t\tLWSSS_FLAG_RELATED_END);\n+\n+\t\t\tpar-\u003efrag1 \u003d 0;\n+\t\t\tus \u003d lws_now_usecs();\n+\n+\t\t\t{\n+\t\t\t\tlws_ss_handle_t *hss;\n+\n+\t\t\t\t/*\n+\t\t\t\t * Proxy - we received some serialized tx from\n+\t\t\t\t * the client.\n+\t\t\t\t *\n+\t\t\t\t * The header for buffering private to the\n+\t\t\t\t * proxy is 23 bytes vs 19, so we can hold the\n+\t\t\t\t * current time when it was buffered\n+\t\t\t\t * additionally\n+\t\t\t\t */\n+\n+\t\t\t\thss \u003d proxy_pss_to_ss_h(pss);\n+\t\t\t\tif (hss)\n+\t\t\t\t\tlwsl_ss_info(hss, \u0022C2P RX: len %d\u0022, (int)n);\n+\n+\t\t\t\tp \u003d pre;\n+\t\t\t\tpre[0] \u003d LWSSS_SER_TXPRE_TX_PAYLOAD;\n+\t\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)((unsigned int)n + 23 - 3));\n+\t\t\t\tlws_ser_wu32be(\u0026p[3], flags);\n+\t\t\t\t/* us held at client before written */\n+\t\t\t\tlws_ser_wu32be(\u0026p[7], par-\u003eusd_phandling);\n+\t\t\t\t/* us taken for transit to proxy */\n+\t\t\t\tlws_ser_wu32be(\u0026p[11], (uint32_t)(us -\n+\t\t\t\t\t\t(lws_usec_t)par-\u003eust_pwait));\n+\t\t\t\t/* time used later to find proxy hold time */\n+\t\t\t\tlws_ser_wu64be(\u0026p[15], (uint64_t)us);\n+\n+\t\t\t\tif ((hss \u0026\u0026\n+\t\t\t\t lws_fi(\u0026hss-\u003efic, \u0022ssproxy_dsh_c2p_pay_oom\u0022)) ||\n+\t\t\t\t lws_dsh_alloc_tail(dsh, KIND_C_TO_P, pre,\n+\t\t\t\t\t\t 23, cp, (unsigned int)n)) {\n+\t\t\t\t\tlwsl_ss_err(hss, \u0022unable to alloc in dsh 3\u0022);\n+\n+\t\t\t\t\treturn LWSSSSRET_DISCONNECT_ME;\n+\t\t\t\t}\n+\n+\t\t\t\tlwsl_notice(\u0022%s: dsh c2p %d, p2c %d\u005cn\u0022, __func__,\n+\t\t\t\t\t (int)lws_dsh_get_size(dsh, KIND_C_TO_P),\n+\t\t\t\t\t (int)lws_dsh_get_size(dsh, 1));\n+\n+\t\t\t\tif (hss)\n+\t\t\t\t\t_lws_ss_request_tx(hss);\n+\t\t\t}\n+\n+\t\t\tif (n) {\n+\t\t\t\tcp +\u003d n;\n+\t\t\t\tpar-\u003erem \u003d (uint16_t)(par-\u003erem -\n+\t\t\t\t\t\t (uint16_t)(unsigned int)n);\n+\t\t\t\tlen \u003d (len + 1) - (unsigned int)n;\n+\t\t\t\t/*\n+\t\t\t\t * if we didn't consume it all, we'll come\n+\t\t\t\t * around again and produce more dsh entries up\n+\t\t\t\t * to 1380 each until it is gone\n+\t\t\t\t */\n+\t\t\t}\n+\t\t\tif (!par-\u003erem)\n+\t\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_RX_TXCR_UPDATE:\n+\t\t\tgoto hangup;\n+\n+\t\tcase RPAR_INIT_PROVERS:\n+\t\t\t/* Protocol version byte for this connection */\n+\t\t\tpar-\u003eprotocol_version \u003d *cp++;\n+\n+\t\t\t/*\n+\t\t\t * So we have to know what versions of the serialization\n+\t\t\t * protocol we can support at the proxy side, and\n+\t\t\t * reject anythng we don't know how to deal with\n+\t\t\t * noisily in the logs.\n+\t\t\t */\n+\n+\t\t\tif (par-\u003eprotocol_version !\u003d 1) {\n+\t\t\t\tlwsl_err(\u0022%s: Rejecting client with \u0022\n+\t\t\t\t\t \u0022unsupported SSv%d protocol\u005cn\u0022,\n+\t\t\t\t\t __func__, par-\u003eprotocol_version);\n+\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tif (!--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_INIT_PID;\n+\t\t\tbreak;\n+\n+\n+\t\tcase RPAR_INIT_PID:\n+\t\t\tif (!--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4)\n+\t\t\t\tbreak;\n+\n+\t\t\tpar-\u003eclient_pid \u003d (uint32_t)par-\u003etemp32;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_INITTXC0;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_INITTXC0:\n+\t\t\tif (!--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4)\n+\t\t\t\tbreak;\n+\n+\t\t\tpar-\u003etxcr_out \u003d par-\u003etemp32;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps \u003d RPAR_STREAMTYPE;\n+\t\t\tbreak;\n+\n+\t\t/*\n+\t\t * These are the client adjusting our / the remote peer ability\n+\t\t * to send back to him. He's sending a signed u32 BE\n+\t\t */\n+\n+\t\tcase RPAR_TXCR0:\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4) {\n+\t\t\t\tif (!--par-\u003erem)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\t/*\n+\t\t\t * We're the proxy, being told by the client\n+\t\t\t * that it wants to allow more tx from the peer\n+\t\t\t * on the onward connection towards it.\n+\t\t\t */\n+#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT)\n+\t\t\tif (proxy_pss_to_ss_h(pss) \u0026\u0026\n+\t\t\t proxy_pss_to_ss_h(pss)-\u003ewsi) {\n+\t\t\t\tlws_wsi_tx_credit(\n+\t\t\t\t\tproxy_pss_to_ss_h(pss)-\u003ewsi,\n+\t\t\t\t\t\t LWSTXCR_PEER_TO_US,\n+\t\t\t\t\t\t par-\u003etemp32);\n+\t\t\t\tlwsl_notice(\u0022%s: proxy RX_PEER_TXCR: +%d (est %d)\u005cn\u0022,\n+\t\t\t\t\t __func__, par-\u003etemp32,\n+\t\t\t\t\t proxy_pss_to_ss_h(pss)-\u003ewsi-\u003e\n+\t\t\t\t\t\t txc.peer_tx_cr_est);\n+\t\t\t\t_lws_ss_request_tx(proxy_pss_to_ss_h(pss));\n+\t\t\t} else\n+#endif\n+\t\t\t\tlwsl_info(\u0022%s: dropping TXCR\u005cn\u0022, __func__);\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_TIMEOUT0:\n+\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4) {\n+\t\t\t\tif (!--par-\u003erem)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\t/*\n+\t\t\t * Proxy...\n+\t\t\t *\n+\t\t\t * *pss may have gone away asynchronously inbetweentimes\n+\t\t\t */\n+\n+\t\t\tif (proxy_pss_to_ss_h(pss)) {\n+\n+\t\t\t\tif ((unsigned int)par-\u003etemp32 \u003d\u003d 0xffffffff) {\n+\t\t\t\t\tlwsl_notice(\u0022%s: cancel ss timeout\u005cn\u0022,\n+\t\t\t\t\t\t\t__func__);\n+\t\t\t\t\tlws_ss_cancel_timeout(\n+\t\t\t\t\t\tproxy_pss_to_ss_h(pss));\n+\t\t\t\t} else {\n+\n+\t\t\t\t\tif (!par-\u003etemp32)\n+\t\t\t\t\t\tpar-\u003etemp32 \u003d (int)\n+\t\t\t\t\t\t proxy_pss_to_ss_h(pss)-\u003e\n+\t\t\t\t\t\t\t policy-\u003etimeout_ms;\n+\n+\t\t\t\t\tlwsl_notice(\u0022%s: set ss timeout for +%ums\u005cn\u0022,\n+\t\t\t\t\t\t __func__, par-\u003etemp32);\n+\n+\t\t\t\t\tlws_ss_start_timeout(\n+\t\t\t\t\t\tproxy_pss_to_ss_h(pss),\n+\t\t\t\t\t\t (unsigned int)par-\u003etemp32);\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_PAYLEN0:\n+\t\t\t/*\n+\t\t\t * It's the length from lws_ss_request_tx_len() being\n+\t\t\t * passed up to the proxy\n+\t\t\t */\n+\t\t\tpar-\u003etemp32 \u003d (par-\u003etemp32 \u003c\u003c 8) | *cp++;\n+\t\t\tif (++par-\u003ectr \u003c 4) {\n+\t\t\t\tif (!--par-\u003erem)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tlwsl_notice(\u0022%s: set payload len %u\u005cn\u0022, __func__,\n+\t\t\t\t par-\u003etemp32);\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\n+\t\t\tif (proxy_pss_to_ss_h(pss)) {\n+\t\t\t\tr \u003d lws_ss_request_tx_len(proxy_pss_to_ss_h(pss),\n+\t\t\t\t\t\t\t(unsigned long)par-\u003etemp32);\n+\t\t\t\tif (r \u003d\u003d LWSSSSRET_DESTROY_ME)\n+\t\t\t\t\tgoto hangup;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_METADATA_NAMELEN:\n+\t\t\t/* both client and proxy */\n+\t\t\tif (!--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\t\t\tpar-\u003eslen \u003d *cp++;\n+\t\t\tif (par-\u003eslen \u003e\u003d sizeof(par-\u003emetadata_name) - 1)\n+\t\t\t\tgoto hangup;\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tpar-\u003eps++;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_METADATA_NAME:\n+\t\t\t/* both client and proxy */\n+\t\t\tif (!--par-\u003erem)\n+\t\t\t\tgoto hangup;\n+\t\t\tpar-\u003emetadata_name[par-\u003ectr++] \u003d (char)*cp++;\n+\t\t\tif (par-\u003ectr !\u003d par-\u003eslen)\n+\t\t\t\tbreak;\n+\t\t\tpar-\u003emetadata_name[par-\u003ectr] \u003d '\u005c0';\n+\t\t\tpar-\u003eps \u003d RPAR_METADATA_VALUE;\n+\n+\t\t\t/* proxy side is receiving it */\n+\n+\t\t\tif (!proxy_pss_to_ss_h(pss))\n+\t\t\t\tgoto hangup;\n+\n+\t\t\tif (!proxy_pss_to_ss_h(pss)-\u003epolicy) {\n+\t\t\t\tlwsl_err(\u0022%s: null policy\u005cn\u0022, __func__);\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * This is the policy's metadata list for the given\n+\t\t\t * name\n+\t\t\t */\n+\t\t\tpm \u003d lws_ss_policy_metadata(\n+\t\t\t\t\tproxy_pss_to_ss_h(pss)-\u003epolicy,\n+\t\t\t\t\tpar-\u003emetadata_name);\n+\t\t\tif (!pm) {\n+\t\t\t\tlwsl_err(\u0022%s: metadata %s not in proxy policy\u005cn\u0022,\n+\t\t\t\t\t __func__, par-\u003emetadata_name);\n+\n+\t\t\t\tgoto hangup;\n+\t\t\t}\n+\n+\t\t\tpar-\u003essmd \u003d lws_ss_get_handle_metadata(\n+\t\t\t\t\tproxy_pss_to_ss_h(pss),\n+\t\t\t\t\tpar-\u003emetadata_name);\n+\n+\t\t\tif (par-\u003essmd) {\n+\n+\t\t\t\tif (par-\u003essmd-\u003evalue_on_lws_heap)\n+\t\t\t\t\tlws_free_set_NULL(par-\u003essmd-\u003evalue__may_own_heap);\n+\t\t\t\tpar-\u003essmd-\u003evalue_on_lws_heap \u003d 0;\n+\n+\t\t\t\tif (proxy_pss_to_ss_h(pss) \u0026\u0026\n+\t\t\t\t lws_fi(\u0026proxy_pss_to_ss_h(pss)-\u003efic, \u0022ssproxy_rx_metadata_oom\u0022))\n+\t\t\t\t\tpar-\u003essmd-\u003evalue__may_own_heap \u003d NULL;\n+\t\t\t\telse\n+\t\t\t\t\tpar-\u003essmd-\u003evalue__may_own_heap \u003d\n+\t\t\t\t\t\tlws_malloc((unsigned int)par-\u003erem + 1, \u0022metadata\u0022);\n+\n+\t\t\t\tif (!par-\u003essmd-\u003evalue__may_own_heap) {\n+\t\t\t\t\tlwsl_err(\u0022%s: OOM mdv\u005cn\u0022, __func__);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tpar-\u003essmd-\u003elength \u003d par-\u003erem;\n+\t\t\t\t((uint8_t *)par-\u003essmd-\u003evalue__may_own_heap)[par-\u003erem] \u003d '\u005c0';\n+\t\t\t\t/* mark it as needing cleanup */\n+\t\t\t\tpar-\u003essmd-\u003evalue_on_lws_heap \u003d 1;\n+\t\t\t}\n+\t\t\tpar-\u003ectr \u003d 0;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_METADATA_VALUE:\n+\t\t\t/* both client and proxy */\n+\n+\t\t\t((uint8_t *)(par-\u003essmd-\u003evalue__may_own_heap))[par-\u003ectr++] \u003d *cp++;\n+\n+\t\t\tif (--par-\u003erem)\n+\t\t\t\tbreak;\n+\n+\t\t\t/* we think we got all the value */\n+\n+\t\t\tlwsl_ss_info(proxy_pss_to_ss_h(pss),\n+\t\t\t\t \u0022RPAR_METADATA_VALUE for %s (len %d)\u0022,\n+\t\t\t\t par-\u003essmd-\u003ename,\n+\t\t\t\t (int)par-\u003essmd-\u003elength);\n+\t\t\tlwsl_hexdump_ss_info(proxy_pss_to_ss_h(pss),\n+\t\t\t\t\tpar-\u003essmd-\u003evalue__may_own_heap,\n+\t\t\t\t\tpar-\u003essmd-\u003elength);\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n+\t\tcase RPAR_STREAMTYPE:\n+\n+\t\t\t/* only the proxy can get these */\n+\n+\t\t\tif (par-\u003ectr \u003d\u003d sizeof(par-\u003estreamtype) - 1)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\t/*\n+\t\t\t * We can only expect to get this if we ourselves are\n+\t\t\t * in the state that we're waiting for it. If it comes\n+\t\t\t * later it's a protocol error.\n+\t\t\t */\n+\n+\t\t\tif (*state !\u003d LPCSPROX_WAIT_INITIAL_TX)\n+\t\t\t\tgoto hangup;\n+\n+\t\t\t/*\n+\t\t\t * We're the proxy, creating an SS on behalf of a\n+\t\t\t * client\n+\t\t\t */\n+\n+\t\t\tpar-\u003estreamtype[par-\u003ectr++] \u003d (char)*cp++;\n+\t\t\tif (--par-\u003erem)\n+\t\t\t\tbreak;\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tpar-\u003estreamtype[par-\u003ectr] \u003d '\u005c0';\n+\t\t\tlwsl_info(\u0022%s: proxy ss '%s', sssv%d, txcr %d\u005cn\u0022,\n+\t\t\t\t __func__, par-\u003estreamtype,\n+\t\t\t\t par-\u003eprotocol_version, par-\u003etxcr_out);\n+\n+\t\t\tssi-\u003estreamtype \u003d par-\u003estreamtype;\n+\t\t\tif (par-\u003etxcr_out) // !!!\n+\t\t\t\tssi-\u003emanual_initial_tx_credit \u003d par-\u003etxcr_out;\n+\n+\t\t\t/*\n+\t\t\t * Even for a synthetic SS proxing action like _lws_smd,\n+\t\t\t * we create an actual SS in the proxy representing the\n+\t\t\t * connection\n+\t\t\t */\n+\n+\t\t\tssi-\u003eflags |\u003d LWSSSINFLAGS_PROXIED;\n+\t\t\tssi-\u003esss_protocol_version \u003d par-\u003eprotocol_version;\n+\t\t\tssi-\u003eclient_pid \u003d par-\u003eclient_pid;\n+\n+\t\t\tif (lws_ss_create(context, 0, ssi, parconn, pss,\n+\t\t\t\t\t NULL, NULL)) {\n+\t\t\t\t/*\n+\t\t\t\t * We're unable to create the onward secure\n+\t\t\t\t * stream he asked for... schedule a chance to\n+\t\t\t\t * inform him\n+\t\t\t\t */\n+\t\t\t\tlwsl_err(\u0022%s: create '%s' fail\u005cn\u0022, __func__,\n+\t\t\t\t\t par-\u003estreamtype);\n+\t\t\t\t*state \u003d LPCSPROX_REPORTING_FAIL;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\tlwsl_debug(\u0022%s: create '%s' OK\u005cn\u0022,\n+\t\t\t\t\t__func__, par-\u003estreamtype);\n+\t\t\t\t*state \u003d LPCSPROX_REPORTING_OK;\n+\t\t\t}\n+\n+\t\t\tif (*pss) {\n+\t\t\t\t(*pss)-\u003ebeing_serialized \u003d 1;\n+#if defined(LWS_WITH_SYS_SMD)\n+\t\t\t\tif ((*pss)-\u003epolicy !\u003d \u0026pol_smd)\n+\t\t\t\t\t/*\n+\t\t\t\t\t * In SMD case we overloaded the\n+\t\t\t\t\t * initial credit to be the class mask\n+\t\t\t\t\t */\n+#endif\n+\t\t\t\t{\n+\t\t\t\t\tlwsl_info(\u0022%s: Created SS initial credit %d\u005cn\u0022,\n+\t\t\t\t\t\t__func__, par-\u003etxcr_out);\n+\n+\t\t\t\t\t(*pss)-\u003einfo.manual_initial_tx_credit \u003d par-\u003etxcr_out;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t/* parent needs to schedule write on client conn */\n+\t\t\tbreak;\n+\n+\t\t/* clientside states */\n+\n+\t\tcase RPAR_RESULT_CREATION:\n+\t\tcase RPAR_RESULT_CREATION_RIDESHARE:\n+\t\tcase RPAR_RESULT_CREATION_DSH:\n+\t\tcase RPAR_STATEINDEX:\n+\t\tcase RPAR_ORD3:\n+\t\tcase RPAR_ORD2:\n+\t\tcase RPAR_ORD1:\n+\t\tcase RPAR_ORD0:\n+\t\t\tgoto hangup;\n+\n+\t\tdefault:\n+\t\t\tgoto hangup;\n+\t\t}\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+\n+hangup:\n+\n+\tlwsl_cx_notice(context, \u0022hangup\u0022);\n+\n+\treturn LWSSSSRET_DISCONNECT_ME;\n+}\ndiff --git a/lib/secure-streams/serialized/proxy/proxy-transport-wsi.c b/lib/secure-streams/serialized/proxy/proxy-transport-wsi.c\nnew file mode 100644\nindex 0000000..b302854\n--- /dev/null\n+++ b/lib/secure-streams/serialized/proxy/proxy-transport-wsi.c\n@@ -0,0 +1,291 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * Proxy side of Client \u003c-\u003e Proxy wsi connection, usually on Unix Domain Socket\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+struct raw_pss {\n+\tstruct lws_sss_proxy_conn\t\t*conn;\n+};\n+\n+static int\n+lws_sss_proxy_transport_wsi_cb(struct lws *wsi, enum lws_callback_reasons reason,\n+\t\t\t void *user, void *in, size_t len)\n+{\n+\tstruct raw_pss *pss \u003d (struct raw_pss *)user;\n+\tstruct lws_sss_proxy_conn *conn \u003d NULL;\n+\n+\tif (pss)\n+\t\tconn \u003d pss-\u003econn;\n+\n+\tswitch (reason) {\n+\n+\t/* callbacks related to raw socket descriptor \u0022accepted side\u0022 */\n+\n+ case LWS_CALLBACK_RAW_ADOPT:\n+\t\tlwsl_user(\u0022LWS_CALLBACK_RAW_ADOPT %s\u005cn\u0022, lws_txp_inside_proxy.name);\n+\n+\t\tif (lws_txp_inside_proxy.event_new_conn(\n+\t\t\t\twsi-\u003ea.context,\n+\t\t\t\t\u0026lws_txp_inside_proxy,\n+\t\t\t\t(lws_transport_priv_t)conn,\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t\u0026wsi-\u003efic,\n+#endif\n+\t\t\t\t\u0026pss-\u003econn,\n+\t\t\t\t(lws_transport_priv_t)wsi)) {\n+\t\t\tlwsl_err(\u0022%s: hangup from new_conn\u005cn\u0022, __func__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/* dsh is allocated when the onward ss is done */\n+\n+\t\twsi-\u003ebound_ss_proxy_conn \u003d 1; /* opaque is conn */\n+\t\tlws_set_opaque_user_data(wsi, pss-\u003econn);\n+\n+\t\tpss-\u003econn-\u003estate \u003d LPCSPROX_WAIT_INITIAL_TX;\n+\n+\t\t/*\n+\t\t * Client is expected to follow the unix domain socket\n+\t\t * acceptance up rapidly with an initial tx containing the\n+\t\t * streamtype name. We can't create the stream until then.\n+\t\t */\n+\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);\n+\t\tlwsl_user(\u0022%s: ADOPT: accepted\u005cn\u0022, __func__);\n+ break;\n+\n+\tcase LWS_CALLBACK_RAW_CLOSE:\n+\t\tlwsl_info(\u0022LWS_CALLBACK_RAW_CLOSE:\u005cn\u0022);\n+\n+\t\tif (!conn)\n+\t\t\tbreak;\n+\n+\t\t/*\n+\t\t * the client unix domain socket connection (wsi / conn-\u003ewsi)\n+\t\t * has closed... eg, client has exited or otherwise has\n+\t\t * definitively finished with the proxying and onward connection\n+\t\t *\n+\t\t * But right now, the SS and possibly the SS onward wsi are\n+\t\t * still live...\n+\t\t */\n+\n+\t\tassert(conn-\u003etxp_path.priv_onw \u003d\u003d wsi);\n+\n+//\t\tif (conn-\u003ess)\n+//\t\t\tconn-\u003ess \u003d NULL;\n+\n+\t\t/* sever relationship with conn */\n+\t\tlws_set_opaque_user_data(wsi, NULL);\n+\n+\t\tlws_txp_inside_proxy.event_close_conn(conn);\n+\n+\t\t/* pss is about to be deleted */\n+\t\tif (pss)\n+\t\t\tpss-\u003econn \u003d NULL;\n+\t\tlwsl_notice(\u0022%s: close finished ok\u005cn\u0022, __func__);\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_RX:\n+\t\t/*\n+\t\t * ie, the proxy is receiving something from a client\n+\t\t */\n+\t\tlwsl_info(\u0022%s: RX: rx %d\u005cn\u0022, __func__, (int)len);\n+\n+\t\tif (!conn) {\n+\t\t\tlwsl_err(\u0022%s: rx with conn %p / priv_in %p\u005cn\u0022, __func__,\n+\t\t\t\t\tconn, conn-\u003etxp_path.priv_in);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (conn-\u003etxp_path.ops_in-\u003eproxy_read(\n+\t\t\t\tconn, in, len))\n+\t\t\treturn -1;\n+\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_WRITEABLE:\n+\n+\t\tlwsl_debug(\u0022%s: %s: LWS_CALLBACK_RAW_WRITEABLE, state 0x%x\u005cn\u0022,\n+\t\t\t\t__func__, lws_wsi_tag(wsi), lwsi_state(wsi));\n+\n+\t\t/*\n+\t\t * We can transmit something back to the client from the dsh\n+\t\t * of stuff we received on its behalf from the ss\n+\t\t */\n+\n+\t\tif (!conn)\n+\t\t\tbreak;\n+\n+\t\tassert_is_conn(conn);\n+\n+\t\tif (lws_txp_inside_proxy.event_proxy_can_write(conn\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t, \u0026wsi-\u003efic\n+#endif\n+\t\t\t\t))\n+\t\t\treturn -1;\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn lws_callback_http_dummy(wsi, reason, user, in, len);\n+}\n+\n+static const struct lws_protocols protocols[] \u003d {\n+\t{\n+\t\t\u0022ssproxy-protocol\u0022,\n+\t\tlws_sss_proxy_transport_wsi_cb,\n+\t\tsizeof(struct raw_pss),\n+\t\t2048, 2048, NULL, 0\n+\t},\n+\t{ NULL, NULL, 0, 0, 0, NULL, 0 }\n+};\n+\n+static void\n+lws_sss_proxy_wsi_onward_bind(lws_transport_priv_t priv, lws_ss_handle_t *h)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\t__lws_lc_tag_append(\u0026wsi-\u003elc, lws_ss_tag(h));\n+}\n+\n+static void\n+lws_sss_proxy_wsi_req_write(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (wsi)\n+\t\tlws_callback_on_writable(wsi);\n+}\n+\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+static const lws_fi_ctx_t *\n+lws_sss_proxy_wsi_fault_context(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (!wsi)\n+\t\treturn NULL;\n+\n+\treturn \u0026wsi-\u003efic;\n+}\n+#endif\n+\n+static int\n+lws_sss_proxy_wsi_write(lws_transport_priv_t priv, uint8_t *buf, size_t *len)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (lws_write(wsi, buf, *len, LWS_WRITE_RAW) !\u003d (ssize_t)*len) {\n+\t\tlwsl_wsi_notice(wsi, \u0022failed\u0022);\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/* leave *len alone */\n+\n+\treturn 0;\n+}\n+\n+\n+int\n+lws_sss_proxy_wsi_init_proxy_server(struct lws_context *context,\n+\t\t\t const struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t lws_transport_priv_t txp_priv_inward,\n+\t\t\t lws_txp_path_proxy_t *txp_ppath,\n+\t\t\t const void *txp_info,\n+\t\t\t const char *bind, int port)\n+{\n+\tstruct lws_context_creation_info info;\n+\n+\tmemset(\u0026info, 0, sizeof(info));\n+\n+\tinfo.vhost_name\t\t\t\u003d \u0022ssproxy\u0022;\n+\tinfo.options \u003d LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG |\n+\t\t\tLWS_SERVER_OPTION_SS_PROXY;\n+\tinfo.port \u003d port;\n+\tif (!port) {\n+\t\tif (!bind)\n+#if defined(__linux__)\n+\t\t\tbind \u003d \u0022@proxy.ss.lws\u0022;\n+#else\n+\t\t\tbind \u003d \u0022/tmp/proxy.ss.lws\u0022;\n+#endif\n+\t\tinfo.options |\u003d LWS_SERVER_OPTION_UNIX_SOCK;\n+\t}\n+\tinfo.iface\t\t\t\u003d bind;\n+#if defined(__linux__)\n+\tinfo.unix_socket_perms\t\t\u003d \u0022root:root\u0022;\n+#else\n+#endif\n+\tinfo.listen_accept_role\t\t\u003d \u0022raw-skt\u0022;\n+\tinfo.listen_accept_protocol\t\u003d \u0022ssproxy-protocol\u0022;\n+\tinfo.protocols\t\t\t\u003d protocols;\n+\n+\tif (!lws_create_vhost(context, \u0026info)) {\n+\t\tlwsl_err(\u0022%s: Failed to create ss proxy vhost\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+lws_sss_proxy_wsi_client_up(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tlws_set_timeout(wsi, 0, 0);\n+}\n+\n+static int\n+lws_sss_proxy_check_write_more(lws_transport_priv_t priv)\n+{\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (lws_send_pipe_choked(wsi))\n+\t\treturn 0;\n+\n+\treturn 1;\n+}\n+\n+const lws_transport_proxy_ops_t txp_ops_ssproxy_wsi \u003d {\n+\t.name\t\t\t\t\u003d \u0022txp_proxy_wsi\u0022,\n+\t.init_proxy_server\t\t\u003d lws_sss_proxy_wsi_init_proxy_server,\n+\t.proxy_req_write\t\t\u003d lws_sss_proxy_wsi_req_write,\n+\t.proxy_write\t\t\t\u003d lws_sss_proxy_wsi_write,\n+\n+\t.event_onward_bind\t\t\u003d lws_sss_proxy_wsi_onward_bind,\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t.fault_context\t\t\t\u003d lws_sss_proxy_wsi_fault_context,\n+#endif\n+\t.event_client_up\t\t\u003d lws_sss_proxy_wsi_client_up,\n+\t.proxy_check_write_more\t\t\u003d lws_sss_proxy_check_write_more,\n+};\ndiff --git a/lib/secure-streams/serialized/proxy/proxy-transport.c b/lib/secure-streams/serialized/proxy/proxy-transport.c\nnew file mode 100644\nindex 0000000..c536be2\n--- /dev/null\n+++ b/lib/secure-streams/serialized/proxy/proxy-transport.c\n@@ -0,0 +1,422 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+\n+/*\n+ * Proxy has received a new connection from a client\n+ */\n+\n+static lws_ss_state_return_t\n+lws_ssproxy_txp_new_conn(struct lws_context *cx,\n+\t\t\t\tconst struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t\tlws_transport_priv_t txp_priv_inward,\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t const lws_fi_ctx_t *fic,\n+#endif\n+\t\t\t struct lws_sss_proxy_conn **conn,\n+\t\t\t lws_transport_priv_t txp_priv)\n+{\n+\tif (\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\tfic \u0026\u0026\n+#endif\n+\t\t\tlws_fi(fic, \u0022ssproxy_client_adopt_oom\u0022))\n+\t\t*conn \u003d NULL;\n+\telse\n+\t\t*conn \u003d lws_zalloc(sizeof(**conn), __func__);\n+\tif (!*conn)\n+\t\treturn 1;\n+\n+\t/* dsh is allocated when the onward ss is done */\n+\n+#if defined(_DEBUG)\n+\t(*conn)-\u003emagic\t\t\t\u003d LWS_PROXY_CONN_MAGIC;\n+#endif\n+\t(*conn)-\u003estate\t\t\t\u003d LPCSPROX_WAIT_INITIAL_TX;\n+\t(*conn)-\u003etxp_path\t\t\u003d cx-\u003etxp_ppath;\n+\t(*conn)-\u003etxp_path.priv_onw\t\u003d txp_priv;\n+\n+\t(*conn)-\u003etxp_path.ops_in\t\u003d txp_ops_inward;\n+\t(*conn)-\u003etxp_path.priv_in\t\u003d txp_priv_inward;\n+\t(*conn)-\u003ecx\t\t\t\u003d cx;\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+/*\n+ * Proxy has received a close indication from a client\n+ */\n+\n+static lws_ss_state_return_t\n+lws_ssproxy_txp_close_conn(struct lws_sss_proxy_conn *conn)\n+{\n+\tlws_transport_priv_t epriv;\n+\n+\tconn-\u003etxp_path.priv_onw \u003d NULL;\n+\tepriv \u003d conn-\u003etxp_path.priv_onw;\n+\n+\t/*\n+\t * If there's an outgoing, proxied SS conn on our behalf, we\n+\t * have to destroy it\n+\t *\n+\t * Wsi related stuff in here is talking about the onward wsi / ss\n+\t * connection, it doesn't introduce any dependency on the proxy -\n+\t * client link transport\n+\t */\n+\n+\tif (conn-\u003ess) {\n+\t\tstruct lws *cw;\n+\n+\t\tcw \u003d conn-\u003ess-\u003ewsi;\n+\n+\t\t/*\n+\t\t * conn-\u003ess is the onward connection SS\n+\t\t */\n+\n+\t\tlwsl_info(\u0022%s: destroying %s, wsi %s\u005cn\u0022,\n+\t\t\t\t__func__, lws_ss_tag(conn-\u003ess),\n+\t\t\t\tlws_wsi_tag(conn-\u003ess-\u003ewsi));\n+\n+\t\t/* sever conn relationship with onward ss about to be deleted */\n+\n+\t\tconn-\u003ess-\u003ewsi \u003d NULL;\n+\n+\t\tif (cw \u0026\u0026 epriv !\u003d (lws_transport_priv_t)cw) {\n+\n+\t\t\t/* disconnect onward SS from its wsi */\n+\n+\t\t\tlws_set_opaque_user_data(cw, NULL);\n+\n+\t\t\t/*\n+\t\t\t * The wsi doing the onward connection can no\n+\t\t\t * longer relate to the conn... otherwise when\n+\t\t\t * he gets callbacks he wants to bind to\n+\t\t\t * the ss we are about to delete\n+\t\t\t */\n+\t\t\tlws_wsi_close(cw, LWS_TO_KILL_ASYNC);\n+\t\t}\n+\n+\t\t/* destroy the onward ss (setting conn-\u003ess NULL) */\n+\t\tlws_ss_destroy(\u0026conn-\u003ess);\n+\n+\t\t/*\n+\t\t * Conn may have gone, at ss destroy handler in\n+\t\t * ssi.state for proxied ss\n+\t\t */\n+\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\tif (conn-\u003estate \u003d\u003d LPCSPROX_DESTROYED || !conn-\u003ess) {\n+\t\t/*\n+\t\t * There's no onward secure stream and our client\n+\t\t * connection is closing. Destroy the conn.\n+\t\t */\n+\t\tlws_dsh_destroy(\u0026conn-\u003edsh);\n+\t\tlws_free(conn);\n+\t} else\n+\t\tlwsl_debug(\u0022%s: CLOSE; %s\u005cn\u0022, __func__, lws_ss_tag(conn-\u003ess));\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+\n+static lws_ss_state_return_t\n+lws_ssproxy_txp_rx(lws_transport_priv_t txp_priv, const uint8_t *in, size_t len)\n+{\n+\tstruct lws_sss_proxy_conn *conn \u003d (struct lws_sss_proxy_conn *)txp_priv;\n+\tlws_ss_state_return_t r;\n+\tlws_ss_info_t ssi;\n+\n+\tassert_is_conn(conn);\n+\n+\t// lwsl_hexdump_info(in, len);\n+\n+\tif (conn-\u003estate \u003d\u003d LPCSPROX_WAIT_INITIAL_TX) {\n+\t\tmemset(\u0026ssi, 0, sizeof(ssi));\n+\t\tssi.user_alloc \u003d sizeof(ss_proxy_t);\n+\t\tssi.handle_offset \u003d offsetof(ss_proxy_t, ss);\n+\t\tssi.opaque_user_data_offset \u003d offsetof(ss_proxy_t, conn);\n+\t\tssi.rx \u003d lws_sss_proxy_onward_rx;\n+\t\tssi.tx \u003d lws_sss_proxy_onward_tx;\n+\t}\n+\tssi.state \u003d lws_sss_proxy_onward_state;\n+\tssi.flags \u003d 0;\n+\n+\tr \u003d lws_ss_proxy_deserialize_parse(\u0026conn-\u003eparser, conn-\u003ecx, conn-\u003edsh,\n+\t\t\t\t\t in, len, \u0026conn-\u003estate, conn,\n+\t\t\t\t\t \u0026conn-\u003ess, \u0026ssi);\n+\tswitch (r) {\n+\tdefault:\n+\t\tbreak;\n+\tcase LWSSSSRET_DISCONNECT_ME:\n+\t\treturn r;\n+\tcase LWSSSSRET_DESTROY_ME:\n+\t\tif (conn-\u003ess)\n+\t\t\tlws_ss_destroy(\u0026conn-\u003ess);\n+\t\treturn r;\n+\t}\n+\n+\tif ((conn-\u003estate \u003d\u003d LPCSPROX_REPORTING_FAIL ||\n+\t conn-\u003estate \u003d\u003d LPCSPROX_REPORTING_OK) \u0026\u0026\n+\t conn-\u003etxp_path.priv_onw)\n+\t\tconn-\u003etxp_path.ops_onw-\u003eproxy_req_write(conn-\u003etxp_path.priv_onw);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+static lws_ss_state_return_t\n+lws_ssproxy_txp_proxy_can_write(lws_transport_priv_t priv\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t, const lws_fi_ctx_t *fic\n+#endif\n+\t\t)\n+{\n+\tstruct lws_sss_proxy_conn *conn \u003d (struct lws_sss_proxy_conn *)priv;\n+\tconst lws_ss_policy_t *rsp;\n+\tlws_ss_metadata_t *md;\n+\tconst uint8_t *cp;\n+\tchar _s[1580 + LWS_PRE], *s \u003d _s + LWS_PRE;\n+\tsize_t si, csi;\n+\tuint8_t *p;\n+\tchar pay;\n+\tint n;\n+\n+\tassert_is_conn(conn);\n+\n+\tn \u003d 0;\n+\tpay \u003d 0;\n+\n+\t*(s + 3) \u003d 0;\n+\tcp \u003d (const uint8_t *)s;\n+\n+\tswitch (conn-\u003estate) {\n+\tcase LPCSPROX_REPORTING_FAIL:\n+\t\t*(s + 3) \u003d 1;\n+\t\t/* fallthru */\n+\tcase LPCSPROX_REPORTING_OK:\n+\t\t*s \u003d LWSSS_SER_RXPRE_CREATE_RESULT;\n+\t\t*(s + 1) \u003d 0;\n+\t\t*(s + 2) \u003d 1;\n+\n+\t\tn \u003d 8;\n+\n+\t\tlws_ser_wu32be((uint8_t *)s + 4, conn-\u003ess \u0026\u0026\n+\t\t\t\t\t\t conn-\u003ess-\u003epolicy ?\n+\t\t\t\tconn-\u003ess-\u003epolicy-\u003eclient_buflen : 0);\n+\n+\t\t/*\n+\t\t * If there's rideshare sequencing, it's added after the\n+\t\t * first 4 bytes or the create result, comma-separated\n+\t\t */\n+\n+\t\tif (conn-\u003ess) {\n+\t\t\trsp \u003d conn-\u003ess-\u003epolicy;\n+\n+\t\t\twhile (rsp) {\n+\t\t\t\tif (n !\u003d 4 \u0026\u0026 n \u003c (int)sizeof(_s) - LWS_PRE - 2)\n+\t\t\t\t\t*(s + (n++)) \u003d ',';\n+\t\t\t\tn +\u003d lws_snprintf(s + n, sizeof(_s) - LWS_PRE - (unsigned int)n,\n+\t\t\t\t\t\t\u0022%s\u0022, rsp-\u003estreamtype);\n+\t\t\t\trsp \u003d lws_ss_policy_lookup(conn-\u003ecx,\n+\t\t\t\t\trsp-\u003erideshare_streamtype);\n+\t\t\t}\n+\t\t}\n+\t\t*(s + 2) \u003d (char)(n - 3);\n+\t\tconn-\u003estate \u003d LPCSPROX_OPERATIONAL;\n+\t\tconn-\u003etxp_path.ops_onw-\u003eevent_client_up(conn-\u003etxp_path.priv_onw);\n+\t\tbreak;\n+\n+\tcase LPCSPROX_OPERATIONAL:\n+\n+\t\t/*\n+\t\t * returning [onward -\u003e ] proxy]-\u003e client\n+\t\t * rx metadata has priority 1\n+\t\t */\n+\n+\t\tmd \u003d conn-\u003ess-\u003emetadata;\n+\t\twhile (md) {\n+\t\t\t// lwsl_notice(\u0022%s: check %s: %d\u005cn\u0022, __func__,\n+\t\t\t// md-\u003ename, md-\u003epending_onward);\n+\t\t\tif (md-\u003epending_onward) {\n+\t\t\t\tsize_t naml \u003d strlen(md-\u003ename);\n+\n+\t\t\t\t// lwsl_notice(\u0022%s: proxy issuing rxmd\u005cn\u0022, __func__);\n+\n+\t\t\t\tif (4 + naml + md-\u003elength \u003e sizeof(_s) - LWS_PRE) {\n+\t\t\t\t\tlwsl_err(\u0022%s: rxmdata too big\u005cn\u0022,\n+\t\t\t\t\t\t\t__func__);\n+\t\t\t\t\tgoto hangup;\n+\t\t\t\t}\n+\t\t\t\tmd-\u003epending_onward \u003d 0;\n+\t\t\t\tp \u003d (uint8_t *)s;\n+\t\t\t\tp[0] \u003d LWSSS_SER_RXPRE_METADATA;\n+\t\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)(1 + naml +\n+\t\t\t\t\t\t md-\u003elength));\n+\t\t\t\tp[3] \u003d (uint8_t)naml;\n+\t\t\t\tmemcpy(\u0026p[4], md-\u003ename, naml);\n+\t\t\t\tp +\u003d 4 + naml;\n+\t\t\t\tmemcpy(p, md-\u003evalue__may_own_heap,\n+\t\t\t\t md-\u003elength);\n+\t\t\t\tp +\u003d md-\u003elength;\n+\n+\t\t\t\tn \u003d lws_ptr_diff(p, cp);\n+\t\t\t\tgoto do_write_nz;\n+\t\t\t}\n+\n+\t\t\tmd \u003d md-\u003enext;\n+\t\t}\n+\n+\t\t/*\n+\t\t * If we have performance data, render it in JSON\n+\t\t * and send that in LWSSS_SER_RXPRE_PERF has\n+\t\t * priority 2\n+\t\t */\n+\n+#if defined(LWS_WITH_CONMON)\n+\t\tif (conn-\u003ess-\u003econmon_json) {\n+\t\t\tunsigned int xlen \u003d conn-\u003ess-\u003econmon_len;\n+\n+\t\t\tif (xlen \u003e sizeof(s) - 3)\n+\t\t\t\txlen \u003d sizeof(s) - 3;\n+\t\t\tcp \u003d (uint8_t *)s;\n+\t\t\tp \u003d (uint8_t *)s;\n+\t\t\tp[0] \u003d LWSSS_SER_RXPRE_PERF;\n+\t\t\tlws_ser_wu16be(\u0026p[1], (uint16_t)xlen);\n+\t\t\tmemcpy(\u0026p[3], conn-\u003ess-\u003econmon_json, xlen);\n+\n+\t\t\tlws_free_set_NULL(conn-\u003ess-\u003econmon_json);\n+\t\t\tn \u003d (int)(xlen + 3);\n+\n+\t\t\tpay \u003d 0;\n+\t\t\tgoto do_write_nz;\n+\t\t}\n+#endif\n+\t\t/*\n+\t\t * if no fresh rx metadata, just pass through incoming\n+\t\t * dsh\n+\t\t */\n+\n+\t\tif (lws_dsh_get_head(conn-\u003edsh, KIND_SS_TO_P, (void **)\u0026p, \u0026si))\n+\t\t\tbreak;\n+\n+\t\tcp \u003d p;\n+\t\tpay \u003d 1;\n+\t\tn \u003d (int)si;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+do_write_nz:\n+\tif (!n)\n+\t\treturn LWSSSSRET_OK;\n+\n+\tif (\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t fic \u0026\u0026\n+#endif\n+\t\t\tlws_fi(fic, \u0022ssproxy_client_write_fail\u0022))\n+\t\tn \u003d -1;\n+\telse {\n+\t\tsi \u003d csi \u003d (size_t)n;\n+\t\tn \u003d conn-\u003etxp_path.ops_onw-\u003eproxy_write(conn-\u003etxp_path.priv_onw,\n+\t\t\t\t\t\t (uint8_t *)cp, \u0026csi);\n+\t}\n+\n+\tif (n \u003c 0) {\n+\t\tlwsl_info(\u0022%s: WRITEABLE: %d\u005cn\u0022, __func__, n);\n+\n+\t\tgoto hangup;\n+\t}\n+\n+\tswitch (conn-\u003estate) {\n+\tcase LPCSPROX_REPORTING_FAIL:\n+\t\tgoto hangup;\n+\tcase LPCSPROX_OPERATIONAL:\n+\t\tif (!conn)\n+\t\t\tbreak;\n+\t\tif (pay) {\n+\t\t\tif (si \u003d\u003d csi)\n+\t\t\t\tlws_dsh_free((void **)\u0026p);\n+\t\t\telse\n+\t\t\t\tlws_dsh_consume(conn-\u003edsh, KIND_SS_TO_P, csi);\n+\n+\t\t\t/*\n+\t\t\t * Did we go below the rx flow threshold for\n+\t\t\t * this dsh?\n+\t\t\t */\n+\n+\t\t\tif (conn-\u003eonward_in_flow_control \u0026\u0026\n+\t\t\t conn-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above \u0026\u0026\n+\t\t\t conn-\u003ess-\u003ewsi \u0026\u0026\n+\t\t\t lws_dsh_get_size(conn-\u003edsh, KIND_SS_TO_P) \u003c\n+\t\t\t conn-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_off_below) {\n+\t\t\t\tlwsl_user(\u0022%s: %s: rxflow enabling rx (%lu / %lu, lwm %lu)\u005cn\u0022, __func__,\n+\t\t\t\t\t lws_wsi_tag(conn-\u003ess-\u003ewsi),\n+\t\t\t\t\t (unsigned long)lws_dsh_get_size(conn-\u003edsh, KIND_SS_TO_P),\n+\t\t\t\t\t (unsigned long)conn-\u003ess-\u003epolicy-\u003eproxy_buflen,\n+\t\t\t\t\t (unsigned long)conn-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_off_below);\n+\t\t\t\t/*\n+\t\t\t\t * Resume receiving taking in rx once\n+\t\t\t\t * below the low threshold\n+\t\t\t\t */\n+\t\t\t\tlws_rx_flow_control(conn-\u003ess-\u003ewsi,\n+\t\t\t\t\t\t LWS_RXFLOW_ALLOW);\n+\t\t\t\tconn-\u003eonward_in_flow_control \u003d 0;\n+\t\t\t}\n+\t\t}\n+\t\tif (!lws_dsh_get_head(conn-\u003edsh, KIND_SS_TO_P,\n+\t\t\t\t (void **)\u0026p, \u0026si)) {\n+\n+\t\t\tif (conn \u0026\u0026 conn-\u003etxp_path.ops_onw-\u003eproxy_check_write_more \u0026\u0026\n+\t\t\t conn-\u003etxp_path.ops_onw-\u003eproxy_check_write_more(\n+\t\t\t\t\tconn-\u003etxp_path.priv_onw)) {\n+\t\t\t\tcp \u003d p;\n+\t\t\t\tpay \u003d 1;\n+\t\t\t\tn \u003d (int)si;\n+\t\t\t\tgoto do_write_nz;\n+\t\t\t}\n+\n+\t\t\tconn-\u003etxp_path.ops_onw-\u003eproxy_req_write(\n+\t\t\t\t\tconn-\u003etxp_path.priv_onw);\n+\t\t}\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+\n+hangup:\n+\treturn LWSSSSRET_DISCONNECT_ME;\n+}\n+\n+const lws_transport_proxy_ops_t lws_txp_inside_proxy \u003d {\n+\t.name\t\t\t\t\u003d \u0022txp_inside_proxy\u0022,\n+\t.event_new_conn\t\t\t\u003d lws_ssproxy_txp_new_conn,\n+\t.proxy_read\t\t\t\u003d lws_ssproxy_txp_rx,\n+\t.event_close_conn\t\t\u003d lws_ssproxy_txp_close_conn,\n+\t.event_proxy_can_write\t\t\u003d lws_ssproxy_txp_proxy_can_write,\n+};\n+\ndiff --git a/lib/secure-streams/serialized/proxy/proxy.c b/lib/secure-streams/serialized/proxy/proxy.c\nnew file mode 100644\nindex 0000000..8492b59\n--- /dev/null\n+++ b/lib/secure-streams/serialized/proxy/proxy.c\n@@ -0,0 +1,492 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2019 - 2021 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ *\n+ *\n+ * When the user code is in a different process, a non-tls unix domain socket\n+ * proxy is used to asynchronusly transfer buffers in each direction via the\n+ * network stack, without explicit IPC\n+ *\n+ * user_process{ [user code] | shim | socket-}------ lws_process{ lws }\n+ *\n+ * Lws exposes a listening unix domain socket in this case, the user processes\n+ * connect to it and pass just info.streamtype in an initial tx packet. All\n+ * packets are prepended by a 1-byte type field when used in this mode. See\n+ * lws-secure-streams.h for documentation and definitions.\n+ *\n+ * Proxying in either direction can face the situation it cannot send the onward\n+ * packet immediately and is subject to separating the write request from the\n+ * write action. To make the best use of memory, a single preallocated buffer\n+ * stashes pending packets in all four directions (c-\u003ep, p-\u003ec, p-\u003ess, ss-\u003ep).\n+ * This allows it to adapt to different traffic patterns without wasted areas\n+ * dedicated to traffic that isn't coming in a particular application.\n+ *\n+ * A shim is provided to monitor the process' unix domain socket and regenerate\n+ * the secure sockets api there with callbacks happening in the process thread\n+ * context.\n+ *\n+ * This file implements the listening unix domain socket proxy... this code is\n+ * only going to run on a Linux-class device with its implications about memory\n+ * availability.\n+ */\n+\n+#include \u003cprivate-lib-core.h\u003e\n+/*\n+ * Proxy - onward secure-stream handler\n+ */\n+\n+void\n+lws_proxy_clean_conn_ss(struct lws *wsi)\n+{\n+#if 0\n+\tlws_ss_handle_t *h \u003d (lws_ss_handle_t *)wsi-\u003ea.opaque_user_data;\n+\tstruct lws_sss_proxy_conn *conn \u003d h-\u003econn_if_sspc_onw;\n+\n+\tif (!wsi)\n+\t\treturn;\n+\n+\tif (conn \u0026\u0026 conn-\u003ess)\n+\t\tconn-\u003ess-\u003ewsi \u003d NULL;\n+#endif\n+}\n+\n+\n+void\n+ss_proxy_onward_link_proxy_req_writeable(lws_ss_handle_t *h_onward)\n+{\n+\tss_proxy_t *m \u003d (ss_proxy_t *)\u0026h_onward[1];\n+\n+\tif (m-\u003econn-\u003etxp_path.priv_onw)\n+\t\tm-\u003econn-\u003etxp_path.ops_onw-\u003eproxy_req_write(m-\u003econn-\u003etxp_path.priv_onw);\n+}\n+\n+int\n+__lws_ss_proxy_bind_ss_to_conn_wsi(void *parconn, size_t dsh_size)\n+{\n+\tstruct lws_sss_proxy_conn *conn \u003d (struct lws_sss_proxy_conn *)parconn;\n+\tstruct lws_context_per_thread *pt;\n+\n+\tif (!conn || !conn-\u003etxp_path.priv_onw || !conn-\u003ess)\n+\t\treturn -1;\n+\n+\tpt \u003d \u0026conn-\u003ess-\u003econtext-\u003ept[(int)conn-\u003ess-\u003etsi];\n+\n+\tif (lws_fi(\u0026conn-\u003ess-\u003efic, \u0022ssproxy_dsh_create_oom\u0022))\n+\t\treturn -1;\n+\tconn-\u003edsh \u003d lws_dsh_create(\u0026pt-\u003ess_dsh_owner, dsh_size,\n+\t\t\t\t (int)(conn-\u003etxp_path.ops_onw-\u003eflags | 2));\n+\tif (!conn-\u003edsh)\n+\t\treturn -1;\n+\n+\tconn-\u003edsh-\u003esplitat \u003d 1300;\n+\n+\tconn-\u003etxp_path.ops_onw-\u003eevent_onward_bind(conn-\u003etxp_path.priv_onw,\n+\t\t\t\t\t\t conn-\u003ess);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * event loop received something and is queueing it for the foreign side of\n+ * the dsh to consume later as serialized rx\n+ */\n+\n+static int\n+lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf,\n+\t\t\t size_t len, int flags, const char *rsp)\n+{\n+\tlws_usec_t us \u003d lws_now_usecs();\n+\tuint8_t pre[128];\n+\tint est \u003d 19, l \u003d 0;\n+\n+\tif (flags \u0026 LWSSS_FLAG_RIDESHARE) {\n+\t\t/*\n+\t\t * We should have the rideshare name if we have been told it's\n+\t\t * on a non-default rideshare\n+\t\t */\n+\t\tassert(rsp);\n+\t\tif (!rsp)\n+\t\t\treturn 1;\n+\t\tl \u003d (int)strlen(rsp);\n+\t\test +\u003d 1 + l;\n+\t} else\n+\t\tassert(!rsp);\n+\n+\t// lwsl_user(\u0022%s: len %d, flags: %d\u005cn\u0022, __func__, (int)len, flags);\n+\t// lwsl_hexdump_info(buf, len);\n+\n+\tpre[0] \u003d LWSSS_SER_RXPRE_RX_PAYLOAD;\n+\tlws_ser_wu16be(\u0026pre[1], (uint16_t)(len + (size_t)est - 3));\n+\tlws_ser_wu32be(\u0026pre[3], (uint32_t)flags);\n+\tlws_ser_wu32be(\u0026pre[7], 0);\t/* write will compute latency here... */\n+\tlws_ser_wu64be(\u0026pre[11], (uint64_t)us);\t/* ... and set this to the write time */\n+\n+\t/*\n+\t * If we are on a non-default rideshare, append the non-default name to\n+\t * the headers of the payload part, 1-byte length first\n+\t */\n+\n+\tif (flags \u0026 LWSSS_FLAG_RIDESHARE) {\n+\t\tpre[19] \u003d (uint8_t)l;\n+\t\tmemcpy(\u0026pre[20], rsp, (unsigned int)l);\n+\t}\n+\n+\tif (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, (unsigned int)est, buf, len)) {\n+#if defined(_DEBUG)\n+\t\tlws_dsh_describe(dsh, __func__);\n+#endif\n+\t\tlwsl_err(\u0022%s: unable to alloc in dsh 1\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tlwsl_notice(\u0022%s: dsh c2p %d, p2c %d\u005cn\u0022, __func__,\n+\t\t (int)lws_dsh_get_size(dsh, KIND_C_TO_P),\n+\t\t (int)lws_dsh_get_size(dsh, KIND_SS_TO_P));\n+\n+\treturn 0;\n+}\n+\n+/* Onward secure streams payload interface */\n+\n+lws_ss_state_return_t\n+lws_sss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)\n+{\n+\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n+\tconst char *rsp \u003d NULL;\n+\tint n;\n+\n+\t// lwsl_notice(\u0022%s: len %d\u005cn\u0022, __func__, (int)len);\n+\n+\t/*\n+\t * The onward secure stream connection has received something.\n+\t */\n+\n+\tif (m-\u003ess-\u003erideshare !\u003d m-\u003ess-\u003epolicy \u0026\u0026 m-\u003ess-\u003erideshare) {\n+\t\trsp \u003d m-\u003ess-\u003erideshare-\u003estreamtype;\n+\t\tflags |\u003d LWSSS_FLAG_RIDESHARE;\n+\t}\n+\n+\t/*\n+\t * Apply SSS framing around this chunk of RX and stash it in the dsh\n+\t * in ss -\u003e proxy [ -\u003e client] direction. This can fail...\n+\t */\n+\n+\tn \u003d 1;\n+\tif (m-\u003econn-\u003edsh \u0026\u0026 !lws_fi(\u0026m-\u003ess-\u003efic, \u0022ssproxy_dsh_rx_queue_oom\u0022))\n+\t\tn \u003d lws_ss_serialize_rx_payload(m-\u003econn-\u003edsh, buf, len,\n+\t\t\t\t\t\tflags, rsp);\n+\tif (n) {\n+\t\tif (m-\u003econn-\u003edsh) {\n+#if defined(_DEBUG)\n+\t\t\tlws_dsh_describe(m-\u003econn-\u003edsh, __func__);\n+#endif\n+\t\t\t/*\n+\t\t\t * We couldn't buffer this rx, eg due to OOM, let's\n+\t\t\t * escalate it to be a \u0022loss of connection\u0022, which it\n+\t\t\t * basically is... as part of that, drop the dshes.\n+\t\t\t *\n+\t\t\t * This just affects the one stream that owns the\n+\t\t\t * dsh, caller should enter stream close flow and not\n+\t\t\t * send any further payload.\n+\t\t\t */\n+\n+\t\t\tlwsl_warn(\u0022%s: dropping SS dsh due to OOM\u005cn\u0022, __func__);\n+\t\t\tlws_dsh_empty(m-\u003econn-\u003edsh);\n+\t\t}\n+\n+\t\treturn LWSSSSRET_DISCONNECT_ME;\n+\t}\n+\n+\t/*\n+\t * Manage rx flow on the SS (onward) side according to our situation\n+\t * in the dsh holding proxy-\u003eclient serialized forwarding rx\n+\t */\n+\n+\tif (!m-\u003econn-\u003eonward_in_flow_control \u0026\u0026 m-\u003ess-\u003ewsi \u0026\u0026\n+\t m-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above \u0026\u0026\n+\t lws_dsh_get_size(m-\u003econn-\u003edsh, KIND_SS_TO_P) \u003e\u003d\n+\t\t\t\tm-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above) {\n+\t\tlwsl_ss_user(m-\u003ess, \u0022rxflow disabling rx (%lu / %lu, hwm %lu)\u0022,\n+\t\t\t(unsigned long)lws_dsh_get_size(m-\u003econn-\u003edsh,\n+\t\t\t\t\t\t\tKIND_SS_TO_P),\n+\t\t\t(unsigned long)m-\u003ess-\u003epolicy-\u003eproxy_buflen,\n+\t\t\t(unsigned long)m-\u003ess-\u003epolicy-\u003eproxy_buflen_rxflow_on_above);\n+\t\t/*\n+\t\t * stop taking in rx once the onward wsi rx is above the\n+\t\t * high water mark\n+\t\t */\n+\t\tlws_rx_flow_control(m-\u003ess-\u003ewsi, 0);\n+\t\tm-\u003econn-\u003eonward_in_flow_control \u003d 1;\n+\t}\n+\n+\tif (m-\u003econn-\u003etxp_path.priv_onw) /* if possible, request client conn write */\n+\t\tm-\u003econn-\u003etxp_path.ops_onw-\u003eproxy_req_write(m-\u003econn-\u003etxp_path.priv_onw);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+/*\n+ * we are transmitting buffered payload originally from the client on to the ss\n+ */\n+\n+lws_ss_state_return_t\n+lws_sss_proxy_onward_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,\n+\t\t size_t *len, int *flags)\n+{\n+\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n+\tvoid *p;\n+\tsize_t si;\n+\n+\tif (!m-\u003econn-\u003ess || m-\u003econn-\u003estate !\u003d LPCSPROX_OPERATIONAL) {\n+\t\tlwsl_notice(\u0022%s: ss not ready\u005cn\u0022, __func__);\n+\t\t*len \u003d 0;\n+\n+\t\treturn LWSSSSRET_TX_DONT_SEND;\n+\t}\n+\n+\t/*\n+\t * The onward secure stream says that we could send something to it\n+\t * (by putting it in buf, and setting *len and *flags)... dredge the\n+\t * next thing out of the dsh\n+\t */\n+\n+\tif (lws_ss_deserialize_tx_payload(m-\u003econn-\u003edsh, m-\u003ess-\u003ewsi,\n+\t\t\t\t\t ord, buf, len, flags))\n+\t\treturn LWSSSSRET_TX_DONT_SEND;\n+\n+\t/* ... there's more we want to send? */\n+\tif (!lws_dsh_get_head(m-\u003econn-\u003edsh, KIND_C_TO_P, (void **)\u0026p, \u0026si))\n+\t\t_lws_ss_request_tx(m-\u003econn-\u003ess);\n+\n+\tif (!*len \u0026\u0026 !*flags)\n+\t\t/* we don't actually want to send anything */\n+\t\treturn LWSSSSRET_TX_DONT_SEND;\n+\n+\tlwsl_info(\u0022%s: onward tx %d fl 0x%x\u005cn\u0022, __func__, (int)*len, *flags);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+/*\n+ * event loop side is issuing state, serialize and put it in the dbuf for\n+ * the foreign side to consume later\n+ */\n+\n+static int\n+lws_ss_serialize_state(struct lws_sss_proxy_conn *conn, lws_ss_constate_t state,\n+\t\t lws_ss_tx_ordinal_t ack)\n+{\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\tconst lws_fi_ctx_t *fic \u003d conn-\u003etxp_path.ops_onw-\u003efault_context(\n+\t\t\t\t\t\tconn-\u003etxp_path.priv_onw);\n+#endif\n+\tstruct lws_dsh *dsh \u003d conn-\u003edsh;\n+\tuint8_t pre[12];\n+\tint n \u003d 4;\n+\n+\tif (state \u003d\u003d LWSSSCS_EVENT_WAIT_CANCELLED)\n+\t\treturn 0;\n+\n+\tlwsl_info(\u0022%s: %s, ord 0x%x\u005cn\u0022, __func__, lws_ss_state_name((int)state),\n+\t\t (unsigned int)ack);\n+\n+\tif (!dsh) {\n+\t\t/* he can't store anything further on the link */\n+\t\tlwsl_notice(\u0022%s: dsh for conn was destroyed\u005cn\u0022, __func__);\n+\t\treturn 0;\n+\t}\n+\n+\tpre[0] \u003d LWSSS_SER_RXPRE_CONNSTATE;\n+\tpre[1] \u003d 0;\n+\n+\tif (state \u003e 255) {\n+\t\tpre[2] \u003d 8;\n+\t\tlws_ser_wu32be(\u0026pre[3], state);\n+\t\tn \u003d 7;\n+\t} else {\n+\t\tpre[2] \u003d 5;\n+\t\tpre[3] \u003d (uint8_t)state;\n+\t}\n+\n+\tlws_ser_wu32be(\u0026pre[n], ack);\n+\n+\tif (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, (unsigned int)n + 4, NULL, 0)\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t|| (fic \u0026\u0026 lws_fi(fic, \u0022sspc_dsh_ss2p_oom\u0022))\n+#endif\n+\t ) {\n+\t\tlwsl_err(\u0022%s: unable to alloc in dsh 2\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+\n+lws_ss_state_return_t\n+lws_sss_proxy_onward_state(void *userobj, void *sh, lws_ss_constate_t state,\n+\t\t\t lws_ss_tx_ordinal_t ack)\n+{\n+\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n+\tsize_t dsh_size;\n+\n+\tswitch (state) {\n+\tcase LWSSSCS_CREATING:\n+\n+\t\t/*\n+\t\t * conn is private to -process.c, call thru to a) adjust\n+\t\t * the accepted incoming proxy link wsi tag name to be\n+\t\t * appended with the onward ss tag information now we\n+\t\t * have it, and b) allocate the dsh buffer now we\n+\t\t * can find out the policy about it for the streamtype.\n+\t\t */\n+\n+\t\tdsh_size \u003d m-\u003ess-\u003epolicy-\u003eproxy_buflen ?\n+\t\t\t\tm-\u003ess-\u003epolicy-\u003eproxy_buflen : 32768;\n+\n+\t\tlwsl_notice(\u0022%s: %s: initializing dsh max len %lu\u005cn\u0022,\n+\t\t\t\t__func__, lws_ss_tag(m-\u003ess),\n+\t\t\t\t(unsigned long)dsh_size);\n+\n+\t\t/* this includes ssproxy_dsh_create_oom fault generation */\n+\n+\t\tif (__lws_ss_proxy_bind_ss_to_conn_wsi(m-\u003econn, dsh_size)) {\n+\n+\t\t\t/* failed to allocate the dsh */\n+\n+\t\t\tlwsl_notice(\u0022%s: dsh init failed\u005cn\u0022, __func__);\n+\n+\t\t\treturn LWSSSSRET_DESTROY_ME;\n+\t\t}\n+\t\tbreak;\n+\n+\tcase LWSSSCS_DESTROYING:\n+\t\tif (!m-\u003econn)\n+\t\t\tbreak;\n+\t\tif (!m-\u003econn-\u003etxp_path.priv_onw) {\n+\t\t\t/*\n+\t\t\t * Our onward secure stream is closing and our client\n+\t\t\t * connection has already gone away... destroy the conn.\n+\t\t\t */\n+\t\t\tlwsl_notice(\u0022%s: Destroying conn\u005cn\u0022, __func__);\n+\t\t\tlws_dsh_empty(m-\u003econn-\u003edsh);\n+\t\t\tif (!m-\u003econn-\u003ess) {\n+\t\t\t\tlws_dsh_destroy(\u0026m-\u003econn-\u003edsh);\n+\t\t\t\tfree(m-\u003econn);\n+\t\t\t\tm-\u003econn \u003d NULL;\n+\t\t\t}\n+\t\t\treturn 0;\n+\t\t} else\n+\t\t\tlwsl_info(\u0022%s: ss DESTROYING, wsi up\u005cn\u0022, __func__);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\tif (!m-\u003econn) {\n+\t\tlwsl_warn(\u0022%s: dropping state due to conn not up\u005cn\u0022, __func__);\n+\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\tif (lws_ss_serialize_state(m-\u003econn, state, ack))\n+\t\t/*\n+\t\t * Failed to alloc state packet that we want to send in dsh,\n+\t\t * we will lose coherence and have to disconnect the link\n+\t\t */\n+\t\treturn LWSSSSRET_DISCONNECT_ME;\n+\n+\tif (state !\u003d LWSSSCS_DESTROYING \u0026\u0026\n+\t m-\u003econn-\u003etxp_path.priv_onw) /* if possible, request client conn write */\n+\t\tm-\u003econn-\u003etxp_path.ops_onw-\u003eproxy_req_write(m-\u003econn-\u003etxp_path.priv_onw);\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+/*\n+ * event loop side was told about remote peer tx credit window update, serialize\n+ * and put it in the dbuf for the foreign side to consume later\n+ */\n+\n+static int\n+lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr)\n+{\n+\tuint8_t pre[7];\n+\n+\tlwsl_info(\u0022%s: %d\u005cn\u0022, __func__, txcr);\n+\n+\tpre[0] \u003d LWSSS_SER_RXPRE_TXCR_UPDATE;\n+\tpre[1] \u003d 0;\n+\tpre[2] \u003d 4;\n+\tlws_ser_wu32be(\u0026pre[3], (uint32_t)txcr);\n+\n+\tif (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, 7, NULL, 0)) {\n+\t\tlwsl_err(\u0022%s: unable to alloc in dsh 2\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void\n+ss_proxy_onward_txcr(void *userobj, int bump)\n+{\n+\tss_proxy_t *m \u003d (ss_proxy_t *)userobj;\n+\n+\tif (!m-\u003econn)\n+\t\treturn;\n+\n+\tlws_ss_serialize_txcr(m-\u003econn-\u003edsh, bump);\n+\n+\tif (m-\u003econn-\u003etxp_path.priv_onw) /* if possible, request client conn write */\n+\t\tm-\u003econn-\u003etxp_path.ops_onw-\u003eproxy_req_write(m-\u003econn-\u003etxp_path.priv_onw);\n+}\n+\n+/*\n+ * called from create_context()\n+ */\n+\n+int\n+lws_ss_proxy_create(struct lws_context *cx, const char *bind, int port)\n+{\n+\tassert(cx-\u003etxp_ppath.ops_onw);\n+\treturn cx-\u003etxp_ppath.ops_onw-\u003einit_proxy_server(cx,\n+\t\t\t\t\t\t\u0026lws_txp_inside_proxy,\n+\t\t\t\t\t\tNULL,\n+\t\t\t\t\t\t\u0026cx-\u003etxp_ppath,\n+\t\t\t\t\t\tcx-\u003etxp_ssproxy_info,\n+\t\t\t\t\t\tbind, port);\n+}\n+\n+lws_ss_state_return_t\n+lws_ss_proxy_destroy(struct lws_context *cx)\n+{\n+\tif (!cx-\u003etxp_ppath.ops_onw)\n+\t\treturn 0;\n+\n+\tif (!cx-\u003etxp_ppath.ops_onw-\u003edestroy_proxy_server)\n+\t\treturn 0;\n+\treturn cx-\u003etxp_ppath.ops_onw-\u003edestroy_proxy_server(cx);\n+}\ndiff --git a/lib/secure-streams/system/auth-sigv4/sign.c b/lib/secure-streams/system/auth-sigv4/sign.c\nindex b1c2bf2..93f96db 100644\n--- a/lib/secure-streams/system/auth-sigv4/sign.c\n+++ b/lib/secure-streams/system/auth-sigv4/sign.c\n@@ -342,7 +342,10 @@ build_auth_string(struct lws *wsi, char * buf, size_t bufsz,\n \t\tstruct lws_ss_handle *h, struct sigv4 *s,\n \t\tuint8_t *signature_bin)\n {\n-\tchar *start \u003d buf, *end \u003d \u0026buf[bufsz - 1];\n+#if defined(_DEBUG)\n+\tchar *start \u003d buf;\n+#endif\n+\tchar *end \u003d \u0026buf[bufsz - 1];\n \tchar *c;\n \tlws_system_blob_t *ab;\n \tsize_t keyidlen \u003d 128; // max keyid len is 128\n@@ -381,9 +384,11 @@ build_auth_string(struct lws *wsi, char * buf, size_t bufsz,\n \t\t\t \u0022%s\u0022, \u0022 Signature\u003d\u0022);\n \tbin2hex(signature_bin, 32, buf);\n \n-\tassert(buf+65 \u003c\u003d start + bufsz);\n+#if defined(_DEBUG)\n+\tassert(buf + 65 \u003c\u003d start + bufsz);\n \n \tlwsl_debug(\u0022%s %s\u005cn\u0022, __func__, start);\n+#endif\n \n \treturn 0;\n \ndiff --git a/lib/system/CMakeLists.txt b/lib/system/CMakeLists.txt\nindex 654264b..0a1d938 100644\n--- a/lib/system/CMakeLists.txt\n+++ b/lib/system/CMakeLists.txt\n@@ -31,8 +31,10 @@\n \n include_directories(./async-dns)\n \n-list(APPEND SOURCES\n-\tsystem/system.c)\n+if (NOT LWS_ONLY_SSPC)\n+\tlist(APPEND SOURCES\n+\t\tsystem/system.c)\n+endif()\n \t\n if (LWS_WITH_NETWORK)\n \ndiff --git a/lib/system/metrics/metrics.c b/lib/system/metrics/metrics.c\nindex 92d5e0d..6a3f2f8 100644\n--- a/lib/system/metrics/metrics.c\n+++ b/lib/system/metrics/metrics.c\n@@ -608,7 +608,7 @@ lws_metrics_hist_bump_describe_wsi(struct lws *wsi, lws_metric_pub_t *pub,\n \t} else\n \t\tif (wsi-\u003eclient_proxy_onward) {\n \t\t\tlws_ss_handle_t *h \u003d (lws_ss_handle_t *)wsi-\u003ea.opaque_user_data;\n-\t\t\tstruct conn *conn \u003d h-\u003econn_if_sspc_onw;\n+\t\t\tstruct lws_sss_proxy_conn *conn \u003d h-\u003econn_if_sspc_onw;\n \n \t\t\tif (conn \u0026\u0026 conn-\u003ess)\n \t\t\t\tp +\u003d lws_snprintf(p, lws_ptr_diff_size_t(end, p),\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/CMakeLists.txt b/minimal-examples/embedded/pico/pico-sspc-binance/CMakeLists.txt\nnew file mode 100644\nindex 0000000..c455fe7\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/CMakeLists.txt\n@@ -0,0 +1,62 @@\n+# pico-sspc-binance demo\n+#\n+# connects a headless, networkless pico to binance over UART transport, using\n+# lws-minimal-secure-streams-custom-proxy-transport minimal example on PC\n+#\n+# Symlink or git submodule ./libwebsockets/ to main lws\n+# Build the whole thing with something like\n+#\n+# cmake .. -DLWS_ONLY_SSPC\u003d1 -DPICO_SDK_PATH\u003d../../pico-sdk\n+#\n+# proxy has to support LWS_ROLE_WS\u003d1 and LWS_WITHOUT_EXTENSIONS\u003d0\n+# to work with binance itself\n+\n+cmake_minimum_required(VERSION 3.13)\n+\n+# initialize the SDK based on PICO_SDK_PATH\n+# note: this must happen before project()\n+include(pico_sdk_import.cmake)\n+\n+project(pico-sspc-binance)\n+pico_sdk_init()\n+\n+set(LWS_WITH_SSL 0)\n+set(LWS_WITH_TLS 0)\n+set(LWS_WITH_SHARED 0)\n+set(LWS_PLAT_BAREMETAL 1)\n+set(LWS_WITH_NETWORK 0)\n+set(LWS_ONLY_SSPC 1) # remove everything except SSPC pieces from library build\n+# build lws as part of this\n+add_subdirectory(libwebsockets)\n+add_executable(pico-sspc-binance\n+ main.c\n+ system.c\n+ helpers.c\n+ transport-serial.c\n+ binance-ss.c\n+ get-ss.c\n+)\n+\n+target_compile_definitions(pico-sspc-binance PUBLIC\n+\t\t\t\tPICO_STACK_SIZE\u003d0x2000\n+\t\t\t\tLWS_SS_USE_SSPC)\n+\n+\n+# make sure we can include libwebsockets.h from the lws build\n+target_include_directories(pico-sspc-binance PRIVATE\n+\t# lws_config.h generated into here\n+\t${PROJECT_BINARY_DIR}/libwebsockets\n+\t# source includes will do for everything else\n+\t${PROJECT_SOURCE_DIR}/libwebsockets/include )\n+\n+target_link_libraries(pico-sspc-binance\n+\t\t\tpico_stdlib\n+\t\t\thardware_uart\n+\t\t\twebsockets)\n+\n+# logs on USB console /dev/ttyACM\n+pico_enable_stdio_usb(pico-sspc-binance 1)\n+# UART0 will be the sspc transport, don't put the console on it\n+pico_enable_stdio_uart(pico-sspc-binance 0)\n+\n+pico_add_extra_outputs(pico-sspc-binance)\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/README.md b/minimal-examples/embedded/pico/pico-sspc-binance/README.md\nnew file mode 100644\nindex 0000000..3a10cf7\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/README.md\n@@ -0,0 +1,211 @@\n+# Pico binance via SSPC / serial transport Example\n+\n+This is a project for an RPi pico, a cheap, widely available US$4 networkless\n+133MHz Cortex M0 embedded SBC.\n+\n+It is designed to run Serialized Secure Streams in a transport mux over\n+a UART running at 2Mbps, and demo two streams concurrently.\n+\n+\n+\n+## Traffic on the UART link\n+\n+The UART link has a lws_transport_mux layer that detects link up and down, and\n+above that Serialized SS travels in each mux channel.\n+\n+1) Mux framing PING / PONG / PONGACK / link detect, channel open / close etc\n+\n+2) SS to Binance compressed wss server\n+\n+The pico runs the generic lws SS binance client and from around 30 messages a\n+second, reports the average price of bitcoin in realtime once a second,\n+same as it does if you run it on a PC and using the same SS code.\n+\n+3) Every 5s another SS does a GET from https://warmcat.com/index.html to\n+demonstrate multiple SS muxing over the UART.\n+\n+That involves compressed wss and h2, but the pico has:\n+\n+ - no network stack (TCP/IP)\n+ - no tls\n+ - no h2\n+ - no wss protocol library\n+ - no wss compression extension\n+ \n+The pico creates a generic SS using the same code as the other examples, the\n+compressed wss connection is actually fulfilled by the proxy part (lws-minimal-\n+secure-streams-custom-proxy-transport) connected over the UART. It passes\n+the bursty 20 - 120 KBytes/sec SS payloads over the UART where the binance\n+SS client consumes them as if it was all happening on the pico.\n+\n+## Hookup\n+\n+\n+\n+The three pins shown above need connecting to an FTDI-based\n+3.3V UART - USB adapter.\n+\n+FTDI is needed because we will run the link at 2Mbps, most cheaper adapters\n+don't support this baud rate.\n+\n+For this demo 2Mbps is required, since the websocket data from binance is very\n+bursty, we have to be able to clear a burst before the next one in order to\n+keep up.\n+\n+## Build\n+\n+This builds as a toplevel project using the pcio sdk, it builds\n+lws via a symlink, and links with liblws-sspc.a rather than libwebsockets.a\n+\n+Adapt `PICO_SDK_PATH` below to where your pico sdk is installed\n+\n+```\n+$ cmake .. -DPICO_SDK_PATH\u003d/projects/pico-sdk\n+$ make\n+```\n+\n+You flash the pico by holding down the button and inserting USB, it mounts as a\n+USB storage device. Copy `pico-sspc-binance.uf2` to the storage device and\n+unmount it.\n+\n+## Running\n+\n+On the PC, you need to build lws-minimal-secure-streams-custom-proxy-transport,\n+which needs lws built with `LWS_WITHOUT_EXTENSIONS\u003d0`. This connects to\n+`/dev/ttyUSB0` and starts the mux + SS Proxy.\n+\n+On the Pico, you can monitor the serial console over USB to see logs via\n+`/dev/ttyACM0`, it's a bit awkward since that is created during Pico boot, so\n+you miss the earliest logs, but the app on the Pico won't do anything\n+interesting until it connects via the proxy, and shows periodic pings on the\n+mux layer until then.\n+\n+Wait for peer (1Hz pings at mux layer)\n+\n+```\n+11010626: lws_transport_mux_retry_connect\n+11010856: lws_transport_path_client_dump: lws_transport_mux_retry_connect: MUX: 2000AC80, IN: ops\u003dtxp_inside_sspc, priv\u003d2000AD80, ONW: ops\u003dtxpmuxc, priv\u003d00000000\n+11011109: lws_transport_mux_retry_connect: transport not operational\n+12004542: sul_ping_cb: issuing ping\n+12004802: lws_transport_path_client_dump: cpath: MUX: 00000000, IN: ops\u003dtxpmuxc, priv\u003d2000AC80 (IsTM), ONW: ops\u003dtxpserial, priv\u003d00000000\n+12005055: txp_serial_write: writing 9\n+12005287: 0000: F6 00 00 00 00 00 B7 2D 54 .......-T \n+12005441: \n+12011287: lws_sspc_sul_retry_cb\n+12011438: lws_transport_mux_retry_connect\n+12011692: lws_transport_path_client_dump: lws_transport_mux_retry_connect: MUX: 2000AC80, IN: ops\u003dtxp_inside_sspc, priv\u003d2000AD80, ONW: ops\u003dtxpmuxc, priv\u003d00000000\n+12011935: lws_transport_mux_retry_connect: transport not operational\n+13012121: lws_sspc_sul_retry_cb\n+13012256: lws_transport_mux_retry_connect\n+13012492: lws_transport_path_client_dump: lws_transport_mux_retry_connect: MUX: 2000AC80, IN: ops\u003dtxp_inside_sspc, priv\u003d2000AD80, ONW: ops\u003dtxpmuxc, priv\u003d00000000\n+13012796: lws_transport_mux_retry_connect: transport not operational\n+```\n+\n+Get mux layer `PING` from proxy / mux side, return PONG\n+\n+```\n+13941468: lws_transport_mux_rx_parse: got PING\n+13941779: lws_transport_path_client_dump: cpath: MUX: 00000000, IN: ops\u003dtxpmuxc, priv\u003d2000AC80 (IsTM), ONW: ops\u003dtxpserial, priv\u003d00000000\n+13942054: txp_serial_write: writing 17\n+13942323: 0000: F7 00 00 00 00 00 00 00 00 00 00 00 00 00 D4 BB ................\n+13942578: 0010: 98 . \n+13942738: \n+```\n+\n+Get mux later `PONGACK` (three way PING) response, both sides are up at mux layer.\n+This also provides the peer with the proxy device's time in us resolution.\n+\n+```\n+13955788: lws_transport_mux_rx_parse: got PONGACK: ustime 1630898701380539\n+13956011: lws_transport_set_link: ******* transport mux link is UP\n+```\n+\n+Retry the SS link to proxy at SSS layer, first request open mux channel\n+\n+```\n+14012976: lws_sspc_sul_retry_cb\n+14013133: lws_transport_mux_retry_connect\n+14013356: lws_transport_path_client_dump: lws_transport_mux_retry_connect: MUX: 2000AC80, IN: ops\u003dtxp_inside_sspc, priv\u003d2000AD80, ONW: ops\u003dtxpmuxc, priv\u003d00000000\n+14013723: lws_transport_mux_retry_connect: added channel\n+14014026: lws_transport_path_client_dump: cpath: MUX: 00000000, IN: ops\u003dtxpmuxc, priv\u003d2000AC80 (IsTM), ONW: ops\u003dtxpserial, priv\u003d00000000\n+14014286: txp_serial_write: writing 2\n+14014471: 0000: F0 FF .. \n+14014702: \n+```\n+\n+Receive mux channel open ACK, mux channel ready on both sides, start to issue\n+Serialized Secure Streams in it: request binance SS stream\n+\n+```\n+14019481: lws_transport_mux_rx_parse: ch 255 fully open\n+14019661: 0\n+14019880: lws_sspc_txp_tx: LPCSCLI_SENDING_INITIAL_TX\n+14020118: lws_transport_mux_write: 19\n+14020280: txp_serial_write: writing 23\n+14020553: 0000: F5 FF 00 13 AA 00 10 01 FF FF FF FF 1D CD 65 00 ..............e.\n+14020838: 0010: 62 69 6E 61 6E 63 65 binance \n+14020995: \n+14037838: (unset) -\u003e LWSSSCS_CREATING\n+```\n+\n+SS states move forward as proxy fulfils the onward connection, then starts to\n+receive SSS payloads (previously compressed wss message payloads) and report\n+summaries at 1Hz\n+\n+```\n+15090846: LWSSSCS_CREATING -\u003e LWSSSCS_CONNECTING\n+15091150: LWSSSCS_CONNECTING -\u003e LWSSSCS_CONNECTED\n+16091462: sul_hz_cb: price: min: 5170001¢, max: 5170867¢, avg: 5170245¢, (26 prices/s)\n+16091795: sul_hz_cb: elatency: min: 137ms, max: 296ms, avg: 203ms, (26 msg/s, 49 KiBytes/s SS RX)\n+17091455: sul_hz_cb: price: min: 5169104¢, max: 5170101¢, avg: 5169602¢, (33 prices/s)\n+17091746: sul_hz_cb: elatency: min: 139ms, max: 304ms, avg: 173ms, (33 msg/s, 94 KiBytes/s SS RX)\n+18091450: sul_hz_cb: price: min: 5169104¢, max: 5171400¢, avg: 5169304¢, (30 prices/s)\n+18091749: sul_hz_cb: elatency: min: 134ms, max: 167ms, avg: 139ms, (30 msg/s, 31 KiBytes/s SS RX)\n+19091460: sul_hz_cb: price: min: 5169183¢, max: 5169370¢, avg: 5169204¢, (29 prices/s)\n+19091753: sul_hz_cb: elatency: min: 136ms, max: 152ms, avg: 139ms, (29 msg/s, 31 KiBytes/s SS RX)\n+20091468: sul_hz_cb: price: min: 5169183¢, max: 5171063¢, avg: 5169664¢, (29 prices/s)\n+20091791: sul_hz_cb: elatency: min: 136ms, max: 151ms, avg: 139ms, (29 msg/s, 30 KiBytes/s SS RX)\n+21091482: sul_hz_cb: price: min: 5169183¢, max: 5171574¢, avg: 5169377¢, (30 prices/s)\n+21091759: sul_hz_cb: elatency: min: 136ms, max: 151ms, avg: 139ms, (30 msg/s, 44 KiBytes/s SS RX)\n+22091477: sul_hz_cb: price: min: 5168614¢, max: 5170771¢, avg: 5169225¢, (30 prices/s)\n+22091772: sul_hz_cb: elatency: min: 135ms, max: 148ms, avg: 139ms, (30 msg/s, 54 KiBytes/s SS RX)\n+23091491: sul_hz_cb: price: min: 5169058¢, max: 5169276¢, avg: 5169122¢, (28 prices/s)\n+23091772: sul_hz_cb: elatency: min: 136ms, max: 144ms, avg: 138ms, (28 msg/s, 22 KiBytes/s SS RX)\n+...\n+```\n+\n+The periodic second SS reads from https://warmcat.com look like this (it dumps the first and\n+last 16 bytes of the rx packet)\n+\n+```\n+42214221: lws_sspc_deserialize_parse: [mintest-lws|1]: : CONNECTED mintest-lws\n+42214520: lws_ss_serialize_state_transition: [mintest-lws|1]: : LPCSCLI_LOCAL_CONNECTED -\u003e LPCSCLI_OPERATIONAL\n+42214849: lws_ss_check_next_state_sspc: [mintest-lws|1]: : LWSSSCS_CONNECTING -\u003e LWSSSCS_CONNECTED\n+42215248: get_state: [mintest-lws|1]: : LWSSSCS_CONNECTED (5), ord 0x0\n+42220399: get_rx: [mintest-lws|1]: : RX 1520, flags 0x1\n+42220862: : 0000: 3C 21 44 4F 43 54 59 50 45 20 68 74 6D 6C 3E 0A \u003c!DOCTYPE html\u003e.\n+42221074: : \n+42221370: : 0000: 3C 6C 69 3E 38 30 20 6D 69 6E 69 6D 61 6C 20 65 \u003cli\u003e80 minimal e\n+42221580: : \n+42305323: get_rx: [mintest-lws|1]: : RX 1520, flags 0x0\n+42305900: : 0000: 78 61 6D 70 6C 65 73 3A 20 3C 61 20 68 72 65 66 xamples: \u003ca href\n+42306152: : \n+42306411: : 0000: AD 98 3C 2F 74 64 3E 3C 74 64 20 63 6C 61 73 73 ..\u003c/td\u003e\u003ctd class\n+42306717: : \n+42343404: get_rx: [mintest-lws|1]: : RX 1520, flags 0x0\n+42343967: : 0000: 3D 22 67 22 3E E2 AD 98 3C 2F 74 64 3E 3C 2F 74 \u003d\u0022g\u0022\u003e...\u003c/td\u003e\u003c/t\n+42344184: : \n+42344412: : 0000: 77 65 62 73 6F 63 6B 65 74 73 2F 74 72 65 65 2F websockets/tree/\n+42344618: : \n+42486791: get_rx: [mintest-lws|1]: : RX 1520, flags 0x0\n+42487366: : 0000: 6C 69 62 2F 65 76 65 6E 74 2D 6C 69 62 73 22 20 lib/event-libs\u0022 \n+42487635: : \n+42487894: : 0000: 31 36 20 69 62 22 3E 0A 20 20 20 20 20 3C 68 31 16 ib\u0022\u003e. \u003ch1\n+42488113: : \n+42527399: get_rx: [mintest-lws|1]: : RX 1277, flags 0x0\n+42527903: : 0000: 3E 51 41 3C 2F 68 31 3E 0A 20 20 20 20 20 20 4C \u003eQA\u003c/h1\u003e. L\n+42528116: : \n+42528404: : 0000: 3C 2F 62 6F 64 79 3E 0A 3C 2F 68 74 6D 6C 3E 0A \u003c/body\u003e.\u003c/html\u003e.\n+42528637: : \n+42570288: get_rx: [mintest-lws|1]: : RX 0, flags 0x2\n+```\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/binance-ss.c b/minimal-examples/embedded/pico/pico-sspc-binance/binance-ss.c\nnew file mode 100644\nindex 0000000..688c929\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/binance-ss.c\n@@ -0,0 +1,227 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ * Kutoga \u003ckutoga@user.github.invalid\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ *\n+ *\n+ * This is a version of minimal-secure-streams-binance that uses a custom\n+ * SS Serialization transport.\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003cctype.h\u003e\n+\n+extern uint64_t\n+get_us_timeofday(void);\n+\n+typedef struct range {\n+\tuint64_t\t\tsum;\n+\tuint64_t\t\tlowest;\n+\tuint64_t\t\thighest;\n+\n+\tunsigned int\t\tsamples;\n+} range_t;\n+\n+typedef struct binance {\n+\tstruct lws_ss_handle \t*ss;\n+\tvoid\t\t\t*opaque_data;\n+\n+\tuint64_t\t\tdata_in;\n+\tuint64_t\t\tdata_in_last_sec;\n+\n+\tlws_sorted_usec_list_t\tsul_hz;\t /* 1hz summary dump */\n+\tchar\t\t\tmsgbuf[10240];\n+\tsize_t\t\t\tmsg_len;\n+\n+\trange_t\t\t\te_lat_range;\n+\trange_t\t\t\tprice_range;\n+} binance_t;\n+\n+\n+/*\n+ * Rest of the file is Binance application SS processing (UNCHANGED from\n+ * minimal-secure-streams-binance)\n+ */\n+\n+static void\n+range_reset(range_t *r)\n+{\n+\tr-\u003esum \u003d r-\u003ehighest \u003d 0;\n+\tr-\u003elowest \u003d 999999999999ull;\n+\tr-\u003esamples \u003d 0;\n+}\n+\n+static uint64_t\n+pennies(const char *s)\n+{\n+\tuint64_t price \u003d (uint64_t)atoll(s) * 100;\n+\n+\ts \u003d strchr(s, '.');\n+\n+\tif (s \u0026\u0026 isdigit(s[1]) \u0026\u0026 isdigit(s[2]))\n+\t\tprice \u003d price + (uint64_t)((10 * (s[1] - '0')) + (s[2] - '0'));\n+\n+\treturn price;\n+}\n+\n+static void\n+sul_hz_cb(lws_sorted_usec_list_t *sul)\n+{\n+\tbinance_t *bin \u003d lws_container_of(sul, binance_t, sul_hz);\n+\n+\t/*\n+\t * We are called once a second to dump statistics on the connection\n+\t */\n+\n+\tlws_sul_schedule(lws_ss_get_context(bin-\u003ess), 0, \u0026bin-\u003esul_hz,\n+\t\t\t sul_hz_cb, LWS_US_PER_SEC);\n+\n+\tif (bin-\u003eprice_range.samples)\n+\t\tlwsl_user(\u0022%s: price: min: %llu¢, max: %llu¢, avg: %llu¢, \u0022\n+\t\t\t \u0022(%d prices/s)\u005cn\u0022, __func__,\n+\t\t\t (unsigned long long)bin-\u003eprice_range.lowest,\n+\t\t\t (unsigned long long)bin-\u003eprice_range.highest,\n+\t\t\t (unsigned long long)(bin-\u003eprice_range.sum /\n+\t\t\t\t\t\t bin-\u003eprice_range.samples),\n+\t\t\t bin-\u003eprice_range.samples);\n+\tif (bin-\u003ee_lat_range.samples)\n+\t\tlwsl_user(\u0022%s: elatency: min: %lums, max: %lums, \u0022\n+\t\t\t \u0022avg: %lums, (%d msg/s, %lu KiBytes/s SS RX)\u005cn\u0022,\n+\t\t\t __func__,\n+\t\t\t (unsigned long)(bin-\u003ee_lat_range.lowest / 1000),\n+\t\t\t (unsigned long)(bin-\u003ee_lat_range.highest / 1000),\n+\t\t\t (unsigned long)((bin-\u003ee_lat_range.sum /\n+\t\t\t\t\t bin-\u003ee_lat_range.samples) / 1000),\n+\t\t\t bin-\u003ee_lat_range.samples,\n+\t\t\t (unsigned long)((bin-\u003edata_in - bin-\u003edata_in_last_sec) / 1024));\n+\n+\trange_reset(\u0026bin-\u003ee_lat_range);\n+\trange_reset(\u0026bin-\u003eprice_range);\n+\n+\tbin-\u003edata_in_last_sec \u003d bin-\u003edata_in;\n+}\n+\n+static lws_ss_state_return_t\n+binance_rx(void *userobj, const uint8_t *in, size_t len, int flags)\n+{\n+\tbinance_t *bin \u003d (binance_t *)userobj;\n+\tuint64_t latency_us, now_us, l1;\n+\tconst uint8_t *msg;\n+\tchar numbuf[20];\n+\tuint64_t price;\n+\tconst char *p;\n+\tsize_t alen;\n+\n+\tbin-\u003edata_in +\u003d len;\n+\n+\tmsg \u003d bin-\u003emsgbuf;\n+\tif (flags \u0026 LWSSS_FLAG_SOM) {\n+\t\tbin-\u003emsg_len \u003d 0;\n+\t\tif (flags \u0026 LWSSS_FLAG_EOM) {\n+\t\t\tmsg \u003d in;\n+\t\t\tbin-\u003emsg_len \u003d len;\n+\t\t\tgoto handle;\n+\t\t}\n+\t}\n+\n+\tif (bin-\u003emsg_len + len \u003c sizeof(bin-\u003emsgbuf)) {\n+\t\tmemcpy(bin-\u003emsgbuf + bin-\u003emsg_len, in, len);\n+\t\tbin-\u003emsg_len +\u003d len;\n+\t}\n+\n+\t/* assemble a full message */\n+\tif (!(flags \u0026 LWSSS_FLAG_EOM))\n+\t\treturn LWSSSSRET_OK;\n+\n+\n+handle:\n+\t//lwsl_notice(\u0022%s: chunk len %d\u005cn\u0022, __func__, (int)len);\n+\n+\tnow_us \u003d (uint64_t)get_us_timeofday();\n+\t//lwsl_notice(\u0022%s: now_us adjusted %llu\u005cn\u0022, __func__, now_us);\n+\n+\tp \u003d lws_json_simple_find(msg, bin-\u003emsg_len, \u0022\u005c\u0022depthUpdate\u005c\u0022\u0022,\n+\t\t\t\t \u0026alen);\n+\tif (!p)\n+\t\treturn LWSSSSRET_OK;\n+\n+\tp \u003d lws_json_simple_find(msg, bin-\u003emsg_len, \u0022\u005c\u0022E\u005c\u0022:\u0022, \u0026alen);\n+\tif (!p) {\n+\t\tlwsl_err(\u0022%s: no E JSON\u005cn\u0022, __func__);\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\tlws_strnncpy(numbuf, p, alen, sizeof(numbuf));\n+\tl1 \u003d ((uint64_t)atoll(numbuf) * LWS_US_PER_MS);\n+\tlatency_us \u003d now_us - l1;\n+\n+\tif (latency_us \u003c bin-\u003ee_lat_range.lowest)\n+\t\tbin-\u003ee_lat_range.lowest \u003d latency_us;\n+\tif (latency_us \u003e bin-\u003ee_lat_range.highest)\n+\t\tbin-\u003ee_lat_range.highest \u003d latency_us;\n+\n+\tbin-\u003ee_lat_range.sum +\u003d latency_us;\n+\tbin-\u003ee_lat_range.samples++;\n+\n+\tp \u003d lws_json_simple_find(msg, bin-\u003emsg_len, \u0022\u005c\u0022a\u005c\u0022:[[\u005c\u0022\u0022, \u0026alen);\n+\tif (!p)\n+\t\treturn LWSSSSRET_OK;\n+\n+\tlws_strnncpy(numbuf, p, alen, sizeof(numbuf));\n+\tprice \u003d pennies(numbuf);\n+\n+\tif (price \u003c bin-\u003eprice_range.lowest)\n+\t\tbin-\u003eprice_range.lowest \u003d price;\n+\tif (price \u003e bin-\u003eprice_range.highest)\n+\t\tbin-\u003eprice_range.highest \u003d price;\n+\n+\tbin-\u003eprice_range.sum +\u003d price;\n+\tbin-\u003eprice_range.samples++;\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+static lws_ss_state_return_t\n+binance_state(void *userobj, void *h_src, lws_ss_constate_t state,\n+\t lws_ss_tx_ordinal_t ack)\n+{\n+\tbinance_t *bin \u003d (binance_t *)userobj;\n+\n+\tlwsl_ss_info(bin-\u003ess, \u0022%s (%d), ord 0x%x\u0022,\n+\t\t lws_ss_state_name((int)state), state, (unsigned int)ack);\n+\n+\tswitch (state) {\n+\n+\tcase LWSSSCS_CONNECTED:\n+\t\tlws_sul_schedule(lws_ss_get_context(bin-\u003ess), 0, \u0026bin-\u003esul_hz,\n+\t\t\t\t sul_hz_cb, LWS_US_PER_SEC);\n+\t\trange_reset(\u0026bin-\u003ee_lat_range);\n+\t\trange_reset(\u0026bin-\u003eprice_range);\n+\n+\t\treturn LWSSSSRET_OK;\n+\n+\tcase LWSSSCS_DISCONNECTED:\n+\t\tlws_sul_cancel(\u0026bin-\u003esul_hz);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+const lws_ss_info_t ssi_binance \u003d {\n+\t.handle_offset\t\t \u003d offsetof(binance_t, ss),\n+\t.opaque_user_data_offset \u003d offsetof(binance_t, opaque_data),\n+\t.rx\t\t\t \u003d binance_rx,\n+\t.state\t\t\t \u003d binance_state,\n+\t.user_alloc\t\t \u003d sizeof(binance_t),\n+\t.streamtype\t\t \u003d \u0022binance\u0022, /* bind to corresponding policy */\n+};\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/get-ss.c b/minimal-examples/embedded/pico/pico-sspc-binance/get-ss.c\nnew file mode 100644\nindex 0000000..fc01451\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/get-ss.c\n@@ -0,0 +1,85 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by 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 SS user struct for the \u0022GET\u0022 stream... it reads from\n+ * https://libwebsockets.org/index.html every 5s\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+typedef struct {\n+\tstruct lws_ss_handle \t*ss;\n+\tvoid\t\t\t*opaque_data;\n+\n+\tlws_sorted_usec_list_t\tsul5;\n+} get_t;\n+\n+static void\n+sul_start_get(lws_sorted_usec_list_t *sul)\n+{\n+\tget_t *g \u003d lws_container_of(sul, get_t, sul5);\n+\n+\tlwsl_ss_notice(g-\u003ess, \u0022conn\u0022);\n+\tlws_ss_request_tx(g-\u003ess);\n+\tlws_sul_schedule(lws_ss_get_context(g-\u003ess), 0, sul, sul_start_get,\n+\t\t\t 5 * LWS_US_PER_SEC);\n+}\n+\n+static lws_ss_state_return_t\n+get_rx(void *userobj, const uint8_t *in, size_t len, int flags)\n+{\n+\tget_t *g \u003d (get_t *)userobj;\n+\n+\tlwsl_ss_notice(g-\u003ess, \u0022RX %u, flags 0x%x\u0022,\n+\t\t (unsigned int)len, (unsigned int)flags);\n+\n+\tif (len) {\n+\t\tlwsl_hexdump_notice(in, 16);\n+\t\tif (len \u003e\u003d 16)\n+\t\t\tlwsl_hexdump_notice(in + len - 16, 16);\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+static lws_ss_state_return_t\n+get_state(void *userobj, void *h_src, lws_ss_constate_t state,\n+\t lws_ss_tx_ordinal_t ack)\n+{\n+\tget_t *g \u003d (get_t *)userobj;\n+\n+\tlwsl_ss_notice(g-\u003ess, \u0022%s (%d), ord 0x%x\u0022,\n+\t\t lws_ss_state_name((int)state), state, (unsigned int)ack);\n+\n+\tswitch (state) {\n+\tcase LWSSSCS_CREATING:\n+\t\t/*\n+\t\t * ... also let's start a sul that creates a second stream to\n+\t\t * GET from libwebsockets.org every 5s, showing we are running\n+\t\t * multiple SS on the transport successfully.\n+\t\t */\n+\n+\t\tlws_sul_schedule(lws_ss_get_context(g-\u003ess), 0, \u0026g-\u003esul5,\n+\t\t\t\t sul_start_get, 5 * LWS_US_PER_SEC);\n+\t\tbreak;\n+\tcase LWSSSCS_DESTROYING:\n+\t\tlws_sul_cancel(\u0026g-\u003esul5);\n+\t\tbreak;\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+const lws_ss_info_t ssi_get \u003d {\n+\t.handle_offset\t\t \u003d offsetof(get_t, ss),\n+\t.opaque_user_data_offset \u003d offsetof(get_t, opaque_data),\n+\t.rx\t\t\t \u003d get_rx,\n+\t.state\t\t\t \u003d get_state,\n+\t.user_alloc\t\t \u003d sizeof(get_t),\n+\t.streamtype\t\t \u003d \u0022mintest-lws\u0022, /* bind to this policy */\n+};\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/helpers.c b/minimal-examples/embedded/pico/pico-sspc-binance/helpers.c\nnew file mode 100644\nindex 0000000..534909b\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/helpers.c\n@@ -0,0 +1,146 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by 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+ * Since LWS_ONLY_SSPC chops down libwebsockets.a to have just the pieces needed\n+ * for SSPC, we need to bring in our own copies of any other lws apis we use in\n+ * the user Binance SS code\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+const char *\n+lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl)\n+{\n+\tconst char *end \u003d buf + len - nl + 1;\n+\tsize_t n;\n+\n+\tif (nl \u003e len)\n+\t\t/* it cannot be found if the needle is longer than the haystack */\n+\t\treturn NULL;\n+\n+\twhile (buf \u003c end) {\n+\t\tif (*buf !\u003d name[0]) {\n+\t\t\tbuf++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (nl \u003d\u003d 1)\n+\t\t\t/* single char match, we are done */\n+\t\t\treturn buf;\n+\n+\t\tif (buf[nl - 1] \u003d\u003d name[nl - 1]) {\n+\t\t\t/*\n+\t\t\t * This is looking interesting then... the first\n+\t\t\t * and last chars match, let's check the insides\n+\t\t\t */\n+\t\t\tn \u003d 1;\n+\t\t\twhile (n \u003c nl \u0026\u0026 buf[n] \u003d\u003d name[n])\n+\t\t\t\tn++;\n+\n+\t\t\tif (n \u003d\u003d nl)\n+\t\t\t\t/* it's a hit */\n+\t\t\t\treturn buf;\n+\t\t}\n+\n+\t\tbuf++;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+\n+const char *\n+lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen)\n+{\n+\tsize_t nl \u003d strlen(name);\n+\tconst char *np \u003d lws_nstrstr(buf, len, name, nl),\n+\t\t *end \u003d buf + len, *as;\n+\tint qu \u003d 0;\n+\n+\tif (!np)\n+\t\treturn NULL;\n+\n+\tnp +\u003d nl;\n+\n+\twhile (np \u003c end \u0026\u0026 (*np \u003d\u003d ' ' || *np \u003d\u003d '\u005ct'))\n+\t\tnp++;\n+\n+\tif (np \u003e\u003d end)\n+\t\treturn NULL;\n+\n+\t/*\n+\t * The arg could be lots of things after \u0022name\u0022: with JSON, commonly a\n+\t * string like \u0022mystring\u0022, true, false, null, [...] or {...} ... we want\n+\t * to handle common, simple cases cheaply with this; the user can choose\n+\t * a full JSON parser like lejp if it's complicated. So if no opening\n+\t * quote, return until a terminator like , ] }. If there's an opening\n+\t * quote, return until closing quote, handling escaped quotes.\n+\t */\n+\n+\tif (*np \u003d\u003d '\u005c\u0022') {\n+\t\tqu \u003d 1;\n+\t\tnp++;\n+\t}\n+\n+\tas \u003d np;\n+\twhile (np \u003c end \u0026\u0026\n+\t (!qu || *np !\u003d '\u005c\u0022') \u0026\u0026 /* end quote is EOT if quoted */\n+\t (qu || (*np !\u003d '}' \u0026\u0026 *np !\u003d ']' \u0026\u0026 *np !\u003d ',')) /* delimiters */\n+\t) {\n+\t\tif (qu \u0026\u0026 *np \u003d\u003d '\u005c\u005c') /* skip next char if quoted escape */\n+\t\t\tnp++;\n+\t\tnp++;\n+\t}\n+\n+\t*alen \u003d (unsigned int)lws_ptr_diff(np, as);\n+\n+\treturn as;\n+}\n+\n+void\n+lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)\n+{\n+\tunsigned char *buf \u003d (unsigned char *)vbuf;\n+\tunsigned int n;\n+\n+\tfor (n \u003d 0; n \u003c len;) {\n+\t\tunsigned int start \u003d n, m;\n+\t\tchar line[80], *p \u003d line;\n+\n+\t\tp +\u003d snprintf(p, 10, \u0022%04X: \u0022, start);\n+\n+\t\tfor (m \u003d 0; m \u003c 16 \u0026\u0026 n \u003c len; m++)\n+\t\t\tp +\u003d snprintf(p, 5, \u0022%02X \u0022, buf[n++]);\n+\t\twhile (m++ \u003c 16)\n+\t\t\tp +\u003d snprintf(p, 5, \u0022 \u0022);\n+\n+\t\tp +\u003d snprintf(p, 6, \u0022 \u0022);\n+\n+\t\tfor (m \u003d 0; m \u003c 16 \u0026\u0026 (start + m) \u003c len; m++) {\n+\t\t\tif (buf[start + m] \u003e\u003d ' ' \u0026\u0026 buf[start + m] \u003c 127)\n+\t\t\t\t*p++ \u003d (char)buf[start + m];\n+\t\t\telse\n+\t\t\t\t*p++ \u003d '.';\n+\t\t}\n+\t\twhile (m++ \u003c 16)\n+\t\t\t*p++ \u003d ' ';\n+\n+\t\t*p++ \u003d '\u005cn';\n+\t\t*p \u003d '\u005c0';\n+\t\t_lws_log(hexdump_level, \u0022%s\u0022, line);\n+\t\t(void)line;\n+\t}\n+\n+\t_lws_log(hexdump_level, \u0022\u005cn\u0022);\n+}\n+\n+uint64_t\n+get_us_timeofday(void)\n+{\n+\treturn lws_now_usecs() + tm-\u003eus_unixtime_peer;\n+}\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/libwebsockets b/minimal-examples/embedded/pico/pico-sspc-binance/libwebsockets\nnew file mode 120000\nindex 0000000..c866b86\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/libwebsockets\n@@ -0,0 +1 @@\n+../../../..\n\u005c No newline at end of file\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/main.c b/minimal-examples/embedded/pico/pico-sspc-binance/main.c\nnew file mode 100644\nindex 0000000..b2e56da\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/main.c\n@@ -0,0 +1,119 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by 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+ *\n+ * This example builds for an rpi pico, and demonstrates using Secure Streams\n+ * over a UART transport, via lws_transport_mux, to an SS proxy.\n+ *\n+ * It allows the pico to perform cloud operations despite it has no network\n+ * capability or IP stack or tls.\n+ *\n+ * In one stream it connects over wss to binance server.\n+ *\n+ * In a second stream, it GETs https://libwebsockets.org/index.html every 5s.\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+lws_dll2_owner_t scheduler;\n+lws_transport_mux_t *tm;\n+\n+/*\n+ * In LWS_ONLY_SSPC build mode, lws just has a very simplified \u0022lws_context\u0022\n+ * that does not reed creating / destroying.\n+ */\n+\n+static struct lws_context_standalone cx \u003d {\n+\t.txp_cpath.ops_onw\t\t\u003d \u0026lws_transport_mux_client_ops,\n+};\n+\n+/*\n+ * Describes how the lws_transport path goes through the transport_mux\n+ */\n+\n+lws_transport_info_t info_serial \u003d {\n+\t.ping_interval_us\t\t\u003d LWS_US_PER_SEC * 10,\n+\t.pong_grace_us\t\t\t\u003d LWS_US_PER_SEC * 2,\n+\t.flags\t\t\t\t\u003d 0,\n+}, info_mux \u003d {\n+\t.ping_interval_us\t\t\u003d LWS_US_PER_SEC * 10,\n+\t.pong_grace_us\t\t\t\u003d LWS_US_PER_SEC * 2,\n+\t.txp_cpath \u003d {\n+\t\t.ops_onw\t\t\u003d \u0026lws_sss_ops_client_serial,\n+\t\t/**\u003c onward transport for mux is serial */\n+\t\t.ops_in\t\t\t\u003d \u0026lws_transport_mux_client_ops,\n+\t},\n+\t.onward_txp_info\t\t\u003d \u0026info_serial,\n+\t.flags\t\t\t\t\u003d 0,\n+};\n+\n+int\n+main(void)\n+{\n+\t/*\n+\t * Set up pico for ttyACM USB cnsole and UART0 at 2Mbps\n+\t */\n+\n+\tstdio_init_all();\n+\tlwsl_user(\u0022\u005cnpico-sspc-binance demo\u005cn\u0022);\n+\n+\tpico_example_open_serial_port(uart0);\n+\n+\t/* create the mux object itself... only one of these */\n+\n+\ttm \u003d lws_transport_mux_create(\u0026cx, \u0026info_mux, NULL);\n+\tif (!tm) {\n+\t\tlwsl_err(\u0022%s: unable to create client mux\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\ttm-\u003einfo.txp_cpath.priv_in \u003d tm;\n+\tcx.txp_cpath.mux \u003d tm;\n+\n+\t/*\n+\t * Now that's done, create the SS and it will try to connect over the\n+\t * mux -\u003e transport -\u003e proxy -\u003e binance wss\n+\t */\n+\n+\tif (lws_ss_create(\u0026cx, 0, \u0026ssi_binance, NULL, NULL, NULL, NULL)) {\n+\t\tprintf(\u0022failed to create binance secure stream\u005cn\u0022);\n+\t\treturn 1;\n+\t}\n+\n+\tif (lws_ss_create(\u0026cx, 0, \u0026ssi_get, NULL, NULL, NULL, NULL)) {\n+\t\tprintf(\u0022failed to create get secure stream\u005cn\u0022);\n+\t\treturn 1;\n+\t}\n+\n+\t/*\n+\t * this represents our application event loop.\n+\t * Your event loop is hopefully better than this\n+\t * one, but either way this shows how to handle\n+\t * everything needed for LWS_ONLY_SSPC\n+\t */\n+\n+\twhile (true) {\n+\n+\t\tserial_handle_events(tm);\n+\n+\t\t/* check the scheduler */\n+\n+\t\twhile (scheduler.head) {\n+\t\t\tlws_sorted_usec_list_t *sul \u003d lws_container_of(\n+\t\t\t\t\tscheduler.head, lws_sorted_usec_list_t, list);\n+\n+\t\t\tif (sul-\u003eus \u003e lws_now_usecs())\n+\t\t\t\tbreak;\n+\t\t\tlws_dll2_remove(\u0026sul-\u003elist);\n+\n+\t\t\tsul-\u003ecb(sul);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/pico_sdk_import.cmake b/minimal-examples/embedded/pico/pico-sspc-binance/pico_sdk_import.cmake\nnew file mode 100644\nindex 0000000..28efe9e\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/pico_sdk_import.cmake\n@@ -0,0 +1,62 @@\n+# This is a copy of \u003cPICO_SDK_PATH\u003e/external/pico_sdk_import.cmake\n+\n+# This can be dropped into an external project to help locate this SDK\n+# It should be include()ed prior to project()\n+\n+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))\n+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})\n+ message(\u0022Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')\u0022)\n+endif ()\n+\n+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))\n+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})\n+ message(\u0022Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')\u0022)\n+endif ()\n+\n+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))\n+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})\n+ message(\u0022Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')\u0022)\n+endif ()\n+\n+set(PICO_SDK_PATH \u0022${PICO_SDK_PATH}\u0022 CACHE PATH \u0022Path to the Raspberry Pi Pico SDK\u0022)\n+set(PICO_SDK_FETCH_FROM_GIT \u0022${PICO_SDK_FETCH_FROM_GIT}\u0022 CACHE BOOL \u0022Set to ON to fetch copy of SDK from git if not otherwise locatable\u0022)\n+set(PICO_SDK_FETCH_FROM_GIT_PATH \u0022${PICO_SDK_FETCH_FROM_GIT_PATH}\u0022 CACHE FILEPATH \u0022location to download SDK\u0022)\n+\n+if (NOT PICO_SDK_PATH)\n+ if (PICO_SDK_FETCH_FROM_GIT)\n+ include(FetchContent)\n+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})\n+ if (PICO_SDK_FETCH_FROM_GIT_PATH)\n+ get_filename_component(FETCHCONTENT_BASE_DIR \u0022${PICO_SDK_FETCH_FROM_GIT_PATH}\u0022 REALPATH BASE_DIR \u0022${CMAKE_SOURCE_DIR}\u0022)\n+ endif ()\n+ FetchContent_Declare(\n+ pico_sdk\n+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n+ GIT_TAG master\n+ )\n+ if (NOT pico_sdk)\n+ message(\u0022Downloading Raspberry Pi Pico SDK\u0022)\n+ FetchContent_Populate(pico_sdk)\n+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})\n+ endif ()\n+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})\n+ else ()\n+ message(FATAL_ERROR\n+ \u0022SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git.\u0022\n+ )\n+ endif ()\n+endif ()\n+\n+get_filename_component(PICO_SDK_PATH \u0022${PICO_SDK_PATH}\u0022 REALPATH BASE_DIR \u0022${CMAKE_BINARY_DIR}\u0022)\n+if (NOT EXISTS ${PICO_SDK_PATH})\n+ message(FATAL_ERROR \u0022Directory '${PICO_SDK_PATH}' not found\u0022)\n+endif ()\n+\n+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)\n+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})\n+ message(FATAL_ERROR \u0022Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK\u0022)\n+endif ()\n+\n+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH \u0022Path to the Raspberry Pi Pico SDK\u0022 FORCE)\n+\n+include(${PICO_SDK_INIT_CMAKE_FILE})\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/private.h b/minimal-examples/embedded/pico/pico-sspc-binance/private.h\nnew file mode 100644\nindex 0000000..11949b9\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/private.h\n@@ -0,0 +1,44 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by 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+\n+/* boilerplate for using PICO_SDK */\n+\n+#include \u003cstdio.h\u003e\n+#include \u0022pico/stdlib.h\u0022\n+#include \u0022pico/types.h\u0022\n+#include \u0022hardware/uart.h\u0022\n+#include \u0022hardware/irq.h\u0022\n+\n+/* boilerplate for LWS_ONLY_SSPC Secure Streams\n+ * LWS_SS_USE_SSPC should be defined by cmake\n+ */\n+\n+#undef STANDALONE\n+#define STANDALONE\n+#include \u003clibwebsockets.h\u003e\n+\n+#define RXBUF_SIZE 2048\n+\n+extern lws_dll2_owner_t scheduler;\n+extern uint16_t rxh, rxt;\n+extern uint8_t rxbuf[RXBUF_SIZE];\n+extern int rx_overflowed;\n+extern unsigned int actual_baud;\n+extern lws_transport_mux_t *tm;\n+\n+/* our transport related apis */\n+\n+extern int pico_example_open_serial_port(uart_inst_t * const port);\n+extern const lws_transport_client_ops_t lws_sss_ops_client_serial;\n+void serial_handle_events(lws_transport_mux_t *tm);\n+\n+/* our SS bindings */\n+\n+extern const lws_ss_info_t ssi_binance; /* binance-ss.c */\n+extern const lws_ss_info_t ssi_get;\t /* get-ss.c */\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/system.c b/minimal-examples/embedded/pico/pico-sspc-binance/system.c\nnew file mode 100644\nindex 0000000..69d1e78\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/system.c\n@@ -0,0 +1,134 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by 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+ *\n+ * These are apis used inside libwebsockets.a in LWS_ONLY_SSPC mode, that must\n+ * be wired up to the client host platform system apis, like its own logging\n+ *\n+ * lws_sul_schedule() - use system event loop to schedule event in the future\n+ * lws_sul_cancel() - rescind a scheduled event\n+ * lws_now_usecs() - unix time in microseconds\n+ * __lws_logv() - core logging function used by liblws-sspc\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003csys/types.h\u003e\n+#include \u003cerrno.h\u003e\n+\n+int log_level \u003d LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO;\n+\n+static const char * const colours[] \u003d {\n+\t\u0022[31;1m\u0022, /* LLL_ERR */\n+\t\u0022[36;1m\u0022, /* LLL_WARN */\n+\t\u0022[35;1m\u0022, /* LLL_NOTICE */\n+\t\u0022[32;1m\u0022, /* LLL_INFO */\n+\t\u0022[34;1m\u0022, /* LLL_DEBUG */\n+\t\u0022[33;1m\u0022, /* LLL_PARSER */\n+\t\u0022[33m\u0022, /* LLL_HEADER */\n+\t\u0022[33m\u0022, /* LLL_EXT */\n+\t\u0022[33m\u0022, /* LLL_CLIENT */\n+\t\u0022[33;1m\u0022, /* LLL_LATENCY */\n+ \u0022[0;1m\u0022, /* LLL_USER */\n+\t\u0022[31m\u0022, /* LLL_THREAD */\n+};\n+\n+static int\n+sul_compare(const lws_dll2_t *d, const lws_dll2_t *i)\n+{\n+\tlws_usec_t a \u003d ((lws_sorted_usec_list_t *)d)-\u003eus;\n+\tlws_usec_t b \u003d ((lws_sorted_usec_list_t *)i)-\u003eus;\n+\n+\t/*\n+\t * Simply returning (a - b) in an int\n+\t * may lead to an integer overflow bug\n+\t */\n+\n+\tif (a \u003e b)\n+\t\treturn 1;\n+\tif (a \u003c b)\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+void\n+lws_sul_schedule(struct lws_context_standalone *ctx, int tsi,\n+\t\t lws_sorted_usec_list_t *sul, sul_cb_t _cb, lws_usec_t _us)\n+{\n+\tif (_us \u003d\u003d (lws_usec_t)LWS_SET_TIMER_USEC_CANCEL) {\n+\t\tlws_sul_cancel(sul);\n+\t\treturn;\n+\t}\n+\n+\tlws_dll2_remove(\u0026sul-\u003elist);\n+\n+\tsul-\u003ecb \u003d _cb;\n+\tsul-\u003eus \u003d lws_now_usecs() + _us;\n+\n+\tlws_dll2_add_sorted(\u0026sul-\u003elist, \u0026scheduler, sul_compare);\n+}\n+\n+void\n+lws_sul_cancel(lws_sorted_usec_list_t *sul)\n+{\n+\tlws_dll2_remove(\u0026sul-\u003elist);\n+\tsul-\u003eus \u003d 0;\n+}\n+\n+lws_usec_t\n+lws_now_usecs(void)\n+{\n+\tabsolute_time_t at \u003d get_absolute_time();\n+\t\n+\treturn (lws_usec_t)to_us_since_boot(at);\n+}\n+\n+/*\n+ * wire up libwebsockets.a logs to native application logs, we just wire it up\n+ * to the device console in our case.\n+ *\n+ * We add the lws loglevel colour scheme ourselves.\n+ */\n+\n+void\n+__lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,\n+\t int filter, const char *_fun, const char *format, va_list ap)\n+{\n+\tint n, m \u003d LWS_ARRAY_SIZE(colours) - 1;\n+\tchar logbuf[200], *p \u003d logbuf, *e \u003d logbuf + sizeof(logbuf);\n+\n+\tif (!(filter \u0026 log_level))\n+\t\treturn;\n+\n+\tn \u003d 1 \u003c\u003c (LWS_ARRAY_SIZE(colours) - 1);\n+\twhile (n) {\n+\t\tif (filter \u0026 n)\n+\t\t\tbreak;\n+\t\tm--;\n+\t\tn \u003e\u003e\u003d 1;\n+\t}\n+\n+\tprintf(\u0022%llu: %c%s%s: \u0022, (unsigned long long)lws_now_usecs(), 27,\n+\t\t\tcolours[m], _fun);\n+\tif (prep \u0026\u0026 obj) {\n+\t\tprep(cx, obj, \u0026p, e);\n+\t\tprintf(\u0022%s: \u0022, logbuf);\n+\t}\n+\n+\tn \u003d vsnprintf(logbuf, sizeof(logbuf) - 2, format, ap);\n+\tif (n \u003e 0 \u0026\u0026 logbuf[n - 1] !\u003d '\u005cn') {\n+\t\tlogbuf[n++] \u003d '\u005cn';\n+\t\tlogbuf[n] \u003d '\u005c0';\n+\t}\n+\tprintf(\u0022%s%c[0m\u0022, logbuf, 27);\n+}\n+\n+\ndiff --git a/minimal-examples/embedded/pico/pico-sspc-binance/transport-serial.c b/minimal-examples/embedded/pico/pico-sspc-binance/transport-serial.c\nnew file mode 100644\nindex 0000000..3c73f82\n--- /dev/null\n+++ b/minimal-examples/embedded/pico/pico-sspc-binance/transport-serial.c\n@@ -0,0 +1,304 @@\n+/*\n+ * pico-sspc-binance\n+ *\n+ * Written in 2010-2021 by 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+ *\n+ * The serial port based custom transport, and helpers used by lws_transport\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+int uart_irq, irqc, need_pollout, rx_overflowed;\n+uint8_t rxbuf[RXBUF_SIZE], txbuf[RXBUF_SIZE];\n+uint16_t rxh, rxt, txh, txt;\n+unsigned int actual_baud;\n+uart_inst_t * uid;\n+\n+static void\n+on_uart_rx(void)\n+{\n+\tint budget \u003d 64;\n+\tirqc++;\n+\n+\twhile (uart_is_readable(uid) \u0026\u0026 budget--) {\n+ \trxbuf[rxh] \u003d uart_getc(uid);\n+\t\trxh \u003d (rxh + 1) \u0026 (sizeof(rxbuf) - 1);\n+\t\tif (rxt \u003d\u003d rxh)\n+\t\t\trx_overflowed++;\n+\t}\n+}\n+\n+/*\n+ * Open and configure the serial transport\n+ *\n+ * This had to be somewhat handrolled to use IRQ rx via the UART FIFOs, we\n+ * get triggered by irq to dump the rx fifo when it starts getting full, but\n+ * if there are only a few bytes coming, we don't get an irq and have to also\n+ * drain the fifo from the foreground.\n+ */\n+\n+int\n+pico_example_open_serial_port(uart_inst_t * const port)\n+{\n+\tuid \u003d port;\n+\n+\tuart_init(port, 2400);\n+\n+\tgpio_set_function(0, GPIO_FUNC_UART);\n+\tgpio_set_function(1, GPIO_FUNC_UART);\n+\n+\tactual_baud \u003d uart_set_baudrate(port, 2000000); // 921600);\n+\tuart_set_hw_flow(port, false, false);\n+\tuart_set_format(port, 8, 1, UART_PARITY_NONE);\n+\tuart_set_fifo_enabled(port, true);\n+\n+\tuart_irq \u003d port \u003d\u003d uart0 ? UART0_IRQ : UART1_IRQ;\n+\n+\tirq_set_exclusive_handler(uart_irq, on_uart_rx);\n+\tirq_set_enabled(uart_irq, true);\n+\tuart_set_irq_enables(port, true, false);\n+}\n+\n+/* incoming parsed channel cbs */\n+\n+static int\n+ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_opens_serial(lws_transport_mux_ch_t *tmc, int determination)\n+{\n+\tlws_transport_mux_t *tm \u003d lws_container_of(tmc-\u003elist.owner,\n+\t\t\t\t\t\t lws_transport_mux_t, owner);\n+\tstruct lws_sspc_handle *h \u003d (struct lws_sspc_handle *)tmc-\u003epriv;\n+\n+\tassert_is_tm(tm);\n+\n+\tlwsl_sspc_err(h, \u0022%d\u0022, determination);\n+\n+ \tif (tm-\u003einfo.txp_cpath.ops_in-\u003eevent_connect_disposition(h, determination))\n+ \t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_closes(lws_transport_mux_ch_t *tmc)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn 0;\n+}\n+\n+static void\n+ltm_txp_req_write(lws_transport_mux_t *tm)\n+{\n+\ttm-\u003einfo.txp_cpath.ops_onw-\u003ereq_write(tm-\u003einfo.txp_cpath.priv_onw);\n+}\n+\n+static int\n+ltm_txp_can_write(lws_transport_mux_ch_t *tmc)\n+{\n+\tassert_is_tmch(tmc);\n+\treturn lws_txp_inside_sspc.event_can_write(\n+\t\t\t(struct lws_sspc_handle *)tmc-\u003epriv, 2048);\n+}\n+\n+/*\n+ * So that we can use the same mux framing parser for both sides, we pass into\n+ * the parser an \u0022ops struct\u0022 that gets called back to customize response to\n+ * mux parser framing.\n+ */\n+\n+static const lws_txp_mux_parse_cbs_t cbs \u003d {\n+\t.payload\t\t\u003d ltm_ch_payload,\n+\t.ch_opens\t\t\u003d ltm_ch_opens_serial,\n+\t.ch_closes\t\t\u003d ltm_ch_closes,\n+\t.txp_req_write\t\t\u003d ltm_txp_req_write,\n+\t.txp_can_write\t\t\u003d ltm_txp_can_write,\n+};\n+\n+void\n+serial_handle_events(lws_transport_mux_t *tm)\n+{\n+\tint tbudget \u003d 32, rbudget \u003d 1024, pbudget \u003d 32;\n+\tuint8_t chonk[256];\n+\tsize_t cl \u003d 0;\n+\n+\t/*\n+\t * UART rx fifo doesn't interrupt until it's full, so drain anything we\n+\t * see lying around in there from the foreground loop as well as the\n+\t * fifo full interrupt\n+\t */\n+\n+\tirq_set_enabled(uart_irq, false);\n+\twhile (uart_is_readable(uid) \u0026\u0026 pbudget--) {\n+ \trxbuf[rxh] \u003d uart_getc(uid);\n+\t\trxh \u003d (rxh + 1) \u0026 (sizeof(rxbuf) - 1);\n+\t\tif (rxt \u003d\u003d rxh)\n+\t\t\trx_overflowed++;\n+\t}\n+\tirq_set_enabled(uart_irq, true);\n+\n+\t/* for POLLIN\n+\t *\n+\t * rxt (rx tail) and rxh (head) are offsets in a ringbuffer, the rx\n+\t * is harvested in one or two chunks depending on if it has wrapped in\n+\t * the ringbuffer yet or not.\n+\t */\n+\n+\tif (rxt \u003e rxh) {\n+\t\tcl \u003d sizeof(rxbuf) - rxt;\n+\t\tif (cl \u003e rbudget)\n+\t\t\tcl \u003d rbudget;\n+\t\t//lwsl_hexdump_level(LLL_NOTICE, rxbuf + rxt, cl);\n+\t\tif (tm-\u003einfo.txp_cpath.ops_in-\u003eevent_read(\n+\t\t\t\ttm-\u003einfo.txp_cpath.priv_in, rxbuf + rxt, cl)) {\n+\t\t\t/*\n+\t\t\t * The SSS parser can identify the framing is broken,\n+\t\t\t * in that case the transport needs to re-link up\n+\t\t\t */\n+\t\t\ttm-\u003einfo.txp_cpath.ops_in-\u003elost_coherence(\n+\t\t\t\t\ttm-\u003einfo.txp_cpath.priv_in);\n+\t\t\trxt \u003d rxh;\n+\t\t\ttxt \u003d txh;\n+\t\t\treturn;\n+\t\t}\n+\t\trbudget -\u003d cl;\n+\t\trxt \u003d (rxt + cl) \u0026 (sizeof(rxbuf) - 1);\n+\t}\n+\n+\tif (rbudget \u0026\u0026 rxt \u003c rxh) {\n+\t\tcl \u003d rxh - rxt;\n+\t\tif (cl \u003e rbudget)\n+\t\t\tcl \u003d rbudget;\n+\t\t//lwsl_hexdump_level(LLL_NOTICE, rxbuf + rxt, cl);\n+\t\tif (tm-\u003einfo.txp_cpath.priv_in) {\n+\t\t\t/* may have been zapped by lost_coherence already */\n+\t\t\tif (tm-\u003einfo.txp_cpath.ops_in-\u003eevent_read(\n+\t\t\t\ttm-\u003einfo.txp_cpath.priv_in, rxbuf + rxt, cl)) {\n+\t\t\t\t/*\n+\t\t\t\t * The SSS parser can identify the framing is broken,\n+\t\t\t\t * in that case the transport needs to re-link up\n+\t\t\t\t */\n+\t\t\t\ttm-\u003einfo.txp_cpath.ops_in-\u003elost_coherence(\n+\t\t\t\t\t\ttm-\u003einfo.txp_cpath.priv_in);\n+\t\t\t\trxt \u003d rxh;\n+\t\t\t\ttxt \u003d txh;\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t\trxt \u003d (rxt + cl) \u0026 (sizeof(rxbuf) - 1);\n+\t}\n+\n+\t/* for serial write drain */\n+\n+\twhile (txh !\u003d txt \u0026\u0026 uart_is_writable(uid) \u0026\u0026 tbudget--) {\n+\t\tuart_putc(uid, txbuf[txt]);\n+\t\ttxt \u003d (txt + 1) \u0026 (sizeof(txbuf) - 1);\n+\t}\n+\n+\t/* for \u0022POLLOUT\u0022 */\n+\n+\tif (need_pollout \u0026\u0026 txh \u003d\u003d txt) {\n+\t\tneed_pollout \u003d 0;\n+\t\tcl \u003d sizeof(chonk);\n+\n+\t\tif (lws_transport_mux_pending(tm, chonk, \u0026cl, \u0026cbs)) {\n+#if defined(_DEBUG)\n+\t\t\tlws_transport_path_client_dump(\u0026tm-\u003einfo.txp_cpath, \u0022cpath\u0022);\n+#endif\n+\t\t\ttm-\u003einfo.txp_cpath.ops_onw-\u003e_write(\n+\t\t\t\ttm-\u003einfo.txp_cpath.priv_onw, chonk, cl);\n+\n+\t\t\treturn;\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * We get called while an individual SS is trying to connect to the proxy to\n+ * be recognized as operational. It's the equivalent of trying to bring up the\n+ * Unix Domain socket\n+ */\n+\n+static int\n+txp_serial_retry_connect(lws_txp_path_client_t *path,\n+\t\t\t\t struct lws_sspc_handle *h)\n+{\n+\tlwsl_user(\u0022%s\u005cn\u0022, __func__);\n+\n+\tif (!path)\n+\t\treturn 0;\n+\n+\tif (path-\u003eops_onw-\u003eevent_connect_disposition(h,\n+\t\t\t\tpath-\u003emux-\u003elink_state !\u003d LWSTM_OPERATIONAL))\n+\t return -1;\n+\n+\treturn 0;\n+}\n+\n+static void\n+txp_serial_req_write(lws_transport_priv_t priv)\n+{\n+\tneed_pollout \u003d 1;\n+}\n+\n+static int\n+txp_serial_write(lws_transport_priv_t priv, uint8_t *buf, size_t len)\n+{\n+\tlwsl_notice(\u0022%s: writing %u\u005cn\u0022, __func__, (unsigned int)len);\n+\n+\tlwsl_hexdump_level(LLL_WARN, buf, len);\n+\n+\twhile (len--) {\n+\t\ttxbuf[txh] \u003d *buf++;\n+\t\ttxh \u003d (txh + 1) \u0026 (sizeof(txbuf) - 1);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+txp_serial_close(lws_transport_priv_t priv)\n+{\n+#if 0\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (!wsi)\n+\t\treturn;\n+\n+\tlws_set_opaque_user_data(wsi, NULL);\n+\tlws_wsi_close(wsi, LWS_TO_KILL_ASYNC);\n+\t*priv \u003d NULL;\n+#endif\n+}\n+\n+static void\n+txp_serial_stream_up(lws_transport_priv_t priv)\n+{\n+//\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+//\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n+}\n+\n+/*\n+ * This is the lws_transport export for our custom serial transport\n+ */\n+\n+const lws_transport_client_ops_t lws_sss_ops_client_serial \u003d {\n+\t.name\t\t\t\u003d \u0022txpserial\u0022,\n+\t.event_retry_connect\t\u003d txp_serial_retry_connect,\n+\t.req_write\t\t\u003d txp_serial_req_write,\n+\t._write\t\t\t\u003d txp_serial_write,\n+\t._close\t\t\t\u003d txp_serial_close,\n+\t.event_stream_up\t\u003d txp_serial_stream_up,\n+\t.flags\t\t\t\u003d LWS_DSHFLAG_ENABLE_COALESCE,\n+\t.dsh_splitat\t\t\u003d 0,\n+};\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-binance/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-binance/CMakeLists.txt\nindex e1f3647..5348bdb 100644\n--- a/minimal-examples/secure-streams/minimal-secure-streams-binance/CMakeLists.txt\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-binance/CMakeLists.txt\n@@ -24,4 +24,19 @@ if (requirements)\n \telse()\n \t\ttarget_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})\n \tendif()\n+\n+\tif (HAS_LWS_WITH_SECURE_STREAMS_PROXY_API OR LWS_WITH_SECURE_STREAMS_PROXY_API)\n+\n+\t\tadd_compile_options(-DLWS_SS_USE_SSPC)\n+\t\tadd_executable(${SAMP}-client ${SRCS})\n+\n+\t\tif (websockets_shared)\n+\t\t\ttarget_link_libraries(${SAMP}-client websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})\n+\t\t\tadd_dependencies(${SAMP}-client websockets_shared)\n+\t\telse()\n+\t\t\ttarget_link_libraries(${SAMP}-client websockets ${LIBWEBSOCKETS_DEP_LIBS})\n+\t\tendif()\n+\tendif()\n+\n+\n endif()\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-binance/main.c b/minimal-examples/secure-streams/minimal-secure-streams-binance/main.c\nindex 8327f3e..150a201 100644\n--- a/minimal-examples/secure-streams/minimal-secure-streams-binance/main.c\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-binance/main.c\n@@ -28,6 +28,7 @@\n #include \u003cctype.h\u003e\n \n static int interrupted;\n+extern const struct lws_protocols lws_sspc_protocols[2];\n \n typedef struct range {\n \tuint64_t\t\tsum;\n@@ -243,6 +244,9 @@ int main(int argc, const char **argv)\n \tinfo.fd_limit_per_thread \u003d 1 + 1 + 1;\n \tinfo.extensions \u003d extensions;\n \tinfo.pss_policies_json \u003d \u0022policy.json\u0022; /* literal JSON, or path */\n+#if defined(LWS_SS_USE_SSPC)\n+\tinfo.protocols \u003d lws_sspc_protocols;\n+#endif\n \n \tcx \u003d lws_create_context(\u0026info);\n \tif (!cx) {\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-blob/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-blob/CMakeLists.txt\nindex 7fc59da..ceb2d15 100644\n--- a/minimal-examples/secure-streams/minimal-secure-streams-blob/CMakeLists.txt\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-blob/CMakeLists.txt\n@@ -91,9 +91,9 @@ if (requirements)\n \t\t\t\tmessage(\u0022testing via valgrind\u0022)\n \t\t\t\tadd_test(NAME sspcblob-minimal COMMAND\n \t\t\t\t\t${VALGRIND} --tool\u003dmemcheck --leak-check\u003dyes --num-callers\u003d20\n-\t\t\t\t\t$\u003cTARGET_FILE:lws-minimal-secure-streams-client\u003e -i +${CTEST_SOCKET_PATH})\n+\t\t\t\t\t$\u003cTARGET_FILE:lws-minimal-secure-streams-blob-client\u003e -i +${CTEST_SOCKET_PATH} --timeout_ms 65000)\n \t\t\telse()\n-\t\t\t\tadd_test(NAME sspcblob-minimal COMMAND lws-minimal-secure-streams-client -i +${CTEST_SOCKET_PATH})\n+\t\t\t\tadd_test(NAME sspcblob-minimal COMMAND lws-minimal-secure-streams-blob-client -i +${CTEST_SOCKET_PATH} --timeout_ms 65000)\n \t\t\tendif()\n \t\t\t\n \t\t\tset(fixlist \u0022ssblobproxy\u0022)\n@@ -102,9 +102,9 @@ if (requirements)\n \t\t\tendif()\n \t\t\t\n \t\t\tset_tests_properties(sspcblob-minimal PROPERTIES\n-\t\t\t\tWORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams\n+\t\t\t\tWORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams-blob\n \t\t\t\tFIXTURES_REQUIRED \u0022${fixlist}\u0022\n-\t\t\t\tTIMEOUT 40)\n+\t\t\t\tTIMEOUT 70)\n \n \t\tendif()\n \ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/CMakeLists.txt\nnew file mode 100644\nindex 0000000..723908c\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/CMakeLists.txt\n@@ -0,0 +1,23 @@\n+project(lws-minimal-secure-streams-custom-client-transport C)\n+cmake_minimum_required(VERSION 2.8.12)\n+find_package(libwebsockets CONFIG REQUIRED)\n+list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})\n+include(CheckIncludeFile)\n+include(CheckCSourceCompiles)\n+include(LwsCheckRequirements)\n+\n+set(SAMP lws-minimal-secure-streams-custom-client-transport)\n+set(SRCS main.c binance-ss.c transport-serial.c app-event-loop.c system.c)\n+\n+set(requirements 1)\n+require_lws_config(LWS_WITH_CLIENT 1 requirements)\n+require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)\n+require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements)\n+require_lws_config(LWS_ONLY_SSPC 1 requirements)\n+\n+if (requirements)\n+\tadd_executable(${SAMP} ${SRCS})\n+\tadd_compile_options(-DLWS_SS_USE_SSPC)\n+\n+\ttarget_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})\n+endif()\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/README.md b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/README.md\nnew file mode 100644\nindex 0000000..5155ddd\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/README.md\n@@ -0,0 +1,56 @@\n+# lws minimal secure streams binance\n+\n+This is a Secure Streams version of minimal-ws-client-binance.\n+\n+\u0022policy.json\u0022 contains all the information about endpoints, protocols and\n+connection validation, tagged by streamtype name.\n+\n+The example tries to load it from the cwd, it lives in\n+./minimal-examples/secure-streams/minimal-secure-streams-binance dir, so\n+either run it from there, or copy the policy.json to your cwd. It's also\n+possible to put the policy json in the code as a string and pass that at\n+context creation time.\n+\n+The secure stream object represents a nailed-up connection that outlives any\n+single socket connection, and can manage reconnections / retries according to\n+the policy to keep the connection nailed up automatically.\n+\n+Secure Streams provides the same simplified communication api without any\n+protocol dependencies.\n+\n+## build\n+\n+Lws must have been built with `LWS_ROLE_WS\u003d1`, `LWS_WITH_SECURE_STREAMS\u003d1`, and\n+`LWS_WITHOUT_EXTENSIONS\u003d0`\n+\n+```\n+ $ cmake . \u0026\u0026 make\n+```\n+\n+## Commandline Options\n+\n+Option|Meaning\n+---|---\n+-d|Set logging verbosity\n+\n+## usage\n+\n+```\n+$ ./bin/lws-minimal-ws-client-binance \n+[2021/08/15 06:42:40:8409] U: LWS minimal Secure Streams binance client\n+[2021/08/15 06:42:40:8410] N: LWS: 4.2.99-v4.2.0-156-g8f352f65e8, NET CLI SRV H1 H2 WS SS-JSON-POL SSPROX ConMon FLTINJ IPV6-on\n+[2021/08/15 06:42:40:8410] N: ++ [495958|wsi|0|pipe] (1)\n+[2021/08/15 06:42:40:8411] N: ++ [495958|vh|0|netlink] (1)\n+[2021/08/15 06:42:40:8433] N: ++ [495958|vh|1|digicert||-1] (2)\n+[2021/08/15 06:42:40:8471] N: ++ [495958|wsiSScli|0|binance] (1)\n+[2021/08/15 06:42:40:8471] N: [495958|wsiSScli|0|binance]: lws_ss_check_next_state_ss: (unset) -\u003e LWSSSCS_CREATING\n+[2021/08/15 06:42:40:8472] N: [495958|wsiSScli|0|binance]: lws_ss_check_next_state_ss: LWSSSCS_CREATING -\u003e LWSSSCS_CONNECTING\n+[2021/08/15 06:42:40:8472] N: ++ [495958|wsicli|0|WS/h1/fstream.binance.com/([495958|wsiSScli|0|binance])] (1)\n+[2021/08/15 06:42:41:8802] N: [495958|wsiSScli|0|binance]: lws_ss_check_next_state_ss: LWSSSCS_CONNECTING -\u003e LWSSSCS_CONNECTED\n+[2021/08/15 06:42:42:8803] N: sul_hz_cb: price: min: 4669185¢, max: 4672159¢, avg: 4670061¢, (53 prices/s)\n+[2021/08/15 06:42:42:8803] N: sul_hz_cb: elatency: min: 131ms, max: 292ms, avg: 154ms, (53 msg/s)\n+[2021/08/15 06:42:43:8803] N: sul_hz_cb: price: min: 4669646¢, max: 4672159¢, avg: 4669953¢, (34 prices/s)\n+[2021/08/15 06:42:43:8803] N: sul_hz_cb: elatency: min: 130ms, max: 149ms, avg: 133ms, (34 msg/s)\n+[2021/08/15 06:42:44:8804] N: sul_hz_cb: price: min: 4669455¢, max: 4672159¢, avg: 4669904¢, (26 prices/s)\n+...\n+```\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/app-event-loop.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/app-event-loop.c\nnew file mode 100644\nindex 0000000..b3e6712\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/app-event-loop.c\n@@ -0,0 +1,162 @@\n+/*\n+ * lws-minimal-secure-streams-custom-proxy-transport\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ * Kutoga \u003ckutoga@user.github.invalid\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ *\n+ *\n+ * This represents some existing application event loop that liblws-sspc must\n+ * cooperate with.\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+custom_poll_ctx_t a_cpcx;\n+\n+static struct pollfd *\n+custom_poll_find_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd)\n+{\n+\tint n;\n+\n+\tfor (n \u003d 0; n \u003c cpcx-\u003ecount_pollfds; n++)\n+\t\tif (cpcx-\u003epollfds[n].fd \u003d\u003d fd)\n+\t\t\treturn \u0026cpcx-\u003epollfds[n];\n+\n+\treturn NULL;\n+}\n+\n+int\n+custom_poll_add_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd, int events,\n+\t\t void *priv)\n+{\n+\tstruct pollfd *pfd;\n+\n+\tlwsl_info(\u0022%s: ADD fd %d, ev %d\u005cn\u0022, __func__, fd, events);\n+\n+\tpfd \u003d custom_poll_find_fd(cpcx, fd);\n+\tif (pfd) {\n+\t\tlwsl_err(\u0022%s: ADD fd %d already in ext table\u005cn\u0022, __func__, fd);\n+\t\treturn 1;\n+\t}\n+\n+\tif (cpcx-\u003ecount_pollfds \u003d\u003d LWS_ARRAY_SIZE(cpcx-\u003epollfds)) {\n+\t\tlwsl_err(\u0022%s: no room left\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\n+\tcpcx-\u003epriv[cpcx-\u003ecount_pollfds] \u003d priv;\n+\tpfd \u003d \u0026cpcx-\u003epollfds[cpcx-\u003ecount_pollfds++];\n+\tpfd-\u003efd \u003d fd;\n+\tpfd-\u003eevents \u003d (short)events;\n+\tpfd-\u003erevents \u003d 0;\n+\n+\treturn 0;\n+}\n+\n+int\n+custom_poll_del_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd)\n+{\n+\tstruct pollfd *pfd;\n+\n+\tlwsl_info(\u0022%s: DEL fd %d\u005cn\u0022, __func__, fd);\n+\n+\tpfd \u003d custom_poll_find_fd(cpcx, fd);\n+\tif (!pfd) {\n+\t\tlwsl_err(\u0022%s: DEL fd %d missing in ext table\u005cn\u0022, __func__, fd);\n+\t\treturn 1;\n+\t}\n+\n+\tif (cpcx-\u003ecount_pollfds \u003e 1)\n+\t\t*pfd \u003d cpcx-\u003epollfds[cpcx-\u003ecount_pollfds - 1];\n+\n+\tcpcx-\u003ecount_pollfds--;\n+\n+\treturn 0;\n+}\n+\n+int\n+custom_poll_change_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd,\n+\t\t int events_add, int events_remove)\n+{\n+\tstruct pollfd *pfd;\n+\n+\tlwsl_info(\u0022%s: CHG fd %d, ev_add %d, ev_rem %d\u005cn\u0022, __func__, fd,\n+\t\t\tevents_add, events_remove);\n+\n+\tpfd \u003d custom_poll_find_fd(cpcx, fd);\n+\tif (!pfd)\n+\t\treturn 1;\n+\n+\tpfd-\u003eevents \u003d (short)((pfd-\u003eevents \u0026 (~events_remove)) | events_add);\n+\n+\treturn 0;\n+}\n+\n+int\n+custom_poll_run(custom_poll_ctx_t *cpcx)\n+{\n+\tint n;\n+\n+\twhile (!interrupted) {\n+\n+\t\tlws_usec_t timeout_us \u003d 2000000000, now \u003d lws_now_usecs();\n+\n+\t\tif (cpcx-\u003escheduler.count) {\n+\t\t\tlws_sorted_usec_list_t *sul \u003d (lws_sorted_usec_list_t *)\n+\t\t\t\t\tlws_dll2_get_head(\u0026cpcx-\u003escheduler);\n+\t\t\tif (sul-\u003eus \u003c now)\n+\t\t\t\ttimeout_us \u003d 0;\n+\t\t\telse\n+\t\t\t\ttimeout_us \u003d sul-\u003eus - now;\n+\t\t}\n+\n+//\t\tlwsl_notice(\u0022%s: entering poll wait %dms\u005cn\u0022, __func__, (int)(timeout_us / 1000));\n+\n+\t\tn \u003d poll(cpcx-\u003epollfds, (nfds_t)cpcx-\u003ecount_pollfds, (int)(timeout_us / 1000));\n+\n+//\t\tlwsl_notice(\u0022%s: exiting poll after %lluus\u005cn\u0022, __func__,\n+//\t\t\t\t(unsigned long long)(lws_now_usecs() - now));\n+\n+\t\tdo {\n+\t\t\tlws_sorted_usec_list_t *sul \u003d (lws_sorted_usec_list_t *)\n+\t\t\t\tlws_dll2_get_head(\u0026cpcx-\u003escheduler);\n+\n+\t\t\tif (!sul)\n+\t\t\t\tbreak;\n+\n+\t\t\tif (sul-\u003eus \u003e now)\n+\t\t\t\tbreak;\n+\n+\t\t\tlws_dll2_remove(\u0026sul-\u003elist);\n+\t\t\tsul-\u003ecb(sul);\n+\t\t} while (1);\n+\n+\t\tif (n \u003c\u003d 0)\n+\t\t\tcontinue;\n+\n+\t\t/* service anything that has active revents */\n+\n+\t\tfor (n \u003d 0; n \u003c cpcx-\u003ecount_pollfds; n++) {\n+\t\t\tint m;\n+\n+\t\t\tif (!cpcx-\u003epollfds[n].revents)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * the only fd we registered in this example is the\n+\t\t\t * transport fd, so we miss out the code to match the\n+\t\t\t * fd to the right callback\n+\t\t\t */\n+\n+\t\t\tm \u003d custom_transport_event(\u0026cpcx-\u003epollfds[n], cpcx-\u003epriv[n]);\n+\t\t\tif (m \u003c 0) {\n+\t\t\t\tcustom_poll_del_fd(cpcx, cpcx-\u003epollfds[n].fd);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/binance-ss.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/binance-ss.c\nnew file mode 100644\nindex 0000000..3c458f5\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/binance-ss.c\n@@ -0,0 +1,312 @@\n+/*\n+ * lws-minimal-secure-streams-custom-proxy-transport\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ * Kutoga \u003ckutoga@user.github.invalid\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ *\n+ *\n+ * This is a version of minimal-secure-streams-binance that uses a custom\n+ * SS Serialization transport.\n+ *\n+ * Because this links against the cut-down liblws-sspc instead of libwebsockets,\n+ */\n+\n+#define LWS_SS_USE_SSPC\n+\n+/* We use the lws headers, but we link against liblws-sspc, not libwebsockets */\n+#include \u003clibwebsockets.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003cctype.h\u003e\n+\n+typedef struct range {\n+\tuint64_t\t\tsum;\n+\tuint64_t\t\tlowest;\n+\tuint64_t\t\thighest;\n+\n+\tunsigned int\t\tsamples;\n+} range_t;\n+\n+typedef struct binance {\n+\tstruct lws_ss_handle \t*ss;\n+\tvoid\t\t\t*opaque_data;\n+\n+\tlws_sorted_usec_list_t\tsul_hz;\t /* 1hz summary dump */\n+\tchar\t\t\tmsgbuf[10240];\n+\tsize_t\t\t\tmsg_len;\n+\n+\trange_t\t\t\te_lat_range;\n+\trange_t\t\t\tprice_range;\n+} binance_t;\n+\n+/*\n+ * Since we don't link to libwebsockets library, we need to bring in our own\n+ * copies of any lws apis we use in the user Binance SS code\n+ */\n+\n+const char *\n+lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl)\n+{\n+\tconst char *end \u003d buf + len - nl + 1;\n+\tsize_t n;\n+\n+\tif (nl \u003e len)\n+\t\t/* it cannot be found if the needle is longer than the haystack */\n+\t\treturn NULL;\n+\n+\twhile (buf \u003c end) {\n+\t\tif (*buf !\u003d name[0]) {\n+\t\t\tbuf++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (nl \u003d\u003d 1)\n+\t\t\t/* single char match, we are done */\n+\t\t\treturn buf;\n+\n+\t\tif (buf[nl - 1] \u003d\u003d name[nl - 1]) {\n+\t\t\t/*\n+\t\t\t * This is looking interesting then... the first\n+\t\t\t * and last chars match, let's check the insides\n+\t\t\t */\n+\t\t\tn \u003d 1;\n+\t\t\twhile (n \u003c nl \u0026\u0026 buf[n] \u003d\u003d name[n])\n+\t\t\t\tn++;\n+\n+\t\t\tif (n \u003d\u003d nl)\n+\t\t\t\t/* it's a hit */\n+\t\t\t\treturn buf;\n+\t\t}\n+\n+\t\tbuf++;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+\n+const char *\n+lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen)\n+{\n+\tsize_t nl \u003d strlen(name);\n+\tconst char *np \u003d lws_nstrstr(buf, len, name, nl),\n+\t\t *end \u003d buf + len, *as;\n+\tint qu \u003d 0;\n+\n+\tif (!np)\n+\t\treturn NULL;\n+\n+\tnp +\u003d nl;\n+\n+\twhile (np \u003c end \u0026\u0026 (*np \u003d\u003d ' ' || *np \u003d\u003d '\u005ct'))\n+\t\tnp++;\n+\n+\tif (np \u003e\u003d end)\n+\t\treturn NULL;\n+\n+\t/*\n+\t * The arg could be lots of things after \u0022name\u0022: with JSON, commonly a\n+\t * string like \u0022mystring\u0022, true, false, null, [...] or {...} ... we want\n+\t * to handle common, simple cases cheaply with this; the user can choose\n+\t * a full JSON parser like lejp if it's complicated. So if no opening\n+\t * quote, return until a terminator like , ] }. If there's an opening\n+\t * quote, return until closing quote, handling escaped quotes.\n+\t */\n+\n+\tif (*np \u003d\u003d '\u005c\u0022') {\n+\t\tqu \u003d 1;\n+\t\tnp++;\n+\t}\n+\n+\tas \u003d np;\n+\twhile (np \u003c end \u0026\u0026\n+\t (!qu || *np !\u003d '\u005c\u0022') \u0026\u0026 /* end quote is EOT if quoted */\n+\t (qu || (*np !\u003d '}' \u0026\u0026 *np !\u003d ']' \u0026\u0026 *np !\u003d ',')) /* delimiters */\n+\t) {\n+\t\tif (qu \u0026\u0026 *np \u003d\u003d '\u005c\u005c') /* skip next char if quoted escape */\n+\t\t\tnp++;\n+\t\tnp++;\n+\t}\n+\n+\t*alen \u003d (unsigned int)lws_ptr_diff(np, as);\n+\n+\treturn as;\n+}\n+\n+/*\n+ * Rest of the file is Binance application SS processing (UNCHANGED from\n+ * minimal-secure-streams-binance)\n+ */\n+\n+static void\n+range_reset(range_t *r)\n+{\n+\tr-\u003esum \u003d r-\u003ehighest \u003d 0;\n+\tr-\u003elowest \u003d 999999999999ull;\n+\tr-\u003esamples \u003d 0;\n+}\n+\n+static uint64_t\n+get_us_timeofday(void)\n+{\n+\tstruct timeval tv;\n+\n+\tgettimeofday(\u0026tv, NULL);\n+\n+\treturn (uint64_t)((lws_usec_t)tv.tv_sec * LWS_US_PER_SEC) +\n+\t\t\t (uint64_t)tv.tv_usec;\n+}\n+\n+static uint64_t\n+pennies(const char *s)\n+{\n+\tuint64_t price \u003d (uint64_t)atoll(s) * 100;\n+\n+\ts \u003d strchr(s, '.');\n+\n+\tif (s \u0026\u0026 isdigit(s[1]) \u0026\u0026 isdigit(s[2]))\n+\t\tprice \u003d price + (uint64_t)((10 * (s[1] - '0')) + (s[2] - '0'));\n+\n+\treturn price;\n+}\n+\n+static void\n+sul_hz_cb(lws_sorted_usec_list_t *sul)\n+{\n+\tbinance_t *bin \u003d lws_container_of(sul, binance_t, sul_hz);\n+\n+\t/*\n+\t * We are called once a second to dump statistics on the connection\n+\t */\n+\n+\tlws_sul_schedule(lws_ss_get_context(bin-\u003ess), 0, \u0026bin-\u003esul_hz,\n+\t\t\t sul_hz_cb, LWS_US_PER_SEC);\n+\n+\tif (bin-\u003eprice_range.samples)\n+\t\tlwsl_user(\u0022%s: price: min: %llu¢, max: %llu¢, avg: %llu¢, \u0022\n+\t\t\t \u0022(%d prices/s)\u005cn\u0022, __func__,\n+\t\t\t (unsigned long long)bin-\u003eprice_range.lowest,\n+\t\t\t (unsigned long long)bin-\u003eprice_range.highest,\n+\t\t\t (unsigned long long)(bin-\u003eprice_range.sum /\n+\t\t\t\t\t\t bin-\u003eprice_range.samples),\n+\t\t\t bin-\u003eprice_range.samples);\n+\tif (bin-\u003ee_lat_range.samples)\n+\t\tlwsl_user(\u0022%s: elatency: min: %llums, max: %llums, \u0022\n+\t\t\t \u0022avg: %llums, (%d msg/s)\u005cn\u0022, __func__,\n+\t\t\t (unsigned long long)bin-\u003ee_lat_range.lowest / 1000,\n+\t\t\t (unsigned long long)bin-\u003ee_lat_range.highest / 1000,\n+\t\t\t (unsigned long long)(bin-\u003ee_lat_range.sum /\n+\t\t\t\t\t bin-\u003ee_lat_range.samples) / 1000,\n+\t\t\t bin-\u003ee_lat_range.samples);\n+\n+\trange_reset(\u0026bin-\u003ee_lat_range);\n+\trange_reset(\u0026bin-\u003eprice_range);\n+}\n+\n+static lws_ss_state_return_t\n+binance_rx(void *userobj, const uint8_t *in, size_t len, int flags)\n+{\n+\tbinance_t *bin \u003d (binance_t *)userobj;\n+\tuint64_t latency_us, now_us;\n+\tchar numbuf[16];\n+\tuint64_t price;\n+\tconst char *p;\n+\tsize_t alen;\n+\n+\tif (flags \u0026 LWSSS_FLAG_SOM)\n+\t\tbin-\u003emsg_len \u003d 0;\n+\n+\tif (bin-\u003emsg_len + len \u003c sizeof(bin-\u003emsgbuf)) {\n+\t\tmemcpy(bin-\u003emsgbuf + bin-\u003emsg_len, in, len);\n+\t\tbin-\u003emsg_len +\u003d len;\n+\t}\n+\n+\t/* assemble a full message */\n+\tif (!(flags \u0026 LWSSS_FLAG_EOM))\n+\t\treturn LWSSSSRET_OK;\n+\n+\t//lwsl_notice(\u0022%s: chunk len %d\u005cn\u0022, __func__, (int)len);\n+\n+\tnow_us \u003d (uint64_t)get_us_timeofday();\n+\n+\tp \u003d lws_json_simple_find(bin-\u003emsgbuf, bin-\u003emsg_len, \u0022\u005c\u0022depthUpdate\u005c\u0022\u0022,\n+\t\t\t\t \u0026alen);\n+\tif (!p)\n+\t\treturn LWSSSSRET_OK;\n+\n+\tp \u003d lws_json_simple_find(bin-\u003emsgbuf, bin-\u003emsg_len, \u0022\u005c\u0022E\u005c\u0022:\u0022, \u0026alen);\n+\tif (!p) {\n+\t\tlwsl_err(\u0022%s: no E JSON\u005cn\u0022, __func__);\n+\t\treturn LWSSSSRET_OK;\n+\t}\n+\n+\tlws_strnncpy(numbuf, p, alen, sizeof(numbuf));\n+\tlatency_us \u003d now_us - ((uint64_t)atoll(numbuf) * LWS_US_PER_MS);\n+\n+\tif (latency_us \u003c bin-\u003ee_lat_range.lowest)\n+\t\tbin-\u003ee_lat_range.lowest \u003d latency_us;\n+\tif (latency_us \u003e bin-\u003ee_lat_range.highest)\n+\t\tbin-\u003ee_lat_range.highest \u003d latency_us;\n+\n+\tbin-\u003ee_lat_range.sum +\u003d latency_us;\n+\tbin-\u003ee_lat_range.samples++;\n+\n+\tp \u003d lws_json_simple_find(bin-\u003emsgbuf, bin-\u003emsg_len, \u0022\u005c\u0022a\u005c\u0022:[[\u005c\u0022\u0022, \u0026alen);\n+\tif (!p)\n+\t\treturn LWSSSSRET_OK;\n+\n+\tlws_strnncpy(numbuf, p, alen, sizeof(numbuf));\n+\tprice \u003d pennies(numbuf);\n+\n+\tif (price \u003c bin-\u003eprice_range.lowest)\n+\t\tbin-\u003eprice_range.lowest \u003d price;\n+\tif (price \u003e bin-\u003eprice_range.highest)\n+\t\tbin-\u003eprice_range.highest \u003d price;\n+\n+\tbin-\u003eprice_range.sum +\u003d price;\n+\tbin-\u003eprice_range.samples++;\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+static lws_ss_state_return_t\n+binance_state(void *userobj, void *h_src, lws_ss_constate_t state,\n+\t lws_ss_tx_ordinal_t ack)\n+{\n+\tbinance_t *bin \u003d (binance_t *)userobj;\n+\n+\tlwsl_ss_info(bin-\u003ess, \u0022%s (%d), ord 0x%x\u0022,\n+\t\t lws_ss_state_name((int)state), state, (unsigned int)ack);\n+\n+\tswitch (state) {\n+\n+\tcase LWSSSCS_CONNECTED:\n+\t\tlws_sul_schedule(lws_ss_get_context(bin-\u003ess), 0, \u0026bin-\u003esul_hz,\n+\t\t\t\t sul_hz_cb, LWS_US_PER_SEC);\n+\t\trange_reset(\u0026bin-\u003ee_lat_range);\n+\t\trange_reset(\u0026bin-\u003eprice_range);\n+\n+\t\treturn LWSSSSRET_OK;\n+\n+\tcase LWSSSCS_DISCONNECTED:\n+\t\tlws_sul_cancel(\u0026bin-\u003esul_hz);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn LWSSSSRET_OK;\n+}\n+\n+const lws_ss_info_t ssi_binance \u003d {\n+\t.handle_offset\t\t \u003d offsetof(binance_t, ss),\n+\t.opaque_user_data_offset \u003d offsetof(binance_t, opaque_data),\n+\t.rx\t\t\t \u003d binance_rx,\n+\t.state\t\t\t \u003d binance_state,\n+\t.user_alloc\t\t \u003d sizeof(binance_t),\n+\t.streamtype\t\t \u003d \u0022binance\u0022, /* bind to corresponding policy */\n+};\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/main.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/main.c\nnew file mode 100644\nindex 0000000..772075e\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/main.c\n@@ -0,0 +1,114 @@\n+/*\n+ * lws-minimal-secure-streams-custom-client-transport\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ * Kutoga \u003ckutoga@user.github.invalid\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ *\n+ *\n+ * This is a version of minimal-secure-streams-binance that uses a custom\n+ * SS Serialization transport.\n+ *\n+ * Lws provides a wsi-based SS serialization transport, so you can connect to\n+ * SS proxy over tcp or Unix Domain Sockets. This example shows how to create\n+ * SS proxy clients with no dependency on libwebsockets library.\n+ *\n+ * libwebsockets header is used, but the application does not have an\n+ * lws_context and does not link against libwebsockets, instead using a much\n+ * smaller SSPC-only library liblws-sspc (built during lws build).\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+int interrupted;\n+int transport_fd;\n+\n+/*\n+ * Apps that bind to liblws-sspc have a fake lws_context with a couple of\n+ * members in it, there is no lws_create_context, it's so trivial you can\n+ * make your own like below.\n+ *\n+ * The is to retain the same SS apis that expect an lws_context, and also the\n+ * place where we bind to the transport to be used\n+ */\n+\n+static struct lws_context_standalone cx \u003d {\n+\t.txp_cpath.ops_onw\t\t\u003d \u0026lws_transport_mux_client_ops,\n+};\n+\n+\n+\n+static void\n+sigint_handler(int sig)\n+{\n+\tinterrupted \u003d 1;\n+}\n+\n+lws_transport_info_t info_serial \u003d {\n+\t.ping_interval_us\t\t\u003d LWS_US_PER_SEC * 10,\n+\t.pong_grace_us\t\t\t\u003d LWS_US_PER_SEC * 2,\n+\t.flags\t\t\t\t\u003d 0,\n+}, info_mux \u003d { /* onward transport for mux is serial */\n+\t.ping_interval_us\t\t\u003d LWS_US_PER_SEC * 10,\n+\t.pong_grace_us\t\t\t\u003d LWS_US_PER_SEC * 2,\n+\t.txp_cpath \u003d {\n+\t\t.ops_onw\t\t\u003d \u0026lws_sss_ops_client_serial,\n+\t\t.ops_in\t\t\t\u003d \u0026lws_transport_mux_client_ops,\n+\t},\n+\t.onward_txp_info\t\t\u003d \u0026info_serial,\n+\t.flags\t\t\t\t\u003d 0,\n+};\n+\n+int\n+main(int argc, const char **argv)\n+{\n+\tstruct lws_ss_handle *h \u003d NULL;\n+\n+\tsignal(SIGINT, sigint_handler);\n+\n+\tlwsl_user(\u0022LWS minimal Secure Streams binance client / custom SS proxy transport\u005cn\u0022);\n+\n+\t/* open the transport itself... only one of these */\n+\n+\ttransport_fd \u003d open_transport_file(\u0026a_cpcx, \u0022/dev/ttyUSB1\u0022, NULL);\n+\tif (transport_fd \u003c 0) {\n+\t\tlwsl_err(\u0022%s: failed to open custom transport tty\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\n+\t/* create the mux object itself... only one of these */\n+\n+\ta_cpcx.tm \u003d lws_transport_mux_create(\u0026cx, \u0026info_mux, NULL);\n+\tif (!a_cpcx.tm) {\n+\t\tlwsl_err(\u0022%s: unable to create client mux\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\ta_cpcx.tm-\u003einfo.txp_cpath.priv_in \u003d a_cpcx.tm;\n+\tcx.txp_cpath.mux \u003d a_cpcx.tm;\n+\n+\t/*\n+\t * Now that's done, create the SS and it will try to connect over the\n+\t * mux -\u003e transport -\u003e proxy\n+\t */\n+\n+\tif (lws_ss_create(\u0026cx, 0, \u0026ssi_binance, NULL, \u0026h, NULL, NULL)) {\n+\t\tprintf(\u0022failed to create secure stream\u005cn\u0022);\n+\t\tinterrupted \u003d 1;\n+\t}\n+\n+\n+\tcustom_poll_run(\u0026a_cpcx);\n+\n+\tif (h)\n+\t\tlws_ss_destroy(\u0026h);\n+\n+\tlws_transport_mux_destroy(\u0026a_cpcx.tm);\n+\n+\tprintf(\u0022Completed\u005cn\u0022);\n+\n+\tclose(transport_fd);\n+\n+\treturn 0;\n+}\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/policy.json b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/policy.json\nnew file mode 100644\nindex 0000000..1ff4e04\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/policy.json\n@@ -0,0 +1,38 @@\n+{\n+\t\u0022release\u0022: \u002201234567\u0022,\n+\t\u0022product\u0022: \u0022myproduct\u0022,\n+\t\u0022schema-version\u0022: 1,\n+\t\u0022retry\u0022: [{\n+\t\t\u0022default\u0022: {\n+\t\t\t\u0022backoff\u0022: [1000, 2000, 3000, 4000, 5000],\n+\t\t\t\u0022conceal\u0022: 65535,\n+\t\t\t\u0022jitterpc\u0022: 20,\n+\t\t\t\u0022svalidping\u0022: 30,\n+\t\t\t\u0022svalidhup\u0022: 35\n+\t\t}\n+\t}],\n+\t\u0022certs\u0022: [{\n+\t\t\u0022digicert_global_root\u0022: \u0022MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQkCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4\u003d\u0022\n+ }\n+\t],\n+\t\t\u0022trust_stores\u0022: [{\n+\t\t\t\u0022name\u0022: \u0022digicert\u0022,\n+\t\t\t\u0022stack\u0022: [\u0022digicert_global_root\u0022]\n+\t\t}\n+\t],\n+\t\u0022s\u0022: [\n+\t\t{ \u0022binance\u0022: {\n+\t\t\t\u0022endpoint\u0022:\t\t\u0022fstream.binance.com\u0022,\n+\t\t\t\u0022port\u0022:\t\t\t443,\n+\t\t\t\u0022protocol\u0022:\t\t\u0022ws\u0022,\n+\t\t\t\u0022http_url\u0022:\t\t\u0022/stream?streams\u003dbtcusdt@depth@0ms/btcusdt@bookTicker/btcusdt@aggTrade\u0022,\n+\t\t\t\u0022nailed_up\u0022: \ttrue,\n+\t\t\t\u0022ws_prioritize_reads\u0022:\ttrue,\n+\t\t\t\u0022tls\u0022:\t\t\ttrue,\n+\t\t\t\u0022tls_trust_store\u0022:\t\u0022digicert\u0022,\n+\t\t\t\u0022retry\u0022:\t\t\u0022default\u0022\n+\t\t\t}\n+\t\t}\n+\t]\n+}\n+\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/private.h b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/private.h\nnew file mode 100644\nindex 0000000..5b23f24\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/private.h\n@@ -0,0 +1,38 @@\n+/*\n+ * lws-minimal-secure-streams-custom-proxy-transport\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ * Kutoga \u003ckutoga@user.github.invalid\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ */\n+\n+#define LWS_SS_USE_SSPC\n+#include \u003clibwebsockets.h\u003e\n+\n+#define MAX_CUSTOM_POLLFDS 10\n+\n+typedef struct custom_poll_ctx {\n+\tstruct pollfd\t\tpollfds[MAX_CUSTOM_POLLFDS];\n+\tvoid\t\t\t*priv[MAX_CUSTOM_POLLFDS];\n+\tint\t\t\tcount_pollfds;\n+\tstruct lws_dll2_owner\tscheduler;\n+\tlws_transport_mux_t\t*tm;\n+} custom_poll_ctx_t;\n+\n+extern custom_poll_ctx_t a_cpcx;\n+extern int interrupted, transport_fd, log_level;\n+\n+extern const lws_transport_client_ops_t lws_sss_ops_client_serial;\n+extern const lws_ss_info_t ssi_binance;\n+extern int open_transport_file(custom_poll_ctx_t *cpcx, const char *filepath, void *priv);\n+\n+extern int custom_poll_add_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd,\n+\t\t\t\tint events, void *priv);\n+extern int custom_poll_del_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd);\n+\n+extern int custom_poll_change_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd,\n+\t\t\t\t int events_add, int events_remove);\n+extern int custom_poll_run(custom_poll_ctx_t *cpcx);\n+extern int custom_transport_event(struct pollfd *pfd, void *priv);\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/system.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/system.c\nnew file mode 100644\nindex 0000000..6b60f78\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/system.c\n@@ -0,0 +1,113 @@\n+/*\n+ * lws-minimal-secure-streams-custom-client-transport\n+ *\n+ * Written in 2010-2021 by 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+ *\n+ * These are apis used inside liblws-sspc that must be wired up to the client\n+ * host platform.\n+ *\n+ * lws_sul_schedule() - use system event loop to schedule event in the future\n+ * lws_sul_cancel() - rescind a scheduled event\n+ * lws_now_usecs() - unix time in microseconds\n+ * __lws_logv() - core logging function used by liblws-sspc\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003csys/types.h\u003e\n+#include \u003cerrno.h\u003e\n+\n+int log_level \u003d LLL_USER | LLL_ERR;// | LLL_WARN | LLL_NOTICE;\n+\n+static int\n+sul_compare(const lws_dll2_t *d, const lws_dll2_t *i)\n+{\n+\tlws_usec_t a \u003d ((lws_sorted_usec_list_t *)d)-\u003eus;\n+\tlws_usec_t b \u003d ((lws_sorted_usec_list_t *)i)-\u003eus;\n+\n+\t/*\n+\t * Simply returning (a - b) in an int\n+\t * may lead to an integer overflow bug\n+\t */\n+\n+\tif (a \u003e b)\n+\t\treturn 1;\n+\tif (a \u003c b)\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+void\n+lws_sul_schedule(struct lws_context_standalone *ctx, int tsi,\n+\t\t lws_sorted_usec_list_t *sul, sul_cb_t _cb, lws_usec_t _us)\n+{\n+\tif (_us \u003d\u003d (lws_usec_t)LWS_SET_TIMER_USEC_CANCEL) {\n+\t\tlws_sul_cancel(sul);\n+\t\treturn;\n+\t}\n+\n+\tlws_dll2_remove(\u0026sul-\u003elist);\n+\n+\tsul-\u003ecb \u003d _cb;\n+\tsul-\u003eus \u003d lws_now_usecs() + _us;\n+\n+\tlws_dll2_add_sorted(\u0026sul-\u003elist, \u0026a_cpcx.scheduler, sul_compare);\n+}\n+\n+void\n+lws_sul_cancel(lws_sorted_usec_list_t *sul)\n+{\n+\tlws_dll2_remove(\u0026sul-\u003elist);\n+\tsul-\u003eus \u003d 0;\n+}\n+\n+lws_usec_t\n+lws_now_usecs(void)\n+{\n+#if defined(LWS_HAVE_CLOCK_GETTIME)\n+\tstruct timespec ts;\n+\n+\tif (clock_gettime(CLOCK_MONOTONIC, \u0026ts))\n+\t\treturn 0;\n+\n+\treturn (((lws_usec_t)ts.tv_sec) * LWS_US_PER_SEC) +\n+\t\t\t((lws_usec_t)ts.tv_nsec / LWS_NS_PER_US);\n+#else\n+\tstruct timeval now;\n+\n+\tgettimeofday(\u0026now, NULL);\n+\treturn (((lws_usec_t)now.tv_sec) * LWS_US_PER_SEC) +\n+\t\t\t(lws_usec_t)now.tv_usec;\n+#endif\n+}\n+\n+/*\n+ * wire up lws-sspc logs to native application logs, we just wire it up to\n+ * stderr\n+ */\n+\n+void\n+__lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,\n+\t int filter, const char *_fun, const char *format, va_list ap)\n+{\n+\tchar logbuf[200];\n+\tint n;\n+\n+\tif (!(filter \u0026 log_level))\n+\t\treturn;\n+\n+\tn \u003d vsnprintf(logbuf, sizeof(logbuf) - 2, format, ap);\n+\tif (n \u003e 0 \u0026\u0026 logbuf[n - 1] !\u003d '\u005cn') {\n+\t\tlogbuf[n++] \u003d '\u005cn';\n+\t\tlogbuf[n] \u003d '\u005c0';\n+\t}\n+\tfprintf(stderr, \u0022%llu: %s\u0022, (unsigned long long)lws_now_usecs(), logbuf);\n+}\n+\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/transport-serial.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/transport-serial.c\nnew file mode 100644\nindex 0000000..07e39bd\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-client-transport/transport-serial.c\n@@ -0,0 +1,350 @@\n+/*\n+ * lws-minimal-secure-streams-custom-client-transport\n+ *\n+ * Written in 2010-2021 by 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+ *\n+ * The serial port based custom transport\n+ */\n+\n+#include \u0022private.h\u0022\n+\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003csys/types.h\u003e\n+#include \u003csys/stat.h\u003e\n+#include \u003cfcntl.h\u003e\n+\n+#include \u003ctermios.h\u003e\n+#include \u003csys/ioctl.h\u003e\n+\n+#if defined(__linux__)\n+#include \u003casm/ioctls.h\u003e\n+#include \u003clinux/serial.h\u003e\n+#endif\n+\n+#include \u003cerrno.h\u003e\n+\n+/* debug helper */\n+\n+void\n+lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)\n+{\n+\tunsigned char *buf \u003d (unsigned char *)vbuf;\n+\tunsigned int n;\n+\n+\tfor (n \u003d 0; n \u003c len;) {\n+\t\tunsigned int start \u003d n, m;\n+\t\tchar line[80], *p \u003d line;\n+\n+\t\tp +\u003d snprintf(p, 10, \u0022%04X: \u0022, start);\n+\n+\t\tfor (m \u003d 0; m \u003c 16 \u0026\u0026 n \u003c len; m++)\n+\t\t\tp +\u003d snprintf(p, 5, \u0022%02X \u0022, buf[n++]);\n+\t\twhile (m++ \u003c 16)\n+\t\t\tp +\u003d snprintf(p, 5, \u0022 \u0022);\n+\n+\t\tp +\u003d snprintf(p, 6, \u0022 \u0022);\n+\n+\t\tfor (m \u003d 0; m \u003c 16 \u0026\u0026 (start + m) \u003c len; m++) {\n+\t\t\tif (buf[start + m] \u003e\u003d ' ' \u0026\u0026 buf[start + m] \u003c 127)\n+\t\t\t\t*p++ \u003d (char)buf[start + m];\n+\t\t\telse\n+\t\t\t\t*p++ \u003d '.';\n+\t\t}\n+\t\twhile (m++ \u003c 16)\n+\t\t\t*p++ \u003d ' ';\n+\n+\t\t*p++ \u003d '\u005cn';\n+\t\t*p \u003d '\u005c0';\n+\t\t_lws_log(hexdump_level, \u0022%s\u0022, line);\n+\t\t(void)line;\n+\t}\n+\n+\t_lws_log(hexdump_level, \u0022\u005cn\u0022);\n+}\n+\n+/*\n+ * Open and configure the serial transport fd\n+ */\n+\n+int\n+open_serial_port(const char *filepath)\n+{\n+#if defined(__linux__)\n+\tstruct serial_struct s_s;\n+#endif\n+\tstruct termios tio;\n+\tint fd \u003d open(filepath, O_RDWR);\n+\n+\tif (fd \u003d\u003d -1) {\n+\t\tlwsl_err(\u0022Unable to open %s\u005cn\u0022, filepath);\n+\n+\t\treturn -1;\n+\t}\n+\n+\tfcntl(fd, F_SETFL, O_NONBLOCK);\n+\ttcflush(fd, TCIOFLUSH);\n+\n+#if defined(__linux__)\n+\tif (ioctl(fd, TIOCGSERIAL, \u0026s_s) \u003d\u003d 0) {\n+\t\ts_s.closing_wait \u003d ASYNC_CLOSING_WAIT_NONE;\n+\t\tioctl(fd, TIOCSSERIAL, \u0026s_s);\n+\t}\n+#endif\n+\n+\t/* enforce suitable tty state */\n+\n+\tmemset(\u0026tio, 0, sizeof tio);\n+\tif (tcgetattr(fd, \u0026tio)) {\n+\t\tclose(fd);\n+\t\tfd \u003d -1;\n+\t\treturn -1;\n+\t}\n+\n+\tcfsetispeed(\u0026tio, B2000000);\n+\tcfsetospeed(\u0026tio, B2000000);\n+\n+\ttio.c_lflag \u0026\u003d (tcflag_t)~(ISIG | ICANON | IEXTEN | ECHO |\n+#if defined(__linux__)\n+\t\t\tXCASE |\n+#endif\n+\t\t\t ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOKE);\n+\ttio.c_iflag \u0026\u003d (tcflag_t)~(INLCR | IGNBRK | IGNPAR | IGNCR | ICRNL |\n+\t\t\t IMAXBEL | IXON | IXOFF | IXANY\n+#if defined(__linux__)\n+\t\t\t | IUCLC\n+#endif\n+\t\t\t| 0xff);\n+\ttio.c_oflag \u003d 0;\n+\n+\ttio.c_cc[VMIN] \u003d 1;\n+\ttio.c_cc[VTIME] \u003d 0;\n+\ttio.c_cc[VEOF] \u003d 1;\n+#if 0\n+\ttio.c_cflag \u003d tio.c_cflag \u0026 (unsigned long) ~(\n+#if defined(__linux__)\n+\t\t\tCBAUD |\n+#endif\n+\t\t\tCSIZE | CSTOPB | PARENB | CRTSCTS);\n+#endif\n+\ttio.c_cflag \u003d B2000000 | /*0x1412 | */ CS8 | CREAD | CLOCAL;\n+\n+\ttcsetattr(fd, TCSANOW, \u0026tio);\n+\n+\treturn fd;\n+}\n+\n+int\n+open_transport_file(custom_poll_ctx_t *cpcx, const char *filepath, void *priv)\n+{\n+\tint fd \u003d open_serial_port(filepath);\n+\n+\tif (fd \u003c 0)\n+\t\treturn -1;\n+\n+\ttransport_fd \u003d fd;\n+\n+\t/* let's add it to the event loop, and set POLLIN */\n+\n+\tif (custom_poll_add_fd(cpcx, fd, POLLIN, priv)) {\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n+\treturn fd;\n+}\n+\n+/****** custom transport to proxy\n+ *\n+ *\n+ **/\n+\n+/* incoming parsed channel cbs */\n+\n+static int\n+ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn 0;\n+}\n+\n+static int\n+ltm_ch_opens_serial(lws_transport_mux_ch_t *tmc, int determination)\n+{\n+\tlws_transport_mux_t *tm \u003d lws_container_of(tmc-\u003elist.owner,\n+\t\t\t\t\t\t lws_transport_mux_t, owner);\n+\tstruct lws_sspc_handle *h \u003d (struct lws_sspc_handle *)tmc-\u003epriv;\n+\n+\tassert_is_tm(tm);\n+\n+\tlwsl_sspc_err(h, \u0022%d\u0022, determination);\n+\n+ \tif (tm-\u003einfo.txp_cpath.ops_in-\u003eevent_connect_disposition(h, determination))\n+ \t\treturn -1;\n+\n+\treturn 0;\n+}\n+static int\n+ltm_ch_closes(lws_transport_mux_ch_t *tmc)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\treturn 0;\n+}\n+\n+static void\n+ltm_txp_req_write(lws_transport_mux_t *tm)\n+{\n+\ta_cpcx.tm-\u003einfo.txp_cpath.ops_onw-\u003ereq_write(\n+\t\t\t\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.priv_onw);\n+}\n+\n+static int\n+ltm_txp_can_write(lws_transport_mux_ch_t *tmc)\n+{\n+\tassert_is_tmch(tmc);\n+\treturn lws_txp_inside_sspc.event_can_write((struct lws_sspc_handle *)tmc-\u003epriv, 2048);\n+}\n+\n+static const lws_txp_mux_parse_cbs_t cbs \u003d {\n+\t.payload\t\t\u003d ltm_ch_payload,\n+\t.ch_opens\t\t\u003d ltm_ch_opens_serial,\n+\t.ch_closes\t\t\u003d ltm_ch_closes,\n+\t.txp_req_write\t\t\u003d ltm_txp_req_write,\n+\t.txp_can_write\t\t\u003d ltm_txp_can_write,\n+};\n+\n+int\n+custom_transport_event(struct pollfd *pfd, void *priv)\n+{\n+\tuint8_t buf[2048];\n+\tssize_t r \u003d sizeof(buf);\n+\n+\tlwsl_notice(\u0022%s: fd %d, revents %d\u005cn\u0022, __func__, pfd-\u003efd, pfd-\u003erevents);\n+\n+\tif (pfd-\u003erevents \u0026 POLLOUT) {\n+\t\tcustom_poll_change_fd(\u0026a_cpcx, pfd-\u003efd, 0, POLLOUT);\n+\t\t/*\n+\t\t * We can write something on the transport... if the transport\n+\t\t * mux layer has something, let that use the write preferentally\n+\t\t * and request another write for whatever this was\n+\t\t */\n+\n+\t\tlwsl_notice(\u0022%s: doing POLLOUT\u005cn\u0022, __func__);\n+\n+\t\tif (lws_transport_mux_pending(a_cpcx.tm, buf, (size_t *)\u0026r, \u0026cbs)) {\n+\n+\t\t\tlws_transport_path_client_dump(\u0026a_cpcx.tm-\u003einfo.txp_cpath, \u0022cpath\u0022);\n+\n+\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.ops_onw-\u003e_write(\n+\t\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.priv_onw,\n+\t\t\t\tbuf, (size_t)r);\n+\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\tif (pfd-\u003erevents \u0026 POLLIN) {\n+\n+\t\tr \u003d read(pfd-\u003efd, buf, sizeof(buf));\n+\t\tif (r \u003c 0) {\n+\t\t\tint eno \u003d errno;\n+\n+\t\t\tlwsl_warn(\u0022%s: read says %d, errno %d\u005cn\u0022, __func__,\n+\t\t\t\t\t(int)r, eno);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t//lwsl_hexdump_notice(buf, (size_t)r);\n+\n+\t\tif (a_cpcx.tm \u0026\u0026 a_cpcx.tm-\u003einfo.txp_cpath.ops_in) {\n+\n+#if 0\n+\t\t\tlwsl_user(\u0022%s: passing read to %s, priv_in %p\u005cn\u0022,\n+\t\t\t\t\t__func__,\n+\t\t\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.ops_in-\u003ename,\n+\t\t\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.priv_in);\n+#endif\n+\n+\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.ops_in-\u003eevent_read(\n+\t\t\t\t\ta_cpcx.tm-\u003einfo.txp_cpath.priv_in,\n+\t\t\t\t\tbuf, (size_t)r);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * We get called while an individual SS is trying to connect to the proxy to\n+ * be recognized as operational. It's the equivalent of trying to bring up the\n+ * Unix Domain socket\n+ */\n+\n+static int\n+txp_serial_retry_connect(lws_txp_path_client_t *path,\n+\t\t\t\t struct lws_sspc_handle *h)\n+{\n+\tlwsl_user(\u0022%s\u005cn\u0022, __func__);\n+\n+\tif (path-\u003eops_onw-\u003eevent_connect_disposition(h,\n+\t\t\t\ta_cpcx.tm-\u003elink_state !\u003d LWSTM_OPERATIONAL))\n+\t return -1;\n+\n+\treturn 0;\n+}\n+\n+static void\n+txp_serial_req_write(lws_transport_priv_t priv)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\tcustom_poll_change_fd(\u0026a_cpcx, transport_fd, POLLOUT, 0);\n+}\n+\n+static int\n+txp_serial_write(lws_transport_priv_t priv, uint8_t *buf, size_t len)\n+{\n+\tlwsl_notice(\u0022%s: writing %u\u005cn\u0022, __func__, (unsigned int)len);\n+\t// lwsl_hexdump_notice(buf, len);\n+\tif (write(transport_fd, buf, len) !\u003d (ssize_t)len) {\n+\t\tlwsl_warn(\u0022%s: write %u failed\u005cn\u0022, __func__, (unsigned int)len);\n+\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static void\n+txp_serial_close(lws_transport_priv_t priv)\n+{\n+#if 0\n+\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+\tif (!wsi)\n+\t\treturn;\n+\n+\tlws_set_opaque_user_data(wsi, NULL);\n+\tlws_wsi_close(wsi, LWS_TO_KILL_ASYNC);\n+\t*priv \u003d NULL;\n+#endif\n+}\n+\n+static void\n+txp_serial_stream_up(lws_transport_priv_t priv)\n+{\n+//\tstruct lws *wsi \u003d (struct lws *)priv;\n+\n+//\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n+}\n+\n+const lws_transport_client_ops_t lws_sss_ops_client_serial \u003d {\n+\t.name\t\t\t\u003d \u0022txpserial\u0022,\n+\t.event_retry_connect\t\u003d txp_serial_retry_connect,\n+\t.req_write\t\t\u003d txp_serial_req_write,\n+\t._write\t\t\t\u003d txp_serial_write,\n+\t._close\t\t\t\u003d txp_serial_close,\n+\t.event_stream_up\t\u003d txp_serial_stream_up,\n+};\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/CMakeLists.txt\nnew file mode 100644\nindex 0000000..c38f4bd\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/CMakeLists.txt\n@@ -0,0 +1,30 @@\n+project(lws-minimal-secure-streams-custom-proxy-transport C)\n+cmake_minimum_required(VERSION 2.8.12)\n+find_package(libwebsockets CONFIG REQUIRED)\n+list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})\n+include(CheckCSourceCompiles)\n+include(LwsCheckRequirements)\n+\n+set(SAMP lws-minimal-secure-streams-custom-proxy-transport)\n+set(SRCS main.c transport-serial.c)\n+\n+set(requirements 1)\n+require_lws_config(LWS_ROLE_H1 1 requirements)\n+require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)\n+require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)\n+require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements)\n+require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)\n+require_lws_config(LWS_WITH_SYS_STATE 1 requirements)\n+\n+# non-linux don't have B2000000 2Mbps USB serial baud rate constants\n+\n+if (requirements AND ${CMAKE_SYSTEM_NAME} STREQUAL \u0022Linux\u0022)\n+\tadd_executable(${SAMP} ${SRCS})\n+\n+\tif (websockets_shared)\n+\t\ttarget_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})\n+\t\tadd_dependencies(${SAMP} websockets_shared)\n+\telse()\n+\t\ttarget_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})\n+\tendif()\n+endif()\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/README.md b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/README.md\nnew file mode 100644\nindex 0000000..ab5cbcb\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/README.md\n@@ -0,0 +1,33 @@\n+# lws minimal secure streams proxy\n+\n+Operates as a secure streams proxy, by default on a listening unix domain socket\n+\u0022proxy.ss.lws\u0022 in the Linux abstract namespace.\n+\n+Give -p \u003cport\u003e to have it listen on a specific tcp port instead.\n+\n+## build\n+\n+```\n+ $ cmake . \u0026\u0026 make\n+```\n+\n+## usage\n+\n+Commandline option|Meaning\n+---|---\n+-d \u003cloglevel\u003e|Debug verbosity in decimal, eg, -d15\n+-f| Force connecting to the wrong endpoint to check backoff retry flow\n+-p \u003cport\u003e|If not given, proxy listens on a Unix Domain Socket, if given listen on specified tcp port\n+-i \u003ciface\u003e|Optionally specify the UDS path (no -p) or network interface to bind to (if -p also given)\n+\n+```\n+[2020/02/26 15:41:27:5768] U: LWS secure streams Proxy [-d\u003cverb\u003e]\n+[2020/02/26 15:41:27:5770] N: lws_ss_policy_set: 2.064KiB, pad 70%: hardcoded\n+[2020/02/26 15:41:27:5771] N: lws_tls_client_create_vhost_context: using mem client CA cert 1391\n+[2020/02/26 15:41:27:8681] N: lws_ss_policy_set: 4.512KiB, pad 15%: updated\n+[2020/02/26 15:41:27:8682] N: lws_tls_client_create_vhost_context: using mem client CA cert 837\n+[2020/02/26 15:41:27:8683] N: lws_tls_client_create_vhost_context: using mem client CA cert 1043\n+[2020/02/26 15:41:27:8684] N: lws_tls_client_create_vhost_context: using mem client CA cert 1167\n+[2020/02/26 15:41:27:8684] N: lws_tls_client_create_vhost_context: using mem client CA cert 1391\n+[2020/02/26 15:41:28:4226] N: ss_api_amazon_auth_rx: acquired 567-byte api.amazon.com auth token, exp 3600s\n+```\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/main.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/main.c\nnew file mode 100644\nindex 0000000..d66f25d\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/main.c\n@@ -0,0 +1,327 @@\n+/*\n+ * lws-minimal-secure-streams-custom-proxy-transport\n+ *\n+ * Written in 2010-2020 by 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+ *\n+ * This is a version of minimal-secure-streams-proxy that uses a custom\n+ * transport.\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+\n+#if defined(__APPLE__) || defined(__linux__)\n+#include \u003cexecinfo.h\u003e\n+#include \u003cassert.h\u003e\n+#endif\n+\n+#include \u0022private.h\u0022\n+\n+static int bad \u003d 1, port \u003d 0 /* unix domain socket */;\n+static const char *ibind \u003d \u0022/dev/ttyUSB0\u0022;\n+static lws_state_notify_link_t nl;\n+static struct lws_context *context;\n+int interrupted;\n+\n+/*\n+ * We just define enough policy so it can fetch the latest one securely\n+ */\n+\n+static const char * const default_ss_policy \u003d\n+\t\u0022{\u0022\n+\t \u0022\u005c\u0022release\u005c\u0022:\u0022\t\t\t\u0022\u005c\u002201234567\u005c\u0022,\u0022\n+\t \u0022\u005c\u0022product\u005c\u0022:\u0022\t\t\t\u0022\u005c\u0022myproduct\u005c\u0022,\u0022\n+\t \u0022\u005c\u0022schema-version\u005c\u0022:\u0022\t\t\t\u00221,\u0022\n+\t \u0022\u005c\u0022retry\u005c\u0022: [\u0022\t/* named backoff / retry strategies */\n+\t\t\u0022{\u005c\u0022default\u005c\u0022: {\u0022\n+\t\t\t\u0022\u005c\u0022backoff\u005c\u0022: [\u0022\t \u00221000,\u0022\n+\t\t\t\t\t\t \u00222000,\u0022\n+\t\t\t\t\t\t \u00223000,\u0022\n+\t\t\t\t\t\t \u00225000,\u0022\n+\t\t\t\t\t\t\u002210000\u0022\n+\t\t\t\t\u0022],\u0022\n+\t\t\t\u0022\u005c\u0022conceal\u005c\u0022:\u0022\t\t\u00225,\u0022\n+\t\t\t\u0022\u005c\u0022jitterpc\u005c\u0022:\u0022\t\t\u002220,\u0022\n+\t\t\t\u0022\u005c\u0022svalidping\u005c\u0022:\u0022\t\u002230,\u0022\n+\t\t\t\u0022\u005c\u0022svalidhup\u005c\u0022:\u0022\t\u002235\u0022\n+\t\t\u0022}}\u0022\n+\t \u0022],\u0022\n+\t \u0022\u005c\u0022certs\u005c\u0022: [\u0022 /* named individual certificates in BASE64 DER */\n+\t\t/*\n+\t\t * Let's Encrypt certs for warmcat.com / libwebsockets.org\n+\t\t *\n+\t\t * We fetch the real policy from there using SS and switch to\n+\t\t * using that.\n+\t\t */\n+\t\t\u0022{\u005c\u0022dst_root_x3\u005c\u0022: \u005c\u0022\u0022\n+\t\u0022MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\u0022\n+\t\u0022MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\u0022\n+\t\u0022DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\u0022\n+\t\u0022PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\u0022\n+\t\u0022Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\u0022\n+\t\u0022AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\u0022\n+\t\u0022rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\u0022\n+\t\u0022OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\u0022\n+\t\u0022xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\u0022\n+\t\u00227BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\u0022\n+\t\u0022aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\u0022\n+\t\u0022HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\u0022\n+\t\u0022SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\u0022\n+\t\u0022ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\u0022\n+\t\u0022AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\u0022\n+\t\u0022R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\u0022\n+\t\u0022JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\u0022\n+\t\u0022Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\u0022\n+\t\t\u0022\u005c\u0022}\u0022\n+\t \u0022],\u0022\n+\t \u0022\u005c\u0022trust_stores\u005c\u0022: [\u0022 /* named cert chains */\n+\t\t\u0022{\u0022\n+\t\t\t\u0022\u005c\u0022name\u005c\u0022: \u005c\u0022le_via_dst\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022stack\u005c\u0022: [\u0022\n+\t\t\t\t\u0022\u005c\u0022dst_root_x3\u005c\u0022\u0022\n+\t\t\t\u0022]\u0022\n+\t\t\u0022}\u0022\n+\t \u0022],\u0022\n+\t \u0022\u005c\u0022s\u005c\u0022: [{\u0022\n+\t\t\u0022\u005c\u0022captive_portal_detect\u005c\u0022: {\u0022\n+\t\t\t\u0022\u005c\u0022endpoint\u005c\u0022: \u005c\u0022connectivitycheck.android.com\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022http_url\u005c\u0022: \u005c\u0022generate_204\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022port\u005c\u0022: 80,\u0022\n+\t\t\t\u0022\u005c\u0022protocol\u005c\u0022: \u005c\u0022h1\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022http_method\u005c\u0022: \u005c\u0022GET\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022opportunistic\u005c\u0022: true,\u0022\n+\t\t\t\u0022\u005c\u0022http_expect\u005c\u0022: 204,\u0022\n+\t\t\t\u0022\u005c\u0022http_fail_redirect\u005c\u0022: true\u0022\n+\t\t\u0022},\u0022\n+\t\t\u0022\u005c\u0022fetch_policy\u005c\u0022: {\u0022\n+\t\t\t\u0022\u005c\u0022endpoint\u005c\u0022:\u0022\t\t\u0022\u005c\u0022warmcat.com\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022port\u005c\u0022:\u0022\t\t\u0022443,\u0022\n+\t\t\t\u0022\u005c\u0022protocol\u005c\u0022:\u0022\t\t\u0022\u005c\u0022h1\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022http_method\u005c\u0022:\u0022\t\u0022\u005c\u0022GET\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022http_url\u005c\u0022:\u0022\t\t\u0022\u005c\u0022policy/minimal-proxy-v4.2-v2.json\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022tls\u005c\u0022:\u0022\t\t\u0022true,\u0022\n+\t\t\t\u0022\u005c\u0022opportunistic\u005c\u0022:\u0022\t\u0022true,\u0022\n+\t\t\t\u0022\u005c\u0022retry\u005c\u0022:\u0022\t\t\u0022\u005c\u0022default\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022tls_trust_store\u005c\u0022:\u0022\t\u0022\u005c\u0022le_via_dst\u005c\u0022\u0022\n+\t\t\u0022}}\u0022\n+\t\u0022}\u0022\n+;\n+\n+static const char *canned_root_token_payload \u003d\n+\t\u0022grant_type\u003drefresh_token\u0022\n+\t\u0022\u0026refresh_token\u003dAtzr|IwEBIJedGXjDqsU_vMxykqOMg\u0022\n+\t\u0022SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP\u0022\n+\t\u0022zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y\u0022\n+\t\u00220flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW\u0022\n+\t\u0022k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE\u0022\n+\t\u0022iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S\u0022\n+\t\u0022KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc\u0022\n+\t\u0022AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI\u0022\n+\t\u0022xL_hDCcTho8opCVX-6QhJHl6SQFlTw13\u0022\n+\t\u0022\u0026client_id\u003d\u0022\n+\t\t\u0022amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d\u0022;\n+\n+#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)\n+static char *aws_keyid \u003d NULL,\n+\t *aws_key \u003d NULL;\n+#endif\n+\n+static int\n+app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,\n+\t\t int current, int target)\n+{\n+\tstruct lws_context *context \u003d lws_system_context_from_system_mgr(mgr);\n+\tlws_system_blob_t *ab \u003d lws_system_get_blob(context,\n+\t\t\t\tLWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);\n+\tsize_t size;\n+\n+\t/*\n+\t * For the things we care about, let's notice if we are trying to get\n+\t * past them when we haven't solved them yet, and make the system\n+\t * state wait while we trigger the dependent action.\n+\t */\n+\tswitch (target) {\n+\tcase LWS_SYSTATE_REGISTERED:\n+\t\tsize \u003d lws_system_blob_get_size(ab);\n+\t\tif (size)\n+\t\t\tbreak;\n+\n+\t\t/* let's register our canned root token so auth can use it */\n+\t\tlws_system_blob_direct_set(ab,\n+\t\t\t\t(const uint8_t *)canned_root_token_payload,\n+\t\t\t\tstrlen(canned_root_token_payload));\n+\t\tbreak;\n+\tcase LWS_SYSTATE_OPERATIONAL:\n+\t\tif (current \u003d\u003d LWS_SYSTATE_OPERATIONAL) {\n+#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)\n+\n+\t\t\tif (lws_aws_filesystem_credentials_helper(\n+\t\t\t\t\t\t \u0022~/.aws/credentials\u0022,\n+\t\t\t\t\t\t \u0022aws_access_key_id\u0022,\n+\t\t\t\t\t\t \u0022aws_secret_access_key\u0022,\n+\t\t\t\t\t\t \u0026aws_keyid, \u0026aws_key))\n+\t\t\t\treturn -1;\n+\n+\t\t\tlws_ss_sigv4_set_aws_key(context, 0, aws_keyid, aws_key);\n+#endif\n+\t\t\t/*\n+\t\t\t * At this point we have DHCP, ntp, system auth token\n+\t\t\t * and we can reasonably create the proxy\n+\t\t\t */\n+\t\t\tif (lws_ss_proxy_create(context, ibind, port)) {\n+\t\t\t\tlwsl_err(\u0022%s: failed to create ss proxy\u005cn\u0022,\n+\t\t\t\t\t\t__func__);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\tcase LWS_SYSTATE_POLICY_INVALID:\n+\t\t/*\n+\t\t * This is a NOP since we used direct set... but in a real\n+\t\t * system this could easily change to be done on the heap, then\n+\t\t * this would be important\n+\t\t */\n+\t\tlws_system_blob_destroy(lws_system_get_blob(context,\n+\t\t\t\t\tLWS_SYSBLOB_TYPE_AUTH,\n+\t\t\t\t\t1 /* AUTH_IDX_ROOT */));\n+\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static lws_state_notify_link_t * const app_notifier_list[] \u003d {\n+\t\u0026nl, NULL\n+};\n+\n+#if defined(LWS_WITH_SYS_METRICS)\n+\n+static int\n+my_metric_report(lws_metric_pub_t *mp)\n+{\n+\tlws_metric_bucket_t *sub \u003d mp-\u003eu.hist.head;\n+\tchar buf[192];\n+\n+\tdo {\n+\t\tif (lws_metrics_format(mp, \u0026sub, buf, sizeof(buf)))\n+\t\t\tlwsl_user(\u0022%s: %s\u005cn\u0022, __func__, buf);\n+\t} while ((mp-\u003eflags \u0026 LWSMTFL_REPORT_HIST) \u0026\u0026 sub);\n+\n+\t/* 0 \u003d leave metric to accumulate, 1 \u003d reset the metric */\n+\n+\treturn 1;\n+}\n+\n+static const lws_system_ops_t system_ops \u003d {\n+\t.metric_report \u003d my_metric_report,\n+};\n+\n+#endif\n+\n+const struct lws_protocols *ppcols[] \u003d {\n+\t\u0026protocol_sspc_serial_transport,\n+\tNULL\n+};\n+\n+lws_transport_info_t info_serial \u003d {\n+\t.ping_interval_us\t\t\u003d LWS_US_PER_SEC * 10,\n+\t.pong_grace_us\t\t\t\u003d LWS_US_PER_SEC * 2,\n+\t.flags\t\t\t\t\u003d LWSTMINFO_SERVER,\n+}, info_mux \u003d { /* onward transport for mux is serial */\n+\t.ping_interval_us\t\t\u003d LWS_US_PER_SEC * 10,\n+\t.pong_grace_us\t\t\t\u003d LWS_US_PER_SEC * 2,\n+\t.txp_ppath \u003d {\n+\t\t.ops_onw\t\t\u003d \u0026lws_transport_ops_serial,\n+\t\t.ops_in\t\t\t\u003d \u0026lws_transport_mux_proxy_ops,\n+\t},\n+\t.onward_txp_info\t\t\u003d \u0026info_serial,\n+\t.flags\t\t\t\t\u003d LWSTMINFO_SERVER,\n+};\n+\n+\u005c\n+static void\n+sigint_handler(int sig)\n+{\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\tinterrupted \u003d 1;\n+\tlws_cancel_service(context);\n+}\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct lws_context_creation_info info;\n+\tconst char *p;\n+\tint n \u003d 0;\n+\n+\tmemset(\u0026info, 0, sizeof info);\n+\tlws_cmdline_option_handle_builtin(argc, argv, \u0026info);\n+\n+\tsignal(SIGINT, sigint_handler);\n+\n+\t/* connect to ssproxy via UDS by default, else via tcp with this port */\n+\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-p\u0022)))\n+\t\tport \u003d atoi(p);\n+\n+\t/* UDS \u0022proxy.ss.lws\u0022 in abstract namespace, else this socket path;\n+\t * when -p given this can specify the network interface to bind to */\n+\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-i\u0022)))\n+\t\tibind \u003d p;\n+\n+\tlwsl_user(\u0022LWS secure streams Proxy [-d\u003cverb\u003e]\u005cn\u0022);\n+\n+\tinfo.options \u003d LWS_SERVER_OPTION_EXPLICIT_VHOSTS |\n+\t\t LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW |\n+\t\t LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;\n+\tinfo.fd_limit_per_thread\t\u003d 1 + 26 + 1;\n+\tinfo.pss_policies_json\t\t\u003d default_ss_policy;\n+\tinfo.port\t\t\t\u003d CONTEXT_PORT_NO_LISTEN;\n+\tinfo.pprotocols\t\t\t\u003d ppcols;\n+\n+\t/* integrate us with lws system state management when context created */\n+\tnl.name\t\t\t\t\u003d \u0022app\u0022;\n+\tnl.notify_cb\t\t\t\u003d app_system_state_nf;\n+\tinfo.register_notifier_list\t\u003d app_notifier_list;\n+\n+\tinfo.pt_serv_buf_size\t\t\u003d (unsigned int)((6144 * 2) + 2048);\n+\tinfo.max_http_header_data\t\u003d (unsigned short)(6144 + 2048);\n+\n+#if defined(LWS_WITH_SYS_METRICS)\n+\tinfo.system_ops\t\t\t\u003d \u0026system_ops;\n+\tinfo.metrics_prefix\t\t\u003d \u0022ssproxy\u0022;\n+#endif\n+\n+\tinfo.txp_ops_ssproxy\t\t\u003d \u0026lws_transport_mux_proxy_ops;\n+\tinfo.txp_ssproxy_info\t\t\u003d \u0026info_mux;\n+\n+\tcontext \u003d lws_create_context(\u0026info);\n+\tif (!context) {\n+\t\tlwsl_err(\u0022lws init failed\u005cn\u0022);\n+\t\treturn 1;\n+\t}\n+\n+\t/* the event loop */\n+\n+\tdo {\n+\t\tn \u003d lws_service(context, 0);\n+\t} while (n \u003e\u003d 0 \u0026\u0026 !interrupted);\n+\n+\tbad \u003d 0;\n+\n+#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)\n+\tif (aws_keyid)\n+\t\tfree(aws_keyid);\n+\tif (aws_key)\n+\t\tfree(aws_key);\n+#endif\n+\n+\tlws_context_destroy(context);\n+\n+\tlwsl_user(\u0022Completed: %s\u005cn\u0022, bad ? \u0022failed\u0022 : \u0022OK\u0022);\n+\n+\treturn bad;\n+}\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/private.h b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/private.h\nnew file mode 100644\nindex 0000000..1d1d86c\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/private.h\n@@ -0,0 +1,16 @@\n+/*\n+ * lws-minimal-secure-streams-custom-proxy-transport\n+ *\n+ * Written in 2010-2020 by 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+ *\n+ * This is a version of minimal-secure-streams-proxy that uses a custom\n+ * transport.\n+ */\n+\n+extern const lws_transport_proxy_ops_t lws_transport_ops_serial;\n+extern struct lws_protocols protocol_sspc_serial_transport;\n+extern const lws_transport_proxy_ops_t lws_transport_mux_transport_ops;\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/transport-serial.c b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/transport-serial.c\nnew file mode 100644\nindex 0000000..9d898cc\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-custom-proxy-transport/transport-serial.c\n@@ -0,0 +1,354 @@\n+/*\n+ * lws-minimal-secure-streams-custom-proxy-transport\n+ *\n+ * Written in 2010-2021 by 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+ *\n+ * This is a version of minimal-secure-streams-proxy that uses a custom\n+ * transport.\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003csys/types.h\u003e\n+#include \u003csys/stat.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003cerrno.h\u003e\n+\n+#include \u003ctermios.h\u003e\n+#include \u003csys/ioctl.h\u003e\n+\n+#if defined(__linux__)\n+#include \u003casm/ioctls.h\u003e\n+#include \u003clinux/serial.h\u003e\n+#endif\n+\n+#include \u003cassert.h\u003e\n+\n+#include \u0022private.h\u0022\n+\n+extern int interrupted;\n+\n+static const char *filepath \u003d \u0022/dev/ttyUSB0\u0022;\n+\n+#define LWS_PSS_MAGIC LWS_FOURCC('p', 'p', 's', 's')\n+#define assert_is_pss(_p) lws_assert_fourcc(_p-\u003emagic, LWS_PSS_MAGIC)\n+\n+struct pss {\n+\tuint32_t\t\t\t\tmagic;\n+\tlws_txp_path_proxy_t\t\t\ttxp_ppath;\n+\tstruct lws\t\t\t\t*wsi;\n+\tint\t\t\t\t\tfilefd;\n+};\n+\n+/*\n+ * Open and configure the serial transport fd\n+ */\n+\n+int\n+open_serial_port(const char *filepath)\n+{\n+#if defined(__linux__)\n+\tstruct serial_struct s_s;\n+#endif\n+\tstruct termios tio;\n+\tint fd \u003d open(filepath, O_RDWR);\n+\n+\tif (fd \u003d\u003d -1) {\n+\t\tlwsl_err(\u0022Unable to open %s: %d\u005cn\u0022, filepath, errno);\n+\n+\t\treturn -1;\n+\t}\n+\n+\tfcntl(fd, F_SETFL, O_NONBLOCK);\n+\ttcflush(fd, TCIOFLUSH);\n+\n+#if defined(__linux__)\n+\tif (ioctl(fd, TIOCGSERIAL, \u0026s_s) \u003d\u003d 0) {\n+\t\ts_s.closing_wait \u003d ASYNC_CLOSING_WAIT_NONE;\n+\t\ts_s.flags \u003d (int)((int)s_s.flags | (int)ASYNC_LOW_LATENCY);\n+\t\tioctl(fd, TIOCSSERIAL, \u0026s_s);\n+\t}\n+#endif\n+\n+\t/* enforce suitable tty state */\n+\n+\tmemset(\u0026tio, 0, sizeof tio);\n+\tif (tcgetattr(fd, \u0026tio)) {\n+\t\tclose(fd);\n+\t\tfd \u003d -1;\n+\t\treturn -1;\n+\t}\n+\n+\tcfsetispeed(\u0026tio, B2000000); // B921600);\n+\tcfsetospeed(\u0026tio, B2000000); // B921600);\n+\n+\ttio.c_lflag \u0026\u003d (tcflag_t)~(ISIG | ICANON | IEXTEN | ECHO |\n+#if defined(__linux__)\n+\t\t\tXCASE |\n+#endif\n+\t\t\t ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOKE);\n+\ttio.c_iflag \u0026\u003d (tcflag_t)~(INLCR | IGNBRK | IGNPAR | IGNCR | ICRNL |\n+\t\t\t IMAXBEL | IXON | IXOFF | IXANY\n+#if defined(__linux__)\n+\t\t\t | IUCLC\n+#endif\n+\t\t\t| 0xff);\n+\ttio.c_oflag \u003d 0;\n+\n+\ttio.c_cc[VMIN] \u003d 1;\n+\ttio.c_cc[VTIME] \u003d 0;\n+\ttio.c_cc[VEOF] \u003d 1;\n+#if 0\n+\ttio.c_cflag \u003d tio.c_cflag \u0026 (unsigned long) ~(\n+#if defined(__linux__)\n+\t\t\tCBAUD |\n+#endif\n+\t\t\tCSIZE | CSTOPB | PARENB | CRTSCTS);\n+#endif\n+\ttio.c_cflag \u003d B2000000 | /* 0x1412 | */CS8 | CREAD | CLOCAL;\n+\n+\ttcsetattr(fd, TCSANOW, \u0026tio);\n+\n+\tlwsl_notice(\u0022%s: serial port opened %d\u005cn\u0022, __func__, fd);\n+\n+\treturn fd;\n+}\n+\n+static int\n+cb_proxy_serial_transport(struct lws *wsi, enum lws_callback_reasons reason,\n+\t\t\t void *user, void *in, size_t len)\n+{\n+\tstruct pss *pss \u003d (struct pss *)lws_get_opaque_user_data(wsi);\n+\tuint8_t buf[1024];\n+\tint n;\n+\n+\tswitch (reason) {\n+\tcase LWS_CALLBACK_PROTOCOL_INIT:\n+\t\tlwsl_user(\u0022%s: PROTOCOL_INIT %s\u005cn\u0022, __func__,\n+\t\t\t\tlws_get_vhost_name(lws_get_vhost(wsi)));\n+\t\tbreak;\n+\n+\t/* callbacks related to raw file descriptor */\n+\n+\tcase LWS_CALLBACK_RAW_ADOPT_FILE:\n+\t\tlwsl_notice(\u0022LWS_CALLBACK_RAW_ADOPT_FILE\u005cn\u0022);\n+\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_RX_FILE:\n+//\t\tlwsl_notice(\u0022LWS_CALLBACK_RAW_RX_FILE\u005cn\u0022);\n+\n+\t\tif (pss)\n+\t\t\tassert_is_pss(pss);\n+\n+\t\tn \u003d (int)read(pss-\u003efilefd, buf, sizeof(buf));\n+\t\tif (n \u003c\u003d 0) {\n+\t\t\tlwsl_err(\u0022Reading from %s failed\u005cn\u0022, filepath);\n+\t\t\tinterrupted \u003d 1;\n+\t\t\treturn 1;\n+\t\t}\n+\t\tlwsl_hexdump_notice(buf, (size_t)n);\n+#if 0\n+\t\tlwsl_info(\u0022%s: passing read to %s, priv_in %p\u005cn\u0022, __func__,\n+\t\t\t pss-\u003etxp_ppath.ops_in-\u003ename, pss-\u003etxp_ppath.priv_in);\n+#endif\n+\t\tpss-\u003etxp_ppath.ops_in-\u003eproxy_read(pss-\u003etxp_ppath.priv_in, buf,\n+\t\t\t\t(size_t)n);\n+\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_CLOSE_FILE:\n+\t\tlwsl_notice(\u0022LWS_CALLBACK_RAW_CLOSE_FILE\u005cn\u0022);\n+\n+\t\tif (pss)\n+\t\t\tassert_is_pss(pss);\n+\n+\t\tlws_set_opaque_user_data(wsi, NULL);\n+\t\t/*\n+\t\t * We also have to eliminate the pss reference in\n+\t\t * \ttm-\u003einfo.txp_ppath.priv_onw\n+\t\t */\n+\n+\t\t\t((lws_transport_mux_t *)pss-\u003etxp_ppath.priv_in)-\u003e\n+\t\t\t\t\tinfo.txp_ppath.priv_onw \u003d NULL;\n+\n+\t\tfree(pss);\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_WRITEABLE_FILE:\n+\t\t//lwsl_notice(\u0022%s: LWS_CALLBACK_RAW_WRITEABLE_FILE: %p\u005cn\u0022,\n+\t\t//\t\t__func__, pss-\u003etxp_ppath.priv_in);\n+\n+\t\tif (pss)\n+\t\t\tassert_is_pss(pss);\n+\n+\t\t/* pass the event back inwards */\n+\t\tpss-\u003etxp_ppath.ops_in-\u003eevent_proxy_can_write(\n+\t\t\t\tpss-\u003etxp_ppath.priv_in\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t\t\t\t\t, NULL\n+#endif\n+\t\t\t\t);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+struct lws_protocols protocol_sspc_serial_transport \u003d\n+\t{ \u0022sspc-serial-transport\u0022,\n+\t cb_proxy_serial_transport,\n+\t 0, 1300, 0, NULL, 0 };\n+\n+\n+static void\n+txp_serial_onward_bind(lws_transport_priv_t priv, struct lws_ss_handle *h)\n+{\n+}\n+\n+static void\n+txp_serial_req_write(lws_transport_priv_t priv)\n+{\n+\tstruct pss *pss \u003d (struct pss *)priv;\n+\n+\tassert_is_pss(pss);\n+\n+\tif (pss-\u003ewsi)\n+\t\tlws_callback_on_writable(pss-\u003ewsi);\n+}\n+\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+static const lws_fi_ctx_t *\n+txp_serial_fault_context(lws_transport_priv_t priv)\n+{\n+\treturn NULL;\n+}\n+#endif\n+\n+/*\n+ * Partial writes are quite possible\n+ */\n+\n+static int\n+txp_serial_write(lws_transport_priv_t priv, uint8_t *buf, size_t *len)\n+{\n+\tstruct pss *pss \u003d (struct pss *)priv;\n+\tssize_t r;\n+\n+\tassert_is_pss(pss);\n+\n+//\tlwsl_warn(\u0022%s: write %d\u005cn\u0022, __func__, (int)*len);\n+//\tlwsl_hexdump_warn(buf, *len);\n+\n+\tr \u003d write(pss-\u003efilefd, buf, *len);\n+\tif (r \u003c 0) {\n+\t\tlwsl_wsi_notice(pss-\u003ewsi, \u0022failed\u0022);\n+\t\tassert(0);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((size_t)r !\u003d *len)\n+\t\tlwsl_warn(\u0022%s: partial, had %d accepted %d\u005cn\u0022, __func__, (int)*len, (int)r);\n+\t*len \u003d (size_t)r;\n+\n+\treturn 0;\n+}\n+\n+int\n+txp_serial_init_proxy_server(struct lws_context *cx,\n+\t\t\t\t const struct lws_transport_proxy_ops *txp_ops_inward,\n+\t\t\t\t lws_transport_priv_t txp_priv_inward,\n+\t\t\t\t lws_txp_path_proxy_t *txp_ppath,\n+\t\t\t\t const void *txp_info,\n+\t\t\t\t const char *bind, int port)\n+{\n+\tint fd \u003d open_serial_port(bind);\n+\tlws_adopt_desc_t ad;\n+\tstruct pss *pss;\n+\n+\tlwsl_user(\u0022%s: txp_priv_inward %p\u005cn\u0022, __func__, txp_priv_inward);\n+\n+\tif (fd \u003c 0) {\n+\t\tlwsl_err(\u0022%s: unable to open %s\u005cn\u0022, __func__, bind);\n+\t\treturn 1;\n+\t}\n+\n+\tpss \u003d malloc(sizeof(*pss));\n+\tif (!pss) {\n+\t\tclose(fd);\n+\t\treturn 1;\n+\t}\n+\n+\tpss-\u003emagic \u003d LWS_PSS_MAGIC;\n+\tpss-\u003efilefd \u003d fd;\n+\n+\tmemset(\u0026ad, 0, sizeof(ad));\n+\tad.vh \u003d lws_get_vhost_by_name(cx, \u0022default\u0022);\n+\tad.type \u003d LWS_ADOPT_RAW_FILE_DESC;\n+\tad.fd.filefd \u003d (lws_filefd_type)(long long)fd;\n+\tad.opaque \u003d pss;\n+\tad.vh_prot_name \u003d \u0022sspc-serial-transport\u0022;\n+\n+\tpss-\u003ewsi \u003d lws_adopt_descriptor_vhost_via_info(\u0026ad);\n+\tif (!pss-\u003ewsi) {\n+\t\tlwsl_err(\u0022%s: Failed to adopt fifo\u005cn\u0022, __func__);\n+\t\tclose(fd);\n+\t\tfree(pss);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tpss-\u003etxp_ppath.ops_in \u003d txp_ops_inward;\n+\tpss-\u003etxp_ppath.priv_in \u003d txp_priv_inward;\n+\ttxp_ppath-\u003epriv_onw \u003d (lws_transport_priv_t)pss;\n+\n+\tlwsl_user(\u0022%s: OK (txp_priv_in %p)\u005cn\u0022, __func__, txp_priv_inward);\n+\n+\treturn 0;\n+}\n+\n+static void\n+txp_serial_client_up(lws_transport_priv_t priv)\n+{\n+\t//struct lws *wsi \u003d (struct lws *)priv;\n+\n+\t//lws_set_timeout(wsi, 0, 0);\n+}\n+\n+static int\n+txp_serial_proxy_check_write_more(lws_transport_priv_t priv)\n+{\n+\tstruct pss *pss \u003d (struct pss *)priv;\n+\n+\tif (pss-\u003ewsi \u0026\u0026 !lws_send_pipe_choked(pss-\u003ewsi))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+const lws_transport_proxy_ops_t lws_transport_ops_serial \u003d {\n+\t.name\t\t\t\u003d \u0022txpserial\u0022,\n+\t.init_proxy_server\t\u003d txp_serial_init_proxy_server,\n+\t.proxy_req_write\t\u003d txp_serial_req_write,\n+\t.proxy_write\t\t\u003d txp_serial_write,\n+\n+\t.event_onward_bind\t\u003d txp_serial_onward_bind,\n+#if defined(LWS_WITH_SYS_FAULT_INJECTION)\n+\t.fault_context\t\t\u003d txp_serial_fault_context,\n+#endif\n+\t.event_client_up\t\u003d txp_serial_client_up,\n+\t.proxy_check_write_more \u003d txp_serial_proxy_check_write_more,\n+\t.flags\t\t\t\u003d LWS_DSHFLAG_ENABLE_COALESCE |\n+\t\t\t\t LWS_DSHFLAG_ENABLE_SPLIT\n+};\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c b/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c\nindex f3a0b69..e4a4021 100644\n--- a/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c\n@@ -114,7 +114,7 @@ int main(int argc, const char **argv)\n \n \t\tm \u003d lws_ss_policy_parse(context, (uint8_t *)buf, (size_t)n);\n \n-\t\tprintf(\u0022%.*s\u0022, n, buf);\n+\t\t// printf(\u0022%.*s\u0022, n, buf);\n \t\tjson_size +\u003d (unsigned int)n;\n \n \t\tif (m \u003c 0 \u0026\u0026 m !\u003d LEJP_CONTINUE) {\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-stress/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-stress/CMakeLists.txt\nindex 6944e7f..769eace 100644\n--- a/minimal-examples/secure-streams/minimal-secure-streams-stress/CMakeLists.txt\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-stress/CMakeLists.txt\n@@ -52,7 +52,7 @@ if (requirements)\n \t\tset_tests_properties(ssstress-warmcat\n \t\t\t\t PROPERTIES\n \t\t\t\t WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams-stress\n-\t\t\t\t TIMEOUT 60)\n+\t\t\t\t TIMEOUT 80)\n \t\tif (DEFINED ENV{SAI_OVN})\n \t\t\tset_tests_properties(ssstress-warmcat PROPERTIES FIXTURES_REQUIRED \u0022res_sspcmin\u0022)\n \t\tendif()\n@@ -73,7 +73,8 @@ if (requirements)\n \t\t\tendif()\n \t\t\tadd_test(NAME st_ssstressproxy COMMAND\n \t\t\t\t${CMAKE_SOURCE_DIR}/scripts/ctest-background.sh\n-\t\t\t\tssstressproxy $\u003cTARGET_FILE:lws-minimal-secure-streams-proxy\u003e\n+\t\t\t\tssstressproxy\n+\t\t\t\t$\u003cTARGET_FILE:lws-minimal-secure-streams-proxy\u003e\n \t\t\t\t-i ${CTEST_SOCKET_PATH} )\n \t\t\tset_tests_properties(st_ssstressproxy PROPERTIES WORKING_DIRECTORY . FIXTURES_SETUP ssstressproxy TIMEOUT 800)\n \n@@ -91,9 +92,9 @@ if (requirements)\n \t\t\t\tmessage(\u0022testing via valgrind\u0022)\n \t\t\t\tadd_test(NAME sspc-minimalstress COMMAND\n \t\t\t\t\t${VALGRIND} --tool\u003dmemcheck --leak-check\u003dyes --num-callers\u003d20\n-\t\t\t\t\t$\u003cTARGET_FILE:lws-minimal-secure-streams-stress-client\u003e -i +${CTEST_SOCKET_PATH} -c 4 --budget 5)\n+\t\t\t\t\t$\u003cTARGET_FILE:lws-minimal-secure-streams-stress-client\u003e -i +${CTEST_SOCKET_PATH} -c 2 --budget 3)\n \t\t\telse()\n-\t\t\t\tadd_test(NAME sspc-minimalstress COMMAND lws-minimal-secure-streams-stress-client -i +${CTEST_SOCKET_PATH} -c 4 --budget 5)\n+\t\t\t\tadd_test(NAME sspc-minimalstress COMMAND lws-minimal-secure-streams-stress-client -i +${CTEST_SOCKET_PATH} -c 2 --budget 3)\n \t\t\tendif()\n \n \t\t\tset(fixlist \u0022ssstressproxy\u0022)\n@@ -104,7 +105,7 @@ if (requirements)\n \t\t\tset_tests_properties(sspc-minimalstress PROPERTIES\n \t\t\t\tWORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams-stress\n \t\t\t\tFIXTURES_REQUIRED \u0022${fixlist}\u0022\n-\t\t\t\tTIMEOUT 40)\n+\t\t\t\tTIMEOUT 80)\n \n \t\tendif()\n \ndiff --git a/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c b/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c\nindex 62580ee..998c5a8 100644\n--- a/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c\n+++ b/minimal-examples/ws-client/minimal-ws-client-spam/minimal-ws-client-spam.c\n@@ -91,13 +91,6 @@ callback_minimal_spam(struct lws *wsi, enum lws_callback_reasons reason,\n \n \tswitch (reason) {\n \n-\tcase LWS_CALLBACK_PROTOCOL_INIT:\n-\t\tfor (n \u003d 0; n \u003c concurrent; n++) {\n-\t\t\tclients[n].index \u003d n;\n-\t\t\tconnect_client(n);\n-\t\t}\n-\t\tbreak;\n-\n \tcase LWS_CALLBACK_CLIENT_CONNECTION_ERROR:\n \t\terrors++;\n \t\tlwsl_err(\u0022CLIENT_CONNECTION_ERROR: %s (try %d, est %d, closed %d, err %d)\u005cn\u0022,\n@@ -268,6 +261,11 @@ int main(int argc, const char **argv)\n \t\treturn 1;\n \t}\n \n+\tfor (n \u003d 0; n \u003c concurrent; n++) {\n+\t\tclients[n].index \u003d n;\n+\t\tconnect_client(n);\n+\t}\n+\n \twhile (n \u003e\u003d 0 \u0026\u0026 !interrupted)\n \t\tn \u003d lws_service(context, 0);\n \n","s":{"c":1747289810,"u": 42102}}
],"g": 236746,"chitpc": 0,"ehitpc": 0,"indexed":0
,
"ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}