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

Refactor everything around ping / pong handling in ws and h2, so there is instead a protocol-independent validity lws_sul tracking how long it has been since the last exchange that confirms the operation of the network connection in both directions. Clean out periodic role callback and replace the last two role users with discrete lws_sul for each pt.
692 lines
18 KiB
C
692 lines
18 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "private-lib-core.h"
|
|
|
|
/*
|
|
* 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;
|
|
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
|
|
unsigned short close_code;
|
|
unsigned char *pp;
|
|
int handled, m, n;
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
int rx_draining_ext = 0;
|
|
#endif
|
|
|
|
pmdrx.eb_in.token = NULL;
|
|
pmdrx.eb_in.len = 0;
|
|
pmdrx.eb_out.token = NULL;
|
|
pmdrx.eb_out.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);
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
/*
|
|
* set the expectation that we will have to
|
|
* fake up the zlib trailer to the inflator for
|
|
* this frame
|
|
*/
|
|
wsi->ws->pmd_trailer_application = !!(c & 0x40);
|
|
#endif
|
|
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);
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
/*
|
|
* set the expectation that we will have to
|
|
* fake up the zlib trailer to the inflator for
|
|
* this frame
|
|
*/
|
|
wsi->ws->pmd_trailer_application = !!(c & 0x40);
|
|
#endif
|
|
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];
|
|
|
|
/*
|
|
* unmask and collect the payload body in
|
|
* rx_ubuf_head + LWS_PRE
|
|
*/
|
|
|
|
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;
|
|
lwsl_debug("%s: spilling as we have the whole frame\n",
|
|
__func__);
|
|
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 */
|
|
|
|
lwsl_debug("%s: spilling as we filled our rx buffer\n",
|
|
__func__);
|
|
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 = &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("%s: client %p received pong\n", __func__, wsi);
|
|
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
|
|
wsi->ws->rx_ubuf_head);
|
|
|
|
lws_validity_confirmed(wsi);
|
|
/* 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.
|
|
*
|
|
* We have been statefully collecting it in the
|
|
* LWS_RXPS_WS_FRAME_PAYLOAD clause above.
|
|
*
|
|
* It's nicely buffered with the pre-padding taken care of
|
|
* so it can be sent straight out again using lws_write.
|
|
*
|
|
* However, now we have a chunk of it, we want to deal with it
|
|
* all here. Since this may be input to permessage-deflate and
|
|
* there are block limits on that for input and output, we may
|
|
* need to iterate.
|
|
*/
|
|
if (handled)
|
|
goto already_done;
|
|
|
|
pmdrx.eb_in.token = &wsi->ws->rx_ubuf[LWS_PRE];
|
|
pmdrx.eb_in.len = wsi->ws->rx_ubuf_head;
|
|
|
|
/* for the non-pm-deflate case */
|
|
|
|
pmdrx.eb_out = pmdrx.eb_in;
|
|
|
|
lwsl_debug("%s: starting disbursal of %d deframed rx\n",
|
|
__func__, wsi->ws->rx_ubuf_head);
|
|
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
drain_extension:
|
|
#endif
|
|
do {
|
|
|
|
// lwsl_notice("%s: pmdrx.eb_in.len: %d\n", __func__,
|
|
// (int)pmdrx.eb_in.len);
|
|
|
|
n = PMDR_DID_NOTHING;
|
|
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
lwsl_ext("%s: +++ passing %d %p to ext\n", __func__,
|
|
pmdrx.eb_in.len, pmdrx.eb_in.token);
|
|
|
|
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX,
|
|
&pmdrx, 0);
|
|
lwsl_ext("Ext RX returned %d\n", n);
|
|
if (n < 0) {
|
|
wsi->socket_is_permanently_unusable = 1;
|
|
return -1;
|
|
}
|
|
if (n == PMDR_DID_NOTHING)
|
|
break;
|
|
#endif
|
|
lwsl_ext("%s: post inflate ebuf in len %d / out len %d\n",
|
|
__func__, pmdrx.eb_in.len, pmdrx.eb_out.len);
|
|
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
if (rx_draining_ext && !pmdrx.eb_out.len) {
|
|
lwsl_debug(" --- ending drain on 0 read result\n");
|
|
goto already_done;
|
|
}
|
|
|
|
if (n == PMDR_HAS_PENDING) { /* 1 means stuff to drain */
|
|
/* extension had more... main loop will come back */
|
|
lwsl_ext("%s: adding to draining ext list\n",
|
|
__func__);
|
|
lws_add_wsi_to_draining_ext_list(wsi);
|
|
} else {
|
|
lwsl_ext("%s: removing from draining ext list\n",
|
|
__func__);
|
|
lws_remove_wsi_from_draining_ext_list(wsi);
|
|
}
|
|
rx_draining_ext = wsi->ws->rx_draining_ext;
|
|
#endif
|
|
|
|
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
|
|
|
|
if (lws_check_utf8(&wsi->ws->utf8,
|
|
pmdrx.eb_out.token,
|
|
pmdrx.eb_out.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)
|
|
/* if ext not negotiated, going to be UNKNOWN */
|
|
&& (n == PMDR_EMPTY_FINAL || n == PMDR_UNKNOWN)
|
|
#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(pmdrx.eb_out.token,
|
|
pmdrx.eb_out.len);
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (pmdrx.eb_out.len < 0 &&
|
|
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
|
|
goto already_done;
|
|
|
|
if (!pmdrx.eb_out.token)
|
|
goto already_done;
|
|
|
|
pmdrx.eb_out.token[pmdrx.eb_out.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 !defined(LWS_WITHOUT_EXTENSIONS)
|
|
if (n == PMDR_HAS_PENDING)
|
|
/* 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);
|
|
#endif
|
|
|
|
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;
|
|
|
|
/* if pmd not enabled, in == out */
|
|
|
|
if (n == PMDR_DID_NOTHING
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
|| n == PMDR_UNKNOWN
|
|
#endif
|
|
)
|
|
pmdrx.eb_in.len -= pmdrx.eb_out.len;
|
|
|
|
m = wsi->protocol->callback(wsi,
|
|
(enum lws_callback_reasons)callback_action,
|
|
wsi->user_space, pmdrx.eb_out.token,
|
|
pmdrx.eb_out.len);
|
|
|
|
wsi->ws->first_fragment = 0;
|
|
|
|
lwsl_debug("%s: bulk ws rx: inp used %d, output %d\n",
|
|
__func__, wsi->ws->rx_ubuf_head,
|
|
pmdrx.eb_out.len);
|
|
|
|
/* if user code wants to close, let caller know */
|
|
if (m)
|
|
return 1;
|
|
|
|
} while (pmdrx.eb_in.len
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
|| rx_draining_ext
|
|
#endif
|
|
);
|
|
|
|
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;
|
|
}
|
|
|
|
|