Update Files
This commit is contained in:
559
Kinc/Sources/kinc/libs/core/ext/extension-permessage-deflate.c
Normal file
559
Kinc/Sources/kinc/libs/core/ext/extension-permessage-deflate.c
Normal file
@ -0,0 +1,559 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.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 = (int)wsi->a.context->pt_serv_buf_size;
|
||||
if (wsi->a.protocol->rx_buffer_size)
|
||||
n = (int)wsi->a.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] = (unsigned char)extra;
|
||||
lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
|
||||
|
||||
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_ext_pm_deflate_rx_ebufs *pmdrx =
|
||||
(struct lws_ext_pm_deflate_rx_ebufs *)in;
|
||||
struct lws_ext_option_arg *oa;
|
||||
int n, ret = 0, was_fin = 0, m;
|
||||
unsigned int pen = 0;
|
||||
int penbits = 0;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_EXT_CB_NAMED_OPTION_SET:
|
||||
oa = in;
|
||||
if (!oa->option_name)
|
||||
break;
|
||||
lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
|
||||
n++)
|
||||
if (!strcmp(lws_ext_pm_deflate_options[n].name,
|
||||
oa->option_name))
|
||||
break;
|
||||
|
||||
if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
|
||||
break;
|
||||
oa->option_index = n;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWS_EXT_CB_OPTION_SET:
|
||||
oa = in;
|
||||
lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
|
||||
oa->option_index, oa->start, oa->len);
|
||||
if (oa->start)
|
||||
priv->args[oa->option_index] = (unsigned char)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 = (int)context->pt_serv_buf_size;
|
||||
if (wsi->a.protocol->rx_buffer_size)
|
||||
n = (int)wsi->a.protocol->rx_buffer_size;
|
||||
|
||||
if (n < 128) {
|
||||
lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
|
||||
"(%s) to have an RX buffer >= 128",
|
||||
wsi->a.protocol->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* fill in **user */
|
||||
priv = lws_zalloc(sizeof(*priv), "pmd priv");
|
||||
*((void **)user) = priv;
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
|
||||
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_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
|
||||
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:
|
||||
/*
|
||||
* ie, we are INFLATING
|
||||
*/
|
||||
lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
|
||||
pmdrx->eb_in.len, priv->rx.avail_in);
|
||||
|
||||
/*
|
||||
* If this frame is not marked as compressed,
|
||||
* there is nothing we should do with it
|
||||
*/
|
||||
|
||||
if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
|
||||
/*
|
||||
* This is a bit different than DID_NOTHING... we have
|
||||
* identified using ext-private bits in the packet, or
|
||||
* by it being a control fragment that we SHOULD not do
|
||||
* anything to it, parent should continue as if we
|
||||
* processed it
|
||||
*/
|
||||
return PMDR_NOTHING_WE_SHOULD_DO;
|
||||
|
||||
/*
|
||||
* we shouldn't come back in here if we already applied the
|
||||
* trailer for this compressed packet
|
||||
*/
|
||||
if (!wsi->ws->pmd_trailer_application)
|
||||
return PMDR_DID_NOTHING;
|
||||
|
||||
pmdrx->eb_out.len = 0;
|
||||
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
|
||||
"existing avail in %d, pkt fin: %d",
|
||||
pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
|
||||
|
||||
/* if needed, initialize the inflator */
|
||||
|
||||
if (!priv->rx_init) {
|
||||
if (inflateInit2(&priv->rx,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
|
||||
lwsl_wsi_err(wsi, "iniflateInit failed");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
priv->rx_init = 1;
|
||||
if (!priv->buf_rx_inflated)
|
||||
priv->buf_rx_inflated = lws_malloc(
|
||||
(unsigned int)(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_RX_BUF_PWR2])),
|
||||
"pmd rx inflate buf");
|
||||
if (!priv->buf_rx_inflated) {
|
||||
lwsl_wsi_err(wsi, "OOM");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* don't give us new input while we still work through
|
||||
* the last input
|
||||
*/
|
||||
|
||||
if (priv->rx.avail_in && pmdrx->eb_in.token &&
|
||||
pmdrx->eb_in.len) {
|
||||
lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
|
||||
priv->rx.avail_in);
|
||||
// assert(0);
|
||||
}
|
||||
#endif
|
||||
if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
|
||||
priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
||||
priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
|
||||
}
|
||||
|
||||
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
|
||||
pmdrx->eb_out.token = priv->rx.next_out;
|
||||
priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
|
||||
|
||||
/* so... 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->ws->final &&
|
||||
!wsi->ws->rx_packet_length &&
|
||||
wsi->ws->pmd_trailer_application) {
|
||||
lwsl_wsi_ext(wsi, "trailer apply 1");
|
||||
was_fin = 1;
|
||||
wsi->ws->pmd_trailer_application = 0;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
}
|
||||
|
||||
/*
|
||||
* if after all that there's nothing pending and nothing to give
|
||||
* him right now, bail without having done anything
|
||||
*/
|
||||
|
||||
if (!priv->rx.avail_in)
|
||||
return PMDR_DID_NOTHING;
|
||||
|
||||
n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
|
||||
lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
|
||||
priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
|
||||
n, priv->rx.msg);
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* track how much input was used, and advance it
|
||||
*/
|
||||
|
||||
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
||||
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
|
||||
pmdrx->eb_in.len = (int)priv->rx.avail_in;
|
||||
|
||||
lwsl_wsi_debug(wsi, "%d %d %d %d %d",
|
||||
priv->rx.avail_in,
|
||||
wsi->ws->final,
|
||||
(int)wsi->ws->rx_packet_length,
|
||||
was_fin,
|
||||
wsi->ws->pmd_trailer_application);
|
||||
|
||||
if (!priv->rx.avail_in &&
|
||||
wsi->ws->final &&
|
||||
!wsi->ws->rx_packet_length &&
|
||||
!was_fin &&
|
||||
wsi->ws->pmd_trailer_application) {
|
||||
lwsl_wsi_ext(wsi, "RX trailer apply 2");
|
||||
|
||||
/* we overallocated just for this situation where
|
||||
* we might issue something */
|
||||
priv->rx.avail_out += 5;
|
||||
|
||||
was_fin = 1;
|
||||
wsi->ws->pmd_trailer_application = 0;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
n = inflate(&priv->rx, Z_SYNC_FLUSH);
|
||||
lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
|
||||
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_wsi_info(wsi, "zlib error inflate %d: %s",
|
||||
n, priv->rx.msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(priv->rx.avail_out);
|
||||
}
|
||||
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
|
||||
|
||||
lwsl_wsi_ext(wsi, " RX leaving with new effbuff len %d, "
|
||||
"rx.avail_in=%d, TOTAL RX since FIN %lu",
|
||||
pmdrx->eb_out.len, priv->rx.avail_in,
|
||||
(unsigned long)priv->count_rx_between_fin);
|
||||
|
||||
if (was_fin) {
|
||||
lwsl_wsi_ext(wsi, "was_fin");
|
||||
priv->count_rx_between_fin = 0;
|
||||
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
|
||||
(void)inflateEnd(&priv->rx);
|
||||
priv->rx_init = 0;
|
||||
}
|
||||
|
||||
return PMDR_EMPTY_FINAL;
|
||||
}
|
||||
|
||||
if (priv->rx.avail_in)
|
||||
return PMDR_HAS_PENDING;
|
||||
|
||||
return PMDR_EMPTY_NONFINAL;
|
||||
|
||||
case LWS_EXT_CB_PAYLOAD_TX:
|
||||
|
||||
/*
|
||||
* ie, we are DEFLATING
|
||||
*
|
||||
* initialize us if needed
|
||||
*/
|
||||
|
||||
if (!priv->tx_init) {
|
||||
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
|
||||
Z_DEFLATED,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
|
||||
(wsi->a.vhost->listen_port <= 0)],
|
||||
priv->args[PMD_MEM_LEVEL],
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (n != Z_OK) {
|
||||
lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
priv->tx_init = 1;
|
||||
}
|
||||
|
||||
if (!priv->buf_tx_deflated)
|
||||
priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_TX_BUF_PWR2])),
|
||||
"pmd tx deflate buf");
|
||||
if (!priv->buf_tx_deflated) {
|
||||
lwsl_wsi_err(wsi, "OOM");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
/* hook us up with any deflated input that the caller has */
|
||||
|
||||
if (pmdrx->eb_in.token) {
|
||||
|
||||
assert(!priv->tx.avail_in);
|
||||
|
||||
priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
|
||||
lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
|
||||
"TOTAL TX since FIN: %d",
|
||||
pmdrx->eb_in.len,
|
||||
(int)priv->count_tx_between_fin);
|
||||
priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
||||
priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
|
||||
}
|
||||
|
||||
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
|
||||
pmdrx->eb_out.token = priv->tx.next_out;
|
||||
priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
|
||||
|
||||
pen = 0;
|
||||
penbits = 0;
|
||||
deflatePending(&priv->tx, &pen, &penbits);
|
||||
pen = pen | (unsigned int)penbits;
|
||||
|
||||
if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
|
||||
lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
|
||||
|
||||
if (!pen)
|
||||
return PMDR_DID_NOTHING;
|
||||
}
|
||||
|
||||
m = Z_NO_FLUSH;
|
||||
if (!(len & LWS_WRITE_NO_FIN)) {
|
||||
lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
|
||||
(int)wsi->ws->rx_packet_length);
|
||||
m = Z_SYNC_FLUSH;
|
||||
}
|
||||
|
||||
n = deflate(&priv->tx, m);
|
||||
if (n == Z_STREAM_ERROR) {
|
||||
lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
|
||||
|
||||
lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
|
||||
(unsigned int)len);
|
||||
|
||||
if ((len & 0xf) == LWS_WRITE_TEXT)
|
||||
priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
|
||||
if ((len & 0xf) == LWS_WRITE_BINARY)
|
||||
priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
|
||||
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
|
||||
if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
|
||||
pmdrx->eb_out.len < 4) {
|
||||
lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
|
||||
(int)pmdrx->eb_out.len);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (!(len & LWS_WRITE_NO_FIN) &&
|
||||
m == Z_SYNC_FLUSH &&
|
||||
!pen &&
|
||||
pmdrx->eb_out.len >= 4) {
|
||||
// lwsl_wsi_err(wsi, "Trimming 4 from end of write");
|
||||
priv->tx.next_out -= 4;
|
||||
priv->tx.avail_out += 4;
|
||||
priv->count_tx_between_fin = 0;
|
||||
|
||||
assert(priv->tx.next_out[0] == 0x00 &&
|
||||
priv->tx.next_out[1] == 0x00 &&
|
||||
priv->tx.next_out[2] == 0xff &&
|
||||
priv->tx.next_out[3] == 0xff);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* track how much input was used and advance it
|
||||
*/
|
||||
|
||||
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
||||
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
|
||||
pmdrx->eb_in.len = (int)priv->tx.avail_in;
|
||||
|
||||
priv->compressed_out = 1;
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
|
||||
lwsl_wsi_ext(wsi, " TX rewritten with new eb_in len %d, "
|
||||
"eb_out len %d, deflatePending %d",
|
||||
pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
|
||||
|
||||
if (pmdrx->eb_in.len || pen)
|
||||
return PMDR_HAS_PENDING;
|
||||
|
||||
if (!(len & LWS_WRITE_NO_FIN))
|
||||
return PMDR_EMPTY_FINAL;
|
||||
|
||||
return PMDR_EMPTY_NONFINAL;
|
||||
|
||||
case LWS_EXT_CB_PACKET_TX_PRESEND:
|
||||
if (!priv->compressed_out)
|
||||
break;
|
||||
priv->compressed_out = 0;
|
||||
|
||||
/*
|
||||
* we may have not produced any output for the actual "first"
|
||||
* write... in that case, we need to fix up the inappropriate
|
||||
* use of CONTINUATION when the first real write does come.
|
||||
*/
|
||||
if (priv->tx_first_frame_type & 0xf) {
|
||||
*pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
|
||||
((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
|
||||
/*
|
||||
* We have now written the "first" fragment, only
|
||||
* do that once
|
||||
*/
|
||||
priv->tx_first_frame_type = 0;
|
||||
}
|
||||
|
||||
n = *(pmdrx->eb_in.token) & 15;
|
||||
|
||||
/* set RSV1, but not on CONTINUATION */
|
||||
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
|
||||
*pmdrx->eb_in.token |= 0x40;
|
||||
|
||||
lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
|
||||
((*pmdrx->eb_in.token) & 0xff),
|
||||
pmdrx->eb_in.len);
|
||||
|
||||
if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
|
||||
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
|
||||
(void)deflateEnd(&priv->tx);
|
||||
priv->tx_init = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_MINIZ)
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#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 */
|
||||
|
||||
unsigned char *buf_tx_holding;
|
||||
|
||||
size_t count_rx_between_fin;
|
||||
size_t count_tx_between_fin;
|
||||
|
||||
size_t len_tx_holding;
|
||||
|
||||
unsigned char args[PMD_ARG_COUNT];
|
||||
|
||||
unsigned char tx_first_frame_type;
|
||||
|
||||
unsigned char tx_init:1;
|
||||
unsigned char rx_init:1;
|
||||
unsigned char compressed_out:1;
|
||||
};
|
||||
|
382
Kinc/Sources/kinc/libs/core/ext/extension.c
Normal file
382
Kinc/Sources/kinc/libs/core/ext/extension.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#include "extension-permessage-deflate.h"
|
||||
|
||||
void
|
||||
lws_context_init_extensions(const struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
{
|
||||
lwsl_cx_info(context, " LWS_MAX_EXTENSIONS_ACTIVE: %u", 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
|
||||
};
|
||||
|
||||
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_wsi_ext(wsi, "'%c' %d", *in, leap);
|
||||
switch (leap) {
|
||||
case LEAPS_SEEK_NAME:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
match_map = (unsigned int)(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)) {
|
||||
m >>= 1;
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
lwsl_wsi_ext(wsi, " m=%d, n=%d, w=%d", m, n, w);
|
||||
|
||||
if (*in == opts[n].name[w]) {
|
||||
if (!opts[n].name[w + 1]) {
|
||||
oa.option_index = (int)n;
|
||||
lwsl_wsi_ext(wsi, "hit %d",
|
||||
oa.option_index);
|
||||
leap = LEAPS_SEEK_VAL;
|
||||
if (len == 1)
|
||||
goto set_arg;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
match_map &= (unsigned int)~(1 << n);
|
||||
if (!match_map) {
|
||||
lwsl_wsi_ext(wsi, "empty match map");
|
||||
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 = lws_ptr_diff(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;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < wsi->ws->count_act_ext; n++) {
|
||||
m = wsi->ws->active_extensions[n]->callback(
|
||||
lws_get_context(wsi), wsi->ws->active_extensions[n],
|
||||
wsi, (enum lws_extension_callback_reasons)reason, wsi->ws->act_ext_user[n], arg, (size_t)len);
|
||||
if (m < 0) {
|
||||
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
|
||||
wsi->ws->active_extensions[n]->name, reason);
|
||||
return -1;
|
||||
}
|
||||
/* valgrind... */
|
||||
if (reason == LWS_EXT_CB_DESTROY)
|
||||
wsi->ws->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->a.vhost || !wsi->ws)
|
||||
return 0;
|
||||
|
||||
ext = wsi->a.vhost->ws.extensions;
|
||||
|
||||
while (ext && ext->callback && !handled) {
|
||||
m = ext->callback(context, ext, wsi, (enum lws_extension_callback_reasons)reason,
|
||||
(void *)(lws_intptr_t)n, arg, (size_t)len);
|
||||
if (m < 0) {
|
||||
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
|
||||
wsi->ws->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 ebuf;
|
||||
int ret, m, n = 0;
|
||||
|
||||
ebuf.token = buf;
|
||||
ebuf.len = (int)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,
|
||||
&ebuf, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */
|
||||
ret = 1;
|
||||
|
||||
if (buf != ebuf.token)
|
||||
/*
|
||||
* extension recreated it:
|
||||
* need to buffer this if not all sent
|
||||
*/
|
||||
wsi->ws->clean_buffer = 0;
|
||||
|
||||
/* assuming they left us something to send, send it */
|
||||
|
||||
if (ebuf.len) {
|
||||
n = lws_issue_raw(wsi, ebuf.token, (size_t)ebuf.len);
|
||||
if (n < 0) {
|
||||
lwsl_wsi_info(wsi, "closing from ext access");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* always either sent it all or privately buffered */
|
||||
if (wsi->ws->clean_buffer)
|
||||
len = (size_t)n;
|
||||
|
||||
lwsl_wsi_ext(wsi, "written %d bytes to client", n);
|
||||
}
|
||||
|
||||
/* no extension has more to spill? Then we can go */
|
||||
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
/* we used up what we had */
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
/*
|
||||
* Did that leave the pipe choked?
|
||||
* Or we had to hold on to some of it?
|
||||
*/
|
||||
|
||||
if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
|
||||
/* no we could add more, lets's do that */
|
||||
continue;
|
||||
|
||||
lwsl_wsi_debug(wsi, "choked");
|
||||
|
||||
/*
|
||||
* 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->ws->extension_data_pending = 1;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return (int)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->a.context;
|
||||
int n, handled = 0;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
/* maybe an extension will take care of it for us */
|
||||
|
||||
for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
|
||||
if (!wsi->ws->active_extensions[n]->callback)
|
||||
continue;
|
||||
|
||||
handled |= wsi->ws->active_extensions[n]->callback(context,
|
||||
wsi->ws->active_extensions[n], wsi,
|
||||
r, wsi->ws->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;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
/* first identify if the ext is active on this wsi */
|
||||
while (idx < wsi->ws->count_act_ext &&
|
||||
strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
|
||||
idx++;
|
||||
|
||||
if (idx == wsi->ws->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->ws->active_extensions[idx]->callback(wsi->a.context,
|
||||
wsi->ws->active_extensions[idx], wsi,
|
||||
LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
|
||||
&oa, 0);
|
||||
}
|
Reference in New Issue
Block a user