mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-30 00:00:16 +01:00

Currently we always reserve a fakewsi per pt so events that don't have a related actual wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks that look reasonable to user protocol handler code expecting a valid wsi every time. This patch splits out stuff that user callbacks often unconditionally expect to be in a wsi, like context pointer, vhost pointer etc into a substructure, which is composed into struct lws at the top of it. Internal references (struct lws is opaque, so there are only internal references) are all updated to go via the substructre, the compiler should make that a NOP. Helpers are added when fakewsi is used and referenced. If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before, although the helpers improve consistency by zeroing down the substructure. There is a huge amount of user code out there over the last 10 years that did not always have the minimal examples to follow, some of it does some unexpected things. If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then create a struct lws_a (the substructure) on the stack, zero it down (but it is only like 4 pointers) and prepare it with whatever we know like the context. Then we cast it to a struct lws * and use it in the user protocol handler call. In this case, the remainder of the struct lws is undefined. However the amount of old protocol handlers that might touch things outside of the substructure in PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is significant on constrained devices. User handlers should not be touching everything in a wsi every time anyway, there are several cases where there is no valid wsi to do the call with. Dereference of things outside the substructure should only happen when the callback reason shows there is a valid wsi bound to the activity (as in all the minimal examples).
549 lines
15 KiB
C
549 lines
15 KiB
C
/*
|
|
* 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 = 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] = extra;
|
|
lwsl_info(" Capping pmd rx to %d\n", 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_ext("%s: named option set: %s\n", __func__,
|
|
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_ext("%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->a.protocol->rx_buffer_size)
|
|
n = (int)wsi->a.protocol->rx_buffer_size;
|
|
|
|
if (n < 128) {
|
|
lwsl_info(" permessage-deflate requires the protocol "
|
|
"(%s) to have an RX buffer >= 128\n",
|
|
wsi->a.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:
|
|
/*
|
|
* ie, we are INFLATING
|
|
*/
|
|
lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
|
|
__func__, pmdrx->eb_in.len, priv->rx.avail_in);
|
|
|
|
/* if this frame is not marked as compressed, we ignore it */
|
|
|
|
if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
|
|
return PMDR_DID_NOTHING;
|
|
|
|
/*
|
|
* 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_ext("%s: LWS_EXT_CB_PAYLOAD_RX: in %d, "
|
|
"existing avail in %d, pkt fin: %d\n", __func__,
|
|
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_err("%s: iniflateInit failed\n", __func__);
|
|
return PMDR_FAILED;
|
|
}
|
|
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 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_warn("%s: priv->rx.avail_in %d while getting new in\n",
|
|
__func__, 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 = 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 = 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_ext("%s: trailer apply 1\n", __func__);
|
|
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_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", 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_err("%s: zlib error inflate %d: \"%s\"\n",
|
|
__func__, n, priv->rx.msg);
|
|
return PMDR_FAILED;
|
|
}
|
|
|
|
/*
|
|
* track how much input was used, and advance it
|
|
*/
|
|
|
|
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
|
(pmdrx->eb_in.len - priv->rx.avail_in);
|
|
pmdrx->eb_in.len = priv->rx.avail_in;
|
|
|
|
lwsl_debug("%s: %d %d %d %d %d\n", __func__,
|
|
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_ext("%s: RX trailer apply 2\n", __func__);
|
|
|
|
/* 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_ext("RX trailer infl ret %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;
|
|
}
|
|
|
|
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 += pmdrx->eb_out.len;
|
|
|
|
lwsl_ext(" %s: RX leaving with new effbuff len %d, "
|
|
"rx.avail_in=%d, TOTAL RX since FIN %lu\n",
|
|
__func__, pmdrx->eb_out.len, priv->rx.avail_in,
|
|
(unsigned long)priv->count_rx_between_fin);
|
|
|
|
if (was_fin) {
|
|
lwsl_ext("%s: was_fin\n", __func__);
|
|
priv->count_rx_between_fin = 0;
|
|
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
|
|
lwsl_ext("PMD_SERVER_NO_CONTEXT_TAKEOVER\n");
|
|
(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_ext("inflateInit2 failed %d\n", n);
|
|
return PMDR_FAILED;
|
|
}
|
|
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 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 += pmdrx->eb_in.len;
|
|
lwsl_ext("%s: TX: eb_in length %d, "
|
|
"TOTAL TX since FIN: %d\n", __func__,
|
|
pmdrx->eb_in.len,
|
|
(int)priv->count_tx_between_fin);
|
|
priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
|
priv->tx.avail_in = 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 = 1 << priv->args[PMD_TX_BUF_PWR2];
|
|
|
|
pen = penbits = 0;
|
|
deflatePending(&priv->tx, &pen, &penbits);
|
|
pen |= penbits;
|
|
|
|
if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
|
|
lwsl_ext("%s: no available in, pen: %u\n", __func__, pen);
|
|
|
|
if (!pen)
|
|
return PMDR_DID_NOTHING;
|
|
}
|
|
|
|
m = Z_NO_FLUSH;
|
|
if (!(len & LWS_WRITE_NO_FIN)) {
|
|
lwsl_ext("%s: deflate with SYNC_FLUSH, pkt len %d\n",
|
|
__func__, (int)wsi->ws->rx_packet_length);
|
|
m = Z_SYNC_FLUSH;
|
|
}
|
|
|
|
n = deflate(&priv->tx, m);
|
|
if (n == Z_STREAM_ERROR) {
|
|
lwsl_notice("%s: Z_STREAM_ERROR\n", __func__);
|
|
return PMDR_FAILED;
|
|
}
|
|
|
|
pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
|
|
|
|
lwsl_ext("%s: deflate ret %d, len 0x%x\n", __func__, 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_err("%s: FAIL want to trim out length %d\n",
|
|
__func__, (int)pmdrx->eb_out.len);
|
|
assert(0);
|
|
}
|
|
|
|
if (!(len & LWS_WRITE_NO_FIN) &&
|
|
m == Z_SYNC_FLUSH &&
|
|
!pen &&
|
|
pmdrx->eb_out.len >= 4) {
|
|
// lwsl_err("%s: Trimming 4 from end of write\n", __func__);
|
|
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 +
|
|
(pmdrx->eb_in.len - priv->tx.avail_in);
|
|
pmdrx->eb_in.len = priv->tx.avail_in;
|
|
|
|
priv->compressed_out = 1;
|
|
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
|
pmdrx->eb_out.token);
|
|
|
|
lwsl_ext(" TX rewritten with new eb_in len %d, "
|
|
"eb_out len %d, deflatePending %d\n",
|
|
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 = ((*pmdrx->eb_in.token) & ~0xf) |
|
|
(priv->tx_first_frame_type & 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_ext("%s: PRESEND compressed: ws frame 0x%02X, len %d\n",
|
|
__func__, ((*pmdrx->eb_in.token) & 0xff),
|
|
pmdrx->eb_in.len);
|
|
|
|
if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
|
|
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
|
|
lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
|
|
(void)deflateEnd(&priv->tx);
|
|
priv->tx_init = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|