summaryrefslogtreecommitdiff
path: root/thirdparty/libwebsockets/lib/roles/ws
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/libwebsockets/lib/roles/ws')
-rw-r--r--thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c607
-rw-r--r--thirdparty/libwebsockets/lib/roles/ws/client-ws.c634
-rw-r--r--thirdparty/libwebsockets/lib/roles/ws/ops-ws.c2021
-rw-r--r--thirdparty/libwebsockets/lib/roles/ws/private.h164
-rw-r--r--thirdparty/libwebsockets/lib/roles/ws/server-ws.c874
5 files changed, 0 insertions, 4300 deletions
diff --git a/thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c b/thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c
deleted file mode 100644
index f5aaa6dbb5..0000000000
--- a/thirdparty/libwebsockets/lib/roles/ws/client-parser-ws.c
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2018 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 "core/private.h"
-
-/*
- * parsers.c: lws_ws_rx_sm() needs to be roughly kept in
- * sync with changes here, esp related to ext draining
- */
-
-int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
-{
- int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
- int handled, m;
- unsigned short close_code;
- struct lws_tokens ebuf;
- unsigned char *pp;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- int rx_draining_ext = 0, n;
-#endif
-
- ebuf.token = NULL;
- ebuf.len = 0;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->rx_draining_ext) {
- assert(!c);
-
- lws_remove_wsi_from_draining_ext_list(wsi);
- rx_draining_ext = 1;
- lwsl_debug("%s: doing draining flow\n", __func__);
-
- goto drain_extension;
- }
-#endif
-
- if (wsi->socket_is_permanently_unusable)
- return -1;
-
- switch (wsi->lws_rx_parse_state) {
- case LWS_RXPS_NEW:
- /* control frames (PING) may interrupt checkable sequences */
- wsi->ws->defeat_check_utf8 = 0;
-
- switch (wsi->ws->ietf_spec_revision) {
- case 13:
- wsi->ws->opcode = c & 0xf;
- /* revisit if an extension wants them... */
- switch (wsi->ws->opcode) {
- case LWSWSOPC_TEXT_FRAME:
- wsi->ws->rsv_first_msg = (c & 0x70);
- wsi->ws->continuation_possible = 1;
- wsi->ws->check_utf8 = lws_check_opt(
- wsi->context->options,
- LWS_SERVER_OPTION_VALIDATE_UTF8);
- wsi->ws->utf8 = 0;
- wsi->ws->first_fragment = 1;
- break;
- case LWSWSOPC_BINARY_FRAME:
- wsi->ws->rsv_first_msg = (c & 0x70);
- wsi->ws->check_utf8 = 0;
- wsi->ws->continuation_possible = 1;
- wsi->ws->first_fragment = 1;
- break;
- case LWSWSOPC_CONTINUATION:
- if (!wsi->ws->continuation_possible) {
- lwsl_info("disordered continuation\n");
- return -1;
- }
- wsi->ws->first_fragment = 0;
- break;
- case LWSWSOPC_CLOSE:
- wsi->ws->check_utf8 = 0;
- wsi->ws->utf8 = 0;
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 0xb:
- case 0xc:
- case 0xd:
- case 0xe:
- case 0xf:
- lwsl_info("illegal opcode\n");
- return -1;
- default:
- wsi->ws->defeat_check_utf8 = 1;
- break;
- }
- wsi->ws->rsv = (c & 0x70);
- /* revisit if an extension wants them... */
- if (
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- !wsi->ws->count_act_ext &&
-#endif
- wsi->ws->rsv) {
- lwsl_info("illegal rsv bits set\n");
- return -1;
- }
- wsi->ws->final = !!((c >> 7) & 1);
- lwsl_ext("%s: This RX frame Final %d\n", __func__,
- wsi->ws->final);
-
- if (wsi->ws->owed_a_fin &&
- (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
- wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
- lwsl_info("hey you owed us a FIN\n");
- return -1;
- }
- if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
- wsi->ws->continuation_possible = 0;
- wsi->ws->owed_a_fin = 0;
- }
-
- if ((wsi->ws->opcode & 8) && !wsi->ws->final) {
- lwsl_info("control msg can't be fragmented\n");
- return -1;
- }
- if (!wsi->ws->final)
- wsi->ws->owed_a_fin = 1;
-
- switch (wsi->ws->opcode) {
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- wsi->ws->frame_is_binary = wsi->ws->opcode ==
- LWSWSOPC_BINARY_FRAME;
- break;
- }
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
- break;
-
- default:
- lwsl_err("unknown spec version %02d\n",
- wsi->ws->ietf_spec_revision);
- break;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN:
-
- wsi->ws->this_frame_masked = !!(c & 0x80);
-
- switch (c & 0x7f) {
- case 126:
- /* control frames are not allowed to have big lengths */
- if (wsi->ws->opcode & 8)
- goto illegal_ctl_length;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
- break;
- case 127:
- /* control frames are not allowed to have big lengths */
- if (wsi->ws->opcode & 8)
- goto illegal_ctl_length;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
- break;
- default:
- wsi->ws->rx_packet_length = c & 0x7f;
- if (wsi->ws->this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else {
- if (wsi->ws->rx_packet_length) {
- wsi->lws_rx_parse_state =
- LWS_RXPS_WS_FRAME_PAYLOAD;
- } else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- }
- break;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_2:
- wsi->ws->rx_packet_length = c << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_1:
- wsi->ws->rx_packet_length |= c;
- if (wsi->ws->this_frame_masked)
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else {
- if (wsi->ws->rx_packet_length)
- wsi->lws_rx_parse_state =
- LWS_RXPS_WS_FRAME_PAYLOAD;
- else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_8:
- if (c & 0x80) {
- lwsl_warn("b63 of length must be zero\n");
- /* kill the connection */
- return -1;
- }
-#if defined __LP64__
- wsi->ws->rx_packet_length = ((size_t)c) << 56;
-#else
- wsi->ws->rx_packet_length = 0;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_7:
-#if defined __LP64__
- wsi->ws->rx_packet_length |= ((size_t)c) << 48;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_6:
-#if defined __LP64__
- wsi->ws->rx_packet_length |= ((size_t)c) << 40;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_5:
-#if defined __LP64__
- wsi->ws->rx_packet_length |= ((size_t)c) << 32;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_4:
- wsi->ws->rx_packet_length |= ((size_t)c) << 24;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_3:
- wsi->ws->rx_packet_length |= ((size_t)c) << 16;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_2:
- wsi->ws->rx_packet_length |= ((size_t)c) << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_1:
- wsi->ws->rx_packet_length |= (size_t)c;
- if (wsi->ws->this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else {
- if (wsi->ws->rx_packet_length)
- wsi->lws_rx_parse_state =
- LWS_RXPS_WS_FRAME_PAYLOAD;
- else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- }
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
- wsi->ws->mask[0] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
- wsi->ws->mask[1] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
- wsi->ws->mask[2] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
- wsi->ws->mask[3] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
-
- if (wsi->ws->rx_packet_length)
- wsi->lws_rx_parse_state =
- LWS_RXPS_WS_FRAME_PAYLOAD;
- else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
-
- case LWS_RXPS_WS_FRAME_PAYLOAD:
-
- assert(wsi->ws->rx_ubuf);
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->rx_draining_ext)
- goto drain_extension;
-#endif
- if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
- c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
-
- wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
-
- if (--wsi->ws->rx_packet_length == 0) {
- /* spill because we have the whole frame */
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
-
- /*
- * if there's no protocol max frame size given, we are
- * supposed to default to context->pt_serv_buf_size
- */
- if (!wsi->protocol->rx_buffer_size &&
- wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
- break;
-
- if (wsi->protocol->rx_buffer_size &&
- wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
- break;
-
- /* spill because we filled our rx buffer */
-spill:
-
- handled = 0;
-
- /*
- * is this frame a control packet we should take care of at this
- * layer? If so service it and hide it from the user callback
- */
-
- switch (wsi->ws->opcode) {
- case LWSWSOPC_CLOSE:
- pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
- if (lws_check_opt(wsi->context->options,
- LWS_SERVER_OPTION_VALIDATE_UTF8) &&
- wsi->ws->rx_ubuf_head > 2 &&
- lws_check_utf8(&wsi->ws->utf8, pp + 2,
- wsi->ws->rx_ubuf_head - 2))
- goto utf8_fail;
-
- /* is this an acknowledgment of our close? */
- if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
- /*
- * fine he has told us he is closing too, let's
- * finish our close
- */
- lwsl_parser("seen server's close ack\n");
- return -1;
- }
-
- lwsl_parser("client sees server close len = %d\n",
- wsi->ws->rx_ubuf_head);
- if (wsi->ws->rx_ubuf_head >= 2) {
- close_code = (pp[0] << 8) | pp[1];
- if (close_code < 1000 ||
- close_code == 1004 ||
- close_code == 1005 ||
- close_code == 1006 ||
- close_code == 1012 ||
- close_code == 1013 ||
- close_code == 1014 ||
- close_code == 1015 ||
- (close_code >= 1016 && close_code < 3000)
- ) {
- pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
- pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
- }
- }
- if (user_callback_handle_rxflow(
- wsi->protocol->callback, wsi,
- LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
- wsi->user_space, pp,
- wsi->ws->rx_ubuf_head))
- return -1;
-
- memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
- wsi->ws->rx_ubuf_head);
- wsi->ws->close_in_ping_buffer_len =
- wsi->ws->rx_ubuf_head;
-
- lwsl_info("%s: scheduling return close as ack\n",
- __func__);
- __lws_change_pollfd(wsi, LWS_POLLIN, 0);
- lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
- wsi->waiting_to_send_close_frame = 1;
- wsi->close_needs_ack = 0;
- lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
- lws_callback_on_writable(wsi);
- handled = 1;
- break;
-
- case LWSWSOPC_PING:
- lwsl_info("received %d byte ping, sending pong\n",
- wsi->ws->rx_ubuf_head);
-
- /* he set a close reason on this guy, ignore PING */
- if (wsi->ws->close_in_ping_buffer_len)
- goto ping_drop;
-
- if (wsi->ws->ping_pending_flag) {
- /*
- * there is already a pending ping payload
- * we should just log and drop
- */
- lwsl_parser("DROP PING since one pending\n");
- goto ping_drop;
- }
-
- /* control packets can only be < 128 bytes long */
- if (wsi->ws->rx_ubuf_head > 128 - 3) {
- lwsl_parser("DROP PING payload too large\n");
- goto ping_drop;
- }
-
- /* stash the pong payload */
- memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
- &wsi->ws->rx_ubuf[LWS_PRE],
- wsi->ws->rx_ubuf_head);
-
- wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
- wsi->ws->ping_pending_flag = 1;
-
- /* get it sent as soon as possible */
- lws_callback_on_writable(wsi);
-ping_drop:
- wsi->ws->rx_ubuf_head = 0;
- handled = 1;
- break;
-
- case LWSWSOPC_PONG:
- lwsl_info("client received pong\n");
- lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
- wsi->ws->rx_ubuf_head);
-
- if (wsi->pending_timeout ==
- PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
- lwsl_info("%p: received expected PONG\n", wsi);
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- }
-
- /* issue it */
- callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
- break;
-
- case LWSWSOPC_CONTINUATION:
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- break;
-
- default:
- /* not handled or failed */
- lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode);
- wsi->ws->rx_ubuf_head = 0;
-
- return -1;
- }
-
- /*
- * No it's real payload, pass it up to the user callback.
- * It's nicely buffered with the pre-padding taken care of
- * so it can be sent straight out again using lws_write
- */
- if (handled)
- goto already_done;
-
- ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
- ebuf.len = wsi->ws->rx_ubuf_head;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-drain_extension:
- lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
-
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
- lwsl_ext("Ext RX returned %d\n", n);
- if (n < 0) {
- wsi->socket_is_permanently_unusable = 1;
- return -1;
- }
-#endif
- lwsl_debug("post inflate ebuf len %d\n", ebuf.len);
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (rx_draining_ext && !ebuf.len) {
- lwsl_debug(" --- ending drain on 0 read result\n");
- goto already_done;
- }
-#endif
-
- if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
- if (lws_check_utf8(&wsi->ws->utf8,
- (unsigned char *)ebuf.token,
- ebuf.len)) {
- lws_close_reason(wsi,
- LWS_CLOSE_STATUS_INVALID_PAYLOAD,
- (uint8_t *)"bad utf8", 8);
- goto utf8_fail;
- }
-
- /* we are ending partway through utf-8 character? */
- if (!wsi->ws->rx_packet_length && wsi->ws->final &&
- wsi->ws->utf8
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- && !n
-#endif
- ) {
- lwsl_info("FINAL utf8 error\n");
- lws_close_reason(wsi,
- LWS_CLOSE_STATUS_INVALID_PAYLOAD,
- (uint8_t *)"partial utf8", 12);
-utf8_fail:
- lwsl_info("utf8 error\n");
- lwsl_hexdump_info(ebuf.token, ebuf.len);
-
- return -1;
- }
- }
-
- if (ebuf.len < 0 &&
- callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
- goto already_done;
-
- if (!ebuf.token)
- goto already_done;
-
- ebuf.token[ebuf.len] = '\0';
-
- if (!wsi->protocol->callback)
- goto already_done;
-
- if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
- lwsl_info("Client doing pong callback\n");
-
- if (
- /* coverity says dead code otherwise */
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- n &&
-#endif
- ebuf.len)
- /* extension had more... main loop will come back
- * we want callback to be done with this set, if so,
- * because lws_is_final() hides it was final until the
- * last chunk
- */
- lws_add_wsi_to_draining_ext_list(wsi);
- else
- lws_remove_wsi_from_draining_ext_list(wsi);
-
- if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
- lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
- lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
- goto already_done;
-
- m = wsi->protocol->callback(wsi,
- (enum lws_callback_reasons)callback_action,
- wsi->user_space, ebuf.token, ebuf.len);
-
- wsi->ws->first_fragment = 0;
-
- // lwsl_notice("%s: bulk ws rx: input used %d, output %d\n",
- // __func__, wsi->ws->rx_ubuf_head, ebuf.len);
-
- /* if user code wants to close, let caller know */
- if (m)
- return 1;
-
-already_done:
- wsi->ws->rx_ubuf_head = 0;
- break;
- default:
- lwsl_err("client rx illegal state\n");
- return 1;
- }
-
- return 0;
-
-illegal_ctl_length:
- lwsl_warn("Control frame asking for extended length is illegal\n");
-
- /* kill the connection */
- return -1;
-}
-
-
diff --git a/thirdparty/libwebsockets/lib/roles/ws/client-ws.c b/thirdparty/libwebsockets/lib/roles/ws/client-ws.c
deleted file mode 100644
index d88833f381..0000000000
--- a/thirdparty/libwebsockets/lib/roles/ws/client-ws.c
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2018 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 <core/private.h>
-
-/*
- * In-place str to lower case
- */
-
-static void
-strtolower(char *s)
-{
- while (*s) {
-#ifdef LWS_PLAT_OPTEE
- int tolower_optee(int c);
- *s = tolower_optee((int)*s);
-#else
- *s = tolower((int)*s);
-#endif
- s++;
- }
-}
-
-int
-lws_create_client_ws_object(const struct lws_client_connect_info *i,
- struct lws *wsi)
-{
- int v = SPEC_LATEST_SUPPORTED;
-
- /* allocate the ws struct for the wsi */
- wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
- if (!wsi->ws) {
- lwsl_notice("OOM\n");
- return 1;
- }
-
- /* -1 means just use latest supported */
- if (i->ietf_version_or_minus_one != -1 &&
- i->ietf_version_or_minus_one)
- v = i->ietf_version_or_minus_one;
-
- wsi->ws->ietf_spec_revision = v;
-
- return 0;
-}
-
-#if !defined(LWS_NO_CLIENT)
-int
-lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
-{
- if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
- (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
- (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
- !lwsi_role_client(wsi))
- return 0;
-
- // lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
-
- while (len) {
- /*
- * we were accepting input but now we stopped doing so
- */
- if (lws_is_flowcontrolled(wsi)) {
- //lwsl_notice("%s: caching %ld\n", __func__, (long)len);
- lws_rxflow_cache(wsi, *buf, 0, (int)len);
- *buf += len;
- return 0;
- }
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->rx_draining_ext) {
- int m;
-
- //lwsl_notice("%s: draining ext\n", __func__);
- if (lwsi_role_client(wsi))
- m = lws_ws_client_rx_sm(wsi, 0);
- else
- m = lws_ws_rx_sm(wsi, 0, 0);
- if (m < 0)
- return -1;
- continue;
- }
-#endif
- /* caller will account for buflist usage */
-
- if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
- lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
- __func__, (int)len);
- return -1;
- }
- len--;
- }
- // lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
-
- return 0;
-}
-#endif
-
-char *
-lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1)
-{
- char buf[128], hash[20], key_b64[40];
- int n;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- const struct lws_extension *ext;
- int ext_count = 0;
-#endif
-
- /*
- * create the random key
- */
- n = lws_get_random(wsi->context, hash, 16);
- if (n != 16) {
- lwsl_err("Unable to read from random dev %s\n",
- SYSTEM_RANDOM_FILEPATH);
- return NULL;
- }
-
- lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
-
- p += sprintf(p, "Upgrade: websocket\x0d\x0a"
- "Connection: %sUpgrade\x0d\x0a"
- "Sec-WebSocket-Key: ", conn1);
- strcpy(p, key_b64);
- p += strlen(key_b64);
- p += sprintf(p, "\x0d\x0a");
- if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
- p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
- lws_hdr_simple_ptr(wsi,
- _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
-
- /* tell the server what extensions we could support */
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- ext = wsi->vhost->ws.extensions;
- while (ext && ext->callback) {
-
- n = wsi->vhost->protocols[0].callback(wsi,
- LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
- wsi->user_space, (char *)ext->name, 0);
-
- /*
- * zero return from callback means go ahead and allow
- * the extension, it's what we get if the callback is
- * unhandled
- */
-
- if (n) {
- ext++;
- continue;
- }
-
- /* apply it */
-
- if (ext_count)
- *p++ = ',';
- else
- p += sprintf(p, "Sec-WebSocket-Extensions: ");
- p += sprintf(p, "%s", ext->client_offer);
- ext_count++;
-
- ext++;
- }
- if (ext_count)
- p += sprintf(p, "\x0d\x0a");
-#endif
-
- if (wsi->ws->ietf_spec_revision)
- p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
- wsi->ws->ietf_spec_revision);
-
- /* prepare the expected server accept response */
-
- key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
- n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
- key_b64);
-
- lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
-
- lws_b64_encode_string(hash, 20,
- wsi->http.ah->initial_handshake_hash_base64,
- sizeof(wsi->http.ah->initial_handshake_hash_base64));
-
- return p;
-}
-
-int
-lws_client_ws_upgrade(struct lws *wsi, const char **cce)
-{
- struct lws_context *context = wsi->context;
- struct lws_tokenize ts;
- int n, len, okay = 0;
- lws_tokenize_elem e;
- char *p, buf[64];
- const char *pc;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char *sb = (char *)&pt->serv_buf[0];
- const struct lws_ext_options *opts;
- const struct lws_extension *ext;
- char ext_name[128];
- const char *c, *a;
- int more = 1;
- char ignore;
-#endif
-
- if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
- lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n",
- __func__);
- *cce = "HS: h2 / ws upgrade unsupported";
- goto bail3;
- }
-
- if (wsi->http.ah->http_response == 401) {
- lwsl_warn(
- "lws_client_handshake: got bad HTTP response '%d'\n",
- wsi->http.ah->http_response);
- *cce = "HS: ws upgrade unauthorized";
- goto bail3;
- }
-
- if (wsi->http.ah->http_response != 101) {
- lwsl_warn(
- "lws_client_handshake: got bad HTTP response '%d'\n",
- wsi->http.ah->http_response);
- *cce = "HS: ws upgrade response not 101";
- goto bail3;
- }
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
- lwsl_info("no ACCEPT\n");
- *cce = "HS: ACCEPT missing";
- goto bail3;
- }
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
- if (!p) {
- lwsl_info("no UPGRADE\n");
- *cce = "HS: UPGRADE missing";
- goto bail3;
- }
- strtolower(p);
- if (strcmp(p, "websocket")) {
- lwsl_warn(
- "lws_client_handshake: got bad Upgrade header '%s'\n", p);
- *cce = "HS: Upgrade to something other than websocket";
- goto bail3;
- }
-
- /* connection: must have "upgrade" */
-
- lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
- LWS_TOKENIZE_F_MINUS_NONTERM);
- ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
- if (ts.len <= 0) /* won't fit, or absent */
- goto bad_conn_format;
-
- do {
- e = lws_tokenize(&ts);
- switch (e) {
- case LWS_TOKZE_TOKEN:
- if (!strcasecmp(ts.token, "upgrade"))
- e = LWS_TOKZE_ENDED;
- break;
-
- case LWS_TOKZE_DELIMITER:
- break;
-
- default: /* includes ENDED */
-bad_conn_format:
- *cce = "HS: UPGRADE malformed";
- goto bail3;
- }
- } while (e > 0);
-
- pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
- if (!pc) {
- lwsl_parser("lws_client_int_s_hs: no protocol list\n");
- } else
- lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
-
- /*
- * confirm the protocol the server wants to talk was in the list
- * of protocols we offered
- */
-
- len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
- if (!len) {
- lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
- /*
- * no protocol name to work from,
- * default to first protocol
- */
- n = 0;
- wsi->protocol = &wsi->vhost->protocols[0];
- goto check_extensions;
- }
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
- len = (int)strlen(p);
-
- while (pc && *pc && !okay) {
- if (!strncmp(pc, p, len) &&
- (pc[len] == ',' || pc[len] == '\0')) {
- okay = 1;
- continue;
- }
- while (*pc && *pc++ != ',')
- ;
- while (*pc == ' ')
- pc++;
- }
-
- if (!okay) {
- lwsl_info("%s: got bad protocol %s\n", __func__, p);
- *cce = "HS: PROTOCOL malformed";
- goto bail2;
- }
-
- /*
- * identify the selected protocol struct and set it
- */
- n = 0;
- /* keep client connection pre-bound protocol */
- if (!lwsi_role_client(wsi))
- wsi->protocol = NULL;
-
- while (wsi->vhost->protocols[n].callback) {
- if (!wsi->protocol &&
- strcmp(p, wsi->vhost->protocols[n].name) == 0) {
- wsi->protocol = &wsi->vhost->protocols[n];
- break;
- }
- n++;
- }
-
- if (!wsi->vhost->protocols[n].callback) { /* no match */
- /* if server, that's already fatal */
- if (!lwsi_role_client(wsi)) {
- lwsl_info("%s: fail protocol %s\n", __func__, p);
- *cce = "HS: Cannot match protocol";
- goto bail2;
- }
-
- /* for client, find the index of our pre-bound protocol */
-
- n = 0;
- while (wsi->vhost->protocols[n].callback) {
- if (wsi->protocol && strcmp(wsi->protocol->name,
- wsi->vhost->protocols[n].name) == 0) {
- wsi->protocol = &wsi->vhost->protocols[n];
- break;
- }
- n++;
- }
-
- if (!wsi->vhost->protocols[n].callback) {
- if (wsi->protocol)
- lwsl_err("Failed to match protocol %s\n",
- wsi->protocol->name);
- else
- lwsl_err("No protocol on client\n");
- goto bail2;
- }
- }
-
- lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
-
-check_extensions:
- /*
- * stitch protocol choice into the vh protocol linked list
- * We always insert ourselves at the start of the list
- *
- * X <-> B
- * X <-> pAn <-> pB
- */
-
- lws_same_vh_protocol_insert(wsi, n);
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- /* instantiate the accepted extensions */
-
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
- lwsl_ext("no client extensions allowed by server\n");
- goto check_accept;
- }
-
- /*
- * break down the list of server accepted extensions
- * and go through matching them or identifying bogons
- */
-
- if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size,
- WSI_TOKEN_EXTENSIONS) < 0) {
- lwsl_warn("ext list from server failed to copy\n");
- *cce = "HS: EXT: list too big";
- goto bail2;
- }
-
- c = sb;
- n = 0;
- ignore = 0;
- a = NULL;
- while (more) {
-
- if (*c && (*c != ',' && *c != '\t')) {
- if (*c == ';') {
- ignore = 1;
- if (!a)
- a = c + 1;
- }
- if (ignore || *c == ' ') {
- c++;
- continue;
- }
-
- ext_name[n] = *c++;
- if (n < (int)sizeof(ext_name) - 1)
- n++;
- continue;
- }
- ext_name[n] = '\0';
- ignore = 0;
- if (!*c)
- more = 0;
- else {
- c++;
- if (!n)
- continue;
- }
-
- /* check we actually support it */
-
- lwsl_notice("checking client ext %s\n", ext_name);
-
- n = 0;
- ext = wsi->vhost->ws.extensions;
- while (ext && ext->callback) {
- if (strcmp(ext_name, ext->name)) {
- ext++;
- continue;
- }
-
- n = 1;
- lwsl_notice("instantiating client ext %s\n", ext_name);
-
- /* instantiate the extension on this conn */
-
- wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
-
- /* allow him to construct his ext instance */
-
- if (ext->callback(lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_CLIENT_CONSTRUCT,
- (void *)&wsi->ws->act_ext_user[
- wsi->ws->count_act_ext],
- (void *)&opts, 0)) {
- lwsl_info(" ext %s failed construction\n",
- ext_name);
- ext++;
- continue;
- }
-
- /*
- * allow the user code to override ext defaults if it
- * wants to
- */
- ext_name[0] = '\0';
- if (user_callback_handle_rxflow(wsi->protocol->callback,
- wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
- (char *)ext->name, ext_name,
- sizeof(ext_name))) {
- *cce = "HS: EXT: failed setting defaults";
- goto bail2;
- }
-
- if (ext_name[0] &&
- lws_ext_parse_options(ext, wsi,
- wsi->ws->act_ext_user[
- wsi->ws->count_act_ext],
- opts, ext_name,
- (int)strlen(ext_name))) {
- lwsl_err("%s: unable to parse user defaults '%s'",
- __func__, ext_name);
- *cce = "HS: EXT: failed parsing defaults";
- goto bail2;
- }
-
- /*
- * give the extension the server options
- */
- if (a && lws_ext_parse_options(ext, wsi,
- wsi->ws->act_ext_user[
- wsi->ws->count_act_ext],
- opts, a, lws_ptr_diff(c, a))) {
- lwsl_err("%s: unable to parse remote def '%s'",
- __func__, a);
- *cce = "HS: EXT: failed parsing options";
- goto bail2;
- }
-
- if (ext->callback(lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_OPTION_CONFIRM,
- wsi->ws->act_ext_user[wsi->ws->count_act_ext],
- NULL, 0)) {
- lwsl_err("%s: ext %s rejects server options %s",
- __func__, ext->name, a);
- *cce = "HS: EXT: Rejects server options";
- goto bail2;
- }
-
- wsi->ws->count_act_ext++;
-
- ext++;
- }
-
- if (n == 0) {
- lwsl_warn("Unknown ext '%s'!\n", ext_name);
- *cce = "HS: EXT: unknown ext";
- goto bail2;
- }
-
- a = NULL;
- n = 0;
- }
-
-check_accept:
-#endif
-
- /*
- * Confirm his accept token is the one we precomputed
- */
-
- p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
- if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
- lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
- wsi->http.ah->initial_handshake_hash_base64);
- *cce = "HS: Accept hash wrong";
- goto bail2;
- }
-
- /* allocate the per-connection user memory (if any) */
- if (lws_ensure_user_space(wsi)) {
- lwsl_err("Problem allocating wsi user mem\n");
- *cce = "HS: OOM";
- goto bail2;
- }
-
- /*
- * we seem to be good to go, give client last chance to check
- * headers and OK it
- */
- if (wsi->protocol->callback(wsi,
- LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
- wsi->user_space, NULL, 0)) {
- *cce = "HS: Rejected by filter cb";
- goto bail2;
- }
-
- /* clear his proxy connection timeout */
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* free up his parsing allocations */
- lws_header_table_detach(wsi, 0);
-
- lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
- &role_ops_ws);
- lws_restart_ws_ping_pong_timer(wsi);
-
- wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
- /*
- * create the frame buffer for this connection according to the
- * size mentioned in the protocol definition. If 0 there, then
- * use a big default for compatibility
- */
- n = (int)wsi->protocol->rx_buffer_size;
- if (!n)
- n = context->pt_serv_buf_size;
- n += LWS_PRE;
- wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
- "client frame buffer");
- if (!wsi->ws->rx_ubuf) {
- lwsl_err("Out of Mem allocating rx buffer %d\n", n);
- *cce = "HS: OOM";
- goto bail2;
- }
- wsi->ws->rx_ubuf_alloc = n;
- lwsl_info("Allocating client RX buffer %d\n", n);
-
-#if !defined(LWS_WITH_ESP32)
- if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
- (const char *)&n, sizeof n)) {
- lwsl_warn("Failed to set SNDBUF to %d", n);
- *cce = "HS: SO_SNDBUF failed";
- goto bail3;
- }
-#endif
-
- lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
-
- /* call him back to inform him he is up */
-
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
- wsi->user_space, NULL, 0)) {
- *cce = "HS: Rejected at CLIENT_ESTABLISHED";
- goto bail3;
- }
-
- return 0;
-
-bail3:
- return 3;
-
-bail2:
- return 2;
-}
diff --git a/thirdparty/libwebsockets/lib/roles/ws/ops-ws.c b/thirdparty/libwebsockets/lib/roles/ws/ops-ws.c
deleted file mode 100644
index 1cbc6ac6a6..0000000000
--- a/thirdparty/libwebsockets/lib/roles/ws/ops-ws.c
+++ /dev/null
@@ -1,2021 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2018 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 <core/private.h>
-
-#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
-
-/*
- * client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in
- * sync with changes here, esp related to ext draining
- */
-
-int
-lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
-{
- int callback_action = LWS_CALLBACK_RECEIVE;
- int ret = 0;
- unsigned short close_code;
- struct lws_tokens ebuf;
- unsigned char *pp;
- int n = 0;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- int rx_draining_ext = 0;
- int lin;
-#endif
-
- ebuf.token = NULL;
- ebuf.len = 0;
- if (wsi->socket_is_permanently_unusable)
- return -1;
-
- switch (wsi->lws_rx_parse_state) {
- case LWS_RXPS_NEW:
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->rx_draining_ext) {
- ebuf.token = NULL;
- ebuf.len = 0;
- lws_remove_wsi_from_draining_ext_list(wsi);
- rx_draining_ext = 1;
- lwsl_debug("%s: doing draining flow\n", __func__);
-
- goto drain_extension;
- }
-#endif
- switch (wsi->ws->ietf_spec_revision) {
- case 13:
- /*
- * no prepended frame key any more
- */
- wsi->ws->all_zero_nonce = 1;
- goto handle_first;
-
- default:
- lwsl_warn("lws_ws_rx_sm: unknown spec version %d\n",
- wsi->ws->ietf_spec_revision);
- break;
- }
- break;
- case LWS_RXPS_04_mask_1:
- wsi->ws->mask[1] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
- break;
- case LWS_RXPS_04_mask_2:
- wsi->ws->mask[2] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
- break;
- case LWS_RXPS_04_mask_3:
- wsi->ws->mask[3] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
-
- /*
- * start from the zero'th byte in the XOR key buffer since
- * this is the start of a frame with a new key
- */
-
- wsi->ws->mask_idx = 0;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
- break;
-
- /*
- * 04 logical framing from the spec (all this is masked when incoming
- * and has to be unmasked)
- *
- * We ignore the possibility of extension data because we don't
- * negotiate any extensions at the moment.
- *
- * 0 1 2 3
- * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-------+-+-------------+-------------------------------+
- * |F|R|R|R| opcode|R| Payload len | Extended payload length |
- * |I|S|S|S| (4) |S| (7) | (16/63) |
- * |N|V|V|V| |V| | (if payload len==126/127) |
- * | |1|2|3| |4| | |
- * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- * | Extended payload length continued, if payload len == 127 |
- * + - - - - - - - - - - - - - - - +-------------------------------+
- * | | Extension data |
- * +-------------------------------+ - - - - - - - - - - - - - - - +
- * : :
- * +---------------------------------------------------------------+
- * : Application data :
- * +---------------------------------------------------------------+
- *
- * We pass payload through to userland as soon as we get it, ignoring
- * FIN. It's up to userland to buffer it up if it wants to see a
- * whole unfragmented block of the original size (which may be up to
- * 2^63 long!)
- */
-
- case LWS_RXPS_04_FRAME_HDR_1:
-handle_first:
-
- wsi->ws->opcode = c & 0xf;
- wsi->ws->rsv = c & 0x70;
- wsi->ws->final = !!((c >> 7) & 1);
- wsi->ws->defeat_check_utf8 = 0;
-
- if (((wsi->ws->opcode) & 8) && !wsi->ws->final) {
- lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
- (uint8_t *)"frag ctl", 8);
- return -1;
- }
-
- switch (wsi->ws->opcode) {
- case LWSWSOPC_TEXT_FRAME:
- wsi->ws->check_utf8 = lws_check_opt(
- wsi->context->options,
- LWS_SERVER_OPTION_VALIDATE_UTF8);
- /* fallthru */
- case LWSWSOPC_BINARY_FRAME:
- if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)
- wsi->ws->check_utf8 = 0;
- if (wsi->ws->continuation_possible) {
- lws_close_reason(wsi,
- LWS_CLOSE_STATUS_PROTOCOL_ERR,
- (uint8_t *)"bad cont", 8);
- return -1;
- }
- wsi->ws->rsv_first_msg = (c & 0x70);
- wsi->ws->frame_is_binary =
- wsi->ws->opcode == LWSWSOPC_BINARY_FRAME;
- wsi->ws->first_fragment = 1;
- wsi->ws->continuation_possible = !wsi->ws->final;
- break;
- case LWSWSOPC_CONTINUATION:
- if (!wsi->ws->continuation_possible) {
- lws_close_reason(wsi,
- LWS_CLOSE_STATUS_PROTOCOL_ERR,
- (uint8_t *)"bad cont", 8);
- return -1;
- }
- break;
- case LWSWSOPC_CLOSE:
- wsi->ws->check_utf8 = 0;
- wsi->ws->utf8 = 0;
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 0xb:
- case 0xc:
- case 0xd:
- case 0xe:
- case 0xf:
- lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
- (uint8_t *)"bad opc", 7);
- lwsl_info("illegal opcode\n");
- return -1;
- }
-
- if (wsi->ws->owed_a_fin &&
- (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
- wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
- lwsl_info("hey you owed us a FIN\n");
- lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
- (uint8_t *)"bad fin", 7);
- return -1;
- }
- if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
- wsi->ws->continuation_possible = 0;
- wsi->ws->owed_a_fin = 0;
- }
-
- if (!wsi->ws->final)
- wsi->ws->owed_a_fin = 1;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
- if (wsi->ws->rsv &&
- (
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- !wsi->ws->count_act_ext ||
-#endif
- (wsi->ws->rsv & ~0x40))) {
- lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
- (uint8_t *)"rsv bits", 8);
- return -1;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN:
-
- wsi->ws->this_frame_masked = !!(c & 0x80);
-
- switch (c & 0x7f) {
- case 126:
- /* control frames are not allowed to have big lengths */
- if (wsi->ws->opcode & 8)
- goto illegal_ctl_length;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
- break;
- case 127:
- /* control frames are not allowed to have big lengths */
- if (wsi->ws->opcode & 8)
- goto illegal_ctl_length;
-
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
- break;
- default:
- wsi->ws->rx_packet_length = c & 0x7f;
-
-
- if (wsi->ws->this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- if (wsi->ws->rx_packet_length) {
- wsi->lws_rx_parse_state =
- LWS_RXPS_WS_FRAME_PAYLOAD;
- } else {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_2:
- wsi->ws->rx_packet_length = c << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN16_1:
- wsi->ws->rx_packet_length |= c;
- if (wsi->ws->this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else {
- wsi->lws_rx_parse_state =
- LWS_RXPS_WS_FRAME_PAYLOAD;
- }
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_8:
- if (c & 0x80) {
- lwsl_warn("b63 of length must be zero\n");
- /* kill the connection */
- return -1;
- }
-#if defined __LP64__
- wsi->ws->rx_packet_length = ((size_t)c) << 56;
-#else
- wsi->ws->rx_packet_length = 0;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_7:
-#if defined __LP64__
- wsi->ws->rx_packet_length |= ((size_t)c) << 48;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_6:
-#if defined __LP64__
- wsi->ws->rx_packet_length |= ((size_t)c) << 40;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_5:
-#if defined __LP64__
- wsi->ws->rx_packet_length |= ((size_t)c) << 32;
-#endif
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_4:
- wsi->ws->rx_packet_length |= ((size_t)c) << 24;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_3:
- wsi->ws->rx_packet_length |= ((size_t)c) << 16;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_2:
- wsi->ws->rx_packet_length |= ((size_t)c) << 8;
- wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
- break;
-
- case LWS_RXPS_04_FRAME_HDR_LEN64_1:
- wsi->ws->rx_packet_length |= ((size_t)c);
- if (wsi->ws->this_frame_masked)
- wsi->lws_rx_parse_state =
- LWS_RXPS_07_COLLECT_FRAME_KEY_1;
- else
- wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
- wsi->ws->mask[0] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
- wsi->ws->mask[1] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
- wsi->ws->mask[2] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
- break;
-
- case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
- wsi->ws->mask[3] = c;
- if (c)
- wsi->ws->all_zero_nonce = 0;
- wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
- wsi->ws->mask_idx = 0;
- if (wsi->ws->rx_packet_length == 0) {
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
- break;
-
-
- case LWS_RXPS_WS_FRAME_PAYLOAD:
- assert(wsi->ws->rx_ubuf);
-
- if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) {
- lwsl_err("Attempted overflow \n");
- return -1;
- }
- if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) {
- if (wsi->ws->all_zero_nonce)
- wsi->ws->rx_ubuf[LWS_PRE +
- (wsi->ws->rx_ubuf_head++)] = c;
- else
- wsi->ws->rx_ubuf[LWS_PRE +
- (wsi->ws->rx_ubuf_head++)] =
- c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
-
- --wsi->ws->rx_packet_length;
- }
-
- if (!wsi->ws->rx_packet_length) {
- lwsl_debug("%s: ws fragment length exhausted\n",
- __func__);
- /* spill because we have the whole frame */
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
- goto spill;
- }
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->rx_draining_ext) {
- lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
- goto drain_extension;
- }
-#endif
- /*
- * if there's no protocol max frame size given, we are
- * supposed to default to context->pt_serv_buf_size
- */
- if (!wsi->protocol->rx_buffer_size &&
- wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
- break;
-
- if (wsi->protocol->rx_buffer_size &&
- wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
- break;
-
- /* spill because we filled our rx buffer */
-spill:
- /*
- * is this frame a control packet we should take care of at this
- * layer? If so service it and hide it from the user callback
- */
-
- lwsl_parser("spill on %s\n", wsi->protocol->name);
-
- switch (wsi->ws->opcode) {
- case LWSWSOPC_CLOSE:
-
- if (wsi->ws->peer_has_sent_close)
- break;
-
- wsi->ws->peer_has_sent_close = 1;
-
- pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
- if (lws_check_opt(wsi->context->options,
- LWS_SERVER_OPTION_VALIDATE_UTF8) &&
- wsi->ws->rx_ubuf_head > 2 &&
- lws_check_utf8(&wsi->ws->utf8, pp + 2,
- wsi->ws->rx_ubuf_head - 2))
- goto utf8_fail;
-
- /* is this an acknowledgment of our close? */
- if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
- /*
- * fine he has told us he is closing too, let's
- * finish our close
- */
- lwsl_parser("seen client close ack\n");
- return -1;
- }
- if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
- /* if he sends us 2 CLOSE, kill him */
- return -1;
-
- if (lws_partial_buffered(wsi)) {
- /*
- * if we're in the middle of something,
- * we can't do a normal close response and
- * have to just close our end.
- */
- wsi->socket_is_permanently_unusable = 1;
- lwsl_parser("Closing on peer close "
- "due to pending tx\n");
- return -1;
- }
-
- if (wsi->ws->rx_ubuf_head >= 2) {
- close_code = (pp[0] << 8) | pp[1];
- if (close_code < 1000 ||
- close_code == 1004 ||
- close_code == 1005 ||
- close_code == 1006 ||
- close_code == 1012 ||
- close_code == 1013 ||
- close_code == 1014 ||
- close_code == 1015 ||
- (close_code >= 1016 && close_code < 3000)
- ) {
- pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
- pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
- }
- }
-
- if (user_callback_handle_rxflow(
- wsi->protocol->callback, wsi,
- LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
- wsi->user_space,
- &wsi->ws->rx_ubuf[LWS_PRE],
- wsi->ws->rx_ubuf_head))
- return -1;
-
- lwsl_parser("server sees client close packet\n");
- lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
- /* deal with the close packet contents as a PONG */
- wsi->ws->payload_is_close = 1;
- goto process_as_ping;
-
- case LWSWSOPC_PING:
- lwsl_info("received %d byte ping, sending pong\n",
- wsi->ws->rx_ubuf_head);
-
- if (wsi->ws->ping_pending_flag) {
- /*
- * there is already a pending ping payload
- * we should just log and drop
- */
- lwsl_parser("DROP PING since one pending\n");
- goto ping_drop;
- }
-process_as_ping:
- /* control packets can only be < 128 bytes long */
- if (wsi->ws->rx_ubuf_head > 128 - 3) {
- lwsl_parser("DROP PING payload too large\n");
- goto ping_drop;
- }
-
- /* stash the pong payload */
- memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
- &wsi->ws->rx_ubuf[LWS_PRE],
- wsi->ws->rx_ubuf_head);
-
- wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
- wsi->ws->ping_pending_flag = 1;
-
- /* get it sent as soon as possible */
- lws_callback_on_writable(wsi);
-ping_drop:
- wsi->ws->rx_ubuf_head = 0;
- return 0;
-
- case LWSWSOPC_PONG:
- lwsl_info("received pong\n");
- lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
- wsi->ws->rx_ubuf_head);
-
- if (wsi->pending_timeout ==
- PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
- lwsl_info("received expected PONG on wsi %p\n",
- wsi);
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- }
-
- /* issue it */
- callback_action = LWS_CALLBACK_RECEIVE_PONG;
- break;
-
- case LWSWSOPC_TEXT_FRAME:
- case LWSWSOPC_BINARY_FRAME:
- case LWSWSOPC_CONTINUATION:
- break;
-
- default:
- lwsl_parser("unknown opc %x\n", wsi->ws->opcode);
-
- return -1;
- }
-
- /*
- * No it's real payload, pass it up to the user callback.
- * It's nicely buffered with the pre-padding taken care of
- * so it can be sent straight out again using lws_write
- */
-
- ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
- ebuf.len = wsi->ws->rx_ubuf_head;
-
- if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
- goto already_done;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-drain_extension:
-#endif
- // lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
-
- if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
- lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
- goto already_done;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- lin = ebuf.len;
- //if (lin)
- // lwsl_hexdump_notice(ebuf.token, ebuf.len);
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
- lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__,
- n, ebuf.len);
- if (wsi->ws->rx_draining_ext)
- already_processed &= ~ALREADY_PROCESSED_NO_CB;
-#endif
- /*
- * ebuf may be pointing somewhere completely different now,
- * it's the output
- */
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (n < 0) {
- /*
- * we may rely on this to get RX, just drop connection
- */
- wsi->socket_is_permanently_unusable = 1;
- return -1;
- }
-#endif
- if (
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- rx_draining_ext &&
-#endif
- ebuf.len == 0)
- goto already_done;
-
- if (
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- n &&
-#endif
- ebuf.len)
- /* extension had more... main loop will come back */
- lws_add_wsi_to_draining_ext_list(wsi);
- else
- lws_remove_wsi_from_draining_ext_list(wsi);
-
- if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
- if (lws_check_utf8(&wsi->ws->utf8,
- (unsigned char *)ebuf.token,
- ebuf.len)) {
- lws_close_reason(wsi,
- LWS_CLOSE_STATUS_INVALID_PAYLOAD,
- (uint8_t *)"bad utf8", 8);
- goto utf8_fail;
- }
-
- /* we are ending partway through utf-8 character? */
- if (!wsi->ws->rx_packet_length && wsi->ws->final &&
- wsi->ws->utf8 && !n) {
- lwsl_info("FINAL utf8 error\n");
- lws_close_reason(wsi,
- LWS_CLOSE_STATUS_INVALID_PAYLOAD,
- (uint8_t *)"partial utf8", 12);
-utf8_fail:
- lwsl_notice("utf8 error\n");
- lwsl_hexdump_notice(ebuf.token, ebuf.len);
-
- return -1;
- }
- }
-
- if (!wsi->wsistate_pre_close && (ebuf.len >= 0 ||
- callback_action == LWS_CALLBACK_RECEIVE_PONG)) {
- if (ebuf.len)
- ebuf.token[ebuf.len] = '\0';
-
- if (wsi->protocol->callback &&
- !(already_processed & ALREADY_PROCESSED_NO_CB)) {
- if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
- lwsl_info("Doing pong callback\n");
-
- ret = user_callback_handle_rxflow(
- wsi->protocol->callback,
- wsi, (enum lws_callback_reasons)
- callback_action,
- wsi->user_space,
- ebuf.token,
- ebuf.len);
- }
- wsi->ws->first_fragment = 0;
- }
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (!lin)
- break;
-#endif
-
-already_done:
- wsi->ws->rx_ubuf_head = 0;
- break;
- }
-
- return ret;
-
-illegal_ctl_length:
-
- lwsl_warn("Control frame with xtended length is illegal\n");
- /* kill the connection */
- return -1;
-}
-
-
-LWS_VISIBLE size_t
-lws_remaining_packet_payload(struct lws *wsi)
-{
- return wsi->ws->rx_packet_length;
-}
-
-LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
-{
- return wsi->ws->frame_is_binary;
-}
-
-void
-lws_add_wsi_to_draining_ext_list(struct lws *wsi)
-{
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-
- if (wsi->ws->rx_draining_ext)
- return;
-
- lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
-
- wsi->ws->rx_draining_ext = 1;
- wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list;
- pt->ws.rx_draining_ext_list = wsi;
-#endif
-}
-
-void
-lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
-{
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- struct lws **w = &pt->ws.rx_draining_ext_list;
-
- if (!wsi->ws->rx_draining_ext)
- return;
-
- lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__);
-
- wsi->ws->rx_draining_ext = 0;
-
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- /* if us, point it instead to who we were pointing to */
- *w = wsi->ws->rx_draining_ext_list;
- break;
- }
- w = &((*w)->ws->rx_draining_ext_list);
- }
- wsi->ws->rx_draining_ext_list = NULL;
-#endif
-}
-
-LWS_EXTERN void
-lws_restart_ws_ping_pong_timer(struct lws *wsi)
-{
- if (!wsi->context->ws_ping_pong_interval ||
- !lwsi_role_ws(wsi))
- return;
-
- wsi->ws->time_next_ping_check = (time_t)lws_now_secs();
-}
-
-static int
-lws_0405_frame_mask_generate(struct lws *wsi)
-{
- int n;
- /* fetch the per-frame nonce */
-
- n = lws_get_random(lws_get_context(wsi), wsi->ws->mask, 4);
- if (n != 4) {
- lwsl_parser("Unable to read from random device %s %d\n",
- SYSTEM_RANDOM_FILEPATH, n);
- return 1;
- }
-
- /* start masking from first byte of masking key buffer */
- wsi->ws->mask_idx = 0;
-
- return 0;
-}
-
-int
-lws_server_init_wsi_for_ws(struct lws *wsi)
-{
- int n;
-
- lwsi_set_state(wsi, LRS_ESTABLISHED);
- lws_restart_ws_ping_pong_timer(wsi);
-
- /*
- * create the frame buffer for this connection according to the
- * size mentioned in the protocol definition. If 0 there, use
- * a big default for compatibility
- */
-
- n = (int)wsi->protocol->rx_buffer_size;
- if (!n)
- n = wsi->context->pt_serv_buf_size;
- n += LWS_PRE;
- wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
- if (!wsi->ws->rx_ubuf) {
- lwsl_err("Out of Mem allocating rx buffer %d\n", n);
- return 1;
- }
- wsi->ws->rx_ubuf_alloc = n;
- lwsl_debug("Allocating RX buffer %d\n", n);
-
-#if !defined(LWS_WITH_ESP32)
- if (!wsi->h2_stream_carries_ws)
- if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
- (const char *)&n, sizeof n)) {
- lwsl_warn("Failed to set SNDBUF to %d", n);
- return 1;
- }
-#endif
-
- /* notify user code that we're ready to roll */
-
- if (wsi->protocol->callback)
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
- wsi->user_space,
-#ifdef LWS_WITH_TLS
- wsi->tls.ssl,
-#else
- NULL,
-#endif
- wsi->h2_stream_carries_ws))
- return 1;
-
- lwsl_debug("ws established\n");
-
- return 0;
-}
-
-
-
-LWS_VISIBLE int
-lws_is_final_fragment(struct lws *wsi)
-{
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
- wsi->ws->final, (long)wsi->ws->rx_packet_length,
- (long)wsi->ws->rx_draining_ext);
- return wsi->ws->final && !wsi->ws->rx_packet_length &&
- !wsi->ws->rx_draining_ext;
-#else
- return wsi->ws->final && !wsi->ws->rx_packet_length;
-#endif
-}
-
-LWS_VISIBLE int
-lws_is_first_fragment(struct lws *wsi)
-{
- return wsi->ws->first_fragment;
-}
-
-LWS_VISIBLE unsigned char
-lws_get_reserved_bits(struct lws *wsi)
-{
- return wsi->ws->rsv;
-}
-
-LWS_VISIBLE LWS_EXTERN int
-lws_get_close_length(struct lws *wsi)
-{
- return wsi->ws->close_in_ping_buffer_len;
-}
-
-LWS_VISIBLE LWS_EXTERN unsigned char *
-lws_get_close_payload(struct lws *wsi)
-{
- return &wsi->ws->ping_payload_buf[LWS_PRE];
-}
-
-LWS_VISIBLE LWS_EXTERN void
-lws_close_reason(struct lws *wsi, enum lws_close_status status,
- unsigned char *buf, size_t len)
-{
- unsigned char *p, *start;
- int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE;
-
- assert(lwsi_role_ws(wsi));
-
- start = p = &wsi->ws->ping_payload_buf[LWS_PRE];
-
- *p++ = (((int)status) >> 8) & 0xff;
- *p++ = ((int)status) & 0xff;
-
- if (buf)
- while (len-- && p < start + budget)
- *p++ = *buf++;
-
- wsi->ws->close_in_ping_buffer_len = lws_ptr_diff(p, start);
-}
-
-static int
-lws_is_ws_with_ext(struct lws *wsi)
-{
-#if defined(LWS_WITHOUT_EXTENSIONS)
- return 0;
-#else
- return lwsi_role_ws(wsi) && !!wsi->ws->count_act_ext;
-#endif
-}
-
-static int
-rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
- struct lws_pollfd *pollfd)
-{
- unsigned int pending = 0;
- struct lws_tokens ebuf;
- char buffered = 0;
- int n = 0, m;
-#if defined(LWS_WITH_HTTP2)
- struct lws *wsi1;
-#endif
-
- if (!wsi->ws) {
- lwsl_err("ws role wsi with no ws\n");
- return 1;
- }
-
- // lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
-
- //lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
- // wsi->wsistate, pollfd->revents & LWS_POLLOUT);
-
- /*
- * something went wrong with parsing the handshake, and
- * we ended up back in the event loop without completing it
- */
- if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) {
- wsi->socket_is_permanently_unusable = 1;
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- }
-
- ebuf.token = NULL;
- ebuf.len = 0;
-
- if (lwsi_state(wsi) == LRS_WAITING_CONNECT) {
-#if !defined(LWS_NO_CLIENT)
- if ((pollfd->revents & LWS_POLLOUT) &&
- lws_handle_POLLOUT_event(wsi, pollfd)) {
- lwsl_debug("POLLOUT event closed it\n");
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- }
-
- n = lws_client_socket_service(wsi, pollfd, NULL);
- if (n)
- return LWS_HPI_RET_WSI_ALREADY_DIED;
-#endif
- return LWS_HPI_RET_HANDLED;
- }
-
- //lwsl_notice("%s: wsi->ws->tx_draining_ext %d revents 0x%x 0x%x %d\n",
- //__func__, wsi->ws->tx_draining_ext, pollfd->revents, wsi->wsistate,
- //lwsi_state_can_handle_POLLOUT(wsi));
-
- /* 1: something requested a callback when it was OK to write */
-
- if ((pollfd->revents & LWS_POLLOUT) &&
- lwsi_state_can_handle_POLLOUT(wsi) &&
- lws_handle_POLLOUT_event(wsi, pollfd)) {
- if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
- lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
-
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- }
-
- if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
- lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
- /*
- * we stopped caring about anything except control
- * packets. Force flow control off, defeat tx
- * draining.
- */
- lws_rx_flow_control(wsi, 1);
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws)
- wsi->ws->tx_draining_ext = 0;
-#endif
- }
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->tx_draining_ext)
- /*
- * We cannot deal with new RX until the TX ext path has
- * been drained. It's because new rx will, eg, crap on
- * the wsi rx buf that may be needed to retain state.
- *
- * TX ext drain path MUST go through event loop to avoid
- * blocking.
- */
- return LWS_HPI_RET_HANDLED;
-#endif
- if (lws_is_flowcontrolled(wsi)) {
- /* We cannot deal with any kind of new RX because we are
- * RX-flowcontrolled.
- */
- lwsl_info("flowcontrolled\n");
- return LWS_HPI_RET_HANDLED;
- }
-
-#if defined(LWS_WITH_HTTP2)
- if (wsi->http2_substream || wsi->upgraded_to_http2) {
- wsi1 = lws_get_network_wsi(wsi);
- if (wsi1 && lws_has_buffered_out(wsi1))
- /* We cannot deal with any kind of new RX
- * because we are dealing with a partial send
- * (new RX may trigger new http_action() that
- * expect to be able to send)
- */
- return LWS_HPI_RET_HANDLED;
- }
-#endif
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- /* 2: RX Extension needs to be drained
- */
-
- if (wsi->ws->rx_draining_ext) {
-
- lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__);
-#ifndef LWS_NO_CLIENT
- if (lwsi_role_client(wsi)) {
- n = lws_ws_client_rx_sm(wsi, 0);
- if (n < 0)
- /* we closed wsi */
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- } else
-#endif
- n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
-
- return LWS_HPI_RET_HANDLED;
- }
-
- if (wsi->ws->rx_draining_ext)
- /*
- * We have RX EXT content to drain, but can't do it
- * right now. That means we cannot do anything lower
- * priority either.
- */
- return LWS_HPI_RET_HANDLED;
-#endif
-
- /* 3: buflist needs to be drained
- */
-read:
- //lws_buflist_describe(&wsi->buflist, wsi);
- ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
- (uint8_t **)&ebuf.token);
- if (ebuf.len) {
- lwsl_info("draining buflist (len %d)\n", ebuf.len);
- buffered = 1;
- goto drain;
- }
-
- if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->http.ah)
- return LWS_HPI_RET_HANDLED;
-
- if (lws_is_flowcontrolled(wsi)) {
- lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
- __func__, wsi, wsi->rxflow_bitmap);
- return LWS_HPI_RET_HANDLED;
- }
-
- if (!(lwsi_role_client(wsi) &&
- (lwsi_state(wsi) != LRS_ESTABLISHED &&
- lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK &&
- lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) {
- /*
- * In case we are going to react to this rx by scheduling
- * writes, we need to restrict the amount of rx to the size
- * the protocol reported for rx buffer.
- *
- * Otherwise we get a situation we have to absorb possibly a
- * lot of reads before we get a chance to drain them by writing
- * them, eg, with echo type tests in autobahn.
- */
-
- buffered = 0;
- ebuf.token = (char *)pt->serv_buf;
- if (lwsi_role_ws(wsi))
- ebuf.len = wsi->ws->rx_ubuf_alloc;
- else
- ebuf.len = wsi->context->pt_serv_buf_size;
-
- if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size)
- ebuf.len = wsi->context->pt_serv_buf_size;
-
- if ((int)pending > ebuf.len)
- pending = ebuf.len;
-
- ebuf.len = lws_ssl_capable_read(wsi, (uint8_t *)ebuf.token,
- pending ? (int)pending :
- ebuf.len);
- switch (ebuf.len) {
- case 0:
- lwsl_info("%s: zero length read\n",
- __func__);
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- case LWS_SSL_CAPABLE_MORE_SERVICE:
- lwsl_info("SSL Capable more service\n");
- return LWS_HPI_RET_HANDLED;
- case LWS_SSL_CAPABLE_ERROR:
- lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
- __func__);
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- }
- // lwsl_notice("Actual RX %d\n", ebuf.len);
-
- lws_restart_ws_ping_pong_timer(wsi);
-
- /*
- * coverity thinks ssl_capable_read() may read over
- * 2GB. Dissuade it...
- */
- ebuf.len &= 0x7fffffff;
- }
-
-drain:
-
- /*
- * give any active extensions a chance to munge the buffer
- * before parse. We pass in a pointer to an lws_tokens struct
- * prepared with the default buffer and content length that's in
- * there. Rather than rewrite the default buffer, extensions
- * that expect to grow the buffer can adapt .token to
- * point to their own per-connection buffer in the extension
- * user allocation. By default with no extensions or no
- * extension callback handling, just the normal input buffer is
- * used then so it is efficient.
- */
- m = 0;
- do {
-
- /* service incoming data */
- //lws_buflist_describe(&wsi->buflist, wsi);
- if (ebuf.len) {
-#if defined(LWS_ROLE_H2)
- if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
- n = lws_read_h2(wsi, (unsigned char *)ebuf.token,
- ebuf.len);
- else
-#endif
- n = lws_read_h1(wsi, (unsigned char *)ebuf.token,
- ebuf.len);
-
- if (n < 0) {
- /* we closed wsi */
- n = 0;
- return LWS_HPI_RET_WSI_ALREADY_DIED;
- }
- //lws_buflist_describe(&wsi->buflist, wsi);
- //lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len);
- if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
- return LWS_HPI_RET_PLEASE_CLOSE_ME;
- }
-
- ebuf.token = NULL;
- ebuf.len = 0;
- } while (m);
-
- if (wsi->http.ah
-#if !defined(LWS_NO_CLIENT)
- && !wsi->client_h2_alpn
-#endif
- ) {
- lwsl_info("%s: %p: detaching ah\n", __func__, wsi);
- lws_header_table_detach(wsi, 0);
- }
-
- pending = lws_ssl_pending(wsi);
- if (pending) {
- if (lws_is_ws_with_ext(wsi))
- pending = pending > wsi->ws->rx_ubuf_alloc ?
- wsi->ws->rx_ubuf_alloc : pending;
- else
- pending = pending > wsi->context->pt_serv_buf_size ?
- wsi->context->pt_serv_buf_size : pending;
- goto read;
- }
-
- if (buffered && /* were draining, now nothing left */
- !lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
- lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
- /* having drained the rxflow buffer, can rearm POLLIN */
-#ifdef LWS_NO_SERVER
- n =
-#endif
- __lws_rx_flow_control(wsi);
- /* n ignored, needed for NO_SERVER case */
- }
-
- /* n = 0 */
- return LWS_HPI_RET_HANDLED;
-}
-
-
-int rops_handle_POLLOUT_ws(struct lws *wsi)
-{
- int write_type = LWS_WRITE_PONG;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- struct lws_tokens ebuf;
- int ret, m;
-#endif
- int n;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
- wsi->protocol->name, wsi->ws->tx_draining_ext);
-#endif
-
- /* Priority 3: pending control packets (pong or close)
- *
- * 3a: close notification packet requested from close api
- */
-
- if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
- lwsl_debug("sending close packet\n");
- lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE],
- wsi->ws->close_in_ping_buffer_len);
- wsi->waiting_to_send_close_frame = 0;
- n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
- wsi->ws->close_in_ping_buffer_len,
- LWS_WRITE_CLOSE);
- if (n >= 0) {
- if (wsi->close_needs_ack) {
- lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
- lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK,
- 5);
- lwsl_debug("sent close, await ack\n");
-
- return LWS_HP_RET_BAIL_OK;
- }
- wsi->close_needs_ack = 0;
- lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
- }
-
- return LWS_HP_RET_BAIL_DIE;
- }
-
- /* else, the send failed and we should just hang up */
-
- if ((lwsi_role_ws(wsi) && wsi->ws->ping_pending_flag) ||
- (lwsi_state(wsi) == LRS_RETURNED_CLOSE &&
- wsi->ws->payload_is_close)) {
-
- if (wsi->ws->payload_is_close)
- write_type = LWS_WRITE_CLOSE;
- else {
- if (wsi->wsistate_pre_close) {
- /* we started close flow, forget pong */
- wsi->ws->ping_pending_flag = 0;
- return LWS_HP_RET_BAIL_OK;
- }
- lwsl_info("issuing pong %d on wsi %p\n",
- wsi->ws->ping_payload_len, wsi);
- }
-
- n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
- wsi->ws->ping_payload_len, write_type);
- if (n < 0)
- return LWS_HP_RET_BAIL_DIE;
-
- /* well he is sent, mark him done */
- wsi->ws->ping_pending_flag = 0;
- if (wsi->ws->payload_is_close) {
- // assert(0);
- /* oh... a close frame was it... then we are done */
- return LWS_HP_RET_BAIL_DIE;
- }
-
- /* otherwise for PING, leave POLLOUT active either way */
- return LWS_HP_RET_BAIL_OK;
- }
-
- if (!wsi->socket_is_permanently_unusable && wsi->ws->send_check_ping) {
-
- lwsl_info("issuing ping on wsi %p\n", wsi);
- wsi->ws->send_check_ping = 0;
- n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
- 0, LWS_WRITE_PING);
- if (n < 0)
- return LWS_HP_RET_BAIL_DIE;
-
- /*
- * we apparently were able to send the PING in a reasonable time
- * now reset the clock on our peer to be able to send the
- * PONG in a reasonable time.
- */
-
- lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG,
- wsi->context->timeout_secs);
-
- return LWS_HP_RET_BAIL_OK;
- }
-
- /* Priority 4: if we are closing, not allowed to send more data frags
- * which means user callback or tx ext flush banned now
- */
- if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
- return LWS_HP_RET_USER_SERVICE;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- /* Priority 5: Tx path extension with more to send
- *
- * These are handled as new fragments each time around
- * So while we must block new writeable callback to enforce
- * payload ordering, but since they are always complete
- * fragments control packets can interleave OK.
- */
- if (wsi->ws->tx_draining_ext) {
- lwsl_ext("SERVICING TX EXT DRAINING\n");
- if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
- return LWS_HP_RET_BAIL_DIE;
- /* leave POLLOUT active */
- return LWS_HP_RET_BAIL_OK;
- }
-
- /* Priority 6: extensions
- */
- if (!wsi->ws->extension_data_pending && !wsi->ws->tx_draining_ext) {
- lwsl_ext("%s: !wsi->ws->extension_data_pending\n", __func__);
- return LWS_HP_RET_USER_SERVICE;
- }
-
- /*
- * check in on the active extensions, see if they
- * had pending stuff to spill... they need to get the
- * first look-in otherwise sequence will be disordered
- *
- * NULL, zero-length ebuf means just spill pending
- */
-
- ret = 1;
- if (wsi->role_ops == &role_ops_raw_skt ||
- wsi->role_ops == &role_ops_raw_file)
- ret = 0;
-
- while (ret == 1) {
-
- /* default to nobody has more to spill */
-
- ret = 0;
- ebuf.token = NULL;
- ebuf.len = 0;
-
- /* give every extension a chance to spill */
-
- m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
- &ebuf, 0);
- if (m < 0) {
- lwsl_err("ext reports fatal error\n");
- return LWS_HP_RET_BAIL_DIE;
- }
- if (m)
- /*
- * at least one extension told us he has more
- * to spill, so we will go around again after
- */
- ret = 1;
-
- /* assuming they gave us something to send, send it */
-
- if (ebuf.len) {
- n = lws_issue_raw(wsi, (unsigned char *)ebuf.token,
- ebuf.len);
- if (n < 0) {
- lwsl_info("closing from POLLOUT spill\n");
- return LWS_HP_RET_BAIL_DIE;
- }
- /*
- * Keep amount spilled small to minimize chance of this
- */
- if (n != ebuf.len) {
- lwsl_err("Unable to spill ext %d vs %d\n",
- ebuf.len, n);
- return LWS_HP_RET_BAIL_DIE;
- }
- } else
- continue;
-
- /* no extension has more to spill */
-
- if (!ret)
- continue;
-
- /*
- * There's more to spill from an extension, but we just sent
- * something... did that leave the pipe choked?
- */
-
- if (!lws_send_pipe_choked(wsi))
- /* no we could add more */
- continue;
-
- lwsl_info("choked in POLLOUT service\n");
-
- /*
- * Yes, he's choked. Leave the POLLOUT masked on so we will
- * come back here when he is unchoked. Don't call the user
- * callback to enforce ordering of spilling, he'll get called
- * when we come back here and there's nothing more to spill.
- */
-
- return LWS_HP_RET_BAIL_OK;
- }
-
- wsi->ws->extension_data_pending = 0;
-#endif
-
- return LWS_HP_RET_USER_SERVICE;
-}
-
-static int
-rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
-{
- struct lws_vhost *vh;
-
- if (!context->ws_ping_pong_interval ||
- context->last_ws_ping_pong_check_s >= now + 10)
- return 0;
-
- vh = context->vhost_list;
- context->last_ws_ping_pong_check_s = now;
-
- while (vh) {
- int n;
-
- lws_vhost_lock(vh);
-
- for (n = 0; n < vh->count_protocols; n++) {
-
- lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
- vh->same_vh_protocol_heads[n].next) {
- struct lws *wsi = lws_container_of(d,
- struct lws, same_vh_protocol);
-
- if (lwsi_role_ws(wsi) &&
- !wsi->socket_is_permanently_unusable &&
- !wsi->ws->send_check_ping &&
- wsi->ws->time_next_ping_check &&
- lws_compare_time_t(context, now,
- wsi->ws->time_next_ping_check) >
- context->ws_ping_pong_interval) {
-
- lwsl_info("req pp on wsi %p\n", wsi);
- wsi->ws->send_check_ping = 1;
- lws_set_timeout(wsi,
- PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
- context->timeout_secs);
- lws_callback_on_writable(wsi);
- wsi->ws->time_next_ping_check = now;
- }
-
- } lws_end_foreach_dll_safe(d, d1);
- }
-
- lws_vhost_unlock(vh);
- vh = vh->vhost_next;
- }
-
- return 0;
-}
-
-static int
-rops_service_flag_pending_ws(struct lws_context *context, int tsi)
-{
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- struct lws_context_per_thread *pt = &context->pt[tsi];
- struct lws *wsi;
- int forced = 0;
-
- /* POLLIN faking (the pt lock is taken by the parent) */
-
- /*
- * 1) For all guys with already-available ext data to drain, if they are
- * not flowcontrolled, fake their POLLIN status
- */
- wsi = pt->ws.rx_draining_ext_list;
- while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
- pt->fds[wsi->position_in_fds_table].revents |=
- pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
- if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
- forced = 1;
-
- wsi = wsi->ws->rx_draining_ext_list;
- }
-
- return forced;
-#else
- return 0;
-#endif
-}
-
-static int
-rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
-{
- if (!wsi->ws)
- return 0;
-
- if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */
- (reason == LWS_CLOSE_STATUS_NOSTATUS ||
- reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY))
- return 0;
-
- lwsl_debug("%s: sending close indication...\n", __func__);
-
- /* if no prepared close reason, use 1000 and no aux data */
-
- if (!wsi->ws->close_in_ping_buffer_len) {
- wsi->ws->close_in_ping_buffer_len = 2;
- wsi->ws->ping_payload_buf[LWS_PRE] = (reason >> 8) & 0xff;
- wsi->ws->ping_payload_buf[LWS_PRE + 1] = reason & 0xff;
- }
-
- wsi->waiting_to_send_close_frame = 1;
- wsi->close_needs_ack = 1;
- lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
- __lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
-
- lws_callback_on_writable(wsi);
-
- return 1;
-}
-
-static int
-rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
-{
- if (!wsi->ws)
- return 0;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-
- if (wsi->ws->rx_draining_ext) {
- struct lws **w = &pt->ws.rx_draining_ext_list;
-
- wsi->ws->rx_draining_ext = 0;
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- *w = wsi->ws->rx_draining_ext_list;
- break;
- }
- w = &((*w)->ws->rx_draining_ext_list);
- }
- wsi->ws->rx_draining_ext_list = NULL;
- }
-
- if (wsi->ws->tx_draining_ext) {
- struct lws **w = &pt->ws.tx_draining_ext_list;
- lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__);
- wsi->ws->tx_draining_ext = 0;
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- *w = wsi->ws->tx_draining_ext_list;
- break;
- }
- w = &((*w)->ws->tx_draining_ext_list);
- }
- wsi->ws->tx_draining_ext_list = NULL;
- }
-#endif
- lws_free_set_NULL(wsi->ws->rx_ubuf);
-
- wsi->ws->ping_payload_len = 0;
- wsi->ws->ping_pending_flag = 0;
-
- /* deallocate any active extension contexts */
-
- if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
- lwsl_warn("extension destruction failed\n");
-
- return 0;
-}
-
-static int
-rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
- enum lws_write_protocol *wp)
-{
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- enum lws_write_protocol wpt;
-#endif
- int masked7 = lwsi_role_client(wsi);
- unsigned char is_masked_bit = 0;
- unsigned char *dropmask = NULL;
- struct lws_tokens ebuf;
- size_t orig_len = len;
- int pre = 0, n = 0;
-
- // lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->tx_draining_ext) {
- /* remove us from the list */
- struct lws **w = &pt->ws.tx_draining_ext_list;
-
- lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__);
- wsi->ws->tx_draining_ext = 0;
- /* remove us from context draining ext list */
- while (*w) {
- if (*w == wsi) {
- *w = wsi->ws->tx_draining_ext_list;
- break;
- }
- w = &((*w)->ws->tx_draining_ext_list);
- }
- wsi->ws->tx_draining_ext_list = NULL;
-
- wpt = *wp;
- *wp = (wsi->ws->tx_draining_stashed_wp & 0xc0)|
- LWS_WRITE_CONTINUATION;
-
- /*
- * When we are just flushing (len == 0), we can trust the
- * stashed wp info completely. Otherwise adjust it to the
- * FIN status of the incoming packet.
- */
-
- if (!(wpt & LWS_WRITE_NO_FIN) && len)
- *wp &= ~LWS_WRITE_NO_FIN;
-
- lwsl_ext("FORCED draining wp to 0x%02X "
- "(stashed 0x%02X, incoming 0x%02X)\n", *wp,
- wsi->ws->tx_draining_stashed_wp, wpt);
- // assert(0);
- }
-#endif
- lws_restart_ws_ping_pong_timer(wsi);
-
- if (((*wp) & 0x1f) == LWS_WRITE_HTTP ||
- ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL ||
- ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
- ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS)
- goto send_raw;
-
-
-
- /* if we are continuing a frame that already had its header done */
-
- if (wsi->ws->inside_frame) {
- lwsl_debug("INSIDE FRAME\n");
- goto do_more_inside_frame;
- }
-
- wsi->ws->clean_buffer = 1;
-
- /*
- * give a chance to the extensions to modify payload
- * the extension may decide to produce unlimited payload erratically
- * (eg, compression extension), so we require only that if he produces
- * something, it will be a complete fragment of the length known at
- * the time (just the fragment length known), and if he has
- * more we will come back next time he is writeable and allow him to
- * produce more fragments until he's drained.
- *
- * This allows what is sent each time it is writeable to be limited to
- * a size that can be sent without partial sends or blocking, allows
- * interleaving of control frames and other connection service.
- */
- ebuf.token = (char *)buf;
- ebuf.len = (int)len;
-
- switch ((int)*wp) {
- case LWS_WRITE_PING:
- case LWS_WRITE_PONG:
- case LWS_WRITE_CLOSE:
- break;
- default:
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- // lwsl_notice("LWS_EXT_CB_PAYLOAD_TX\n");
- // m = (int)ebuf.len;
- /* returns 0 if no more tx pending, 1 if more pending */
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &ebuf, *wp);
- if (n < 0)
- return -1;
- // lwsl_notice("ext processed %d plaintext into %d compressed"
- // " (wp 0x%x)\n", m, (int)ebuf.len, *wp);
-
- if (n && ebuf.len) {
- lwsl_ext("write drain len %d (wp 0x%x) SETTING "
- "tx_draining_ext\n", (int)ebuf.len, *wp);
- /* extension requires further draining */
- wsi->ws->tx_draining_ext = 1;
- wsi->ws->tx_draining_ext_list =
- pt->ws.tx_draining_ext_list;
- pt->ws.tx_draining_ext_list = wsi;
- /* we must come back to do more */
- lws_callback_on_writable(wsi);
- /*
- * keep a copy of the write type for the overall
- * action that has provoked generation of these
- * fragments, so the last guy can use its FIN state.
- */
- wsi->ws->tx_draining_stashed_wp = *wp;
- /* this is definitely not actually the last fragment
- * because the extension asserted he has more coming
- * So make sure this intermediate one doesn't go out
- * with a FIN.
- */
- *wp |= LWS_WRITE_NO_FIN;
- }
-#endif
- if (ebuf.len && wsi->ws->stashed_write_pending) {
- wsi->ws->stashed_write_pending = 0;
- *wp = ((*wp) & 0xc0) | (int)wsi->ws->stashed_write_type;
- }
- }
-
- /*
- * an extension did something we need to keep... for example, if
- * compression extension, it has already updated its state according
- * to this being issued
- */
- if ((char *)buf != ebuf.token) {
- /*
- * ext might eat it, but not have anything to issue yet.
- * In that case we have to follow his lead, but stash and
- * replace the write type that was lost here the first time.
- */
- if (len && !ebuf.len) {
- if (!wsi->ws->stashed_write_pending)
- wsi->ws->stashed_write_type =
- (char)(*wp) & 0x3f;
- wsi->ws->stashed_write_pending = 1;
- return (int)len;
- }
- /*
- * extension recreated it:
- * need to buffer this if not all sent
- */
- wsi->ws->clean_buffer = 0;
- }
-
- buf = (unsigned char *)ebuf.token;
- len = ebuf.len;
-
- if (!buf) {
- lwsl_err("null buf (%d)\n", (int)len);
- return -1;
- }
-
- switch (wsi->ws->ietf_spec_revision) {
- case 13:
- if (masked7) {
- pre += 4;
- dropmask = &buf[0 - pre];
- is_masked_bit = 0x80;
- }
-
- switch ((*wp) & 0xf) {
- case LWS_WRITE_TEXT:
- n = LWSWSOPC_TEXT_FRAME;
- break;
- case LWS_WRITE_BINARY:
- n = LWSWSOPC_BINARY_FRAME;
- break;
- case LWS_WRITE_CONTINUATION:
- n = LWSWSOPC_CONTINUATION;
- break;
-
- case LWS_WRITE_CLOSE:
- n = LWSWSOPC_CLOSE;
- break;
- case LWS_WRITE_PING:
- n = LWSWSOPC_PING;
- break;
- case LWS_WRITE_PONG:
- n = LWSWSOPC_PONG;
- break;
- default:
- lwsl_warn("lws_write: unknown write opc / wp\n");
- return -1;
- }
-
- if (!((*wp) & LWS_WRITE_NO_FIN))
- n |= 1 << 7;
-
- if (len < 126) {
- pre += 2;
- buf[-pre] = n;
- buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
- } else {
- if (len < 65536) {
- pre += 4;
- buf[-pre] = n;
- buf[-pre + 1] = 126 | is_masked_bit;
- buf[-pre + 2] = (unsigned char)(len >> 8);
- buf[-pre + 3] = (unsigned char)len;
- } else {
- pre += 10;
- buf[-pre] = n;
- buf[-pre + 1] = 127 | is_masked_bit;
-#if defined __LP64__
- buf[-pre + 2] = (len >> 56) & 0x7f;
- buf[-pre + 3] = len >> 48;
- buf[-pre + 4] = len >> 40;
- buf[-pre + 5] = len >> 32;
-#else
- buf[-pre + 2] = 0;
- buf[-pre + 3] = 0;
- buf[-pre + 4] = 0;
- buf[-pre + 5] = 0;
-#endif
- buf[-pre + 6] = (unsigned char)(len >> 24);
- buf[-pre + 7] = (unsigned char)(len >> 16);
- buf[-pre + 8] = (unsigned char)(len >> 8);
- buf[-pre + 9] = (unsigned char)len;
- }
- }
- break;
- }
-
-do_more_inside_frame:
-
- /*
- * Deal with masking if we are in client -> server direction and
- * the wp demands it
- */
-
- if (masked7) {
- if (!wsi->ws->inside_frame)
- if (lws_0405_frame_mask_generate(wsi)) {
- lwsl_err("frame mask generation failed\n");
- return -1;
- }
-
- /*
- * in v7, just mask the payload
- */
- if (dropmask) { /* never set if already inside frame */
- for (n = 4; n < (int)len + 4; n++)
- dropmask[n] = dropmask[n] ^ wsi->ws->mask[
- (wsi->ws->mask_idx++) & 3];
-
- /* copy the frame nonce into place */
- memcpy(dropmask, wsi->ws->mask, 4);
- }
- }
-
- if (lwsi_role_h2_ENCAPSULATION(wsi)) {
- struct lws *encap = lws_get_network_wsi(wsi);
-
- assert(encap != wsi);
- return encap->role_ops->write_role_protocol(wsi, buf - pre,
- len + pre, wp);
- }
-
- switch ((*wp) & 0x1f) {
- case LWS_WRITE_TEXT:
- case LWS_WRITE_BINARY:
- case LWS_WRITE_CONTINUATION:
- if (!wsi->h2_stream_carries_ws) {
-
- /*
- * give any active extensions a chance to munge the
- * buffer before send. We pass in a pointer to an
- * lws_tokens struct prepared with the default buffer
- * and content length that's in there. Rather than
- * rewrite the default buffer, extensions that expect
- * to grow the buffer can adapt .token to point to their
- * own per-connection buffer in the extension user
- * allocation. By default with no extensions or no
- * extension callback handling, just the normal input
- * buffer is used then so it is efficient.
- *
- * callback returns 1 in case it wants to spill more
- * buffers
- *
- * This takes care of holding the buffer if send is
- * incomplete, ie, if wsi->ws->clean_buffer is 0
- * (meaning an extension meddled with the buffer). If
- * wsi->ws->clean_buffer is 1, it will instead return
- * to the user code how much OF THE USER BUFFER was
- * consumed.
- */
-
- n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
- wsi->ws->inside_frame = 1;
- if (n <= 0)
- return n;
-
- if (n == (int)len + pre) {
- /* everything in the buffer was handled
- * (or rebuffered...) */
- wsi->ws->inside_frame = 0;
- return (int)orig_len;
- }
-
- /*
- * it is how many bytes of user buffer got sent... may
- * be < orig_len in which case callback when writable
- * has already been arranged and user code can call
- * lws_write() again with the rest later.
- */
-
- return n - pre;
- }
- break;
- default:
- break;
- }
-
-send_raw:
- return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
-}
-
-static int
-rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason)
-{
- /* deal with ws encapsulation in h2 */
-#if defined(LWS_WITH_HTTP2)
- if (wsi->http2_substream && wsi->h2_stream_carries_ws)
- return role_ops_h2.close_kill_connection(wsi, reason);
-
- return 0;
-#else
- return 0;
-#endif
-}
-
-static int
-rops_callback_on_writable_ws(struct lws *wsi)
-{
-#if defined(LWS_WITH_HTTP2)
- if (lwsi_role_h2_ENCAPSULATION(wsi)) {
- /* we know then that it has an h2 parent */
- struct lws *enc = role_ops_h2.encapsulation_parent(wsi);
-
- assert(enc);
- if (enc->role_ops->callback_on_writable(wsi))
- return 1;
- }
-#endif
- return 0;
-}
-
-static int
-rops_init_vhost_ws(struct lws_vhost *vh,
- const struct lws_context_creation_info *info)
-{
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-#ifdef LWS_WITH_PLUGINS
- struct lws_plugin *plugin = vh->context->plugin_list;
- int m;
-
- if (vh->context->plugin_extension_count) {
-
- m = 0;
- while (info->extensions && info->extensions[m].callback)
- m++;
-
- /*
- * give the vhost a unified list of extensions including the
- * ones that came from plugins
- */
- vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
- (m + vh->context->plugin_extension_count + 1),
- "extensions");
- if (!vh->ws.extensions)
- return 1;
-
- memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
- sizeof(struct lws_extension) * m);
- plugin = vh->context->plugin_list;
- while (plugin) {
- memcpy((struct lws_extension *)&vh->ws.extensions[m],
- plugin->caps.extensions,
- sizeof(struct lws_extension) *
- plugin->caps.count_extensions);
- m += plugin->caps.count_extensions;
- plugin = plugin->list;
- }
- } else
-#endif
- vh->ws.extensions = info->extensions;
-#endif
-
- return 0;
-}
-
-static int
-rops_destroy_vhost_ws(struct lws_vhost *vh)
-{
-#ifdef LWS_WITH_PLUGINS
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (vh->context->plugin_extension_count)
- lws_free((void *)vh->ws.extensions);
-#endif
-#endif
-
- return 0;
-}
-
-static int
-rops_destroy_role_ws(struct lws *wsi)
-{
- lws_free_set_NULL(wsi->ws);
-
- return 0;
-}
-
-struct lws_role_ops role_ops_ws = {
- /* role name */ "ws",
- /* alpn id */ NULL,
- /* check_upgrades */ NULL,
- /* init_context */ NULL,
- /* init_vhost */ rops_init_vhost_ws,
- /* destroy_vhost */ rops_destroy_vhost_ws,
- /* periodic_checks */ rops_periodic_checks_ws,
- /* service_flag_pending */ rops_service_flag_pending_ws,
- /* handle_POLLIN */ rops_handle_POLLIN_ws,
- /* handle_POLLOUT */ rops_handle_POLLOUT_ws,
- /* perform_user_POLLOUT */ NULL,
- /* callback_on_writable */ rops_callback_on_writable_ws,
- /* tx_credit */ NULL,
- /* write_role_protocol */ rops_write_role_protocol_ws,
- /* encapsulation_parent */ NULL,
- /* alpn_negotiated */ NULL,
- /* close_via_role_protocol */ rops_close_via_role_protocol_ws,
- /* close_role */ rops_close_role_ws,
- /* close_kill_connection */ rops_close_kill_connection_ws,
- /* destroy_role */ rops_destroy_role_ws,
- /* adoption_bind */ NULL,
- /* client_bind */ NULL,
- /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE,
- LWS_CALLBACK_SERVER_WRITEABLE },
- /* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED,
- LWS_CALLBACK_CLOSED },
- /* protocol_bind cb c, srv */ { LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL,
- LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL },
- /* protocol_unbind cb c, srv */ { LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL,
- LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL },
- /* file handles */ 0
-};
diff --git a/thirdparty/libwebsockets/lib/roles/ws/private.h b/thirdparty/libwebsockets/lib/roles/ws/private.h
deleted file mode 100644
index 71ffcaea96..0000000000
--- a/thirdparty/libwebsockets/lib/roles/ws/private.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010 - 2018 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
- *
- * This is included from core/private.h if LWS_ROLE_WS
- */
-
-extern struct lws_role_ops role_ops_ws;
-
-#define lwsi_role_ws(wsi) (wsi->role_ops == &role_ops_ws)
-
-enum lws_rx_parse_state {
- LWS_RXPS_NEW,
-
- LWS_RXPS_04_mask_1,
- LWS_RXPS_04_mask_2,
- LWS_RXPS_04_mask_3,
-
- LWS_RXPS_04_FRAME_HDR_1,
- LWS_RXPS_04_FRAME_HDR_LEN,
- LWS_RXPS_04_FRAME_HDR_LEN16_2,
- LWS_RXPS_04_FRAME_HDR_LEN16_1,
- LWS_RXPS_04_FRAME_HDR_LEN64_8,
- LWS_RXPS_04_FRAME_HDR_LEN64_7,
- LWS_RXPS_04_FRAME_HDR_LEN64_6,
- LWS_RXPS_04_FRAME_HDR_LEN64_5,
- LWS_RXPS_04_FRAME_HDR_LEN64_4,
- LWS_RXPS_04_FRAME_HDR_LEN64_3,
- LWS_RXPS_04_FRAME_HDR_LEN64_2,
- LWS_RXPS_04_FRAME_HDR_LEN64_1,
-
- LWS_RXPS_07_COLLECT_FRAME_KEY_1,
- LWS_RXPS_07_COLLECT_FRAME_KEY_2,
- LWS_RXPS_07_COLLECT_FRAME_KEY_3,
- LWS_RXPS_07_COLLECT_FRAME_KEY_4,
-
- LWS_RXPS_WS_FRAME_PAYLOAD
-};
-
-enum lws_websocket_opcodes_07 {
- LWSWSOPC_CONTINUATION = 0,
- LWSWSOPC_TEXT_FRAME = 1,
- LWSWSOPC_BINARY_FRAME = 2,
-
- LWSWSOPC_NOSPEC__MUX = 7,
-
- /* control extensions 8+ */
-
- LWSWSOPC_CLOSE = 8,
- LWSWSOPC_PING = 9,
- LWSWSOPC_PONG = 0xa,
-};
-
-/* this is not usable directly by user code any more, lws_close_reason() */
-#define LWS_WRITE_CLOSE 4
-
-#define ALREADY_PROCESSED_IGNORE_CHAR 1
-#define ALREADY_PROCESSED_NO_CB 2
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-struct lws_vhost_role_ws {
- const struct lws_extension *extensions;
-};
-
-struct lws_pt_role_ws {
- struct lws *rx_draining_ext_list;
- struct lws *tx_draining_ext_list;
-};
-#endif
-
-struct _lws_websocket_related {
- char *rx_ubuf;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
- void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
- struct lws *rx_draining_ext_list;
- struct lws *tx_draining_ext_list;
-#endif
- /* Also used for close content... control opcode == < 128 */
- uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
- uint8_t mask[4];
-
- time_t time_next_ping_check;
- size_t rx_packet_length;
- uint32_t rx_ubuf_head;
- uint32_t rx_ubuf_alloc;
-
- uint8_t ping_payload_len;
- uint8_t mask_idx;
- uint8_t opcode;
- uint8_t rsv;
- uint8_t rsv_first_msg;
- /* zero if no info, or length including 2-byte close code */
- uint8_t close_in_ping_buffer_len;
- uint8_t utf8;
- uint8_t stashed_write_type;
- uint8_t tx_draining_stashed_wp;
- uint8_t ietf_spec_revision;
-
- unsigned int final:1;
- unsigned int frame_is_binary:1;
- unsigned int all_zero_nonce:1;
- unsigned int this_frame_masked:1;
- unsigned int inside_frame:1; /* next write will be more of frame */
- unsigned int clean_buffer:1; /* buffer not rewritten by extension */
- unsigned int payload_is_close:1; /* process as PONG, but it is close */
- unsigned int ping_pending_flag:1;
- unsigned int continuation_possible:1;
- unsigned int owed_a_fin:1;
- unsigned int check_utf8:1;
- unsigned int defeat_check_utf8:1;
- unsigned int stashed_write_pending:1;
- unsigned int send_check_ping:1;
- unsigned int first_fragment:1;
- unsigned int peer_has_sent_close:1;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- unsigned int extension_data_pending:1;
- unsigned int rx_draining_ext:1;
- unsigned int tx_draining_ext:1;
-
- uint8_t count_act_ext;
-#endif
-};
-
-int
-lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-LWS_VISIBLE void
-lws_context_init_extensions(const struct lws_context_creation_info *info,
- struct lws_context *context);
-LWS_EXTERN int
-lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
- void *v, size_t len);
-
-LWS_EXTERN int
-lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len);
-LWS_EXTERN int
-lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason,
- void *arg, int len);
-#endif
-
-int
-handshake_0405(struct lws_context *context, struct lws *wsi);
-int
-lws_process_ws_upgrade(struct lws *wsi);
-int
-lws_server_init_wsi_for_ws(struct lws *wsi);
diff --git a/thirdparty/libwebsockets/lib/roles/ws/server-ws.c b/thirdparty/libwebsockets/lib/roles/ws/server-ws.c
deleted file mode 100644
index bdc45367d4..0000000000
--- a/thirdparty/libwebsockets/lib/roles/ws/server-ws.c
+++ /dev/null
@@ -1,874 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2018 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 <core/private.h>
-
-#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
-static int
-lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
-{
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- char ext_name[64], *args, *end = (*p) + budget - 1;
- const struct lws_ext_options *opts, *po;
- const struct lws_extension *ext;
- struct lws_ext_option_arg oa;
- int n, m, more = 1;
- int ext_count = 0;
- char ignore;
- char *c;
-
- /*
- * Figure out which extensions the client has that we want to
- * enable on this connection, and give him back the list
- */
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
- return 0;
-
- /*
- * break down the list of client extensions
- * and go through them
- */
-
- if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
- WSI_TOKEN_EXTENSIONS) < 0)
- return 1;
-
- c = (char *)pt->serv_buf;
- lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
- wsi->ws->count_act_ext = 0;
- ignore = 0;
- n = 0;
- args = NULL;
-
- /*
- * We may get a simple request
- *
- * Sec-WebSocket-Extensions: permessage-deflate
- *
- * or an elaborated one with requested options
- *
- * Sec-WebSocket-Extensions: permessage-deflate; \
- * server_no_context_takeover; \
- * client_no_context_takeover
- */
-
- while (more) {
-
- if (c >= (char *)pt->serv_buf + 255)
- return -1;
-
- if (*c && (*c != ',' && *c != '\t')) {
- if (*c == ';') {
- ignore = 1;
- if (!args)
- args = c + 1;
- }
- if (ignore || *c == ' ') {
- c++;
- continue;
- }
- ext_name[n] = *c++;
- if (n < (int)sizeof(ext_name) - 1)
- n++;
- continue;
- }
- ext_name[n] = '\0';
-
- ignore = 0;
- if (!*c)
- more = 0;
- else {
- c++;
- if (!n)
- continue;
- }
-
- while (args && *args == ' ')
- args++;
-
- /* check a client's extension against our support */
-
- ext = wsi->vhost->ws.extensions;
-
- while (ext && ext->callback) {
-
- if (strcmp(ext_name, ext->name)) {
- ext++;
- continue;
- }
-
- /*
- * oh, we do support this one he asked for... but let's
- * confirm he only gave it once
- */
- for (m = 0; m < wsi->ws->count_act_ext; m++)
- if (wsi->ws->active_extensions[m] == ext) {
- lwsl_info("ext mentioned twice\n");
- return 1; /* shenanigans */
- }
-
- /*
- * ask user code if it's OK to apply it on this
- * particular connection + protocol
- */
- m = (wsi->protocol->callback)(wsi,
- LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
- wsi->user_space, ext_name, 0);
-
- /*
- * zero return from callback means go ahead and allow
- * the extension, it's what we get if the callback is
- * unhandled
- */
- if (m) {
- ext++;
- continue;
- }
-
- /* apply it */
-
- ext_count++;
-
- /* instantiate the extension on this conn */
-
- wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
-
- /* allow him to construct his context */
-
- if (ext->callback(lws_get_context(wsi), ext, wsi,
- LWS_EXT_CB_CONSTRUCT,
- (void *)&wsi->ws->act_ext_user[
- wsi->ws->count_act_ext],
- (void *)&opts, 0)) {
- lwsl_info("ext %s failed construction\n",
- ext_name);
- ext_count--;
- ext++;
-
- continue;
- }
-
- if (ext_count > 1)
- *(*p)++ = ',';
- else
- LWS_CPYAPP(*p,
- "\x0d\x0aSec-WebSocket-Extensions: ");
- *p += lws_snprintf(*p, (end - *p), "%s", ext_name);
-
- /*
- * The client may send a bunch of different option
- * sets for the same extension, we are supposed to
- * pick one we like the look of. The option sets are
- * separated by comma.
- *
- * Actually we just either accept the first one or
- * nothing.
- *
- * Go through the options trying to apply the
- * recognized ones
- */
-
- lwsl_info("ext args %s\n", args);
-
- while (args && *args && *args != ',') {
- while (*args == ' ')
- args++;
- po = opts;
- while (po->name) {
- /* only support arg-less options... */
- if (po->type != EXTARG_NONE ||
- strncmp(args, po->name,
- strlen(po->name))) {
- po++;
- continue;
- }
- oa.option_name = NULL;
- oa.option_index = (int)(po - opts);
- oa.start = NULL;
- oa.len = 0;
- lwsl_info("setting '%s'\n", po->name);
- if (!ext->callback(lws_get_context(wsi),
- ext, wsi,
- LWS_EXT_CB_OPTION_SET,
- wsi->ws->act_ext_user[
- wsi->ws->count_act_ext],
- &oa, (end - *p))) {
-
- *p += lws_snprintf(*p,
- (end - *p),
- "; %s", po->name);
- lwsl_debug("adding option %s\n",
- po->name);
- }
- po++;
- }
- while (*args && *args != ',' && *args != ';')
- args++;
-
- if (*args == ';')
- args++;
- }
-
- wsi->ws->count_act_ext++;
- lwsl_parser("cnt_act_ext <- %d\n",
- wsi->ws->count_act_ext);
-
- if (args && *args == ',')
- more = 0;
-
- ext++;
- }
-
- n = 0;
- args = NULL;
- }
-
- return 0;
-}
-#endif
-
-int
-lws_process_ws_upgrade(struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- const struct lws_protocols *pcol = NULL;
- char buf[128], name[64];
- struct lws_tokenize ts;
- lws_tokenize_elem e;
-
- if (!wsi->protocol)
- lwsl_err("NULL protocol at lws_read\n");
-
- /*
- * We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined
- * header considerations about keeping the ah around no longer apply.
- *
- * However it's common for the first ws protocol data to have been
- * coalesced with the browser upgrade request and to already be in the
- * ah rx buffer.
- */
-
- lws_pt_lock(pt, __func__);
-
- if (!wsi->h2_stream_carries_ws)
- lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED,
- &role_ops_ws);
-
- lws_pt_unlock(pt);
-
- /*
- * It's either websocket or h2->websocket
- *
- * If we are on h1, confirm we got the required "connection: upgrade"
- * header. h2 / ws-over-h2 does not have this.
- */
-
-#if defined(LWS_WITH_HTTP2)
- if (wsi->http2_substream)
- goto check_protocol;
-#endif
-
- lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
- LWS_TOKENIZE_F_MINUS_NONTERM);
- ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
- if (ts.len <= 0)
- goto bad_conn_format;
-
- do {
- e = lws_tokenize(&ts);
- switch (e) {
- case LWS_TOKZE_TOKEN:
- if (!strcasecmp(ts.token, "upgrade"))
- e = LWS_TOKZE_ENDED;
- break;
-
- case LWS_TOKZE_DELIMITER:
- break;
-
- default: /* includes ENDED */
-bad_conn_format:
- lwsl_err("%s: malformed or absent connection hdr\n",
- __func__);
-
- return 1;
- }
- } while (e > 0);
-
-#if defined(LWS_WITH_HTTP2)
-check_protocol:
-#endif
-
- /*
- * Select the first protocol we support from the list
- * the client sent us.
- */
-
- lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
- LWS_TOKENIZE_F_MINUS_NONTERM |
- LWS_TOKENIZE_F_RFC7230_DELIMS);
- ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_PROTOCOL);
- if (ts.len < 0) {
- lwsl_err("%s: protocol list too long\n", __func__);
- return 1;
- }
- if (!ts.len) {
- int n = wsi->vhost->default_protocol_index;
- /*
- * some clients only have one protocol and do not send the
- * protocol list header... allow it and match to the vhost's
- * default protocol (which itself defaults to zero)
- */
- lwsl_info("%s: defaulting to prot handler %d\n", __func__, n);
-
- lws_bind_protocol(wsi, &wsi->vhost->protocols[n],
- "ws upgrade default pcol");
-
- goto alloc_ws;
- }
-
- /* otherwise go through the user-provided protocol list */
-
- do {
- e = lws_tokenize(&ts);
- switch (e) {
- case LWS_TOKZE_TOKEN:
-
- if (lws_tokenize_cstr(&ts, name, sizeof(name))) {
- lwsl_err("%s: pcol name too long\n", __func__);
-
- return 1;
- }
- lwsl_debug("checking %s\n", name);
- pcol = lws_vhost_name_to_protocol(wsi->vhost, name);
- if (pcol) {
- /* if we know it, bind to it and stop looking */
- lws_bind_protocol(wsi, pcol, "ws upg pcol");
- e = LWS_TOKZE_ENDED;
- }
- break;
-
- case LWS_TOKZE_DELIMITER:
- case LWS_TOKZE_ENDED:
- break;
-
- default:
- lwsl_err("%s: malformatted protocol list", __func__);
-
- return 1;
- }
- } while (e > 0);
-
- /* we didn't find a protocol he wanted? */
-
- if (!pcol) {
- lwsl_notice("No supported protocol \"%s\"\n", buf);
-
- return 1;
- }
-
-alloc_ws:
-
- /* allocate the ws struct for the wsi */
-
- wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
- if (!wsi->ws) {
- lwsl_notice("OOM\n");
- return 1;
- }
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
- wsi->ws->ietf_spec_revision =
- atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
-
- /* allocate wsi->user storage */
- if (lws_ensure_user_space(wsi)) {
- lwsl_notice("problem with user space\n");
- return 1;
- }
-
- /*
- * Give the user code a chance to study the request and
- * have the opportunity to deny it
- */
- if ((wsi->protocol->callback)(wsi,
- LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
- wsi->user_space,
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
- lwsl_warn("User code denied connection\n");
- return 1;
- }
-
- /*
- * Perform the handshake according to the protocol version the
- * client announced
- */
-
- switch (wsi->ws->ietf_spec_revision) {
- default:
- lwsl_notice("Unknown client spec version %d\n",
- wsi->ws->ietf_spec_revision);
- wsi->ws->ietf_spec_revision = 13;
- //return 1;
- /* fallthru */
- case 13:
-#if defined(LWS_WITH_HTTP2)
- if (wsi->h2_stream_carries_ws) {
- if (lws_h2_ws_handshake(wsi)) {
- lwsl_notice("h2 ws handshake failed\n");
- return 1;
- }
- lws_role_transition(wsi,
- LWSIFR_SERVER | LWSIFR_P_ENCAP_H2,
- LRS_ESTABLISHED, &role_ops_ws);
- } else
-#endif
- {
- lwsl_parser("lws_parse calling handshake_04\n");
- if (handshake_0405(wsi->context, wsi)) {
- lwsl_notice("hs0405 has failed the connection\n");
- return 1;
- }
- }
- break;
- }
-
- lws_server_init_wsi_for_ws(wsi);
- lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision);
-
- lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi);
- lws_header_table_detach(wsi, 1);
-
- return 0;
-}
-
-int
-handshake_0405(struct lws_context *context, struct lws *wsi)
-{
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- struct lws_process_html_args args;
- unsigned char hash[20];
- int n, accept_len;
- char *response;
- char *p;
-
- if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
- !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
- lwsl_info("handshake_04 missing pieces\n");
- /* completed header processing, but missing some bits */
- goto bail;
- }
-
- if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
- MAX_WEBSOCKET_04_KEY_LEN) {
- lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
- goto bail;
- }
-
- /*
- * since key length is restricted above (currently 128), cannot
- * overflow
- */
- n = sprintf((char *)pt->serv_buf,
- "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
-
- lws_SHA1(pt->serv_buf, n, hash);
-
- accept_len = lws_b64_encode_string((char *)hash, 20,
- (char *)pt->serv_buf, context->pt_serv_buf_size);
- if (accept_len < 0) {
- lwsl_warn("Base64 encoded hash too long\n");
- goto bail;
- }
-
- /* allocate the per-connection user memory (if any) */
- if (lws_ensure_user_space(wsi))
- goto bail;
-
- /* create the response packet */
-
- /* make a buffer big enough for everything */
-
- response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN +
- 256 + LWS_PRE;
- p = response;
- LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
- "Upgrade: WebSocket\x0d\x0a"
- "Connection: Upgrade\x0d\x0a"
- "Sec-WebSocket-Accept: ");
- strcpy(p, (char *)pt->serv_buf);
- p += accept_len;
-
- /* we can only return the protocol header if:
- * - one came in, and ... */
- if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
- /* - it is not an empty string */
- wsi->protocol->name &&
- wsi->protocol->name[0]) {
- LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
- p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
- }
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- /*
- * Figure out which extensions the client has that we want to
- * enable on this connection, and give him back the list.
- *
- * Give him a limited write bugdet
- */
- if (lws_extension_server_handshake(wsi, &p, 192))
- goto bail;
-#endif
- LWS_CPYAPP(p, "\x0d\x0a");
-
- args.p = p;
- args.max_len = lws_ptr_diff((char *)pt->serv_buf +
- context->pt_serv_buf_size, p);
- if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
- LWS_CALLBACK_ADD_HEADERS,
- wsi->user_space, &args, 0))
- goto bail;
-
- p = args.p;
-
- /* end of response packet */
-
- LWS_CPYAPP(p, "\x0d\x0a");
-
- /* okay send the handshake response accepting the connection */
-
- lwsl_parser("issuing resp pkt %d len\n",
- lws_ptr_diff(p, response));
-#if defined(DEBUG)
- fwrite(response, 1, p - response, stderr);
-#endif
- n = lws_write(wsi, (unsigned char *)response, p - response,
- LWS_WRITE_HTTP_HEADERS);
- if (n != (p - response)) {
- lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
- goto bail;
- }
-
- /* alright clean up and set ourselves into established state */
-
- lwsi_set_state(wsi, LRS_ESTABLISHED);
- wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-
- {
- const char * uri_ptr =
- lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
- int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
- const struct lws_http_mount *hit =
- lws_find_mount(wsi, uri_ptr, uri_len);
- if (hit && hit->cgienv &&
- wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
- wsi->user_space, (void *)hit->cgienv, 0))
- return 1;
- }
-
- return 0;
-
-bail:
- /* caller will free up his parsing allocations */
- return -1;
-}
-
-
-
-/*
- * Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much
- * to expect in that state and can deal with it in bulk more efficiently.
- */
-
-static int
-lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
-{
- unsigned int avail = (unsigned int)len;
- uint8_t *buffer = *buf, mask[4];
- struct lws_tokens ebuf;
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
-#endif
- int n = 0;
-
- /*
- * With zlib, we can give it as much input as we like. The pmd
- * extension will draw it down in chunks (default 1024).
- *
- * If we try to restrict how much we give it, because we must go
- * back to the event loop each time, we will drop the remainder...
- */
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (!wsi->ws->count_act_ext)
-#endif
- {
- if (wsi->protocol->rx_buffer_size)
- avail = (int)wsi->protocol->rx_buffer_size;
- else
- avail = wsi->context->pt_serv_buf_size;
- }
-
- /* do not consume more than we should */
- if (avail > wsi->ws->rx_packet_length)
- avail = (unsigned int)wsi->ws->rx_packet_length;
-
- /* do not consume more than what is in the buffer */
- if (avail > len)
- avail = (unsigned int)len;
-
- if (!avail)
- return 0;
-
- ebuf.token = (char *)buffer;
- ebuf.len = avail;
-
- //lwsl_hexdump_notice(ebuf.token, ebuf.len);
-
- if (!wsi->ws->all_zero_nonce) {
-
- for (n = 0; n < 4; n++)
- mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
-
- /* deal with 4-byte chunks using unwrapped loop */
- n = avail >> 2;
- while (n--) {
- *(buffer) = *(buffer) ^ mask[0];
- buffer++;
- *(buffer) = *(buffer) ^ mask[1];
- buffer++;
- *(buffer) = *(buffer) ^ mask[2];
- buffer++;
- *(buffer) = *(buffer) ^ mask[3];
- buffer++;
- }
- /* and the remaining bytes bytewise */
- for (n = 0; n < (int)(avail & 3); n++) {
- *(buffer) = *(buffer) ^ mask[n];
- buffer++;
- }
-
- wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
- }
-
- lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__,
- avail, (int)len);
-
- (*buf) += avail;
- len -= avail;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
- lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
-#endif
- /*
- * ebuf may be pointing somewhere completely different now,
- * it's the output
- */
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (n < 0) {
- /*
- * we may rely on this to get RX, just drop connection
- */
- lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__);
- wsi->socket_is_permanently_unusable = 1;
- return -1;
- }
-#endif
-
- wsi->ws->rx_packet_length -= avail;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- /*
- * if we had an rx fragment right at the last compressed byte of the
- * message, we can get a zero length inflated output, where no prior
- * rx inflated output marked themselves with FIN, since there was
- * raw ws payload still to drain at that time.
- *
- * Then we need to generate a zero length ws rx that can be understood
- * as the message completion.
- */
-
- if (!ebuf.len && /* zero-length inflation output */
- !n && /* nothing left to drain from the inflator */
- wsi->ws->count_act_ext && /* we are using pmd */
- old_packet_length && /* we gave the inflator new input */
- !wsi->ws->rx_packet_length && /* raw ws packet payload all gone */
- wsi->ws->final && /* the raw ws packet is a FIN guy */
- wsi->protocol->callback &&
- !wsi->wsistate_pre_close) {
-
- if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
- LWS_CALLBACK_RECEIVE,
- wsi->user_space, NULL, 0))
- return -1;
-
- return avail;
- }
-#endif
-
- if (!ebuf.len)
- return avail;
-
- if (
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- n &&
-#endif
- ebuf.len)
- /* extension had more... main loop will come back */
- lws_add_wsi_to_draining_ext_list(wsi);
- else
- lws_remove_wsi_from_draining_ext_list(wsi);
-
- if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
- if (lws_check_utf8(&wsi->ws->utf8,
- (unsigned char *)ebuf.token, ebuf.len)) {
- lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
- (uint8_t *)"bad utf8", 8);
- goto utf8_fail;
- }
-
- /* we are ending partway through utf-8 character? */
- if (!wsi->ws->rx_packet_length && wsi->ws->final &&
- wsi->ws->utf8 && !n) {
- lwsl_info("FINAL utf8 error\n");
- lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
- (uint8_t *)"partial utf8", 12);
-
-utf8_fail:
- lwsl_info("utf8 error\n");
- lwsl_hexdump_info(ebuf.token, ebuf.len);
-
- return -1;
- }
- }
-
- if (wsi->protocol->callback && !wsi->wsistate_pre_close)
- if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
- LWS_CALLBACK_RECEIVE,
- wsi->user_space,
- ebuf.token, ebuf.len))
- return -1;
-
- wsi->ws->first_fragment = 0;
-
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
- __func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
-#endif
-
- return avail; /* how much we used from the input */
-}
-
-
-int
-lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
-{
- int m, bulk = 0;
-
- lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
-
- //lwsl_hexdump_notice(*buf, len);
-
- /* let the rx protocol state machine have as much as it needs */
-
- while (len) {
- /*
- * we were accepting input but now we stopped doing so
- */
- if (wsi->rxflow_bitmap) {
- lwsl_info("%s: doing rxflow\n", __func__);
- lws_rxflow_cache(wsi, *buf, 0, (int)len);
- lwsl_parser("%s: cached %ld\n", __func__, (long)len);
- *buf += len; /* stashing it is taking care of it */
- return 1;
- }
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- if (wsi->ws->rx_draining_ext) {
- lwsl_debug("%s: draining rx ext\n", __func__);
- m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
- if (m < 0)
- return -1;
- continue;
- }
-#endif
-
- /* consume payload bytes efficiently */
- while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
- (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
- wsi->ws->opcode == LWSWSOPC_BINARY_FRAME ||
- wsi->ws->opcode == LWSWSOPC_CONTINUATION) &&
- len) {
- uint8_t *bin = *buf;
-
- bulk = 1;
- m = lws_ws_frame_rest_is_payload(wsi, buf, len);
- assert((int)lws_ptr_diff(*buf, bin) <= (int)len);
- len -= lws_ptr_diff(*buf, bin);
-
- if (!m) {
-
- break;
- }
- if (m < 0) {
- lwsl_info("%s: rest_is_payload bailed\n",
- __func__);
- return -1;
- }
- }
-
- if (!bulk) {
- /* process the byte */
- m = lws_ws_rx_sm(wsi, 0, *(*buf)++);
- len--;
- } else {
- /*
- * We already handled this byte in bulk, just deal
- * with the ramifications
- */
-#if !defined(LWS_WITHOUT_EXTENSIONS)
- lwsl_debug("%s: coming out of bulk with len %d, "
- "wsi->ws->rx_draining_ext %d\n",
- __func__, (int)len,
- wsi->ws->rx_draining_ext);
-#endif
- m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
- ALREADY_PROCESSED_NO_CB, 0);
- }
-
- if (m < 0) {
- lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
- bulk);
-
- return -1;
- }
-
- bulk = 0;
- }
-
- lwsl_debug("%s: exit with %d unused\n", __func__, (int)len);
-
- return 0;
-}