summaryrefslogtreecommitdiff
path: root/thirdparty/lws/ext
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/lws/ext')
-rw-r--r--thirdparty/lws/ext/extension-permessage-deflate.c473
-rw-r--r--thirdparty/lws/ext/extension-permessage-deflate.h41
-rw-r--r--thirdparty/lws/ext/extension.c344
3 files changed, 858 insertions, 0 deletions
diff --git a/thirdparty/lws/ext/extension-permessage-deflate.c b/thirdparty/lws/ext/extension-permessage-deflate.c
new file mode 100644
index 0000000000..e2be2ae615
--- /dev/null
+++ b/thirdparty/lws/ext/extension-permessage-deflate.c
@@ -0,0 +1,473 @@
+/*
+ * ./lib/extension-permessage-deflate.c
+ *
+ * Copyright (C) 2016 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "private-libwebsockets.h"
+#include "extension-permessage-deflate.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#define LWS_ZLIB_MEMLEVEL 8
+
+const struct lws_ext_options lws_ext_pm_deflate_options[] = {
+ /* public RFC7692 settings */
+ { "server_no_context_takeover", EXTARG_NONE },
+ { "client_no_context_takeover", EXTARG_NONE },
+ { "server_max_window_bits", EXTARG_OPT_DEC },
+ { "client_max_window_bits", EXTARG_OPT_DEC },
+ /* ones only user code can set */
+ { "rx_buf_size", EXTARG_DEC },
+ { "tx_buf_size", EXTARG_DEC },
+ { "compression_level", EXTARG_DEC },
+ { "mem_level", EXTARG_DEC },
+ { NULL, 0 }, /* sentinel */
+};
+
+static void
+lws_extension_pmdeflate_restrict_args(struct lws *wsi,
+ struct lws_ext_pm_deflate_priv *priv)
+{
+ int n, extra;
+
+ /* cap the RX buf at the nearest power of 2 to protocol rx buf */
+
+ n = wsi->context->pt_serv_buf_size;
+ if (wsi->protocol->rx_buffer_size)
+ n = wsi->protocol->rx_buffer_size;
+
+ extra = 7;
+ while (n >= 1 << (extra + 1))
+ extra++;
+
+ if (extra < priv->args[PMD_RX_BUF_PWR2]) {
+ priv->args[PMD_RX_BUF_PWR2] = extra;
+ lwsl_info(" Capping pmd rx to %d\n", 1 << extra);
+ }
+}
+
+LWS_VISIBLE int
+lws_extension_callback_pm_deflate(struct lws_context *context,
+ const struct lws_extension *ext,
+ struct lws *wsi,
+ enum lws_extension_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ struct lws_ext_pm_deflate_priv *priv =
+ (struct lws_ext_pm_deflate_priv *)user;
+ struct lws_tokens *eff_buf = (struct lws_tokens *)in;
+ static unsigned char trail[] = { 0, 0, 0xff, 0xff };
+ int n, ret = 0, was_fin = 0, extra;
+ struct lws_ext_option_arg *oa;
+
+ switch (reason) {
+ case LWS_EXT_CB_NAMED_OPTION_SET:
+ oa = in;
+ if (!oa->option_name)
+ break;
+ for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
+ if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name))
+ break;
+
+ if (n == ARRAY_SIZE(lws_ext_pm_deflate_options))
+ break;
+ oa->option_index = n;
+
+ /* fallthru */
+
+ case LWS_EXT_CB_OPTION_SET:
+ oa = in;
+ lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__,
+ oa->option_index, oa->start, oa->len);
+ if (oa->start)
+ priv->args[oa->option_index] = atoi(oa->start);
+ else
+ priv->args[oa->option_index] = 1;
+
+ if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
+ priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
+
+ lws_extension_pmdeflate_restrict_args(wsi, priv);
+ break;
+
+ case LWS_EXT_CB_OPTION_CONFIRM:
+ if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
+ priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
+ priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
+ priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
+ return -1;
+ break;
+
+ case LWS_EXT_CB_CLIENT_CONSTRUCT:
+ case LWS_EXT_CB_CONSTRUCT:
+
+ n = context->pt_serv_buf_size;
+ if (wsi->protocol->rx_buffer_size)
+ n = wsi->protocol->rx_buffer_size;
+
+ if (n < 128) {
+ lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n",
+ wsi->protocol->name);
+ return -1;
+ }
+
+ /* fill in **user */
+ priv = lws_zalloc(sizeof(*priv), "pmd priv");
+ *((void **)user) = priv;
+ lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
+ memset(priv, 0, sizeof(*priv));
+
+ /* fill in pointer to options list */
+ if (in)
+ *((const struct lws_ext_options **)in) =
+ lws_ext_pm_deflate_options;
+
+ /* fallthru */
+
+ case LWS_EXT_CB_OPTION_DEFAULT:
+
+ /* set the public, RFC7692 defaults... */
+
+ priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
+ priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
+ priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
+ priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
+
+ /* ...and the ones the user code can override */
+
+ priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
+ priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
+ priv->args[PMD_COMP_LEVEL] = 1;
+ priv->args[PMD_MEM_LEVEL] = 8;
+
+ lws_extension_pmdeflate_restrict_args(wsi, priv);
+ break;
+
+ case LWS_EXT_CB_DESTROY:
+ lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
+ lws_free(priv->buf_rx_inflated);
+ lws_free(priv->buf_tx_deflated);
+ if (priv->rx_init)
+ (void)inflateEnd(&priv->rx);
+ if (priv->tx_init)
+ (void)deflateEnd(&priv->tx);
+ lws_free(priv);
+ return ret;
+
+ case LWS_EXT_CB_PAYLOAD_RX:
+ lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
+ __func__, eff_buf->token_len, priv->rx.avail_in);
+ if (!(wsi->u.ws.rsv_first_msg & 0x40))
+ return 0;
+
+#if 0
+ for (n = 0; n < eff_buf->token_len; n++) {
+ printf("%02X ", (unsigned char)eff_buf->token[n]);
+ if ((n & 15) == 15)
+ printf("\n");
+ }
+ printf("\n");
+#endif
+ if (!priv->rx_init)
+ if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
+ lwsl_err("%s: iniflateInit failed\n", __func__);
+ return -1;
+ }
+ priv->rx_init = 1;
+ if (!priv->buf_rx_inflated)
+ priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 +
+ (1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf");
+ if (!priv->buf_rx_inflated) {
+ lwsl_err("%s: OOM\n", __func__);
+ return -1;
+ }
+
+ /*
+ * We have to leave the input stream alone if we didn't
+ * finish with it yet. The input stream is held in the wsi
+ * rx buffer by the caller, so this assumption is safe while
+ * we block new rx while draining the existing rx
+ */
+ if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) {
+ priv->rx.next_in = (unsigned char *)eff_buf->token;
+ priv->rx.avail_in = eff_buf->token_len;
+ }
+ priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
+ eff_buf->token = (char *)priv->rx.next_out;
+ priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
+
+ if (priv->rx_held_valid) {
+ lwsl_ext("-- RX piling on held byte --\n");
+ *(priv->rx.next_out++) = priv->rx_held;
+ priv->rx.avail_out--;
+ priv->rx_held_valid = 0;
+ }
+
+ /* if...
+ *
+ * - he has no remaining input content for this message, and
+ * - and this is the final fragment, and
+ * - we used everything that could be drained on the input side
+ *
+ * ...then put back the 00 00 FF FF the sender stripped as our
+ * input to zlib
+ */
+ if (!priv->rx.avail_in && wsi->u.ws.final &&
+ !wsi->u.ws.rx_packet_length) {
+ lwsl_ext("RX APPEND_TRAILER-DO\n");
+ was_fin = 1;
+ priv->rx.next_in = trail;
+ priv->rx.avail_in = sizeof(trail);
+ }
+
+ n = inflate(&priv->rx, Z_NO_FLUSH);
+ lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
+ priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final);
+ switch (n) {
+ case Z_NEED_DICT:
+ case Z_STREAM_ERROR:
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ lwsl_info("zlib error inflate %d: %s\n",
+ n, priv->rx.msg);
+ return -1;
+ }
+ /*
+ * If we did not already send in the 00 00 FF FF, and he's
+ * out of input, he did not EXACTLY fill the output buffer
+ * (which is ambiguous and we will force it to go around
+ * again by withholding a byte), and he's otherwise working on
+ * being a FIN fragment, then do the FIN message processing
+ * of faking up the 00 00 FF FF that the sender stripped.
+ */
+ if (!priv->rx.avail_in && wsi->u.ws.final &&
+ !wsi->u.ws.rx_packet_length && !was_fin &&
+ priv->rx.avail_out /* ambiguous as to if it is the end */
+ ) {
+ lwsl_ext("RX APPEND_TRAILER-DO\n");
+ was_fin = 1;
+ priv->rx.next_in = trail;
+ priv->rx.avail_in = sizeof(trail);
+ n = inflate(&priv->rx, Z_SYNC_FLUSH);
+ lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n,
+ priv->rx.avail_in, priv->rx.avail_out);
+ switch (n) {
+ case Z_NEED_DICT:
+ case Z_STREAM_ERROR:
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ lwsl_info("zlib error inflate %d: %s\n",
+ n, priv->rx.msg);
+ return -1;
+ }
+ }
+ /*
+ * we must announce in our returncode now if there is more
+ * output to be expected from inflate, so we can decide to
+ * set the FIN bit on this bufferload or not. However zlib
+ * is ambiguous when we exactly filled the inflate buffer. It
+ * does not give us a clue as to whether we should understand
+ * that to mean he ended on a buffer boundary, or if there is
+ * more in the pipeline.
+ *
+ * So to work around that safely, if it used all output space
+ * exactly, we ALWAYS say there is more coming and we withhold
+ * the last byte of the buffer to guarantee that is true.
+ *
+ * That still leaves us at least one byte to finish with a FIN
+ * on, even if actually nothing more is coming from the next
+ * inflate action itself.
+ */
+ if (!priv->rx.avail_out) { /* he used all available out buf */
+ lwsl_ext("-- rx grabbing held --\n");
+ /* snip the last byte and hold it for next time */
+ priv->rx_held = *(--priv->rx.next_out);
+ priv->rx_held_valid = 1;
+ }
+
+ eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token;
+ priv->count_rx_between_fin += eff_buf->token_len;
+
+ lwsl_ext(" %s: RX leaving with new effbuff len %d, "
+ "ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n",
+ __func__, eff_buf->token_len, priv->rx_held_valid,
+ priv->rx.avail_in,
+ (unsigned long)priv->count_rx_between_fin);
+
+ if (was_fin) {
+ priv->count_rx_between_fin = 0;
+ if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
+ (void)inflateEnd(&priv->rx);
+ priv->rx_init = 0;
+ }
+ }
+#if 0
+ for (n = 0; n < eff_buf->token_len; n++)
+ putchar(eff_buf->token[n]);
+ puts("\n");
+#endif
+
+ return priv->rx_held_valid;
+
+ case LWS_EXT_CB_PAYLOAD_TX:
+
+ if (!priv->tx_init) {
+ n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
+ Z_DEFLATED,
+ -priv->args[PMD_SERVER_MAX_WINDOW_BITS +
+ (wsi->vhost->listen_port <= 0)],
+ priv->args[PMD_MEM_LEVEL],
+ Z_DEFAULT_STRATEGY);
+ if (n != Z_OK) {
+ lwsl_ext("inflateInit2 failed %d\n", n);
+ return 1;
+ }
+ }
+ priv->tx_init = 1;
+ if (!priv->buf_tx_deflated)
+ priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 +
+ (1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf");
+ if (!priv->buf_tx_deflated) {
+ lwsl_err("%s: OOM\n", __func__);
+ return -1;
+ }
+
+ if (eff_buf->token) {
+ lwsl_ext("%s: TX: eff_buf length %d\n", __func__,
+ eff_buf->token_len);
+ priv->tx.next_in = (unsigned char *)eff_buf->token;
+ priv->tx.avail_in = eff_buf->token_len;
+ }
+
+#if 0
+ for (n = 0; n < eff_buf->token_len; n++) {
+ printf("%02X ", (unsigned char)eff_buf->token[n]);
+ if ((n & 15) == 15)
+ printf("\n");
+ }
+ printf("\n");
+#endif
+
+ priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
+ eff_buf->token = (char *)priv->tx.next_out;
+ priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2];
+
+ n = deflate(&priv->tx, Z_SYNC_FLUSH);
+ if (n == Z_STREAM_ERROR) {
+ lwsl_ext("%s: Z_STREAM_ERROR\n", __func__);
+ return -1;
+ }
+
+ if (priv->tx_held_valid) {
+ priv->tx_held_valid = 0;
+ if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
+ /*
+ * we can get a situation he took something in
+ * but did not generate anything out, at the end
+ * of a message (eg, next thing he sends is 80
+ * 00, a zero length FIN, like Authobahn can
+ * send).
+ * If we have come back as a FIN, we must not
+ * place the pending trailer 00 00 FF FF, just
+ * the 1 byte of live data
+ */
+ *(--eff_buf->token) = priv->tx_held[0];
+ else {
+ /* he generated data, prepend whole pending */
+ eff_buf->token -= 5;
+ for (n = 0; n < 5; n++)
+ eff_buf->token[n] = priv->tx_held[n];
+
+ }
+ }
+ priv->compressed_out = 1;
+ eff_buf->token_len = (int)(priv->tx.next_out -
+ (unsigned char *)eff_buf->token);
+
+ /*
+ * we must announce in our returncode now if there is more
+ * output to be expected from inflate, so we can decide to
+ * set the FIN bit on this bufferload or not. However zlib
+ * is ambiguous when we exactly filled the inflate buffer. It
+ * does not give us a clue as to whether we should understand
+ * that to mean he ended on a buffer boundary, or if there is
+ * more in the pipeline.
+ *
+ * Worse, the guy providing the stuff we are sending may not
+ * know until after that this was, actually, the last chunk,
+ * that can happen even if we did not fill the output buf, ie
+ * he may send after this a zero-length FIN fragment.
+ *
+ * This is super difficult because we must snip the last 4
+ * bytes in the case this is the last compressed output of the
+ * message. The only way to deal with it is defer sending the
+ * last 5 bytes of each frame until the next one, when we will
+ * be in a position to understand if that has a FIN or not.
+ */
+
+ extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
+
+ if (eff_buf->token_len >= 4 + extra) {
+ lwsl_ext("tx held %d\n", 4 + extra);
+ priv->tx_held_valid = extra;
+ for (n = 3 + extra; n >= 0; n--)
+ priv->tx_held[n] = *(--priv->tx.next_out);
+ eff_buf->token_len -= 4 + extra;
+ }
+ lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
+ eff_buf->token_len, !priv->tx.avail_out);
+
+ return !priv->tx.avail_out; /* 1 == have more tx pending */
+
+ case LWS_EXT_CB_PACKET_TX_PRESEND:
+ if (!priv->compressed_out)
+ break;
+ priv->compressed_out = 0;
+
+ if ((*(eff_buf->token) & 0x80) &&
+ priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
+ lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
+ (void)deflateEnd(&priv->tx);
+ priv->tx_init = 0;
+ }
+
+ n = *(eff_buf->token) & 15;
+ /* set RSV1, but not on CONTINUATION */
+ if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
+ *eff_buf->token |= 0x40;
+#if 0
+ for (n = 0; n < eff_buf->token_len; n++) {
+ printf("%02X ", (unsigned char)eff_buf->token[n]);
+ if ((n & 15) == 15)
+ puts("\n");
+ }
+ puts("\n");
+#endif
+ lwsl_ext("%s: tx opcode 0x%02X\n", __func__,
+ (unsigned char)*eff_buf->token);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/thirdparty/lws/ext/extension-permessage-deflate.h b/thirdparty/lws/ext/extension-permessage-deflate.h
new file mode 100644
index 0000000000..8737736897
--- /dev/null
+++ b/thirdparty/lws/ext/extension-permessage-deflate.h
@@ -0,0 +1,41 @@
+
+#include <zlib.h>
+
+#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
+#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
+
+enum arg_indexes {
+ PMD_SERVER_NO_CONTEXT_TAKEOVER,
+ PMD_CLIENT_NO_CONTEXT_TAKEOVER,
+ PMD_SERVER_MAX_WINDOW_BITS,
+ PMD_CLIENT_MAX_WINDOW_BITS,
+ PMD_RX_BUF_PWR2,
+ PMD_TX_BUF_PWR2,
+ PMD_COMP_LEVEL,
+ PMD_MEM_LEVEL,
+
+ PMD_ARG_COUNT
+};
+
+struct lws_ext_pm_deflate_priv {
+ z_stream rx;
+ z_stream tx;
+
+ unsigned char *buf_rx_inflated; /* RX inflated output buffer */
+ unsigned char *buf_tx_deflated; /* TX deflated output buffer */
+
+ size_t count_rx_between_fin;
+
+ unsigned char args[PMD_ARG_COUNT];
+ unsigned char tx_held[5];
+ unsigned char rx_held;
+
+ unsigned char tx_init:1;
+ unsigned char rx_init:1;
+ unsigned char compressed_out:1;
+ unsigned char rx_held_valid:1;
+ unsigned char tx_held_valid:1;
+ unsigned char rx_append_trailer:1;
+ unsigned char pending_tx_trailer:1;
+};
+
diff --git a/thirdparty/lws/ext/extension.c b/thirdparty/lws/ext/extension.c
new file mode 100644
index 0000000000..ac28204034
--- /dev/null
+++ b/thirdparty/lws/ext/extension.c
@@ -0,0 +1,344 @@
+#include "private-libwebsockets.h"
+
+#include "extension-permessage-deflate.h"
+
+LWS_VISIBLE void
+lws_context_init_extensions(struct lws_context_creation_info *info,
+ struct lws_context *context)
+{
+ lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
+}
+
+enum lws_ext_option_parser_states {
+ LEAPS_SEEK_NAME,
+ LEAPS_EAT_NAME,
+ LEAPS_SEEK_VAL,
+ LEAPS_EAT_DEC,
+ LEAPS_SEEK_ARG_TERM
+};
+
+LWS_VISIBLE int
+lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
+ void *ext_user, const struct lws_ext_options *opts,
+ const char *in, int len)
+{
+ enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
+ unsigned int match_map = 0, n, m, w = 0, count_options = 0,
+ pending_close_quote = 0;
+ struct lws_ext_option_arg oa;
+
+ oa.option_name = NULL;
+
+ while (opts[count_options].name)
+ count_options++;
+ while (len) {
+ lwsl_ext("'%c' %d", *in, leap);
+ switch (leap) {
+ case LEAPS_SEEK_NAME:
+ if (*in == ' ')
+ break;
+ if (*in == ',') {
+ len = 1;
+ break;
+ }
+ match_map = (1 << count_options) - 1;
+ leap = LEAPS_EAT_NAME;
+ w = 0;
+
+ /* fallthru */
+
+ case LEAPS_EAT_NAME:
+ oa.start = NULL;
+ oa.len = 0;
+ m = match_map;
+ n = 0;
+ pending_close_quote = 0;
+ while (m) {
+ if (m & 1) {
+ lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w);
+
+ if (*in == opts[n].name[w]) {
+ if (!opts[n].name[w + 1]) {
+ oa.option_index = n;
+ lwsl_ext("hit %d\n", oa.option_index);
+ leap = LEAPS_SEEK_VAL;
+ if (len == 1)
+ goto set_arg;
+ break;
+ }
+ } else {
+ match_map &= ~(1 << n);
+ if (!match_map) {
+ lwsl_ext("empty match map\n");
+ return -1;
+ }
+ }
+ }
+ m >>= 1;
+ n++;
+ }
+ w++;
+ break;
+ case LEAPS_SEEK_VAL:
+ if (*in == ' ')
+ break;
+ if (*in == ',') {
+ len = 1;
+ break;
+ }
+ if (*in == ';' || len == 1) { /* ie,nonoptional */
+ if (opts[oa.option_index].type == EXTARG_DEC)
+ return -1;
+ leap = LEAPS_SEEK_NAME;
+ goto set_arg;
+ }
+ if (*in == '=') {
+ w = 0;
+ pending_close_quote = 0;
+ if (opts[oa.option_index].type == EXTARG_NONE)
+ return -1;
+
+ leap = LEAPS_EAT_DEC;
+ break;
+ }
+ return -1;
+
+ case LEAPS_EAT_DEC:
+ if (*in >= '0' && *in <= '9') {
+ if (!w)
+ oa.start = in;
+ w++;
+ if (len != 1)
+ break;
+ }
+ if (!w && *in =='"') {
+ pending_close_quote = 1;
+ break;
+ }
+ if (!w)
+ return -1;
+ if (pending_close_quote && *in != '"' && len != 1)
+ return -1;
+ leap = LEAPS_SEEK_ARG_TERM;
+ if (oa.start)
+ oa.len = in - oa.start;
+ if (len == 1)
+ oa.len++;
+
+set_arg:
+ ext->callback(lws_get_context(wsi),
+ ext, wsi, LWS_EXT_CB_OPTION_SET,
+ ext_user, (char *)&oa, 0);
+ if (len == 1)
+ break;
+ if (pending_close_quote && *in == '"')
+ break;
+
+ /* fallthru */
+
+ case LEAPS_SEEK_ARG_TERM:
+ if (*in == ' ')
+ break;
+ if (*in == ';') {
+ leap = LEAPS_SEEK_NAME;
+ break;
+ }
+ if (*in == ',') {
+ len = 1;
+ break;
+ }
+ return -1;
+ }
+ len--;
+ in++;
+ }
+
+ return 0;
+}
+
+
+/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
+
+int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
+{
+ int n, m, handled = 0;
+
+ for (n = 0; n < wsi->count_act_ext; n++) {
+ m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
+ wsi->active_extensions[n], wsi, reason,
+ wsi->act_ext_user[n], arg, len);
+ if (m < 0) {
+ lwsl_ext("Ext '%s' failed to handle callback %d!\n",
+ wsi->active_extensions[n]->name, reason);
+ return -1;
+ }
+ /* valgrind... */
+ if (reason == LWS_EXT_CB_DESTROY)
+ wsi->act_ext_user[n] = NULL;
+ if (m > handled)
+ handled = m;
+ }
+
+ return handled;
+}
+
+int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
+ int reason, void *arg, int len)
+{
+ int n = 0, m, handled = 0;
+ const struct lws_extension *ext;
+
+ if (!wsi || !wsi->vhost)
+ return 0;
+
+ ext = wsi->vhost->extensions;
+
+ while (ext && ext->callback && !handled) {
+ m = ext->callback(context, ext, wsi, reason,
+ (void *)(lws_intptr_t)n, arg, len);
+ if (m < 0) {
+ lwsl_ext("Ext '%s' failed to handle callback %d!\n",
+ wsi->active_extensions[n]->name, reason);
+ return -1;
+ }
+ if (m)
+ handled = 1;
+
+ ext++;
+ n++;
+ }
+
+ return 0;
+}
+
+int
+lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
+{
+ struct lws_tokens eff_buf;
+ int ret, m, n = 0;
+
+ eff_buf.token = (char *)buf;
+ eff_buf.token_len = len;
+
+ /*
+ * while we have original buf to spill ourselves, or extensions report
+ * more in their pipeline
+ */
+
+ ret = 1;
+ while (ret == 1) {
+
+ /* default to nobody has more to spill */
+
+ ret = 0;
+
+ /* show every extension the new incoming data */
+ m = lws_ext_cb_active(wsi,
+ LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
+ if (m < 0)
+ return -1;
+ if (m) /* handled */
+ ret = 1;
+
+ if ((char *)buf != eff_buf.token)
+ /*
+ * extension recreated it:
+ * need to buffer this if not all sent
+ */
+ wsi->u.ws.clean_buffer = 0;
+
+ /* assuming they left us something to send, send it */
+
+ if (eff_buf.token_len) {
+ n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
+ eff_buf.token_len);
+ if (n < 0) {
+ lwsl_info("closing from ext access\n");
+ return -1;
+ }
+
+ /* always either sent it all or privately buffered */
+ if (wsi->u.ws.clean_buffer)
+ len = n;
+ }
+
+ lwsl_parser("written %d bytes to client\n", n);
+
+ /* no extension has more to spill? Then we can go */
+
+ if (!ret)
+ break;
+
+ /* we used up what we had */
+
+ eff_buf.token = NULL;
+ eff_buf.token_len = 0;
+
+ /*
+ * Did that leave the pipe choked?
+ * Or we had to hold on to some of it?
+ */
+
+ if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
+ /* no we could add more, lets's do that */
+ continue;
+
+ lwsl_debug("choked\n");
+
+ /*
+ * Yes, he's choked. Don't spill the rest now get a callback
+ * when he is ready to send and take care of it there
+ */
+ lws_callback_on_writable(wsi);
+ wsi->extension_data_pending = 1;
+ ret = 0;
+ }
+
+ return len;
+}
+
+int
+lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
+ void *v, size_t len)
+{
+ struct lws_context *context = wsi->context;
+ int n, handled = 0;
+
+ /* maybe an extension will take care of it for us */
+
+ for (n = 0; n < wsi->count_act_ext && !handled; n++) {
+ if (!wsi->active_extensions[n]->callback)
+ continue;
+
+ handled |= wsi->active_extensions[n]->callback(context,
+ wsi->active_extensions[n], wsi,
+ r, wsi->act_ext_user[n], v, len);
+ }
+
+ return handled;
+}
+
+int
+lws_set_extension_option(struct lws *wsi, const char *ext_name,
+ const char *opt_name, const char *opt_val)
+{
+ struct lws_ext_option_arg oa;
+ int idx = 0;
+
+ /* first identify if the ext is active on this wsi */
+ while (idx < wsi->count_act_ext &&
+ strcmp(wsi->active_extensions[idx]->name, ext_name))
+ idx++;
+
+ if (idx == wsi->count_act_ext)
+ return -1; /* request ext not active on this wsi */
+
+ oa.option_name = opt_name;
+ oa.option_index = 0;
+ oa.start = opt_val;
+ oa.len = 0;
+
+ return wsi->active_extensions[idx]->callback(
+ wsi->context, wsi->active_extensions[idx], wsi,
+ LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0);
+}