1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci */
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci#include "private-lib-core.h"
26d4afb5ceSopenharmony_ci#include "extension-permessage-deflate.h"
27d4afb5ceSopenharmony_ci#include <stdio.h>
28d4afb5ceSopenharmony_ci#include <string.h>
29d4afb5ceSopenharmony_ci#include <assert.h>
30d4afb5ceSopenharmony_ci
31d4afb5ceSopenharmony_ci#define LWS_ZLIB_MEMLEVEL 8
32d4afb5ceSopenharmony_ci
33d4afb5ceSopenharmony_ciconst struct lws_ext_options lws_ext_pm_deflate_options[] = {
34d4afb5ceSopenharmony_ci	/* public RFC7692 settings */
35d4afb5ceSopenharmony_ci	{ "server_no_context_takeover", EXTARG_NONE },
36d4afb5ceSopenharmony_ci	{ "client_no_context_takeover", EXTARG_NONE },
37d4afb5ceSopenharmony_ci	{ "server_max_window_bits",	EXTARG_OPT_DEC },
38d4afb5ceSopenharmony_ci	{ "client_max_window_bits",	EXTARG_OPT_DEC },
39d4afb5ceSopenharmony_ci	/* ones only user code can set */
40d4afb5ceSopenharmony_ci	{ "rx_buf_size",		EXTARG_DEC },
41d4afb5ceSopenharmony_ci	{ "tx_buf_size",		EXTARG_DEC },
42d4afb5ceSopenharmony_ci	{ "compression_level",		EXTARG_DEC },
43d4afb5ceSopenharmony_ci	{ "mem_level",			EXTARG_DEC },
44d4afb5ceSopenharmony_ci	{ NULL, 0 }, /* sentinel */
45d4afb5ceSopenharmony_ci};
46d4afb5ceSopenharmony_ci
47d4afb5ceSopenharmony_cistatic void
48d4afb5ceSopenharmony_cilws_extension_pmdeflate_restrict_args(struct lws *wsi,
49d4afb5ceSopenharmony_ci				      struct lws_ext_pm_deflate_priv *priv)
50d4afb5ceSopenharmony_ci{
51d4afb5ceSopenharmony_ci	int n, extra;
52d4afb5ceSopenharmony_ci
53d4afb5ceSopenharmony_ci	/* cap the RX buf at the nearest power of 2 to protocol rx buf */
54d4afb5ceSopenharmony_ci
55d4afb5ceSopenharmony_ci	n = (int)wsi->a.context->pt_serv_buf_size;
56d4afb5ceSopenharmony_ci	if (wsi->a.protocol->rx_buffer_size)
57d4afb5ceSopenharmony_ci		n = (int)wsi->a.protocol->rx_buffer_size;
58d4afb5ceSopenharmony_ci
59d4afb5ceSopenharmony_ci	extra = 7;
60d4afb5ceSopenharmony_ci	while (n >= 1 << (extra + 1))
61d4afb5ceSopenharmony_ci		extra++;
62d4afb5ceSopenharmony_ci
63d4afb5ceSopenharmony_ci	if (extra < priv->args[PMD_RX_BUF_PWR2]) {
64d4afb5ceSopenharmony_ci		priv->args[PMD_RX_BUF_PWR2] = (unsigned char)extra;
65d4afb5ceSopenharmony_ci		lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
66d4afb5ceSopenharmony_ci	}
67d4afb5ceSopenharmony_ci}
68d4afb5ceSopenharmony_ci
69d4afb5ceSopenharmony_cistatic unsigned char trail[] = { 0, 0, 0xff, 0xff };
70d4afb5ceSopenharmony_ci
71d4afb5ceSopenharmony_ciLWS_VISIBLE int
72d4afb5ceSopenharmony_cilws_extension_callback_pm_deflate(struct lws_context *context,
73d4afb5ceSopenharmony_ci				  const struct lws_extension *ext,
74d4afb5ceSopenharmony_ci				  struct lws *wsi,
75d4afb5ceSopenharmony_ci				  enum lws_extension_callback_reasons reason,
76d4afb5ceSopenharmony_ci				  void *user, void *in, size_t len)
77d4afb5ceSopenharmony_ci{
78d4afb5ceSopenharmony_ci	struct lws_ext_pm_deflate_priv *priv =
79d4afb5ceSopenharmony_ci				     (struct lws_ext_pm_deflate_priv *)user;
80d4afb5ceSopenharmony_ci	struct lws_ext_pm_deflate_rx_ebufs *pmdrx =
81d4afb5ceSopenharmony_ci				(struct lws_ext_pm_deflate_rx_ebufs *)in;
82d4afb5ceSopenharmony_ci	struct lws_ext_option_arg *oa;
83d4afb5ceSopenharmony_ci	int n, ret = 0, was_fin = 0, m;
84d4afb5ceSopenharmony_ci	unsigned int pen = 0;
85d4afb5ceSopenharmony_ci	int penbits = 0;
86d4afb5ceSopenharmony_ci
87d4afb5ceSopenharmony_ci	switch (reason) {
88d4afb5ceSopenharmony_ci	case LWS_EXT_CB_NAMED_OPTION_SET:
89d4afb5ceSopenharmony_ci		oa = in;
90d4afb5ceSopenharmony_ci		if (!oa->option_name)
91d4afb5ceSopenharmony_ci			break;
92d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
93d4afb5ceSopenharmony_ci		for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
94d4afb5ceSopenharmony_ci		     n++)
95d4afb5ceSopenharmony_ci			if (!strcmp(lws_ext_pm_deflate_options[n].name,
96d4afb5ceSopenharmony_ci				    oa->option_name))
97d4afb5ceSopenharmony_ci				break;
98d4afb5ceSopenharmony_ci
99d4afb5ceSopenharmony_ci		if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
100d4afb5ceSopenharmony_ci			break;
101d4afb5ceSopenharmony_ci		oa->option_index = n;
102d4afb5ceSopenharmony_ci
103d4afb5ceSopenharmony_ci		/* fallthru */
104d4afb5ceSopenharmony_ci
105d4afb5ceSopenharmony_ci	case LWS_EXT_CB_OPTION_SET:
106d4afb5ceSopenharmony_ci		oa = in;
107d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
108d4afb5ceSopenharmony_ci			 oa->option_index, oa->start, oa->len);
109d4afb5ceSopenharmony_ci		if (oa->start)
110d4afb5ceSopenharmony_ci			priv->args[oa->option_index] = (unsigned char)atoi(oa->start);
111d4afb5ceSopenharmony_ci		else
112d4afb5ceSopenharmony_ci			priv->args[oa->option_index] = 1;
113d4afb5ceSopenharmony_ci
114d4afb5ceSopenharmony_ci		if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
115d4afb5ceSopenharmony_ci			priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
116d4afb5ceSopenharmony_ci
117d4afb5ceSopenharmony_ci		lws_extension_pmdeflate_restrict_args(wsi, priv);
118d4afb5ceSopenharmony_ci		break;
119d4afb5ceSopenharmony_ci
120d4afb5ceSopenharmony_ci	case LWS_EXT_CB_OPTION_CONFIRM:
121d4afb5ceSopenharmony_ci		if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
122d4afb5ceSopenharmony_ci		    priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
123d4afb5ceSopenharmony_ci		    priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
124d4afb5ceSopenharmony_ci		    priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
125d4afb5ceSopenharmony_ci			return -1;
126d4afb5ceSopenharmony_ci		break;
127d4afb5ceSopenharmony_ci
128d4afb5ceSopenharmony_ci	case LWS_EXT_CB_CLIENT_CONSTRUCT:
129d4afb5ceSopenharmony_ci	case LWS_EXT_CB_CONSTRUCT:
130d4afb5ceSopenharmony_ci
131d4afb5ceSopenharmony_ci		n = (int)context->pt_serv_buf_size;
132d4afb5ceSopenharmony_ci		if (wsi->a.protocol->rx_buffer_size)
133d4afb5ceSopenharmony_ci			n = (int)wsi->a.protocol->rx_buffer_size;
134d4afb5ceSopenharmony_ci
135d4afb5ceSopenharmony_ci		if (n < 128) {
136d4afb5ceSopenharmony_ci			lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
137d4afb5ceSopenharmony_ci				  "(%s) to have an RX buffer >= 128",
138d4afb5ceSopenharmony_ci				  wsi->a.protocol->name);
139d4afb5ceSopenharmony_ci			return -1;
140d4afb5ceSopenharmony_ci		}
141d4afb5ceSopenharmony_ci
142d4afb5ceSopenharmony_ci		/* fill in **user */
143d4afb5ceSopenharmony_ci		priv = lws_zalloc(sizeof(*priv), "pmd priv");
144d4afb5ceSopenharmony_ci		*((void **)user) = priv;
145d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
146d4afb5ceSopenharmony_ci		memset(priv, 0, sizeof(*priv));
147d4afb5ceSopenharmony_ci
148d4afb5ceSopenharmony_ci		/* fill in pointer to options list */
149d4afb5ceSopenharmony_ci		if (in)
150d4afb5ceSopenharmony_ci			*((const struct lws_ext_options **)in) =
151d4afb5ceSopenharmony_ci					lws_ext_pm_deflate_options;
152d4afb5ceSopenharmony_ci
153d4afb5ceSopenharmony_ci		/* fallthru */
154d4afb5ceSopenharmony_ci
155d4afb5ceSopenharmony_ci	case LWS_EXT_CB_OPTION_DEFAULT:
156d4afb5ceSopenharmony_ci
157d4afb5ceSopenharmony_ci		/* set the public, RFC7692 defaults... */
158d4afb5ceSopenharmony_ci
159d4afb5ceSopenharmony_ci		priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
160d4afb5ceSopenharmony_ci		priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
161d4afb5ceSopenharmony_ci		priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
162d4afb5ceSopenharmony_ci		priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
163d4afb5ceSopenharmony_ci
164d4afb5ceSopenharmony_ci		/* ...and the ones the user code can override */
165d4afb5ceSopenharmony_ci
166d4afb5ceSopenharmony_ci		priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
167d4afb5ceSopenharmony_ci		priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
168d4afb5ceSopenharmony_ci		priv->args[PMD_COMP_LEVEL] = 1;
169d4afb5ceSopenharmony_ci		priv->args[PMD_MEM_LEVEL] = 8;
170d4afb5ceSopenharmony_ci
171d4afb5ceSopenharmony_ci		lws_extension_pmdeflate_restrict_args(wsi, priv);
172d4afb5ceSopenharmony_ci		break;
173d4afb5ceSopenharmony_ci
174d4afb5ceSopenharmony_ci	case LWS_EXT_CB_DESTROY:
175d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
176d4afb5ceSopenharmony_ci		lws_free(priv->buf_rx_inflated);
177d4afb5ceSopenharmony_ci		lws_free(priv->buf_tx_deflated);
178d4afb5ceSopenharmony_ci		if (priv->rx_init)
179d4afb5ceSopenharmony_ci			(void)inflateEnd(&priv->rx);
180d4afb5ceSopenharmony_ci		if (priv->tx_init)
181d4afb5ceSopenharmony_ci			(void)deflateEnd(&priv->tx);
182d4afb5ceSopenharmony_ci		lws_free(priv);
183d4afb5ceSopenharmony_ci
184d4afb5ceSopenharmony_ci		return ret;
185d4afb5ceSopenharmony_ci
186d4afb5ceSopenharmony_ci
187d4afb5ceSopenharmony_ci	case LWS_EXT_CB_PAYLOAD_RX:
188d4afb5ceSopenharmony_ci		/*
189d4afb5ceSopenharmony_ci		 * ie, we are INFLATING
190d4afb5ceSopenharmony_ci		 */
191d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
192d4afb5ceSopenharmony_ci			 pmdrx->eb_in.len, priv->rx.avail_in);
193d4afb5ceSopenharmony_ci
194d4afb5ceSopenharmony_ci		/*
195d4afb5ceSopenharmony_ci		 * If this frame is not marked as compressed,
196d4afb5ceSopenharmony_ci		 * there is nothing we should do with it
197d4afb5ceSopenharmony_ci		 */
198d4afb5ceSopenharmony_ci
199d4afb5ceSopenharmony_ci		if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
200d4afb5ceSopenharmony_ci			/*
201d4afb5ceSopenharmony_ci			 * This is a bit different than DID_NOTHING... we have
202d4afb5ceSopenharmony_ci			 * identified using ext-private bits in the packet, or
203d4afb5ceSopenharmony_ci			 * by it being a control fragment that we SHOULD not do
204d4afb5ceSopenharmony_ci			 * anything to it, parent should continue as if we
205d4afb5ceSopenharmony_ci			 * processed it
206d4afb5ceSopenharmony_ci			 */
207d4afb5ceSopenharmony_ci			return PMDR_NOTHING_WE_SHOULD_DO;
208d4afb5ceSopenharmony_ci
209d4afb5ceSopenharmony_ci		/*
210d4afb5ceSopenharmony_ci		 * we shouldn't come back in here if we already applied the
211d4afb5ceSopenharmony_ci		 * trailer for this compressed packet
212d4afb5ceSopenharmony_ci		 */
213d4afb5ceSopenharmony_ci		if (!wsi->ws->pmd_trailer_application)
214d4afb5ceSopenharmony_ci			return PMDR_DID_NOTHING;
215d4afb5ceSopenharmony_ci
216d4afb5ceSopenharmony_ci		pmdrx->eb_out.len = 0;
217d4afb5ceSopenharmony_ci
218d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
219d4afb5ceSopenharmony_ci			 "existing avail in %d, pkt fin: %d",
220d4afb5ceSopenharmony_ci			 pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
221d4afb5ceSopenharmony_ci
222d4afb5ceSopenharmony_ci		/* if needed, initialize the inflator */
223d4afb5ceSopenharmony_ci
224d4afb5ceSopenharmony_ci		if (!priv->rx_init) {
225d4afb5ceSopenharmony_ci			if (inflateInit2(&priv->rx,
226d4afb5ceSopenharmony_ci			     -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
227d4afb5ceSopenharmony_ci				lwsl_wsi_err(wsi, "iniflateInit failed");
228d4afb5ceSopenharmony_ci				return PMDR_FAILED;
229d4afb5ceSopenharmony_ci			}
230d4afb5ceSopenharmony_ci			priv->rx_init = 1;
231d4afb5ceSopenharmony_ci			if (!priv->buf_rx_inflated)
232d4afb5ceSopenharmony_ci				priv->buf_rx_inflated = lws_malloc(
233d4afb5ceSopenharmony_ci					(unsigned int)(LWS_PRE + 7 + 5 +
234d4afb5ceSopenharmony_ci					    (1 << priv->args[PMD_RX_BUF_PWR2])),
235d4afb5ceSopenharmony_ci					    "pmd rx inflate buf");
236d4afb5ceSopenharmony_ci			if (!priv->buf_rx_inflated) {
237d4afb5ceSopenharmony_ci				lwsl_wsi_err(wsi, "OOM");
238d4afb5ceSopenharmony_ci				return PMDR_FAILED;
239d4afb5ceSopenharmony_ci			}
240d4afb5ceSopenharmony_ci		}
241d4afb5ceSopenharmony_ci
242d4afb5ceSopenharmony_ci#if 0
243d4afb5ceSopenharmony_ci		/*
244d4afb5ceSopenharmony_ci		 * don't give us new input while we still work through
245d4afb5ceSopenharmony_ci		 * the last input
246d4afb5ceSopenharmony_ci		 */
247d4afb5ceSopenharmony_ci
248d4afb5ceSopenharmony_ci		if (priv->rx.avail_in && pmdrx->eb_in.token &&
249d4afb5ceSopenharmony_ci					 pmdrx->eb_in.len) {
250d4afb5ceSopenharmony_ci			lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
251d4afb5ceSopenharmony_ci					priv->rx.avail_in);
252d4afb5ceSopenharmony_ci	//		assert(0);
253d4afb5ceSopenharmony_ci		}
254d4afb5ceSopenharmony_ci#endif
255d4afb5ceSopenharmony_ci		if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
256d4afb5ceSopenharmony_ci			priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
257d4afb5ceSopenharmony_ci			priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
258d4afb5ceSopenharmony_ci		}
259d4afb5ceSopenharmony_ci
260d4afb5ceSopenharmony_ci		priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
261d4afb5ceSopenharmony_ci		pmdrx->eb_out.token = priv->rx.next_out;
262d4afb5ceSopenharmony_ci		priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
263d4afb5ceSopenharmony_ci
264d4afb5ceSopenharmony_ci		/* so... if...
265d4afb5ceSopenharmony_ci		 *
266d4afb5ceSopenharmony_ci		 *  - he has no remaining input content for this message, and
267d4afb5ceSopenharmony_ci		 *
268d4afb5ceSopenharmony_ci		 *  - and this is the final fragment, and
269d4afb5ceSopenharmony_ci		 *
270d4afb5ceSopenharmony_ci		 *  - we used everything that could be drained on the input side
271d4afb5ceSopenharmony_ci		 *
272d4afb5ceSopenharmony_ci		 * ...then put back the 00 00 FF FF the sender stripped as our
273d4afb5ceSopenharmony_ci		 * input to zlib
274d4afb5ceSopenharmony_ci		 */
275d4afb5ceSopenharmony_ci		if (!priv->rx.avail_in &&
276d4afb5ceSopenharmony_ci		    wsi->ws->final &&
277d4afb5ceSopenharmony_ci		    !wsi->ws->rx_packet_length &&
278d4afb5ceSopenharmony_ci		    wsi->ws->pmd_trailer_application) {
279d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "trailer apply 1");
280d4afb5ceSopenharmony_ci			was_fin = 1;
281d4afb5ceSopenharmony_ci			wsi->ws->pmd_trailer_application = 0;
282d4afb5ceSopenharmony_ci			priv->rx.next_in = trail;
283d4afb5ceSopenharmony_ci			priv->rx.avail_in = sizeof(trail);
284d4afb5ceSopenharmony_ci		}
285d4afb5ceSopenharmony_ci
286d4afb5ceSopenharmony_ci		/*
287d4afb5ceSopenharmony_ci		 * if after all that there's nothing pending and nothing to give
288d4afb5ceSopenharmony_ci		 * him right now, bail without having done anything
289d4afb5ceSopenharmony_ci		 */
290d4afb5ceSopenharmony_ci
291d4afb5ceSopenharmony_ci		if (!priv->rx.avail_in)
292d4afb5ceSopenharmony_ci			return PMDR_DID_NOTHING;
293d4afb5ceSopenharmony_ci
294d4afb5ceSopenharmony_ci		n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
295d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
296d4afb5ceSopenharmony_ci			 priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
297d4afb5ceSopenharmony_ci		switch (n) {
298d4afb5ceSopenharmony_ci		case Z_NEED_DICT:
299d4afb5ceSopenharmony_ci		case Z_STREAM_ERROR:
300d4afb5ceSopenharmony_ci		case Z_DATA_ERROR:
301d4afb5ceSopenharmony_ci		case Z_MEM_ERROR:
302d4afb5ceSopenharmony_ci			lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
303d4afb5ceSopenharmony_ci				  n, priv->rx.msg);
304d4afb5ceSopenharmony_ci			return PMDR_FAILED;
305d4afb5ceSopenharmony_ci		}
306d4afb5ceSopenharmony_ci
307d4afb5ceSopenharmony_ci		/*
308d4afb5ceSopenharmony_ci		 * track how much input was used, and advance it
309d4afb5ceSopenharmony_ci		 */
310d4afb5ceSopenharmony_ci
311d4afb5ceSopenharmony_ci		pmdrx->eb_in.token = pmdrx->eb_in.token +
312d4afb5ceSopenharmony_ci				         ((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
313d4afb5ceSopenharmony_ci		pmdrx->eb_in.len = (int)priv->rx.avail_in;
314d4afb5ceSopenharmony_ci
315d4afb5ceSopenharmony_ci		lwsl_wsi_debug(wsi, "%d %d %d %d %d",
316d4afb5ceSopenharmony_ci				priv->rx.avail_in,
317d4afb5ceSopenharmony_ci				wsi->ws->final,
318d4afb5ceSopenharmony_ci				(int)wsi->ws->rx_packet_length,
319d4afb5ceSopenharmony_ci				was_fin,
320d4afb5ceSopenharmony_ci				wsi->ws->pmd_trailer_application);
321d4afb5ceSopenharmony_ci
322d4afb5ceSopenharmony_ci		if (!priv->rx.avail_in &&
323d4afb5ceSopenharmony_ci		    wsi->ws->final &&
324d4afb5ceSopenharmony_ci		    !wsi->ws->rx_packet_length &&
325d4afb5ceSopenharmony_ci		    !was_fin &&
326d4afb5ceSopenharmony_ci		    wsi->ws->pmd_trailer_application) {
327d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "RX trailer apply 2");
328d4afb5ceSopenharmony_ci
329d4afb5ceSopenharmony_ci			/* we overallocated just for this situation where
330d4afb5ceSopenharmony_ci			 * we might issue something */
331d4afb5ceSopenharmony_ci			priv->rx.avail_out += 5;
332d4afb5ceSopenharmony_ci
333d4afb5ceSopenharmony_ci			was_fin = 1;
334d4afb5ceSopenharmony_ci			wsi->ws->pmd_trailer_application = 0;
335d4afb5ceSopenharmony_ci			priv->rx.next_in = trail;
336d4afb5ceSopenharmony_ci			priv->rx.avail_in = sizeof(trail);
337d4afb5ceSopenharmony_ci			n = inflate(&priv->rx, Z_SYNC_FLUSH);
338d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
339d4afb5ceSopenharmony_ci				 n, priv->rx.avail_in, priv->rx.avail_out);
340d4afb5ceSopenharmony_ci			switch (n) {
341d4afb5ceSopenharmony_ci			case Z_NEED_DICT:
342d4afb5ceSopenharmony_ci			case Z_STREAM_ERROR:
343d4afb5ceSopenharmony_ci			case Z_DATA_ERROR:
344d4afb5ceSopenharmony_ci			case Z_MEM_ERROR:
345d4afb5ceSopenharmony_ci				lwsl_wsi_info(wsi, "zlib error inflate %d: %s",
346d4afb5ceSopenharmony_ci					  n, priv->rx.msg);
347d4afb5ceSopenharmony_ci				return -1;
348d4afb5ceSopenharmony_ci			}
349d4afb5ceSopenharmony_ci
350d4afb5ceSopenharmony_ci			assert(priv->rx.avail_out);
351d4afb5ceSopenharmony_ci		}
352d4afb5ceSopenharmony_ci
353d4afb5ceSopenharmony_ci		pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
354d4afb5ceSopenharmony_ci						 pmdrx->eb_out.token);
355d4afb5ceSopenharmony_ci		priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
356d4afb5ceSopenharmony_ci
357d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "  RX leaving with new effbuff len %d, "
358d4afb5ceSopenharmony_ci			 "rx.avail_in=%d, TOTAL RX since FIN %lu",
359d4afb5ceSopenharmony_ci			 pmdrx->eb_out.len, priv->rx.avail_in,
360d4afb5ceSopenharmony_ci			 (unsigned long)priv->count_rx_between_fin);
361d4afb5ceSopenharmony_ci
362d4afb5ceSopenharmony_ci		if (was_fin) {
363d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "was_fin");
364d4afb5ceSopenharmony_ci			priv->count_rx_between_fin = 0;
365d4afb5ceSopenharmony_ci			if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
366d4afb5ceSopenharmony_ci				lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
367d4afb5ceSopenharmony_ci				(void)inflateEnd(&priv->rx);
368d4afb5ceSopenharmony_ci				priv->rx_init = 0;
369d4afb5ceSopenharmony_ci			}
370d4afb5ceSopenharmony_ci
371d4afb5ceSopenharmony_ci			return PMDR_EMPTY_FINAL;
372d4afb5ceSopenharmony_ci		}
373d4afb5ceSopenharmony_ci
374d4afb5ceSopenharmony_ci		if (priv->rx.avail_in)
375d4afb5ceSopenharmony_ci			return PMDR_HAS_PENDING;
376d4afb5ceSopenharmony_ci
377d4afb5ceSopenharmony_ci		return PMDR_EMPTY_NONFINAL;
378d4afb5ceSopenharmony_ci
379d4afb5ceSopenharmony_ci	case LWS_EXT_CB_PAYLOAD_TX:
380d4afb5ceSopenharmony_ci
381d4afb5ceSopenharmony_ci		/*
382d4afb5ceSopenharmony_ci		 * ie, we are DEFLATING
383d4afb5ceSopenharmony_ci		 *
384d4afb5ceSopenharmony_ci		 * initialize us if needed
385d4afb5ceSopenharmony_ci		 */
386d4afb5ceSopenharmony_ci
387d4afb5ceSopenharmony_ci		if (!priv->tx_init) {
388d4afb5ceSopenharmony_ci			n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
389d4afb5ceSopenharmony_ci					 Z_DEFLATED,
390d4afb5ceSopenharmony_ci					 -priv->args[PMD_SERVER_MAX_WINDOW_BITS +
391d4afb5ceSopenharmony_ci						(wsi->a.vhost->listen_port <= 0)],
392d4afb5ceSopenharmony_ci					 priv->args[PMD_MEM_LEVEL],
393d4afb5ceSopenharmony_ci					 Z_DEFAULT_STRATEGY);
394d4afb5ceSopenharmony_ci			if (n != Z_OK) {
395d4afb5ceSopenharmony_ci				lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
396d4afb5ceSopenharmony_ci				return PMDR_FAILED;
397d4afb5ceSopenharmony_ci			}
398d4afb5ceSopenharmony_ci			priv->tx_init = 1;
399d4afb5ceSopenharmony_ci		}
400d4afb5ceSopenharmony_ci
401d4afb5ceSopenharmony_ci		if (!priv->buf_tx_deflated)
402d4afb5ceSopenharmony_ci			priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
403d4afb5ceSopenharmony_ci					    (1 << priv->args[PMD_TX_BUF_PWR2])),
404d4afb5ceSopenharmony_ci					    "pmd tx deflate buf");
405d4afb5ceSopenharmony_ci		if (!priv->buf_tx_deflated) {
406d4afb5ceSopenharmony_ci			lwsl_wsi_err(wsi, "OOM");
407d4afb5ceSopenharmony_ci			return PMDR_FAILED;
408d4afb5ceSopenharmony_ci		}
409d4afb5ceSopenharmony_ci
410d4afb5ceSopenharmony_ci		/* hook us up with any deflated input that the caller has */
411d4afb5ceSopenharmony_ci
412d4afb5ceSopenharmony_ci		if (pmdrx->eb_in.token) {
413d4afb5ceSopenharmony_ci
414d4afb5ceSopenharmony_ci			assert(!priv->tx.avail_in);
415d4afb5ceSopenharmony_ci
416d4afb5ceSopenharmony_ci			priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
417d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
418d4afb5ceSopenharmony_ci				    "TOTAL TX since FIN: %d",
419d4afb5ceSopenharmony_ci				    pmdrx->eb_in.len,
420d4afb5ceSopenharmony_ci				    (int)priv->count_tx_between_fin);
421d4afb5ceSopenharmony_ci			priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
422d4afb5ceSopenharmony_ci			priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
423d4afb5ceSopenharmony_ci		}
424d4afb5ceSopenharmony_ci
425d4afb5ceSopenharmony_ci		priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
426d4afb5ceSopenharmony_ci		pmdrx->eb_out.token = priv->tx.next_out;
427d4afb5ceSopenharmony_ci		priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
428d4afb5ceSopenharmony_ci
429d4afb5ceSopenharmony_ci		pen = 0;
430d4afb5ceSopenharmony_ci		penbits = 0;
431d4afb5ceSopenharmony_ci		deflatePending(&priv->tx, &pen, &penbits);
432d4afb5ceSopenharmony_ci		pen = pen | (unsigned int)penbits;
433d4afb5ceSopenharmony_ci
434d4afb5ceSopenharmony_ci		if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
435d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
436d4afb5ceSopenharmony_ci
437d4afb5ceSopenharmony_ci			if (!pen)
438d4afb5ceSopenharmony_ci				return PMDR_DID_NOTHING;
439d4afb5ceSopenharmony_ci		}
440d4afb5ceSopenharmony_ci
441d4afb5ceSopenharmony_ci		m = Z_NO_FLUSH;
442d4afb5ceSopenharmony_ci		if (!(len & LWS_WRITE_NO_FIN)) {
443d4afb5ceSopenharmony_ci			lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
444d4afb5ceSopenharmony_ci					(int)wsi->ws->rx_packet_length);
445d4afb5ceSopenharmony_ci			m = Z_SYNC_FLUSH;
446d4afb5ceSopenharmony_ci		}
447d4afb5ceSopenharmony_ci
448d4afb5ceSopenharmony_ci		n = deflate(&priv->tx, m);
449d4afb5ceSopenharmony_ci		if (n == Z_STREAM_ERROR) {
450d4afb5ceSopenharmony_ci			lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
451d4afb5ceSopenharmony_ci			return PMDR_FAILED;
452d4afb5ceSopenharmony_ci		}
453d4afb5ceSopenharmony_ci
454d4afb5ceSopenharmony_ci		pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
455d4afb5ceSopenharmony_ci
456d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
457d4afb5ceSopenharmony_ci				(unsigned int)len);
458d4afb5ceSopenharmony_ci
459d4afb5ceSopenharmony_ci		if ((len & 0xf) == LWS_WRITE_TEXT)
460d4afb5ceSopenharmony_ci			priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
461d4afb5ceSopenharmony_ci		if ((len & 0xf) == LWS_WRITE_BINARY)
462d4afb5ceSopenharmony_ci			priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
463d4afb5ceSopenharmony_ci
464d4afb5ceSopenharmony_ci		pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
465d4afb5ceSopenharmony_ci						 pmdrx->eb_out.token);
466d4afb5ceSopenharmony_ci
467d4afb5ceSopenharmony_ci		if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
468d4afb5ceSopenharmony_ci		    pmdrx->eb_out.len < 4) {
469d4afb5ceSopenharmony_ci			lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
470d4afb5ceSopenharmony_ci					(int)pmdrx->eb_out.len);
471d4afb5ceSopenharmony_ci			assert(0);
472d4afb5ceSopenharmony_ci		}
473d4afb5ceSopenharmony_ci
474d4afb5ceSopenharmony_ci		if (!(len & LWS_WRITE_NO_FIN) &&
475d4afb5ceSopenharmony_ci		    m == Z_SYNC_FLUSH &&
476d4afb5ceSopenharmony_ci		    !pen &&
477d4afb5ceSopenharmony_ci		    pmdrx->eb_out.len >= 4) {
478d4afb5ceSopenharmony_ci			// lwsl_wsi_err(wsi, "Trimming 4 from end of write");
479d4afb5ceSopenharmony_ci			priv->tx.next_out -= 4;
480d4afb5ceSopenharmony_ci			priv->tx.avail_out += 4;
481d4afb5ceSopenharmony_ci			priv->count_tx_between_fin = 0;
482d4afb5ceSopenharmony_ci
483d4afb5ceSopenharmony_ci			assert(priv->tx.next_out[0] == 0x00 &&
484d4afb5ceSopenharmony_ci			       priv->tx.next_out[1] == 0x00 &&
485d4afb5ceSopenharmony_ci			       priv->tx.next_out[2] == 0xff &&
486d4afb5ceSopenharmony_ci			       priv->tx.next_out[3] == 0xff);
487d4afb5ceSopenharmony_ci		}
488d4afb5ceSopenharmony_ci
489d4afb5ceSopenharmony_ci
490d4afb5ceSopenharmony_ci		/*
491d4afb5ceSopenharmony_ci		 * track how much input was used and advance it
492d4afb5ceSopenharmony_ci		 */
493d4afb5ceSopenharmony_ci
494d4afb5ceSopenharmony_ci		pmdrx->eb_in.token = pmdrx->eb_in.token +
495d4afb5ceSopenharmony_ci					((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
496d4afb5ceSopenharmony_ci		pmdrx->eb_in.len = (int)priv->tx.avail_in;
497d4afb5ceSopenharmony_ci
498d4afb5ceSopenharmony_ci		priv->compressed_out = 1;
499d4afb5ceSopenharmony_ci		pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
500d4afb5ceSopenharmony_ci						 pmdrx->eb_out.token);
501d4afb5ceSopenharmony_ci
502d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "  TX rewritten with new eb_in len %d, "
503d4afb5ceSopenharmony_ci				"eb_out len %d, deflatePending %d",
504d4afb5ceSopenharmony_ci				pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
505d4afb5ceSopenharmony_ci
506d4afb5ceSopenharmony_ci		if (pmdrx->eb_in.len || pen)
507d4afb5ceSopenharmony_ci			return PMDR_HAS_PENDING;
508d4afb5ceSopenharmony_ci
509d4afb5ceSopenharmony_ci		if (!(len & LWS_WRITE_NO_FIN))
510d4afb5ceSopenharmony_ci			return PMDR_EMPTY_FINAL;
511d4afb5ceSopenharmony_ci
512d4afb5ceSopenharmony_ci		return PMDR_EMPTY_NONFINAL;
513d4afb5ceSopenharmony_ci
514d4afb5ceSopenharmony_ci	case LWS_EXT_CB_PACKET_TX_PRESEND:
515d4afb5ceSopenharmony_ci		if (!priv->compressed_out)
516d4afb5ceSopenharmony_ci			break;
517d4afb5ceSopenharmony_ci		priv->compressed_out = 0;
518d4afb5ceSopenharmony_ci
519d4afb5ceSopenharmony_ci		/*
520d4afb5ceSopenharmony_ci		 * we may have not produced any output for the actual "first"
521d4afb5ceSopenharmony_ci		 * write... in that case, we need to fix up the inappropriate
522d4afb5ceSopenharmony_ci		 * use of CONTINUATION when the first real write does come.
523d4afb5ceSopenharmony_ci		 */
524d4afb5ceSopenharmony_ci		if (priv->tx_first_frame_type & 0xf) {
525d4afb5ceSopenharmony_ci			*pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
526d4afb5ceSopenharmony_ci				((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
527d4afb5ceSopenharmony_ci			/*
528d4afb5ceSopenharmony_ci			 * We have now written the "first" fragment, only
529d4afb5ceSopenharmony_ci			 * do that once
530d4afb5ceSopenharmony_ci			 */
531d4afb5ceSopenharmony_ci			priv->tx_first_frame_type = 0;
532d4afb5ceSopenharmony_ci		}
533d4afb5ceSopenharmony_ci
534d4afb5ceSopenharmony_ci		n = *(pmdrx->eb_in.token) & 15;
535d4afb5ceSopenharmony_ci
536d4afb5ceSopenharmony_ci		/* set RSV1, but not on CONTINUATION */
537d4afb5ceSopenharmony_ci		if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
538d4afb5ceSopenharmony_ci			*pmdrx->eb_in.token |= 0x40;
539d4afb5ceSopenharmony_ci
540d4afb5ceSopenharmony_ci		lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
541d4afb5ceSopenharmony_ci			    ((*pmdrx->eb_in.token) & 0xff),
542d4afb5ceSopenharmony_ci			    pmdrx->eb_in.len);
543d4afb5ceSopenharmony_ci
544d4afb5ceSopenharmony_ci		if (((*pmdrx->eb_in.token) & 0x80) &&	/* fin */
545d4afb5ceSopenharmony_ci		    priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
546d4afb5ceSopenharmony_ci			lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
547d4afb5ceSopenharmony_ci			(void)deflateEnd(&priv->tx);
548d4afb5ceSopenharmony_ci			priv->tx_init = 0;
549d4afb5ceSopenharmony_ci		}
550d4afb5ceSopenharmony_ci
551d4afb5ceSopenharmony_ci		break;
552d4afb5ceSopenharmony_ci
553d4afb5ceSopenharmony_ci	default:
554d4afb5ceSopenharmony_ci		break;
555d4afb5ceSopenharmony_ci	}
556d4afb5ceSopenharmony_ci
557d4afb5ceSopenharmony_ci	return 0;
558d4afb5ceSopenharmony_ci}
559d4afb5ceSopenharmony_ci
560