{"schema":"libjg2-1",
"vpath":"/git/",
"avatar":"/git/avatar/",
"alang":"",
"gen_ut":1750824794,
"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":"9538af484d611a77880f52e5489e825b",
"commit": {"type":"commit",
"time": 1597068250,
"time_ofs": 60,
"oid_tree": { "oid": "f9cb4539726abd0f3ee8753d840d41299d6ad31d", "alias": []},
"oid":{ "oid": "1b4bf38d5e2c29db14cc67981ced3d557a04dd64", "alias": []},
"msg": "sspc: add request_tx length variant",
"sig_commit": { "git_time": { "time": 1597068250, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },
"sig_author": { "git_time": { "time": 1596465189, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }},
"body": "sspc: add request_tx length variant\n\nAdd in the missing request_tx length variant, serialization and proxy\nhandling for it"
,
"diff": "diff --git a/include/libwebsockets/lws-secure-streams-client.h b/include/libwebsockets/lws-secure-streams-client.h\nindex 64fb8cd..47e686a 100644\n--- a/include/libwebsockets/lws-secure-streams-client.h\n+++ b/include/libwebsockets/lws-secure-streams-client.h\n@@ -45,6 +45,7 @@\n #define lws_ss_create\t\t\tlws_sspc_create\n #define lws_ss_destroy\t\t\tlws_sspc_destroy\n #define lws_ss_request_tx\t\tlws_sspc_request_tx\n+#define lws_ss_request_tx_len\t\tlws_sspc_request_tx_len\n #define lws_ss_client_connect\t\tlws_sspc_client_connect\n #define lws_ss_get_sequencer\t\tlws_sspc_get_sequencer\n #define lws_ss_proxy_create\t\tlws_sspc_proxy_create\n@@ -64,8 +65,8 @@ struct lws_sspc_handle;\n \n LWS_VISIBLE LWS_EXTERN int\n lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,\n-\t void *opaque_user_data, struct lws_sspc_handle **ppss,\n-\t struct lws_sequencer *seq_owner, const char **ppayload_fmt);\n+\t\tvoid *opaque_user_data, struct lws_sspc_handle **ppss,\n+\t\tstruct lws_sequencer *seq_owner, const char **ppayload_fmt);\n \n /**\n * lws_sspc_destroy() - Destroy secure stream\n@@ -90,6 +91,26 @@ LWS_VISIBLE LWS_EXTERN void\n lws_sspc_request_tx(struct lws_sspc_handle *pss);\n \n /**\n+ * lws_sspc_request_tx_len() - Schedule stream for tx with length hint\n+ *\n+ * \u005cparam h: pointer to handle representing stream that wants to transmit\n+ * \u005cparam len: the length of the write in bytes\n+ *\n+ * Schedules a write on the stream represented by \u005cp pss. When it's possible to\n+ * write on this stream, the *tx callback will occur with an empty buffer for\n+ * the stream owner to fill in.\n+ *\n+ * This api variant should be used when it's possible the payload will go out\n+ * over h1 with x-web-form-urlencoded or similar Content-Type.\n+ *\n+ * The serialized, sspc type api actually serializes and forwards the length\n+ * hint to its upstream proxy, where it's available for use to produce the\n+ * internet-capable protocol framing.\n+ */\n+LWS_VISIBLE LWS_EXTERN void\n+lws_sspc_request_tx_len(struct lws_sspc_handle *h, unsigned long len);\n+\n+/**\n * lws_sspc_client_connect() - Attempt the client connect\n *\n * \u005cparam h: secure streams handle\ndiff --git a/include/libwebsockets/lws-secure-streams.h b/include/libwebsockets/lws-secure-streams.h\nindex 2972c00..05b9043 100644\n--- a/include/libwebsockets/lws-secure-streams.h\n+++ b/include/libwebsockets/lws-secure-streams.h\n@@ -100,6 +100,11 @@\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@@ -233,6 +238,7 @@ enum {\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 \ndiff --git a/lib/secure-streams/private-lib-secure-streams.h b/lib/secure-streams/private-lib-secure-streams.h\nindex a6af660..eeef897 100644\n--- a/lib/secure-streams/private-lib-secure-streams.h\n+++ b/lib/secure-streams/private-lib-secure-streams.h\n@@ -269,6 +269,8 @@ typedef struct lws_sspc_handle {\n \n \tlws_usec_t\t\tus_earliest_write_req;\n \n+\tunsigned long\t\twriteable_len;\n+\n \tlws_ss_conn_states_t\tstate;\n \n \tuint32_t\t\ttimeout_ms;\ndiff --git a/lib/secure-streams/secure-streams-client.c b/lib/secure-streams/secure-streams-client.c\nindex 45f154f..f0a54e1 100644\n--- a/lib/secure-streams/secure-streams-client.c\n+++ b/lib/secure-streams/secure-streams-client.c\n@@ -248,30 +248,49 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,\n \t\t\t\tcp \u003d p;\n \t\t\t\tn \u003d lws_sspc_serialize_metadata(md, p);\n \n-\t\t\t\t/* in case anything else to write */\n-\t\t\t\tlws_callback_on_writable(h-\u003ecwsi);\n+\t\t\t\tlwsl_debug(\u0022%s: (local_conn) metadata\u005cn\u0022, __func__);\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_debug(\u0022%s: (local_conn) PAYLOAD_LENGTH_HINT %u\u005cn\u0022,\n+\t\t\t\t\t __func__, (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], 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_info(\u0022%s: conn_req_state %d\u005cn\u0022, __func__,\n+\t\t\t\t\t\th-\u003econn_req_state);\n \t\t\t\tbreak;\n \t\t\t}\n \n+\t\t\tlwsl_info(\u0022%s: (local_conn) onward connect\u005cn\u0022, __func__);\n+\n+\t\t\th-\u003econn_req_state \u003d LWSSSPC_ONW_ONGOING;\n \n-\t\t\th-\u003econn_req \u003d 0;\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 LPCS_OPERATIONAL:\n+\t\tcase LPCSCLI_OPERATIONAL:\n \n-\t\t\t/*\n-\t\t\t * Do we want to adjust the peer's ability to write\n-\t\t\t * to us?\n-\t\t\t */\n+\t\t\tlwsl_notice(\u0022%s: LPCSCLI_OPERATIONAL\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+\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\tif (h-\u003emetadata_owner.count) {\n@@ -282,12 +301,19 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,\n \t\t\t\tcp \u003d p;\n \t\t\t\tn \u003d lws_sspc_serialize_metadata(md, p);\n \n-\t\t\t\t/* in case anything else to write */\n-\t\t\t\tlws_callback_on_writable(h-\u003ecwsi);\n-\n-\t\t\t\tbreak;\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_info(\u0022%s: PAYLOAD_LENGTH_HINT %u\u005cn\u0022,\n+\t\t\t\t\t __func__, (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], 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@@ -328,6 +354,8 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,\n \t\t\tbreak;\n \t\t}\n \n+do_write_nz:\n+\n \t\tif (!n)\n \t\t\tbreak;\n \n@@ -350,6 +378,11 @@ hangup:\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@@ -484,17 +517,72 @@ lws_sspc_request_tx(lws_sspc_handle_t *h)\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+\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+void\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 has\n+\t * called CREATED state)\n+\t */\n+\n+\tif (!h)\n+\t\treturn;\n+\n+\tlwsl_notice(\u0022%s: setting h %p writeable_len %u\u005cn\u0022, __func__, h,\n+\t\t\t(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 \tlws_callback_on_writable(h-\u003ecwsi);\n }\n \n int\n lws_sspc_client_connect(lws_sspc_handle_t *h)\n {\n-\tif (!h || h-\u003estate \u003d\u003d LPCS_OPERATIONAL)\n+\tif (!h || h-\u003estate \u003d\u003d LPCSCLI_OPERATIONAL)\n \t\treturn 0;\n \n-\tassert(h-\u003estate \u003d\u003d LPCS_LOCAL_CONNECTED);\n-\th-\u003econn_req \u003d 1;\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 \ndiff --git a/lib/secure-streams/secure-streams-serialize.c b/lib/secure-streams/secure-streams-serialize.c\nindex 24bb911..9a1d61d 100644\n--- a/lib/secure-streams/secure-streams-serialize.c\n+++ b/lib/secure-streams/secure-streams-serialize.c\n@@ -74,6 +74,8 @@ typedef enum {\n \n \tRPAR_TIMEOUT0,\n \n+\tRPAR_PAYLEN0,\n+\n \tRPAR_RESULT_CREATION,\n \n \tRPAR_STATEINDEX,\n@@ -392,6 +394,15 @@ lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,\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@@ -727,6 +738,29 @@ payload_ff:\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 us\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\tlws_ss_request_tx_len(*pss, par-\u003etemp32);\n+\n+\t\t\tpar-\u003eps \u003d RPAR_TYPE;\n+\t\t\tbreak;\n+\n \t\tcase RPAR_METADATA_NAMELEN:\n \t\t\tif (!--par-\u003erem)\n \t\t\t\tgoto hangup;\ndiff --git a/lib/secure-streams/secure-streams.c b/lib/secure-streams/secure-streams.c\nindex 24fe673..597730f 100644\n--- a/lib/secure-streams/secure-streams.c\n+++ b/lib/secure-streams/secure-streams.c\n@@ -947,10 +947,14 @@ lws_ss_request_tx(lws_ss_handle_t *h)\n void\n lws_ss_request_tx_len(lws_ss_handle_t *h, unsigned long len)\n {\n-\tif (h-\u003ewsi)\n+\tif (h-\u003ewsi \u0026\u0026\n+\t (h-\u003epolicy-\u003eprotocol \u003d\u003d LWSSSP_H1 ||\n+\t h-\u003epolicy-\u003eprotocol \u003d\u003d LWSSSP_H2 ||\n+\t h-\u003epolicy-\u003eprotocol \u003d\u003d LWSSSP_WS))\n \t\th-\u003ewsi-\u003ehttp.writeable_len \u003d len;\n \telse\n \t\th-\u003ewriteable_len \u003d len;\n+\n \tlws_ss_request_tx(h);\n }\n \ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-post/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-post/CMakeLists.txt\nnew file mode 100644\nindex 0000000..c0bd62c\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-post/CMakeLists.txt\n@@ -0,0 +1,47 @@\n+project(lws-minimal-secure-streams-post C)\n+cmake_minimum_required(VERSION 2.8)\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-post)\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_STATIC_POLICY_ONLY 0 requirements)\n+require_lws_config(LWS_WITH_SYS_STATE 1 requirements)\n+\n+if (requirements)\n+\tadd_executable(${SAMP} minimal-secure-streams-post.c)\n+\n+\tif (LWS_CTEST_INTERNET_AVAILABLE)\n+\t\tadd_test(NAME sspost-warmcat COMMAND lws-minimal-secure-streams-post)\n+\t\tset_tests_properties(sspost-warmcat\n+\t\t\t\t PROPERTIES\n+\t\t\t\t WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams-post\n+\t\t\t\t TIMEOUT 20)\n+\tendif()\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+\n+\tif (LWS_WITH_SECURE_STREAMS_PROXY_API)\n+\t\tadd_compile_options(-DLWS_SS_USE_SSPC)\n+\n+\t\tadd_executable(${SAMP}-client minimal-secure-streams-post.c)\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+endif()\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-post/README.md b/minimal-examples/secure-streams/minimal-secure-streams-post/README.md\nnew file mode 100644\nindex 0000000..cb9b3b3\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-post/README.md\n@@ -0,0 +1,66 @@\n+# lws minimal secure streams\n+\n+The application goes to https://warmcat.com and reads index.html there.\n+\n+It does it using Secure Streams... the main code in minimal-secure-streams.c\n+just sets up the context and opens a secure stream of type \u0022mintest\u0022.\n+\n+The handler for state changes and payloads for \u0022mintest\u0022 is in ss-myss.c\n+\n+The information about how a \u0022mintest\u0022 stream should connect and the\n+protocol it uses is kept separated in policy-database.c\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| Run as proxy server for clients to connect to over unix domain socket\n+--force-portal|Force the SS Captive Portal Detection to feel it's behind a portal\n+--force-no-internet|Force the SS Captive Portal Detection to feel it can't reach the internet\n+\n+```\n+[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d\u003cverbosity\u003e] [-f]\n+[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0\n+[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0\n+[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com /\n+[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0\n+[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0\n+[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1\n+[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0\n+[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0\n+[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0\n+[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0\n+[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0\n+[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2\n+[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0\n+[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0\n+[2019/08/12 07:16:13:4781] USR: Completed: OK\n+```\ndiff --git a/minimal-examples/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c b/minimal-examples/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c\nnew file mode 100644\nindex 0000000..d112ee2\n--- /dev/null\n+++ b/minimal-examples/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c\n@@ -0,0 +1,520 @@\n+/*\n+ * lws-minimal-secure-streams-post\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 demonstrates a minimal http client using secure streams api.\n+ *\n+ * It visits https://warmcat.com/ and receives the html page there.\n+ *\n+ * This example is built two different ways from the same source... one includes\n+ * the policy everything needed to fulfil the stream directly. The other -client\n+ * variant has no policy itself and some other minor init changes, and connects\n+ * to the -proxy example to actually get the connection done.\n+ *\n+ * In the -client build case, the example does not even init the tls libraries\n+ * since the proxy part will take care of all that.\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csignal.h\u003e\n+\n+/*\n+ * uncomment to force network traffic through 127.0.0.1:1080\n+ *\n+ * On your local machine, you can run a SOCKS5 proxy like this\n+ *\n+ * $ ssh -N -D 0.0.0.0:1080 localhost -v\n+ *\n+ * If enabled, this also fetches a remote policy that also\n+ * specifies that all traffic should go through the remote\n+ * proxy.\n+ */\n+// #define VIA_LOCALHOST_SOCKS\n+\n+static int interrupted, bad \u003d 1, force_cpd_fail_portal,\n+\t force_cpd_fail_no_internet;\n+static unsigned int timeout_ms \u003d 3000;\n+static lws_state_notify_link_t nl;\n+\n+static const char * const postbody \u003d\n+\t\u0022--boundary\u005cr\u005cn\u0022\n+\t\u0022Content-Disposition: form-data; name\u003d\u005c\u0022text\u005c\u0022\u005cr\u005cn\u0022\n+\t\u0022\u005cr\u005cn\u0022\n+\t\u0022value1\u005cr\u005cn\u0022\n+\t\u0022--boundary\u005cr\u005cn\u0022\n+\t\u0022Content-Disposition: form-data; \u0022\n+\t\t\u0022name\u003d\u005c\u0022field2\u005c\u0022; filename\u003d\u005c\u0022example.txt\u005c\u0022\u005cr\u005cn\u0022\n+\t\u0022\u005cr\u005cn\u0022\n+\t\u0022value2\u005cr\u005cn\u0022\n+\t\u0022--boundary--\u005cr\u005cn\u0022;\n+\n+#define POSTBODY_SIZE strlen(postbody)\n+\n+/*\n+ * If the -proxy app is fulfilling our connection, then we don't need to have\n+ * the policy in the client.\n+ *\n+ * When we build with LWS_SS_USE_SSPC, the apis hook up to a proxy process over\n+ * a Unix Domain Socket. To test that, you need to separately run the\n+ * ./lws-minimal-secure-streams-proxy test app on the same machine.\n+ */\n+\n+#if !defined(LWS_SS_USE_SSPC)\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+#if defined(VIA_LOCALHOST_SOCKS)\n+\t \u0022\u005c\u0022via-socks5\u005c\u0022:\u0022 \u0022\u005c\u0022127.0.0.1:1080\u005c\u0022,\u0022\n+#endif\n+\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\u0022isrg_root_x1\u005c\u0022: \u005c\u0022\u0022 /* ISRG ROOT X1 */\n+\t\u0022MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\u0022\n+\t\u0022TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\u0022\n+\t\u0022cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\u0022\n+\t\u0022WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\u0022\n+\t\u0022ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\u0022\n+\t\u0022MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\u0022\n+\t\u0022h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\u0022\n+\t\u00220TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\u0022\n+\t\u0022A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\u0022\n+\t\u0022T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\u0022\n+\t\u0022B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\u0022\n+\t\u0022B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\u0022\n+\t\u0022KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\u0022\n+\t\u0022OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\u0022\n+\t\u0022jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\u0022\n+\t\u0022qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\u0022\n+\t\u0022rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\u0022\n+\t\u0022HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\u0022\n+\t\u0022hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\u0022\n+\t\u0022ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\u0022\n+\t\u00223BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\u0022\n+\t\u0022NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\u0022\n+\t\u0022ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\u0022\n+\t\u0022TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\u0022\n+\t\u0022jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\u0022\n+\t\u0022oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\u0022\n+\t\u00224RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\u0022\n+\t\u0022mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\u0022\n+\t\u0022emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc\u003d\u0022\n+\t\t\u0022\u005c\u0022},\u0022\n+\t\t\u0022{\u005c\u0022LEX3_isrg_root_x1\u005c\u0022: \u005c\u0022\u0022 /* LE X3 signed by ISRG X1 root */\n+\t\u0022MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw\u0022\n+\t\u0022TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\u0022\n+\t\u0022cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1\u0022\n+\t\u0022WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\u0022\n+\t\u0022RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi\u0022\n+\t\u0022MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX\u0022\n+\t\u0022NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf\u0022\n+\t\u002289B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl\u0022\n+\t\u0022Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc\u0022\n+\t\u0022Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz\u0022\n+\t\u0022uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB\u0022\n+\t\u0022AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU\u0022\n+\t\u0022BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB\u0022\n+\t\u0022FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo\u0022\n+\t\u0022SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js\u0022\n+\t\u0022LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF\u0022\n+\t\u0022BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG\u0022\n+\t\u0022AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD\u0022\n+\t\u0022VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB\u0022\n+\t\u0022ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx\u0022\n+\t\u0022A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM\u0022\n+\t\u0022UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2\u0022\n+\t\u0022DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1\u0022\n+\t\u0022eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu\u0022\n+\t\u0022OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw\u0022\n+\t\u0022p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY\u0022\n+\t\u00222PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0\u0022\n+\t\u0022ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR\u0022\n+\t\u0022PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b\u0022\n+\t\u0022rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt\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_isrg\u005c\u0022,\u0022\n+\t\t\t\u0022\u005c\u0022stack\u005c\u0022: [\u0022\n+\t\t\t\t\u0022\u005c\u0022isrg_root_x1\u005c\u0022,\u0022\n+\t\t\t\t\u0022\u005c\u0022LEX3_isrg_root_x1\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/*\n+\t\t * \u0022fetch_policy\u0022 decides from where the real policy\n+\t\t * will be fetched, if present. Otherwise the initial\n+\t\t * policy is treated as the whole, hardcoded, policy.\n+\t\t */\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+#if defined(VIA_LOCALHOST_SOCKS)\n+\t\t\t\u0022\u005c\u0022http_url\u005c\u0022:\u0022\t\t\u0022\u005c\u0022policy/minimal-proxy-socks.json\u005c\u0022,\u0022\n+#else\n+\t\t\t\u0022\u005c\u0022http_url\u005c\u0022:\u0022\t\t\u0022\u005c\u0022policy/minimal-proxy.json\u005c\u0022,\u0022\n+#endif\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_isrg\u005c\u0022\u0022\n+\t\t\u0022}},{\u0022\n+\t\t\t/*\n+\t\t\t * \u0022captive_portal_detect\u0022 describes\n+\t\t\t * what to do in order to check if the path to\n+\t\t\t * the Internet is being interrupted by a\n+\t\t\t * captive portal. If there's a larger policy\n+\t\t\t * fetched from elsewhere, it should also include\n+\t\t\t * this since it needs to be done at least after\n+\t\t\t * every DHCP acquisition\n+\t\t\t */\n+\t\t \u0022\u005c\u0022captive_portal_detect\u005c\u0022: {\u0022\n+ \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+ \u0022\u005c\u0022protocol\u005c\u0022: \u005c\u0022h1\u005c\u0022,\u0022\n+ \u0022\u005c\u0022http_method\u005c\u0022: \u005c\u0022GET\u005c\u0022,\u0022\n+ \u0022\u005c\u0022opportunistic\u005c\u0022: true,\u0022\n+ \u0022\u005c\u0022http_expect\u005c\u0022: 204,\u0022\n+\t\t\t\u0022\u005c\u0022http_fail_redirect\u005c\u0022: true\u0022\n+ \u0022}}\u0022\n+\t\u0022]}\u0022\n+;\n+\n+#endif\n+\n+typedef struct myss {\n+\tstruct lws_ss_handle \t\t*ss;\n+\tvoid\t\t\t\t*opaque_data;\n+\t/* ... application specific state ... */\n+\tlws_sorted_usec_list_t\t\tsul;\n+} myss_t;\n+\n+#if !defined(LWS_SS_USE_SSPC)\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+#endif\n+\n+/* secure streams payload interface */\n+\n+static int\n+myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)\n+{\n+//\tmyss_t *m \u003d (myss_t *)userobj;\n+\n+\tlwsl_user(\u0022%s: len %d, flags: %d\u005cn\u0022, __func__, (int)len, flags);\n+\tlwsl_hexdump_info(buf, len);\n+\n+\t/*\n+\t * If we received the whole message, for our example it means\n+\t * we are done.\n+\t */\n+\tif (flags \u0026 LWSSS_FLAG_EOM) {\n+\t\tbad \u003d 0;\n+\t\tinterrupted \u003d 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,\n+\tint *flags)\n+{\n+\t// myss_t *m \u003d (myss_t *)userobj;\n+\n+\t/*\n+\t * A more flexible solution would send incrementally tracking the\n+\t * status in members in m above.\n+\t */\n+\n+\tif (*len \u003c POSTBODY_SIZE)\n+\t\treturn LWSSSSRET_TX_DONT_SEND;\n+\n+\t*flags \u003d LWSSS_FLAG_SOM | LWSSS_FLAG_EOM;\n+\n+\tmemcpy(buf, postbody, strlen(postbody));\n+\t*len \u003d POSTBODY_SIZE;\n+\n+\treturn 0;\n+}\n+\n+static int\n+myss_state(void *userobj, void *sh, lws_ss_constate_t state,\n+\t lws_ss_tx_ordinal_t ack)\n+{\n+\tmyss_t *m \u003d (myss_t *)userobj;\n+\n+\tlwsl_user(\u0022%s: h %p, %s, ord 0x%x\u005cn\u0022, __func__, m-\u003ess,\n+\t\t\tlws_ss_state_name(state), (unsigned int)ack);\n+\n+\tswitch (state) {\n+\tcase LWSSSCS_CREATING:\n+\n+\t\t/*\n+\t\t * CREATING is only coming after we have asked the upstream\n+\t\t * proxy to create the stream and it has been allowed.\n+\t\t */\n+\n+\t\tlws_ss_set_metadata(m-\u003ess, \u0022ctype\u0022,\n+\t\t\t\t \u0022multipart/form-data;boundary\u003d\u005c\u0022boundary\u005c\u0022\u0022,\n+\t\t\t\t 39);\n+\n+\t\t/* provide a hint about the payload size */\n+\t\tlws_ss_request_tx_len(m-\u003ess, strlen(postbody));\n+\t\tbreak;\n+\tcase LWSSSCS_CONNECTED:\n+\t\tlws_ss_request_tx(m-\u003ess);\n+\t\tbreak;\n+\tcase LWSSSCS_ALL_RETRIES_FAILED:\n+\t\t/* if we're out of retries, we want to close the app and FAIL */\n+\t\tinterrupted \u003d 1;\n+\t\tbreak;\n+\tcase LWSSSCS_QOS_ACK_REMOTE:\n+\t\tlwsl_notice(\u0022%s: LWSSSCS_QOS_ACK_REMOTE\u005cn\u0022, __func__);\n+\t\tbreak;\n+\n+\tcase LWSSSCS_TIMEOUT:\n+\t\tlwsl_notice(\u0022%s: LWSSSCS_TIMEOUT\u005cn\u0022, __func__);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\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+#if !defined(LWS_SS_USE_SSPC)\n+\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+#endif\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+\n+#if !defined(LWS_SS_USE_SSPC)\n+\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+\n+#endif\n+\n+\tcase LWS_SYSTATE_OPERATIONAL:\n+\t\tif (current \u003d\u003d LWS_SYSTATE_OPERATIONAL) {\n+\t\t\tlws_ss_info_t ssi;\n+\n+\t\t\t/* We're making an outgoing secure stream ourselves */\n+\n+\t\t\tmemset(\u0026ssi, 0, sizeof(ssi));\n+\t\t\tssi.handle_offset \u003d offsetof(myss_t, ss);\n+\t\t\tssi.opaque_user_data_offset \u003d offsetof(myss_t,\n+\t\t\t\t\t\t\t opaque_data);\n+\t\t\tssi.rx \u003d myss_rx;\n+\t\t\tssi.tx \u003d myss_tx;\n+\t\t\tssi.state \u003d myss_state;\n+\t\t\tssi.user_alloc \u003d sizeof(myss_t);\n+\t\t\tssi.streamtype \u003d \u0022minpost\u0022;\n+\n+\t\t\tif (lws_ss_create(context, 0, \u0026ssi, NULL, NULL,\n+\t\t\t\t\t NULL, NULL)) {\n+\t\t\t\tlwsl_err(\u0022%s: failed to create secure stream\u005cn\u0022,\n+\t\t\t\t\t __func__);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\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+static void\n+sigint_handler(int sig)\n+{\n+\tinterrupted \u003d 1;\n+}\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct lws_context_creation_info info;\n+\tstruct lws_context *context;\n+\tconst char *p;\n+\tint n \u003d 0;\n+\n+\tsignal(SIGINT, sigint_handler);\n+\n+\tmemset(\u0026info, 0, sizeof info);\n+\tlws_cmdline_option_handle_builtin(argc, argv, \u0026info);\n+\n+\tlwsl_user(\u0022LWS secure streams test client [-d\u003cverb\u003e]\u005cn\u0022);\n+\n+\t/* these options are mutually exclusive if given */\n+\n+\tif (lws_cmdline_option(argc, argv, \u0022--force-portal\u0022))\n+\t\tforce_cpd_fail_portal \u003d 1;\n+\n+\tif (lws_cmdline_option(argc, argv, \u0022--force-no-internet\u0022))\n+\t\tforce_cpd_fail_no_internet \u003d 1;\n+\n+\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--timeout_ms\u0022)))\n+\t\ttimeout_ms \u003d atoi(p);\n+\n+\tinfo.fd_limit_per_thread \u003d 1 + 6 + 1;\n+\tinfo.port \u003d CONTEXT_PORT_NO_LISTEN;\n+#if defined(LWS_SS_USE_SSPC)\n+\tinfo.protocols \u003d lws_sspc_protocols;\n+\t{\n+\t\tconst char *p;\n+\n+\t\t/* connect to ssproxy via UDS by default, else via\n+\t\t * tcp connection to this port */\n+\t\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-p\u0022)))\n+\t\t\tinfo.ss_proxy_port \u003d atoi(p);\n+\n+\t\t/* UDS \u0022proxy.ss.lws\u0022 in abstract namespace, else this socket\n+\t\t * path; when -p given this can specify the network interface\n+\t\t * to bind to */\n+\t\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-i\u0022)))\n+\t\t\tinfo.ss_proxy_bind \u003d p;\n+\n+\t\t/* if -p given, -a specifies the proxy address to connect to */\n+\t\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-a\u0022)))\n+\t\t\tinfo.ss_proxy_address \u003d p;\n+\t}\n+#else\n+\tinfo.pss_policies_json \u003d default_ss_policy;\n+\tinfo.options \u003d LWS_SERVER_OPTION_EXPLICIT_VHOSTS |\n+\t\t LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;\n+#endif\n+#if defined(LWS_WITH_DETAILED_LATENCY)\n+\tinfo.detailed_latency_cb \u003d lws_det_lat_plot_cb;\n+\tinfo.detailed_latency_filepath \u003d \u0022/tmp/lws-latency-ssproxy\u0022;\n+#endif\n+\n+\t/* integrate us with lws system state management when context created */\n+\n+\tnl.name \u003d \u0022app\u0022;\n+\tnl.notify_cb \u003d app_system_state_nf;\n+\tinfo.register_notifier_list \u003d app_notifier_list;\n+\n+\t/* create the context */\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+#if !defined(LWS_SS_USE_SSPC)\n+\t/*\n+\t * If we're being a proxied client, the proxy does all this\n+\t */\n+\n+\t/*\n+\t * Set the related lws_system blobs\n+\t *\n+\t * ...direct_set() sets a pointer, so the thing pointed to has to have\n+\t * a suitable lifetime, eg, something that already exists on the heap or\n+\t * a const string in .rodata like this\n+\t */\n+\n+\tlws_system_blob_direct_set(lws_system_get_blob(context,\n+\t\t\t\t LWS_SYSBLOB_TYPE_DEVICE_SERIAL, 0),\n+\t\t\t\t (const uint8_t *)\u0022SN12345678\u0022, 10);\n+\tlws_system_blob_direct_set(lws_system_get_blob(context,\n+\t\t\t\t LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, 0),\n+\t\t\t\t (const uint8_t *)\u0022v0.01\u0022, 5);\n+\n+\t/*\n+\t * ..._heap_append() appends to a buflist kind of arrangement on heap,\n+\t * just one block is fine, otherwise it will concatenate the fragments\n+\t * in the order they were appended (and take care of freeing them at\n+\t * context destroy time). ..._heap_empty() is also available to remove\n+\t * everything that was already allocated.\n+\t *\n+\t * Here we use _heap_append() just so it's tested as well as direct set.\n+\t */\n+\n+\tlws_system_blob_heap_append(lws_system_get_blob(context,\n+\t\t\t\t LWS_SYSBLOB_TYPE_DEVICE_TYPE, 0),\n+\t\t\t\t (const uint8_t *)\u0022spacerocket\u0022, 11);\n+#endif\n+\n+\t/* the event loop */\n+\n+\twhile (n \u003e\u003d 0 \u0026\u0026 !interrupted)\n+\t\tn \u003d lws_service(context, 0);\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/minimal-secure-streams.c b/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c\nindex 0e579a1..4a79881 100644\n--- a/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c\n+++ b/minimal-examples/secure-streams/minimal-secure-streams/minimal-secure-streams.c\n@@ -253,7 +253,9 @@ myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,\n {\n \t//myss_t *m \u003d (myss_t *)userobj;\n \n-\treturn 0;\n+\t/* in this example, we don't send stuff */\n+\n+\treturn LWSSSSRET_TX_DONT_SEND;\n }\n \n static int\n","s":{"c":1750805505,"u": 7512}}
],"g": 3504,"chitpc": 0,"ehitpc": 0,"indexed":0
,
"ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "7d0a"}