{"schema":"libjg2-1",
"vpath":"/git/",
"avatar":"/git/avatar/",
"alang":"",
"gen_ut":1745907226,
"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":"2896e02611a9bc47a05a66514caad11a",
"commit": {"type":"commit",
"time": 1535870585,
"time_ofs": 480,
"oid_tree": { "oid": "0ea752ffb582f240b0c9f28018d3e6ed8d6333e6", "alias": []},
"oid":{ "oid": "d58828692e6832a2878df9375d39289df5144fa0", "alias": []},
"msg": "http: compression methods",
"sig_commit": { "git_time": { "time": 1535870585, "offset": 480 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },
"sig_author": { "git_time": { "time": 1535870585, "offset": 480 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }},
"body": "http: compression methods\n\nAdd generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.\n\nThis is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist\noutside that context.\n\nCurrently provides 'deflate' and 'br' compression methods for server side only.\n\n'br' requires also -DLWS_WITH_HTTP_BROTLI\u003d1 at cmake and the brotli libraries (available in\nyour distro already) and dev package.\n\nOther compression methods can be added nicely using an ops struct.\n\nThe built-in file serving stuff will use this is the client says he can handle it, and the\nmimetype of the file either starts with \u0022text/\u0022 (html and css etc) or is the mimetype of\nJavascript.\n\nzlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this\nis only useful on relatively strong servers with lots of memory. However for some usecases\nwhere you are serving a lot of css and js assets, it's a nice help.\n\nThe patch performs special treatment for http/1.1 pipelining, since the compression is\nperformed on the fly the compressed content-length is not known until the end. So for h1\nonly, chunked transfer-encoding is automatically added so pipelining can continue of the\nconnection.\n\nFor h2 the chunking is neither supported nor required, so it \u0022just works\u0022.\n\nUser code can also request to add a compression transform before the reply headers were\nsent using the new api\n\nLWS_VISIBLE int\nlws_http_compression_apply(struct lws *wsi, const char *name,\n\t\t\t unsigned char **p, unsigned char *end, char decomp);\n\n... this allows transparent compression of dynamically generated HTTP. The requested\ncompression (eg, \u0022deflate\u0022) is only applied if the client headers indicated it was\nsupported, otherwise it's a NOP.\n\nName may be NULL in which case the first compression method in the internal table at\nstream.c that is mentioned as acceptable by the client will be used.\n\nNOTE: the compression translation, same as h2 support, relies on the user code using\nLWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal\nlws fileserving code already does this."
,
"diff": "diff --git a/CMakeLists.txt b/CMakeLists.txt\nindex edd94a7..1717102 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -37,6 +37,8 @@ option(LWS_WITH_PEER_LIMITS \u0022Track peers and restrict resources a single peer ca\n option(LWS_WITH_ACCESS_LOG \u0022Support generating Apache-compatible access logs\u0022 OFF)\n option(LWS_WITH_RANGES \u0022Support http ranges (RFC7233)\u0022 OFF)\n option(LWS_WITH_SERVER_STATUS \u0022Support json + jscript server monitoring\u0022 OFF)\n+option(LWS_WITH_HTTP_STREAM_COMPRESSION \u0022Support HTTP stream compression\u0022 OFF)\n+option(LWS_WITH_HTTP_BROTLI \u0022Also offer brotli http stream compression (requires LWS_WITH_HTTP_STREAM_COMPRESSION)\u0022 OFF)\n option(LWS_WITH_ACME \u0022Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)\u0022 OFF)\n #\n # TLS library options... all except mbedTLS are basically OpenSSL variants.\n@@ -438,6 +440,10 @@ if (LWS_WITH_SSL AND LWS_WITH_MBEDTLS)\n \tset(USE_MBEDTLS 1)\n endif()\n \n+if (LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tset(LWS_WITH_ZLIB 1)\n+endif()\n+\n if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB)\n \tif (\u0022${LWS_ZLIB_LIBRARIES}\u0022 STREQUAL \u0022\u0022 OR \u0022${LWS_ZLIB_INCLUDE_DIRS}\u0022 STREQUAL \u0022\u0022)\n \telse()\n@@ -746,6 +752,15 @@ if (LWS_ROLE_H1 OR LWS_ROLE_H2)\n \tlist(APPEND SOURCES\n \t\tlib/roles/http/header.c\n \t\tlib/roles/http/server/parsers.c)\n+\tif (LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\tlist(APPEND SOURCES\n+\t\t\tlib/roles/http/compression/stream.c\n+\t\t\tlib/roles/http/compression/deflate/deflate.c)\n+\t\tif (LWS_WITH_HTTP_BROTLI)\n+\t\t\tlist(APPEND SOURCES\n+\t\t\t\tlib/roles/http/compression/brotli/brotli.c)\n+\t\tendif()\n+\tendif()\n endif()\n \n if (LWS_ROLE_H1)\n@@ -1201,7 +1216,7 @@ set(LIB_LIST)\n #\n \n #\n-# ZLIB (Only needed for deflate extensions).\n+# ZLIB (needed for deflate extension and if LWS_WITH_HTTP_STREAM_COMPRESSION)\n #\n if (LWS_WITH_ZLIB)\n \tif (LWS_WITH_BUNDLED_ZLIB)\n@@ -1242,6 +1257,10 @@ if (LWS_WITH_ZLIB)\n \tlist(APPEND LIB_LIST ${ZLIB_LIBRARIES})\n endif()\n \n+if (LWS_WITH_HTTP_BROTLI)\n+\tlist(APPEND LIB_LIST brotlienc brotlidec brotlidec)\n+endif()\n+\n #\n # OpenSSL\n #\n@@ -1620,6 +1639,10 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS)\n \t\t\tadd_dependencies(${TEST_NAME} websockets)\n \t\tendif()\n \n+\t\tif (LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t\ttarget_link_libraries(${TEST_NAME} z)\n+\t\tendif()\n+\n \t\t# Set test app specific defines.\n \t\tset_property(TARGET ${TEST_NAME}\n \t\t\t\t\tPROPERTY COMPILE_DEFINITIONS\ndiff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in\nindex ea5367c..6c6418c 100644\n--- a/cmake/lws_config.h.in\n+++ b/cmake/lws_config.h.in\n@@ -180,4 +180,7 @@\n \n #cmakedefine LWS_HAS_INTPTR_T\n \n+#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION\n+#cmakedefine LWS_WITH_HTTP_BROTLI\n+\n ${LWS_SIZEOFPTR_CODE}\ndiff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c\nindex 10ecb47..eeb9e16 100644\n--- a/lib/core/libwebsockets.c\n+++ b/lib/core/libwebsockets.c\n@@ -723,14 +723,24 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *\n \t\tgoto just_kill_connection;\n \n \tcase LRS_FLUSHING_BEFORE_CLOSE:\n-\t\tif (lws_has_buffered_out(wsi)) {\n+\t\tif (lws_has_buffered_out(wsi)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t || wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t\t ) {\n \t\t\tlws_callback_on_writable(wsi);\n \t\t\treturn;\n \t\t}\n \t\tlwsl_info(\u0022%p: end LRS_FLUSHING_BEFORE_CLOSE\u005cn\u0022, wsi);\n \t\tgoto just_kill_connection;\n \tdefault:\n-\t\tif (lws_has_buffered_out(wsi)) {\n+\t\tif (lws_has_buffered_out(wsi)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t\t\t|| wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t\t) {\n \t\t\tlwsl_info(\u0022%p: LRS_FLUSHING_BEFORE_CLOSE\u005cn\u0022, wsi);\n \t\t\tlwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);\n \t\t\t__lws_set_timeout(wsi,\ndiff --git a/lib/core/output.c b/lib/core/output.c\nindex 3e0ffe4..8f6349b 100644\n--- a/lib/core/output.c\n+++ b/lib/core/output.c\n@@ -31,12 +31,14 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)\n \tsize_t real_len \u003d len;\n \tunsigned int n;\n \n+\t// lwsl_notice(\u0022%s: len %d\u005cn\u0022, __func__, (int)len);\n+\n \t/*\n \t * Detect if we got called twice without going through the\n \t * event loop to handle pending. Since that guarantees extending any\n \t * existing buflist_out it's inefficient.\n \t */\n-\tif (buf \u0026\u0026 wsi-\u003ecould_have_pending) {\n+\tif (0 \u0026\u0026 buf \u0026\u0026 wsi-\u003ecould_have_pending) {\n \t\tlwsl_hexdump_level(LLL_INFO, buf, len);\n \t\tlwsl_info(\u0022** %p: vh: %s, prot: %s, role %s: \u0022\n \t\t\t \u0022Inefficient back-to-back write of %lu detected...\u005cn\u0022,\n@@ -49,7 +51,11 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)\n \n \t/* just ignore sends after we cleared the truncation buffer */\n \tif (lwsi_state(wsi) \u003d\u003d LRS_FLUSHING_BEFORE_CLOSE \u0026\u0026\n-\t !lws_has_buffered_out(wsi))\n+\t !lws_has_buffered_out(wsi)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t \u0026\u0026 !wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t )\n \t\treturn (int)len;\n \n \tif (buf \u0026\u0026 lws_has_buffered_out(wsi)) {\n@@ -74,6 +80,8 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)\n \n \t\tlen \u003d lws_buflist_next_segment_len(\u0026wsi-\u003ebuflist_out, \u0026buf);\n \t\treal_len \u003d len;\n+\n+\t\tlwsl_debug(\u0022%s: draining %d\u005cn\u0022, __func__, (int)len);\n \t}\n \n \tif (!len)\n@@ -155,6 +163,11 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)\n \t\treturn n;\n \t}\n \n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tif (wsi-\u003ehttp.comp_ctx.may_have_more)\n+\t\tlws_callback_on_writable(wsi);\n+#endif\n+\n \tif ((unsigned int)n \u003d\u003d real_len)\n \t\t/* what we just sent went out cleanly */\n \t\treturn n;\ndiff --git a/lib/core/service.c b/lib/core/service.c\nindex 994b169..3c4b8f1 100644\n--- a/lib/core/service.c\n+++ b/lib/core/service.c\n@@ -74,6 +74,8 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)\n \t * Priority 1: pending truncated sends are incomplete ws fragments\n \t *\t If anything else sent first the protocol would be\n \t *\t corrupted.\n+\t *\n+\t *\t These are post- any compression transform\n \t */\n \n \tif (lws_has_buffered_out(wsi)) {\n@@ -90,6 +92,28 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)\n \t\t\tgoto bail_die; /* retry closing now */\n \t\t}\n \n+\t/* Priority 2: pre- compression transform */\n+\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tif (wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more) {\n+\t\tenum lws_write_protocol wp \u003d LWS_WRITE_HTTP;\n+\n+\t\tlwsl_debug(\u0022%s: completing comp partial (buflist_comp %p, may %d)\u005cn\u0022,\n+\t\t\t\t__func__, wsi-\u003ehttp.comp_ctx.buflist_comp,\n+\t\t\t\twsi-\u003ehttp.comp_ctx.may_have_more\n+\t\t\t\t);\n+\n+\t\tif (wsi-\u003erole_ops-\u003ewrite_role_protocol(wsi, NULL, 0, \u0026wp) \u003c 0) {\n+\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n+\t\t\tgoto bail_die;\n+\t\t}\n+\t\tlws_callback_on_writable(wsi);\n+\n+\t\tgoto bail_ok;\n+\t}\n+#endif\n+\n #ifdef LWS_WITH_CGI\n \t/*\n \t * A cgi master's wire protocol remains h1 or h2. He is just getting\ndiff --git a/lib/libwebsockets.h b/lib/libwebsockets.h\nindex 65de30e..74a414a 100644\n--- a/lib/libwebsockets.h\n+++ b/lib/libwebsockets.h\n@@ -3410,6 +3410,35 @@ struct lws_http_mount {\n \n \tvoid *_unused[2]; /**\u003c dummy */\n };\n+\n+/**\n+ * lws_http_compression_apply() - apply an http compression transform\n+ *\n+ * \u005cparam wsi: the wsi to apply the compression transform to\n+ * \u005cparam name: NULL, or the name of the compression transform, eg, \u0022deflate\u0022\n+ * \u005cparam p: pointer to pointer to headers buffer\n+ * \u005cparam end: pointer to end of headers buffer\n+ * \u005cparam decomp: 0 \u003d add compressor to wsi, 1 \u003d add decompressor\n+ *\n+ * This allows transparent compression of dynamically generated HTTP. The\n+ * requested compression (eg, \u0022deflate\u0022) is only applied if the client headers\n+ * indicated it was supported (and it has support in lws), otherwise it's a NOP.\n+ *\n+ * If the requested compression method is NULL, then the supported compression\n+ * formats are tried, and for non-decompression (server) mode the first that's\n+ * found on the client's accept-encoding header is chosen.\n+ *\n+ * NOTE: the compression transform, same as h2 support, relies on the user\n+ * code using LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part\n+ * written. The internal lws fileserving code already does this.\n+ *\n+ * If the library was built without the cmake option\n+ * LWS_WITH_HTTP_STREAM_COMPRESSION set, then a NOP is provided for this api,\n+ * allowing user code to build either way and use compression if available.\n+ */\n+LWS_VISIBLE int\n+lws_http_compression_apply(struct lws *wsi, const char *name,\n+\t\t\t unsigned char **p, unsigned char *end, char decomp);\n ///@}\n ///@}\n \ndiff --git a/lib/plat/esp32/esp32-sockets.c b/lib/plat/esp32/esp32-sockets.c\nindex 9bcaf3b..67b39a8 100644\n--- a/lib/plat/esp32/esp32-sockets.c\n+++ b/lib/plat/esp32/esp32-sockets.c\n@@ -44,7 +44,12 @@ lws_send_pipe_choked(struct lws *wsi)\n \twsi_eff-\u003ecould_have_pending \u003d 0;\n \n \t/* treat the fact we got a truncated send pending as if we're choked */\n-\tif (lws_has_buffered_out(wsi))\n+\tif (lws_has_buffered_out(wsi)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t || wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t)\n \t\treturn 1;\n \n \tFD_ZERO(\u0026writefds);\ndiff --git a/lib/plat/optee/lws-plat-optee.c b/lib/plat/optee/lws-plat-optee.c\nindex 6d5e32b..f100018 100644\n--- a/lib/plat/optee/lws-plat-optee.c\n+++ b/lib/plat/optee/lws-plat-optee.c\n@@ -54,7 +54,12 @@ lws_send_pipe_choked(struct lws *wsi)\n \twsi_eff-\u003ecould_have_pending \u003d 0;\n \n \t/* treat the fact we got a truncated send pending as if we're choked */\n-\tif (lws_has_buffered_out(wsi_eff))\n+\tif (lws_has_buffered_out(wsi_eff)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t || wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t)\n \t\treturn 1;\n \n #if 0\ndiff --git a/lib/plat/unix/unix-sockets.c b/lib/plat/unix/unix-sockets.c\nindex e8b734a..b423eaa 100644\n--- a/lib/plat/unix/unix-sockets.c\n+++ b/lib/plat/unix/unix-sockets.c\n@@ -41,7 +41,12 @@ lws_send_pipe_choked(struct lws *wsi)\n \twsi_eff-\u003ecould_have_pending \u003d 0;\n \n \t/* treat the fact we got a truncated send pending as if we're choked */\n-\tif (lws_has_buffered_out(wsi_eff))\n+\tif (lws_has_buffered_out(wsi_eff)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t ||wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t )\n \t\treturn 1;\n \n \tfds.fd \u003d wsi_eff-\u003edesc.sockfd;\ndiff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c\nindex 3a5d725..99d9f6a 100644\n--- a/lib/plat/windows/windows-sockets.c\n+++ b/lib/plat/windows/windows-sockets.c\n@@ -15,7 +15,12 @@ lws_send_pipe_choked(struct lws *wsi)\n \twsi_eff-\u003ecould_have_pending \u003d 0;\n \n \t/* treat the fact we got a truncated send pending as if we're choked */\n-\tif (lws_has_buffered_out(wsi_eff))\n+\tif (lws_has_buffered_out(wsi_eff)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t ||wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t)\n \t\treturn 1;\n \n \treturn (int)wsi_eff-\u003esock_send_blocking;\ndiff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c\nindex 09ff884..9d53730 100644\n--- a/lib/roles/h1/ops-h1.c\n+++ b/lib/roles/h1/ops-h1.c\n@@ -522,6 +522,27 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,\n \t}\n #endif\n \n+#if 0\n+\n+\t/*\n+\t * !!! lws_serve_http_file_fragment() seems to duplicate most of\n+\t * lws_handle_POLLOUT_event() in its own loop...\n+\t */\n+\tlwsl_debug(\u0022%s: %d %d\u005cn\u0022, __func__, (pollfd-\u003erevents \u0026 LWS_POLLOUT),\n+\t\t\tlwsi_state_can_handle_POLLOUT(wsi));\n+\n+\tif ((pollfd-\u003erevents \u0026 LWS_POLLOUT) \u0026\u0026\n+\t lwsi_state_can_handle_POLLOUT(wsi) \u0026\u0026\n+\t lws_handle_POLLOUT_event(wsi, pollfd)) {\n+\t\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE)\n+\t\t\tlwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);\n+\t\t/* the write failed... it's had it */\n+\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n+\n+\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n+\t}\n+#endif\n+\n if (lws_is_flowcontrolled(wsi))\n /* We cannot deal with any kind of new RX because we are\n * RX-flowcontrolled.\n@@ -611,19 +632,67 @@ static int\n rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t\t enum lws_write_protocol *wp)\n {\n-#if 0\n-\t/* if not in a state to send stuff, then just send nothing */\n+\tsize_t olen \u003d len;\n+\tint n;\n \n-\tif ((lwsi_state(wsi) !\u003d LRS_RETURNED_CLOSE \u0026\u0026\n-\t lwsi_state(wsi) !\u003d LRS_WAITING_TO_SEND_CLOSE \u0026\u0026\n-\t lwsi_state(wsi) !\u003d LRS_AWAITING_CLOSE_ACK)) {\n-\t\t//assert(0);\n-\t\tlwsl_debug(\u0022binning %d %d\u005cn\u0022, lwsi_state(wsi), *wp);\n-\t\treturn 0;\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tif (wsi-\u003ehttp.lcs \u0026\u0026 (((*wp) \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP_FINAL ||\n+\t\t\t ((*wp) \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP)) {\n+\t\tunsigned char mtubuf[1400 + LWS_PRE +\n+\t\t\t\t LWS_HTTP_CHUNK_HDR_MAX_SIZE +\n+\t\t\t\t LWS_HTTP_CHUNK_TRL_MAX_SIZE],\n+\t\t\t *out \u003d mtubuf + LWS_PRE +\n+\t\t\t\t LWS_HTTP_CHUNK_HDR_MAX_SIZE;\n+\t\tsize_t o \u003d sizeof(mtubuf) - LWS_PRE -\n+\t\t\t LWS_HTTP_CHUNK_HDR_MAX_SIZE -\n+\t\t\t LWS_HTTP_CHUNK_TRL_MAX_SIZE;\n+\t\tchar c[LWS_HTTP_CHUNK_HDR_MAX_SIZE + 2];\n+\n+\t\tn \u003d lws_http_compression_transform(wsi, buf, len, wp, \u0026out, \u0026o);\n+\t\tif (n)\n+\t\t\treturn n;\n+\n+\t\tlwsl_debug(\u0022%s: %p: transformed %d bytes to %d \u0022\n+\t\t\t \u0022(wp 0x%x, more %d)\u005cn\u0022, __func__, wsi, (int)len,\n+\t\t\t (int)o, (int)*wp, wsi-\u003ehttp.comp_ctx.may_have_more);\n+\n+\t\tif (!o)\n+\t\t\treturn olen;\n+\n+\t\tif (wsi-\u003ehttp.comp_ctx.chunking) {\n+\t\t\t/*\n+\t\t\t * this only needs dealing with on http/1.1 to allow\n+\t\t\t * pipelining\n+\t\t\t */\n+\t\t\tn \u003d lws_snprintf(c, sizeof(c), \u0022%X\u005cx0d\u005cx0a\u0022, (int)o);\n+\t\t\tlwsl_notice(\u0022%s: chunk %s\u005cn\u0022, __func__, c);\n+\t\t\tout -\u003d n;\n+\t\t\to +\u003d n;\n+\t\t\tmemcpy(out, c, n);\n+\t\t\tout[o++] \u003d '\u005cx0d';\n+\t\t\tout[o++] \u003d '\u005cx0a';\n+\n+\t\t\tif (((*wp) \u0026 0x1f) \u003d\u003d LWS_WRITE_HTTP_FINAL) {\n+\t\t\t\tout[o++] \u003d '0';\n+\t\t\t\tout[o++] \u003d '\u005cx0d';\n+\t\t\t\tout[o++] \u003d '\u005cx0a';\n+\t\t\t\tout[o++] \u003d '\u005cx0d';\n+\t\t\t\tout[o++] \u003d '\u005cx0a';\n+\t\t\t}\n+\t\t}\n+\n+\t\tbuf \u003d out;\n+\t\tlen \u003d o;\n \t}\n #endif\n \n-\treturn lws_issue_raw(wsi, (unsigned char *)buf, len);\n+\tn \u003d lws_issue_raw(wsi, (unsigned char *)buf, len);\n+\tif (n \u003c 0)\n+\t\treturn n;\n+\n+\t/* hide there may have been compression */\n+\n+\treturn olen;\n }\n \n static int\n@@ -669,6 +738,10 @@ rops_destroy_role_h1(struct lws *wsi)\n \t\tah \u003d ah-\u003enext;\n \t}\n \n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tlws_http_compression_destroy(wsi);\n+#endif\n+\n #ifdef LWS_ROLE_WS\n \tlws_free_set_NULL(wsi-\u003ews);\n #endif\n@@ -802,6 +875,73 @@ fail_wsi:\n }\n #endif\n \n+#if 0\n+static int\n+rops_perform_user_POLLOUT_h1(struct lws *wsi)\n+{\n+\tvolatile struct lws *vwsi \u003d (volatile struct lws *)wsi;\n+\tint n;\n+\n+\t/* priority 1: post compression-transform buffered output */\n+\n+\tif (lws_has_buffered_out(wsi)) {\n+\t\tlwsl_debug(\u0022%s: completing partial\u005cn\u0022, __func__);\n+\t\tif (lws_issue_raw(wsi, NULL, 0) \u003c 0) {\n+\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tn \u003d 0;\n+\t\tvwsi-\u003eleave_pollout_active \u003d 1;\n+\t\tgoto cleanup;\n+\t}\n+\n+\t/* priority 2: pre compression-transform buffered output */\n+\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tif (wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more) {\n+\t\tenum lws_write_protocol wp \u003d LWS_WRITE_HTTP;\n+\n+\t\tlwsl_debug(\u0022%s: completing comp partial\u0022\n+\t\t\t \u0022(buflist_comp %p, may %d)\u005cn\u0022,\n+\t\t\t __func__, wsi-\u003ehttp.comp_ctx.buflist_comp,\n+\t\t\t wsi-\u003ehttp.comp_ctx.may_have_more);\n+\n+\t\tif (rops_write_role_protocol_h1(wsi, NULL, 0, \u0026wp) \u003c 0) {\n+\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n+\t\t\tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,\n+\t\t\t\t\t \u0022comp write fail\u0022);\n+\t\t}\n+\t\tn \u003d 0;\n+\t\tvwsi-\u003eleave_pollout_active \u003d 1;\n+\t\tgoto cleanup;\n+\t}\n+#endif\n+\n+\t/* priority 3: if no buffered out and waiting for that... */\n+\n+\tif (lwsi_state(wsi) \u003d\u003d LRS_FLUSHING_BEFORE_CLOSE) {\n+\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n+\t\treturn -1;\n+\t}\n+\n+\t/* priority 4: user writeable callback */\n+\n+\tvwsi \u003d (volatile struct lws *)wsi;\n+\tvwsi-\u003eleave_pollout_active \u003d 0;\n+\n+\tn \u003d lws_callback_as_writeable(wsi);\n+\n+cleanup:\n+\tvwsi-\u003ehandling_pollout \u003d 0;\n+\n+\tif (vwsi-\u003eleave_pollout_active)\n+\t\tlws_change_pollfd(wsi, 0, LWS_POLLOUT);\n+\n+\treturn n;\n+}\n+#endif\n+\n struct lws_role_ops role_ops_h1 \u003d {\n \t/* role name */\t\t\t\u0022h1\u0022,\n \t/* alpn id */\t\t\t\u0022http/1.1\u0022,\ndiff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c\nindex e93cd6a..cea4965 100644\n--- a/lib/roles/h2/ops-h2.c\n+++ b/lib/roles/h2/ops-h2.c\n@@ -360,6 +360,7 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t\t\t enum lws_write_protocol *wp)\n {\n \tunsigned char flags \u003d 0, base \u003d (*wp) \u0026 0x1f;\n+\tsize_t olen \u003d len;\n \tint n;\n \n \t/* if not in a state to send stuff, then just send nothing */\n@@ -381,6 +382,31 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t\treturn 0;\n \t}\n \n+\t/* compression transform... */\n+\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tif (wsi-\u003ehttp.lcs) {\n+\t\tunsigned char mtubuf[1450 + LWS_PRE], *out \u003d mtubuf + LWS_PRE;\n+\t\tsize_t o \u003d sizeof(mtubuf) - LWS_PRE;\n+\n+\t\tn \u003d lws_http_compression_transform(wsi, buf, len, wp, \u0026out, \u0026o);\n+\t\tif (n)\n+\t\t\treturn n;\n+\n+\t\tlwsl_debug(\u0022%s: %p: transformed %d bytes to %d \u0022\n+\t\t\t \u0022(wp 0x%x, more %d)\u005cn\u0022, __func__,\n+\t\t\t wsi, (int)len, (int)o, (int)*wp,\n+\t\t\t wsi-\u003ehttp.comp_ctx.may_have_more);\n+\n+\t\tbuf \u003d out;\n+\t\tlen \u003d o;\n+\t\tbase \u003d (*wp) \u0026 0x1f;\n+\n+\t\tif (!len)\n+\t\t\treturn olen;\n+\t}\n+#endif\n+\n \t/*\n \t * ws-over-h2 also ends up here after the ws framing applied\n \t */\n@@ -401,7 +427,8 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t\tn \u003d LWS_H2_FRAME_TYPE_CONTINUATION;\n \t\tif (!((*wp) \u0026 LWS_WRITE_NO_FIN))\n \t\t\tflags \u003d LWS_H2_FLAG_END_HEADERS;\n-\t\tif (wsi-\u003eh2.send_END_STREAM || ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n+\t\tif (wsi-\u003eh2.send_END_STREAM ||\n+\t\t ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n \t\t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n \t\t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n \t\t}\n@@ -420,12 +447,18 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n \t}\n \n \tif (base \u003d\u003d LWS_WRITE_HTTP_FINAL || ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n-\t\tlwsl_info(\u0022%s: setting END_STREAM\u005cn\u0022, __func__);\n+\t\tlwsl_info(\u0022%s: %p: setting END_STREAM\u005cn\u0022, __func__, wsi);\n \t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n \t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n \t}\n \n-\treturn lws_h2_frame_write(wsi, n, flags, wsi-\u003eh2.my_sid, (int)len, buf);\n+\tn \u003d lws_h2_frame_write(wsi, n, flags, wsi-\u003eh2.my_sid, (int)len, buf);\n+\tif (n \u003c 0)\n+\t\treturn n;\n+\n+\t/* hide it may have been compressed... */\n+\n+\treturn olen;\n }\n \n static int\n@@ -523,6 +556,10 @@ rops_destroy_role_h2(struct lws *wsi)\n \t\tah \u003d ah-\u003enext;\n \t}\n \n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tlws_http_compression_destroy(wsi);\n+#endif\n+\n \tif (wsi-\u003eupgraded_to_http2 || wsi-\u003ehttp2_substream) {\n \t\tlws_hpack_destroy_dynamic_header(wsi);\n \n@@ -822,6 +859,55 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)\n \t\tlwsl_info(\u0022%s: child %p (wsistate 0x%x)\u005cn\u0022, __func__, w,\n \t\t\t w-\u003ewsistate);\n \n+\t\t/* priority 1: post compression-transform buffered output */\n+\n+\t\tif (lws_has_buffered_out(w)) {\n+\t\t\tlwsl_debug(\u0022%s: completing partial\u005cn\u0022, __func__);\n+\t\t\tif (lws_issue_raw(w, NULL, 0) \u003c 0) {\n+\t\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n+\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n+\t\t\t\t\t\t \u0022h2 end stream 1\u0022);\n+\t\t\t\twa \u003d \u0026wsi-\u003eh2.child_list;\n+\t\t\t\tgoto next_child;\n+\t\t\t}\n+\t\t\tlws_callback_on_writable(w);\n+\t\t\twa \u003d \u0026wsi-\u003eh2.child_list;\n+\t\t\tgoto next_child;\n+\t\t}\n+\n+\t\t/* priority 2: pre compression-transform buffered output */\n+\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\tif (w-\u003ehttp.comp_ctx.buflist_comp ||\n+\t\t w-\u003ehttp.comp_ctx.may_have_more) {\n+\t\t\tenum lws_write_protocol wp \u003d LWS_WRITE_HTTP;\n+\n+\t\t\tlwsl_debug(\u0022%s: completing comp partial\u0022\n+\t\t\t\t \u0022(buflist_comp %p, may %d)\u005cn\u0022,\n+\t\t\t\t __func__, w-\u003ehttp.comp_ctx.buflist_comp,\n+\t\t\t\t w-\u003ehttp.comp_ctx.may_have_more);\n+\n+\t\t\tif (rops_write_role_protocol_h2(w, NULL, 0, \u0026wp) \u003c 0) {\n+\t\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n+\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n+\t\t\t\t\t\t \u0022comp write fail\u0022);\n+\t\t\t}\n+\t\t\tlws_callback_on_writable(w);\n+\t\t\twa \u003d \u0026wsi-\u003eh2.child_list;\n+\t\t\tgoto next_child;\n+\t\t}\n+#endif\n+\n+\t\t/* priority 3: if no buffered out and waiting for that... */\n+\n+\t\tif (lwsi_state(w) \u003d\u003d LRS_FLUSHING_BEFORE_CLOSE) {\n+\t\t\tw-\u003esocket_is_permanently_unusable \u003d 1;\n+\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n+\t\t\t\t\t \u0022h2 end stream 1\u0022);\n+\t\t\twa \u003d \u0026wsi-\u003eh2.child_list;\n+\t\t\tgoto next_child;\n+\t\t}\n+\n \t\t/* if we arrived here, even by looping, we checked choked */\n \t\tw-\u003ecould_have_pending \u003d 0;\n \t\twsi-\u003ecould_have_pending \u003d 0;\ndiff --git a/lib/roles/http/compression/README.md b/lib/roles/http/compression/README.md\nnew file mode 100644\nindex 0000000..8d9d57f\n--- /dev/null\n+++ b/lib/roles/http/compression/README.md\n@@ -0,0 +1,17 @@\n+HTTP compression\n+----------------\n+\n+This directory contains generic compression transforms that can be applied to\n+specifically HTTP content streams, after the header, be it h1 or h2.\n+\n+The compression transforms expose an \u0022ops\u0022 type struct and a compressor name\n+as used by `content-encoding`... the ops struct definition can be found in\n+./private.h.\n+\n+Because the compression transform depends on being able to send on its output\n+before it can process new input, the transform adds a new kind of buflist\n+`wsi-\u003ebuflist_comp` that represents pre-compression transform data\n+(\u0022input data\u0022 from the perspective of the compression transform) that was\n+delivered to be processed but couldn't be accepted.\n+\n+Currently, zlib 'deflate' and brotli 'br' are supported on the server side.\ndiff --git a/lib/roles/http/compression/brotli/brotli.c b/lib/roles/http/compression/brotli/brotli.c\nnew file mode 100644\nindex 0000000..14ad961\n--- /dev/null\n+++ b/lib/roles/http/compression/brotli/brotli.c\n@@ -0,0 +1,122 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2018 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation:\n+ * version 2.1 of the License.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n+ * MA 02110-1301 USA\n+ */\n+\n+#include \u0022core/private.h\u0022\n+\n+\n+static int\n+lcs_init_compression_brotli(lws_comp_ctx_t *ctx, int decomp)\n+{\n+\tctx-\u003eis_decompression \u003d decomp;\n+\n+\tif (!decomp) {\n+\t\tctx-\u003eu.br_en \u003d BrotliEncoderCreateInstance(NULL, NULL, NULL);\n+\t\tif (ctx-\u003eu.br_en) {\n+\t\t\tBrotliEncoderSetParameter(ctx-\u003eu.br_en,\n+\t\t\t\t\tBROTLI_PARAM_MODE, BROTLI_MODE_TEXT);\n+\t\t\tBrotliEncoderSetParameter(ctx-\u003eu.br_en,\n+\t\t\t\t\tBROTLI_PARAM_QUALITY, BROTLI_MIN_QUALITY);\n+\t\t}\n+\t}\n+\telse\n+\t\tctx-\u003eu.br_de \u003d BrotliDecoderCreateInstance(NULL, NULL, NULL);\n+\n+\treturn !ctx-\u003eu.br_de;\n+}\n+\n+static int\n+lcs_process_brotli(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,\n+\t\t void *out, size_t *olen_oused)\n+{\n+\tsize_t a_in, a_out, t_out;\n+\tconst uint8_t *n_in;\n+\tuint8_t *n_out;\n+\tint n;\n+\n+\tn_in \u003d (void *)in;\n+\ta_in \u003d *ilen_iused;\n+\ta_out \u003d *olen_oused;\n+\tn_out \u003d out;\n+\tt_out \u003d 0;\n+\n+\tif (!ctx-\u003eis_decompression) {\n+\n+\t\tif (!a_in \u0026\u0026 !BrotliEncoderHasMoreOutput(ctx-\u003eu.br_en)) {\n+\t\t\t*olen_oused \u003d 0;\n+\n+\t\t\tgoto bail;\n+\t\t}\n+\n+\t\tn \u003d BROTLI_OPERATION_PROCESS;\n+\t\tif (!ctx-\u003ebuflist_comp \u0026\u0026 ctx-\u003efinal_on_input_side)\n+\t\t\tn \u003d BROTLI_OPERATION_FINISH;\n+\n+\t\tif (BrotliEncoderCompressStream(ctx-\u003eu.br_en, n, \u0026a_in, \u0026n_in,\n+\t\t\t\t\t\t\u0026a_out, \u0026n_out, \u0026t_out) \u003d\u003d\n+\t\t BROTLI_FALSE) {\n+\t\t\tlwsl_err(\u0022brotli encode failed\u005cn\u0022);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tctx-\u003emay_have_more \u003d !a_out;//!BrotliEncoderIsFinished(ctx-\u003eu.br_en);\n+\n+\t} else {\n+\t\tn \u003d BrotliDecoderDecompressStream(ctx-\u003eu.br_de, \u0026a_in, \u0026n_in,\n+\t\t\t\t\t\t \u0026a_out, \u0026n_out, \u0026t_out);\n+\n+\t\tswitch (n) {\n+\t\tcase BROTLI_DECODER_RESULT_ERROR:\n+\t\t\tlwsl_err(\u0022brotli decoder error\u005cn\u0022);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\t*ilen_iused -\u003d a_in;\n+\t*olen_oused -\u003d a_out;\n+\n+bail:\n+\tif (!ctx-\u003eis_decompression)\n+\t\treturn BrotliEncoderIsFinished(ctx-\u003eu.br_en);\n+\telse\n+\t\treturn BrotliDecoderIsFinished(ctx-\u003eu.br_de);\n+}\n+\n+static void\n+lcs_destroy_brotli(lws_comp_ctx_t *ctx)\n+{\n+\tif (!ctx)\n+\t\treturn;\n+\n+\tif (!(*ctx).is_decompression)\n+\t\tBrotliEncoderDestroyInstance((*ctx).u.br_en);\n+\telse\n+\t\tBrotliDecoderDestroyInstance((*ctx).u.br_de);\n+\n+\t(*ctx).u.generic_ctx_ptr \u003d NULL;\n+}\n+\n+struct lws_compression_support lcs_brotli \u003d {\n+\t/* .encoding_name */\t\t\u0022br\u0022,\n+\t/* .init_compression */\t\tlcs_init_compression_brotli,\n+\t/* .process */\t\t\tlcs_process_brotli,\n+\t/* .destroy */\t\t\tlcs_destroy_brotli,\n+};\ndiff --git a/lib/roles/http/compression/deflate/deflate.c b/lib/roles/http/compression/deflate/deflate.c\nnew file mode 100644\nindex 0000000..2f3fab5\n--- /dev/null\n+++ b/lib/roles/http/compression/deflate/deflate.c\n@@ -0,0 +1,110 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2018 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation:\n+ * version 2.1 of the License.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n+ * MA 02110-1301 USA\n+ */\n+\n+#include \u0022core/private.h\u0022\n+\n+static int\n+lcs_init_compression_deflate(lws_comp_ctx_t *ctx, int decomp)\n+{\n+\tint n;\n+\n+\tctx-\u003eis_decompression \u003d decomp;\n+\tctx-\u003eu.deflate \u003d lws_malloc(sizeof(*ctx-\u003eu.deflate), __func__);\n+\n+\tif (!ctx-\u003eu.deflate)\n+\t\treturn 2;\n+\n+\tmemset(ctx-\u003eu.deflate, 0, sizeof(*ctx-\u003eu.deflate));\n+\n+\tif (!decomp \u0026\u0026\n+\t (n \u003d deflateInit2(ctx-\u003eu.deflate, 1, Z_DEFLATED, -15, 8,\n+\t\t\t Z_DEFAULT_STRATEGY)) !\u003d Z_OK) {\n+\t\tlwsl_err(\u0022deflate init failed: %d\u005cn\u0022, n);\n+\t\tlws_free_set_NULL(ctx-\u003eu.deflate);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tif (decomp \u0026\u0026\n+\t inflateInit2(ctx-\u003eu.deflate, 16 + 15) !\u003d Z_OK) {\n+\t\tlws_free_set_NULL(ctx-\u003eu.deflate);\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+lcs_process_deflate(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,\n+\t\t void *out, size_t *olen_oused)\n+{\n+\tsize_t olen_oused_in \u003d *olen_oused;\n+\tint n;\n+\n+\tctx-\u003eu.deflate-\u003enext_in \u003d (void *)in;\n+\tctx-\u003eu.deflate-\u003eavail_in \u003d *ilen_iused;\n+\n+\tctx-\u003eu.deflate-\u003enext_out \u003d out;\n+\tctx-\u003eu.deflate-\u003eavail_out \u003d *olen_oused;\n+\n+\tif (!ctx-\u003eis_decompression)\n+\t\tn \u003d deflate(ctx-\u003eu.deflate, Z_SYNC_FLUSH);\n+\telse\n+\t\tn \u003d inflate(ctx-\u003eu.deflate, Z_SYNC_FLUSH);\n+\n+\tswitch (n) {\n+\tcase Z_NEED_DICT:\n+\tcase Z_STREAM_ERROR:\n+\tcase Z_DATA_ERROR:\n+\tcase Z_MEM_ERROR:\n+\t\tlwsl_err(\u0022zlib error inflate %d\u005cn\u0022, n);\n+\t\treturn -1;\n+\t}\n+\n+\t*ilen_iused -\u003d ctx-\u003eu.deflate-\u003eavail_in;\n+\t*olen_oused -\u003d ctx-\u003eu.deflate-\u003eavail_out;\n+\n+\t/* it's ambiguous with zlib... */\n+\tctx-\u003emay_have_more \u003d (*olen_oused \u003d\u003d olen_oused_in);\n+\n+\treturn n \u003d\u003d Z_STREAM_END;\n+}\n+\n+static void\n+lcs_destroy_deflate(lws_comp_ctx_t *ctx)\n+{\n+\tif (!ctx)\n+\t\treturn;\n+\n+\tif (!(*ctx).is_decompression)\n+\t\tdeflateEnd((*ctx).u.deflate);\n+\telse\n+\t\tinflateEnd((*ctx).u.deflate);\n+\n+\tlws_free_set_NULL(ctx-\u003eu.deflate);\n+}\n+\n+struct lws_compression_support lcs_deflate \u003d {\n+\t/* .encoding_name */\t\t\u0022deflate\u0022,\n+\t/* .init_compression */\t\tlcs_init_compression_deflate,\n+\t/* .process */\t\t\tlcs_process_deflate,\n+\t/* .destroy */\t\t\tlcs_destroy_deflate,\n+};\ndiff --git a/lib/roles/http/compression/private.h b/lib/roles/http/compression/private.h\nnew file mode 100644\nindex 0000000..dafa77c\n--- /dev/null\n+++ b/lib/roles/http/compression/private.h\n@@ -0,0 +1,80 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2018 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation:\n+ * version 2.1 of the License.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n+ * MA 02110-1301 USA\n+ *\n+ * This is included from core/private.h if LWS_WITH_HTTP_STREAM_COMPRESSION\n+ */\n+\n+#include \u003czlib.h\u003e\n+#if defined(LWS_WITH_HTTP_BROTLI)\n+#include \u003cbrotli/encode.h\u003e\n+#include \u003cbrotli/decode.h\u003e\n+#endif\n+\n+/*\n+ * struct holding union of all the available compression methods' context data,\n+ * and state if it's compressing or decompressing\n+ */\n+\n+typedef struct lws_compression_ctx {\n+\tunion {\n+\n+#if defined(LWS_WITH_HTTP_BROTLI)\n+\t\tBrotliEncoderState *br_en;\n+\t\tBrotliDecoderState *br_de;\n+#endif\n+\t\tz_stream *deflate;\n+\t\tvoid *generic_ctx_ptr;\n+\t} u;\n+\n+\tstruct lws_buflist *buflist_comp;\n+\n+\tunsigned int is_decompression:1;\n+\tunsigned int final_on_input_side:1;\n+\tunsigned int may_have_more:1;\n+\tunsigned int chunking:1;\n+} lws_comp_ctx_t;\n+\n+/* generic structure defining the interface to a compression method */\n+\n+struct lws_compression_support {\n+\t/** compression name as used by, eg, content-ecoding */\n+\tconst char *encoding_name;\n+\t/** create a compression context for the compression method, or NULL */\n+\tint (*init_compression)(lws_comp_ctx_t *ctx, int decomp);\n+\t/** pass data into the context to be processed */\n+\tint (*process)(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,\n+\t\t\t\t\tvoid *out, size_t *olen_oused);\n+\t/** destroy the de/compression context */\n+\tvoid (*destroy)(lws_comp_ctx_t *ctx);\n+};\n+\n+extern struct lws_compression_support lcs_deflate;\n+extern struct lws_compression_support lcs_brotli;\n+\n+int\n+lws_http_compression_validate(struct lws *wsi);\n+\n+int\n+lws_http_compression_transform(struct lws *wsi, unsigned char *buf,\n+\t\t\t size_t len, enum lws_write_protocol *wp,\n+\t\t\t unsigned char **outbuf, size_t *olen_oused);\n+\n+void\n+lws_http_compression_destroy(struct lws *wsi);\ndiff --git a/lib/roles/http/compression/stream.c b/lib/roles/http/compression/stream.c\nnew file mode 100644\nindex 0000000..617750a\n--- /dev/null\n+++ b/lib/roles/http/compression/stream.c\n@@ -0,0 +1,221 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2018 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation:\n+ * version 2.1 of the License.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n+ * MA 02110-1301 USA\n+ */\n+\n+#include \u0022core/private.h\u0022\n+\n+/* compression methods listed in order of preference */\n+\n+struct lws_compression_support *lcs_available[] \u003d {\n+#if defined(LWS_WITH_HTTP_BROTLI)\n+\t\u0026lcs_brotli,\n+#endif\n+\t\u0026lcs_deflate,\n+};\n+\n+/* compute acceptable compression encodings while we still have an ah */\n+\n+int\n+lws_http_compression_validate(struct lws *wsi)\n+{\n+\tconst char *a;\n+\tsize_t n;\n+\n+\twsi-\u003ehttp.comp_accept_mask \u003d 0;\n+\n+\tif (!wsi-\u003ehttp.ah || !lwsi_role_server(wsi))\n+\t\treturn 0;\n+\n+\ta \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);\n+\tif (!a)\n+\t\treturn 0;\n+\n+\tfor (n \u003d 0; n \u003c LWS_ARRAY_SIZE(lcs_available); n++)\n+\t\tif (strstr(a, lcs_available[n]-\u003eencoding_name))\n+\t\t\twsi-\u003ehttp.comp_accept_mask |\u003d 1 \u003c\u003c n;\n+\n+\treturn 0;\n+}\n+\n+LWS_VISIBLE int\n+lws_http_compression_apply(struct lws *wsi, const char *name,\n+\t\t\t unsigned char **p, unsigned char *end, char decomp)\n+{\n+\tsize_t n;\n+\n+\tfor (n \u003d 0; n \u003c LWS_ARRAY_SIZE(lcs_available); n++) {\n+\t\t/* if name is non-NULL, choose only that compression method */\n+\t\tif (name \u0026\u0026 !strcmp(lcs_available[n]-\u003eencoding_name, name))\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * If we're the server, confirm that the client told us he could\n+\t\t * handle this kind of compression transform...\n+\t\t */\n+\t\tif (!decomp \u0026\u0026 !(wsi-\u003ehttp.comp_accept_mask \u0026 (1 \u003c\u003c n)))\n+\t\t\tcontinue;\n+\n+\t\t/* let's go with this one then... */\n+\t\tbreak;\n+\t}\n+\n+\tif (n \u003d\u003d LWS_ARRAY_SIZE(lcs_available))\n+\t\treturn 1;\n+\n+\tlcs_available[n]-\u003einit_compression(\u0026wsi-\u003ehttp.comp_ctx, decomp);\n+\tif (!wsi-\u003ehttp.comp_ctx.u.generic_ctx_ptr) {\n+\t\tlwsl_err(\u0022%s: init_compression %d failed\u005cn\u0022, __func__, (int)n);\n+\t\treturn 1;\n+\t}\n+\n+\twsi-\u003ehttp.lcs \u003d lcs_available[n];\n+\twsi-\u003ehttp.comp_ctx.may_have_more \u003d 0;\n+\twsi-\u003ehttp.comp_ctx.final_on_input_side \u003d 0;\n+\twsi-\u003ehttp.comp_ctx.chunking \u003d 0;\n+\twsi-\u003ehttp.comp_ctx.is_decompression \u003d decomp;\n+\n+\tif (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,\n+\t\t\t(unsigned char *)lcs_available[n]-\u003eencoding_name,\n+\t\t\tstrlen(lcs_available[n]-\u003eencoding_name), p, end))\n+\t\treturn -1;\n+\n+\tlwsl_info(\u0022%s: wsi %p: applied %s content-encoding\u005cn\u0022, __func__,\n+\t\t wsi, lcs_available[n]-\u003eencoding_name);\n+\n+\treturn 0;\n+}\n+\n+void\n+lws_http_compression_destroy(struct lws *wsi)\n+{\n+\tif (!wsi-\u003ehttp.lcs || !wsi-\u003ehttp.comp_ctx.u.generic_ctx_ptr)\n+\t\treturn;\n+\n+\twsi-\u003ehttp.lcs-\u003edestroy(\u0026wsi-\u003ehttp.comp_ctx);\n+\n+\twsi-\u003ehttp.lcs \u003d NULL;\n+}\n+\n+/*\n+ * This manages the compression transform independent of h1 or h2.\n+ *\n+ * wsi-\u003ebuflist_comp stashes pre-transform input that was not yet compressed\n+ */\n+\n+int\n+lws_http_compression_transform(struct lws *wsi, unsigned char *buf,\n+\t\t\t size_t len, enum lws_write_protocol *wp,\n+\t\t\t unsigned char **outbuf, size_t *olen_oused)\n+{\n+\tsize_t ilen_iused \u003d len;\n+\tint n, use \u003d 0, wp1f \u003d (*wp) \u0026 0x1f;\n+\tlws_comp_ctx_t *ctx \u003d \u0026wsi-\u003ehttp.comp_ctx;\n+\n+\tctx-\u003emay_have_more \u003d 0;\n+\n+\tif (!wsi-\u003ehttp.lcs ||\n+\t (wp1f !\u003d LWS_WRITE_HTTP \u0026\u0026 wp1f !\u003d LWS_WRITE_HTTP_FINAL)) {\n+\t\t*outbuf \u003d buf;\n+\t\t*olen_oused \u003d len;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tif (wp1f \u003d\u003d LWS_WRITE_HTTP_FINAL) {\n+\t\t/*\n+\t\t * ...we may get a large buffer that represents the final input\n+\t\t * buffer, but it may form multiple frames after being\n+\t\t * tranformed by compression; only the last of those is actually\n+\t\t * the final frame on the output stream.\n+\t\t *\n+\t\t * Note that we have received the FINAL input, and downgrade it\n+\t\t * to a non-final for now.\n+\t\t */\n+\t\tctx-\u003efinal_on_input_side \u003d 1;\n+\t\t*wp \u003d LWS_WRITE_HTTP | ((*wp) \u0026 ~0x1f);\n+\t}\n+\n+\tif (ctx-\u003ebuflist_comp || ctx-\u003emay_have_more) {\n+\t\t/*\n+\t\t * we can't send this new stuff when we have old stuff\n+\t\t * buffered and not compressed yet. Add it to the tail\n+\t\t * and switch to trying to process the head.\n+\t\t */\n+\t\tif (buf \u0026\u0026 len) {\n+\t\t\tlws_buflist_append_segment(\n+\t\t\t\t\u0026ctx-\u003ebuflist_comp, buf, len);\n+\t\t\tlwsl_debug(\u0022%s: %p: adding %d to comp buflist\u005cn\u0022,\n+\t\t\t\t __func__,wsi, (int)len);\n+\t\t}\n+\n+\t\tlen \u003d lws_buflist_next_segment_len(\u0026ctx-\u003ebuflist_comp, \u0026buf);\n+\t\tilen_iused \u003d len;\n+\t\tuse \u003d 1;\n+\t\tlwsl_debug(\u0022%s: %p: trying comp buflist %d\u005cn\u0022, __func__, wsi,\n+\t\t\t (int)len);\n+\t}\n+\n+\tif (!buf \u0026\u0026 ilen_iused)\n+\t\treturn 0;\n+\n+\tlwsl_debug(\u0022%s: %p: pre-process: ilen_iused %d, olen_oused %d\u005cn\u0022,\n+\t\t __func__, wsi, (int)ilen_iused, (int)*olen_oused);\n+\n+\tn \u003d wsi-\u003ehttp.lcs-\u003eprocess(ctx, buf, \u0026ilen_iused, *outbuf, olen_oused);\n+\n+\tif (n \u0026\u0026 n !\u003d 1) {\n+\t\tlwsl_err(\u0022%s: problem with compression\u005cn\u0022, __func__);\n+\n+\t\treturn -1;\n+\t}\n+\n+\tif (!ctx-\u003emay_have_more \u0026\u0026 ctx-\u003efinal_on_input_side)\n+\t\t*wp \u003d LWS_WRITE_HTTP_FINAL | ((*wp) \u0026 ~0x1f);\n+\n+\tlwsl_debug(\u0022%s: %p: more %d, ilen_iused %d\u005cn\u0022, __func__, wsi,\n+\t\t ctx-\u003emay_have_more, (int)ilen_iused);\n+\n+\tif (use \u0026\u0026 ilen_iused) {\n+\t\t/*\n+\t\t * we were flushing stuff from the buflist head... account for\n+\t\t * however much actually got processed by the compression\n+\t\t * transform\n+\t\t */\n+\t\tlws_buflist_use_segment(\u0026ctx-\u003ebuflist_comp, ilen_iused);\n+\t\tlwsl_debug(\u0022%s: %p: marking %d of comp buflist as used \u0022\n+\t\t\t \u0022(ctx-\u003ebuflist_comp %p)\u005cn\u0022, __func__, wsi,\n+\t\t\t (int)len, ctx-\u003ebuflist_comp);\n+\t}\n+\n+\tif (!use \u0026\u0026 ilen_iused !\u003d len) {\n+\t\t /*\n+\t\t * ...we were sending stuff from the caller directly and not\n+\t\t * all of it got processed... stash on the buflist tail\n+\t\t */\n+\t\tlws_buflist_append_segment(\u0026ctx-\u003ebuflist_comp,\n+\t\t\t\t\t buf + ilen_iused, len - ilen_iused);\n+\n+\t\tlwsl_debug(\u0022%s: buffering %d unused comp input\u005cn\u0022, __func__,\n+\t\t\t (int)(len - ilen_iused));\n+\t}\n+\tif (ctx-\u003ebuflist_comp || ctx-\u003emay_have_more)\n+\t\tlws_callback_on_writable(wsi);\n+\n+\treturn 0;\n+}\ndiff --git a/lib/roles/http/header.c b/lib/roles/http/header.c\nindex ca2e6da..d99c3c8 100644\n--- a/lib/roles/http/header.c\n+++ b/lib/roles/http/header.c\n@@ -413,3 +413,20 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,\n \treturn lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS |\n \t\t\t\t\t\t LWS_WRITE_H2_STREAM_END);\n }\n+\n+#if !defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+LWS_VISIBLE int\n+lws_http_compression_apply(struct lws *wsi, const char *name,\n+\t\t\t unsigned char **p, unsigned char *end, char decomp)\n+{\n+\t(void)wsi;\n+\t(void)name;\n+\t(void)p;\n+\t(void)end;\n+\t(void)decomp;\n+\n+\treturn 0;\n+}\n+#endif\n+\n+\ndiff --git a/lib/roles/http/private.h b/lib/roles/http/private.h\nindex d54f4e4..f901ea0 100644\n--- a/lib/roles/http/private.h\n+++ b/lib/roles/http/private.h\n@@ -27,6 +27,10 @@\n #include \u003chubbub/parser.h\u003e\n #endif\n \n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+#include \u0022roles/http/compression/private.h\u0022\n+#endif\n+\n #define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi))\n \n enum http_version {\n@@ -192,6 +196,9 @@ struct lws_access_log {\n };\n #endif\n \n+#define LWS_HTTP_CHUNK_HDR_MAX_SIZE (6 + 2) /* 6 hex digits and then CRLF */\n+#define LWS_HTTP_CHUNK_TRL_MAX_SIZE (2 + 5) /* CRLF, then maybe 0 CRLF CRLF */\n+\n struct _lws_http_mode_related {\n \tstruct lws *new_wsi_list;\n \n@@ -216,6 +223,10 @@ struct _lws_http_mode_related {\n #ifdef LWS_WITH_CGI\n \tstruct lws_cgi *cgi; /* wsi being cgi master have one of these */\n #endif\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tstruct lws_compression_support *lcs;\n+\tlws_comp_ctx_t comp_ctx;\n+#endif\n \n \tenum http_version request_version;\n \tenum http_connection_type connection_type;\ndiff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c\nindex 1c0f656..ca05a78 100644\n--- a/lib/roles/http/server/parsers.c\n+++ b/lib/roles/http/server/parsers.c\n@@ -529,6 +529,9 @@ char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)\n {\n \tint n;\n \n+\tif (!wsi-\u003ehttp.ah)\n+\t\treturn NULL;\n+\n \tn \u003d wsi-\u003ehttp.ah-\u003efrag_index[h];\n \tif (!n)\n \t\treturn NULL;\n@@ -539,6 +542,9 @@ char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)\n static int LWS_WARN_UNUSED_RESULT\n lws_pos_in_bounds(struct lws *wsi)\n {\n+\tif (!wsi-\u003ehttp.ah)\n+\t\treturn -1;\n+\n \tif (wsi-\u003ehttp.ah-\u003epos \u003c\n \t (unsigned int)wsi-\u003econtext-\u003emax_http_header_data)\n \t\treturn 0;\ndiff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c\nindex 666a16c..9930632 100644\n--- a/lib/roles/http/server/server.c\n+++ b/lib/roles/http/server/server.c\n@@ -1713,7 +1713,12 @@ lws_http_transaction_completed(struct lws *wsi)\n {\n \tint n \u003d NO_PENDING_TIMEOUT;\n \n-\tif (lws_has_buffered_out(wsi)) {\n+\tif (lws_has_buffered_out(wsi)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t\t|| wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t) {\n \t\t/*\n \t\t * ...so he tried to send something large as the http reply,\n \t\t * it went as a partial, but he immediately said the\n@@ -1722,14 +1727,18 @@ lws_http_transaction_completed(struct lws *wsi)\n \t\t * Defer the transaction completed until the last part of the\n \t\t * partial is sent.\n \t\t */\n-\t\tlwsl_notice(\u0022%s: deferring due to partial\u005cn\u0022, __func__);\n+\t\tlwsl_debug(\u0022%s: %p: deferring due to partial\u005cn\u0022, __func__, wsi);\n \t\twsi-\u003ehttp.deferred_transaction_completed \u003d 1;\n+\t\tlws_callback_on_writable(wsi);\n \n \t\treturn 0;\n \t}\n \n \tlwsl_info(\u0022%s: wsi %p\u005cn\u0022, __func__, wsi);\n \n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tlws_http_compression_destroy(wsi);\n+#endif\n \tlws_access_log(wsi);\n \n \tif (!wsi-\u003ehdr_parsing_completed) {\n@@ -1764,6 +1773,7 @@ lws_http_transaction_completed(struct lws *wsi)\n \twsi-\u003ehttp.tx_content_length \u003d 0;\n \twsi-\u003ehttp.tx_content_remain \u003d 0;\n \twsi-\u003ehdr_parsing_completed \u003d 0;\n+\twsi-\u003esending_chunked \u003d 0;\n #ifdef LWS_WITH_ACCESS_LOG\n \twsi-\u003ehttp.access_log.sent \u003d 0;\n #endif\n@@ -1922,6 +1932,20 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,\n \t\t\treturn -1;\n \t\tlwsl_info(\u0022file is being provided in gzip\u005cn\u0022);\n \t}\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\telse {\n+\t\t/*\n+\t\t * if we know its very compressible, and we can use\n+\t\t * compression, then use the most preferred compression\n+\t\t * method that the client said he will accept\n+\t\t */\n+\n+\t\tif (!strncmp(content_type, \u0022text/\u0022, 5) ||\n+\t\t !strcmp(content_type, \u0022application/javascript\u0022) ||\n+\t\t !strcmp(content_type, \u0022image/svg+xml\u0022))\n+\t\t\tlws_http_compression_apply(wsi, NULL, \u0026p, end, 0);\n+\t}\n+#endif\n \n \tif (\n #if defined(LWS_WITH_RANGES)\n@@ -2003,17 +2027,46 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,\n #endif\n \n \tif (!wsi-\u003ehttp2_substream) {\n-\t\tif (!wsi-\u003esending_chunked) {\n+\t\t/* for http/1.1 ... */\n+\t\tif (!wsi-\u003esending_chunked\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t\t\t\u0026\u0026 !wsi-\u003ehttp.lcs\n+#endif\n+\t\t) {\n+\t\t\t/* ... if not already using chunked and not using an\n+\t\t\t * http compression translation, then send the naive\n+\t\t\t * content length\n+\t\t\t */\n \t\t\tif (lws_add_http_header_content_length(wsi,\n-\t\t\t\t\t\ttotal_content_length,\n-\t\t\t\t\t \u0026p, end))\n+\t\t\t\t\t\ttotal_content_length, \u0026p, end))\n \t\t\t\treturn -1;\n \t\t} else {\n+\t\t\t/* ...otherwise, for http 1 it must go chunked. For\n+\t\t\t * the compression case, the reason is we compress on\n+\t\t\t * the fly and do not know the compressed content-length\n+\t\t\t * until it has all been sent. Http/1.1 pipelining must\n+\t\t\t * be able to know where the transaction boundaries are\n+\t\t\t * ... so chunking...\n+\t\t\t */\n \t\t\tif (lws_add_http_header_by_token(wsi,\n-\t\t\t\t\t\t WSI_TOKEN_HTTP_TRANSFER_ENCODING,\n-\t\t\t\t\t\t (unsigned char *)\u0022chunked\u0022,\n-\t\t\t\t\t\t 7, \u0026p, end))\n+\t\t\t\t\tWSI_TOKEN_HTTP_TRANSFER_ENCODING,\n+\t\t\t\t\t(unsigned char *)\u0022chunked\u0022, 7, \u0026p, end))\n \t\t\t\treturn -1;\n+\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t\tif (wsi-\u003ehttp.lcs) {\n+\t\t\t\t/*\n+\t\t\t\t * ...this is fun, isn't it :-) For h1 that is\n+\t\t\t\t * using an http compression translation, the\n+\t\t\t\t * compressor must chunk its output privately.\n+\t\t\t\t *\n+\t\t\t\t * h2 doesn't need (or support) any of this\n+\t\t\t\t * crap.\n+\t\t\t\t */\n+\t\t\t\tlwsl_debug(\u0022setting chunking\u005cn\u0022);\n+\t\t\t\twsi-\u003ehttp.comp_ctx.chunking \u003d 1;\n+\t\t\t}\n+#endif\n \t\t}\n \t}\n \n@@ -2082,6 +2135,8 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)\n \n \tdo {\n \n+\t\t/* priority 1: buffered output */\n+\n \t\tif (lws_has_buffered_out(wsi)) {\n \t\t\tif (lws_issue_raw(wsi, NULL, 0) \u003c 0) {\n \t\t\t\tlwsl_info(\u0022%s: closing\u005cn\u0022, __func__);\n@@ -2090,6 +2145,27 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)\n \t\t\tbreak;\n \t\t}\n \n+\t\t/* priority 2: buffered pre-compression-transform */\n+\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\tif (wsi-\u003ehttp.comp_ctx.buflist_comp ||\n+\t wsi-\u003ehttp.comp_ctx.may_have_more) {\n+\t\tenum lws_write_protocol wp \u003d LWS_WRITE_HTTP;\n+\n+\t\tlwsl_debug(\u0022%s: completing comp partial (buflist_comp %p, may %d)\u005cn\u0022,\n+\t\t\t __func__, wsi-\u003ehttp.comp_ctx.buflist_comp,\n+\t\t\t wsi-\u003ehttp.comp_ctx.may_have_more);\n+\n+\t\tif (wsi-\u003erole_ops-\u003ewrite_role_protocol(wsi, NULL, 0, \u0026wp) \u003c 0) {\n+\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n+\t\t\tgoto file_had_it;\n+\t\t}\n+\t\tlws_callback_on_writable(wsi);\n+\n+\t\tbreak;\n+\t}\n+#endif\n+\n \t\tif (wsi-\u003ehttp.filepos \u003d\u003d wsi-\u003ehttp.filelen)\n \t\t\tgoto all_sent;\n \n@@ -2257,12 +2333,18 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)\n \t\t}\n \n all_sent:\n-\t\tif ((!lws_has_buffered_out(wsi) \u0026\u0026 wsi-\u003ehttp.filepos \u003e\u003d wsi-\u003ehttp.filelen)\n+\t\tif ((!lws_has_buffered_out(wsi)\n+#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n+\t\t\t\t\u0026\u0026 !wsi-\u003ehttp.comp_ctx.buflist_comp \u0026\u0026\n+\t\t !wsi-\u003ehttp.comp_ctx.may_have_more\n+#endif\n+\t\t ) \u0026\u0026 (wsi-\u003ehttp.filepos \u003e\u003d wsi-\u003ehttp.filelen\n #if defined(LWS_WITH_RANGES)\n \t\t || finished)\n #else\n \t\t)\n #endif\n+\t\t)\n \t\t {\n \t\t\tlwsi_set_state(wsi, LRS_ESTABLISHED);\n \t\t\t/* we might be in keepalive, so close it off here */\n@@ -2297,7 +2379,7 @@ all_sent:\n \n \t\t\treturn 1; /* \u003e0 indicates completed */\n \t\t}\n-\t} while (0); // while (!lws_send_pipe_choked(wsi))\n+\t} while (1); //(!lws_send_pipe_choked(wsi));\n \n \tlws_callback_on_writable(wsi);\n \ndiff --git a/lib/tls/mbedtls/ssl.c b/lib/tls/mbedtls/ssl.c\nindex f311ef5..da25a27 100644\n--- a/lib/tls/mbedtls/ssl.c\n+++ b/lib/tls/mbedtls/ssl.c\n@@ -189,7 +189,7 @@ lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)\n \n \t\tif (m \u003d\u003d SSL_ERROR_WANT_WRITE || SSL_want_write(wsi-\u003etls.ssl)) {\n \t\t\tlws_set_blocking_send(wsi);\n-\t\t\tlwsl_notice(\u0022%s: want write\u005cn\u0022, __func__);\n+\t\t\tlwsl_debug(\u0022%s: want write\u005cn\u0022, __func__);\n \n \t\t\treturn LWS_SSL_CAPABLE_MORE_SERVICE;\n \t\t}\n","s":{"c":1745736680,"u": 43668}}
],"g": 2341,"chitpc": 0,"ehitpc": 0,"indexed":0
,
"ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "7d0a"}