diff options
Diffstat (limited to 'thirdparty/lws/ext')
-rw-r--r-- | thirdparty/lws/ext/extension-permessage-deflate.c | 473 | ||||
-rw-r--r-- | thirdparty/lws/ext/extension-permessage-deflate.h | 41 | ||||
-rw-r--r-- | thirdparty/lws/ext/extension.c | 344 |
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); +} |