Update Files
This commit is contained in:
61
Kinc/Sources/kinc/libs/roles/ws/CMakeLists.txt
Normal file
61
Kinc/Sources/kinc/libs/roles/ws/CMakeLists.txt
Normal file
@ -0,0 +1,61 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 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.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/ws/ops-ws.c)
|
||||
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
roles/ws/client-ws.c
|
||||
roles/ws/client-parser-ws.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
roles/ws/server-ws.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_EXTENSIONS)
|
||||
list(APPEND HDR_PRIVATE
|
||||
roles/ws/ext/extension-permessage-deflate.h)
|
||||
list(APPEND SOURCES
|
||||
roles/ws/ext/extension.c
|
||||
roles/ws/ext/extension-permessage-deflate.c)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
696
Kinc/Sources/kinc/libs/roles/ws/client-parser-ws.c
Normal file
696
Kinc/Sources/kinc/libs/roles/ws/client-parser-ws.c
Normal file
@ -0,0 +1,696 @@
|
||||
/*
|
||||
* 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_wsi_debug(wsi, "doing draining flow");
|
||||
|
||||
goto drain_extension;
|
||||
}
|
||||
#endif
|
||||
|
||||
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->a.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_wsi_info(wsi, "disordered continuation");
|
||||
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:
|
||||
if (wsi->ws->allow_unknown_opcode)
|
||||
break;
|
||||
lwsl_wsi_info(wsi, "illegal opcode");
|
||||
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 && !wsi->ws->allow_reserved_bits) {
|
||||
lwsl_wsi_info(wsi, "illegal rsv bits set");
|
||||
return -1;
|
||||
}
|
||||
wsi->ws->final = !!((c >> 7) & 1);
|
||||
lwsl_wsi_ext(wsi, " This RX frame Final %d",
|
||||
wsi->ws->final);
|
||||
|
||||
if (wsi->ws->owed_a_fin &&
|
||||
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
|
||||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
|
||||
lwsl_wsi_info(wsi, "hey you owed us a FIN");
|
||||
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_wsi_info(wsi, "control msg can't be fragmented");
|
||||
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_wsi_err(wsi, "unknown spec version %02d",
|
||||
wsi->ws->ietf_spec_revision);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN:
|
||||
|
||||
wsi->ws->this_frame_masked = !!(c & 0x80);
|
||||
if (wsi->ws->this_frame_masked)
|
||||
goto server_cannot_mask;
|
||||
|
||||
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 = (size_t)((unsigned int)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_wsi_warn(wsi, "b63 of length must be zero");
|
||||
/* 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_wsi_debug(wsi, "spilling as we have the whole frame");
|
||||
goto spill;
|
||||
}
|
||||
|
||||
/*
|
||||
* if there's no protocol max frame size given, we are
|
||||
* supposed to default to context->pt_serv_buf_size
|
||||
*/
|
||||
if (!wsi->a.protocol->rx_buffer_size &&
|
||||
wsi->ws->rx_ubuf_head != wsi->a.context->pt_serv_buf_size)
|
||||
break;
|
||||
|
||||
if (wsi->a.protocol->rx_buffer_size &&
|
||||
wsi->ws->rx_ubuf_head != wsi->a.protocol->rx_buffer_size)
|
||||
break;
|
||||
|
||||
/* spill because we filled our rx buffer */
|
||||
|
||||
lwsl_wsi_debug(wsi, "spilling as 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 = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
if (lws_check_opt(wsi->a.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_wsi_parser(wsi, "seen server's close ack");
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_wsi_parser(wsi, "client sees server close len = %d",
|
||||
(int)wsi->ws->rx_ubuf_head);
|
||||
if (wsi->ws->rx_ubuf_head >= 2) {
|
||||
close_code = (unsigned short)((pp[0] << 8) | pp[1]);
|
||||
if (close_code < 1000 ||
|
||||
close_code == 1004 ||
|
||||
close_code == 1005 ||
|
||||
close_code == 1006 ||
|
||||
(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->a.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 =
|
||||
(uint8_t)wsi->ws->rx_ubuf_head;
|
||||
|
||||
lwsl_wsi_info(wsi, "scheduling return close as ack");
|
||||
__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_wsi_info(wsi, "received %d byte ping, sending pong",
|
||||
(int)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->pong_pending_flag) {
|
||||
/*
|
||||
* there is already a pending pong payload
|
||||
* we should just log and drop
|
||||
*/
|
||||
lwsl_wsi_parser(wsi, "DROP PING since one pending");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* control packets can only be < 128 bytes long */
|
||||
if (wsi->ws->rx_ubuf_head > 128 - 3) {
|
||||
lwsl_wsi_parser(wsi, "DROP PING payload too large");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* stash the pong payload */
|
||||
memcpy(wsi->ws->pong_payload_buf + LWS_PRE,
|
||||
&wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head);
|
||||
|
||||
wsi->ws->pong_payload_len = (uint8_t)wsi->ws->rx_ubuf_head;
|
||||
wsi->ws->pong_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_wsi_info(wsi, "Received pong");
|
||||
lwsl_hexdump_wsi_debug(wsi, &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_wsi_ext(wsi, "Unhandled ext opc 0x%x", 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 = (int)wsi->ws->rx_ubuf_head;
|
||||
|
||||
/* for the non-pm-deflate case */
|
||||
|
||||
pmdrx.eb_out = pmdrx.eb_in;
|
||||
|
||||
lwsl_wsi_debug(wsi, "starting disbursal of %d deframed rx",
|
||||
(int)wsi->ws->rx_ubuf_head);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
drain_extension:
|
||||
#endif
|
||||
do {
|
||||
|
||||
// lwsl_wsi_notice("pmdrx.eb_in.len: %d",
|
||||
// (int)pmdrx.eb_in.len);
|
||||
|
||||
n = PMDR_DID_NOTHING;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_wsi_ext(wsi, "+++ passing %d %p to ext",
|
||||
pmdrx.eb_in.len, pmdrx.eb_in.token);
|
||||
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX,
|
||||
&pmdrx, 0);
|
||||
lwsl_wsi_ext(wsi, "Ext RX returned %d", n);
|
||||
if (n < 0) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
}
|
||||
if (n == PMDR_DID_NOTHING)
|
||||
/* ie, not PMDR_NOTHING_WE_SHOULD_DO */
|
||||
break;
|
||||
#endif
|
||||
lwsl_wsi_ext(wsi, "post inflate ebuf in len %d / out len %d",
|
||||
pmdrx.eb_in.len, pmdrx.eb_out.len);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (rx_draining_ext && !pmdrx.eb_out.len) {
|
||||
lwsl_wsi_debug(wsi, " --- ending drain on 0 read result");
|
||||
goto already_done;
|
||||
}
|
||||
|
||||
if (n == PMDR_HAS_PENDING) { /* 1 means stuff to drain */
|
||||
/* extension had more... main loop will come back */
|
||||
lwsl_wsi_ext(wsi, "adding to draining ext list");
|
||||
lws_add_wsi_to_draining_ext_list(wsi);
|
||||
} else {
|
||||
lwsl_wsi_ext(wsi, "removing from draining ext list");
|
||||
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,
|
||||
(unsigned int)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_wsi_info(wsi, "FINAL utf8 error");
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"partial utf8", 12);
|
||||
utf8_fail:
|
||||
lwsl_wsi_info(wsi, "utf8 error");
|
||||
lwsl_hexdump_wsi_info(wsi, pmdrx.eb_out.token,
|
||||
(unsigned int)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->a.protocol->callback)
|
||||
goto already_done;
|
||||
|
||||
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
|
||||
lwsl_wsi_info(wsi, "Client doing pong callback");
|
||||
|
||||
#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_NOTHING_WE_SHOULD_DO
|
||||
|| n == PMDR_UNKNOWN
|
||||
#endif
|
||||
)
|
||||
pmdrx.eb_in.len -= pmdrx.eb_out.len;
|
||||
|
||||
m = wsi->a.protocol->callback(wsi,
|
||||
(enum lws_callback_reasons)callback_action,
|
||||
wsi->user_space, pmdrx.eb_out.token,
|
||||
(unsigned int)pmdrx.eb_out.len);
|
||||
|
||||
wsi->ws->first_fragment = 0;
|
||||
|
||||
lwsl_wsi_debug(wsi, "bulk ws rx: inp used %d, output %d",
|
||||
(int)wsi->ws->rx_ubuf_head,
|
||||
(int)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_wsi_err(wsi, "client rx illegal state");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
illegal_ctl_length:
|
||||
lwsl_wsi_warn(wsi, "Control frame asking for extended length is illegal");
|
||||
|
||||
/* kill the connection */
|
||||
return -1;
|
||||
|
||||
server_cannot_mask:
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_PROTOCOL_ERR,
|
||||
(uint8_t *)"srv mask", 8);
|
||||
|
||||
lwsl_wsi_warn(wsi, "Server must not mask");
|
||||
|
||||
/* kill the connection */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
674
Kinc/Sources/kinc/libs/roles/ws/client-ws.c
Normal file
674
Kinc/Sources/kinc/libs/roles/ws/client-ws.c
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* 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 = (char)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_wsi_notice(wsi, "OOM");
|
||||
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 = (uint8_t)v;
|
||||
|
||||
if (i->allow_reserved_bits)
|
||||
wsi->ws->allow_reserved_bits = 1;
|
||||
|
||||
if (i->allow_unknown_opcode)
|
||||
wsi->ws->allow_unknown_opcode = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
int
|
||||
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
unsigned char *bufin = *buf;
|
||||
|
||||
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_wsi_debug(wsi, "hs client feels it has %d in", (int)len);
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lwsl_wsi_debug(wsi, "caching %ld", (long)len);
|
||||
/*
|
||||
* Since we cached the remaining available input, we
|
||||
* can say we "consumed" it.
|
||||
*
|
||||
* But what about the case where the available input
|
||||
* came out of the rxflow cache already? If we are
|
||||
* effectively "putting it back in the cache", we have
|
||||
* to place it at the cache head, not the tail as usual.
|
||||
*/
|
||||
if (lws_rxflow_cache(wsi, *buf, 0, len) ==
|
||||
LWSRXFC_TRIMMED) {
|
||||
/*
|
||||
* we dealt with it by trimming the existing
|
||||
* rxflow cache HEAD to account for what we used.
|
||||
*
|
||||
* indicate we didn't use anything to the caller
|
||||
* so he doesn't do any consumed processing
|
||||
*/
|
||||
lwsl_wsi_info(wsi, "trimming inside rxflow cache");
|
||||
*buf = bufin;
|
||||
} else
|
||||
*buf += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
int m;
|
||||
|
||||
lwsl_wsi_info(wsi, "draining ext");
|
||||
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 by studying what
|
||||
* happened to *buf
|
||||
*/
|
||||
|
||||
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_wsi_info(wsi, "client_rx_sm exited, DROPPING %d",
|
||||
(int)len);
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
// lwsl_wsi_notice(wsi, "finished with %ld", (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
|
||||
*/
|
||||
if (lws_get_random(wsi->a.context, hash, 16) != 16) {
|
||||
lwsl_wsi_err(wsi, "Unable to read from random dev %s",
|
||||
SYSTEM_RANDOM_FILEPATH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* coverity[tainted_scalar] */
|
||||
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->a.vhost->ws.extensions;
|
||||
while (ext && ext->callback) {
|
||||
|
||||
n = wsi->a.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, (unsigned int)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->a.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 = &wsi->a.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_mux_substream) {/* !!! client ws-over-h2 not there yet */
|
||||
lwsl_wsi_warn(wsi, "client ws-over-h2 upgrade not supported yet");
|
||||
*cce = "HS: h2 / ws upgrade unsupported";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (wsi->http.ah->http_response == 401) {
|
||||
lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
|
||||
(long)wsi->http.ah->http_response);
|
||||
*cce = "HS: ws upgrade unauthorized";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (wsi->http.ah->http_response != 101) {
|
||||
lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
|
||||
(long)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_wsi_info(wsi, "no ACCEPT");
|
||||
*cce = "HS: ACCEPT missing";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
|
||||
if (!p) {
|
||||
lwsl_wsi_info(wsi, "no UPGRADE");
|
||||
*cce = "HS: UPGRADE missing";
|
||||
goto bail3;
|
||||
}
|
||||
strtolower(p);
|
||||
if (strcmp(p, "websocket")) {
|
||||
lwsl_wsi_warn(wsi, "got bad Upgrade header '%s'", 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);
|
||||
n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
|
||||
if (n <= 0) /* won't fit, or absent */
|
||||
goto bad_conn_format;
|
||||
ts.len = (unsigned int)n;
|
||||
|
||||
do {
|
||||
e = lws_tokenize(&ts);
|
||||
switch (e) {
|
||||
case LWS_TOKZE_TOKEN:
|
||||
if (!strncasecmp(ts.token, "upgrade", ts.token_len))
|
||||
e = LWS_TOKZE_ENDED;
|
||||
break;
|
||||
|
||||
case LWS_TOKZE_DELIMITER:
|
||||
break;
|
||||
|
||||
default: /* includes ENDED found by the tokenizer itself */
|
||||
bad_conn_format:
|
||||
lwsl_wsi_info(wsi, "malformed connection '%s'", buf);
|
||||
*cce = "HS: UPGRADE malformed";
|
||||
goto bail3;
|
||||
}
|
||||
} while (e > 0);
|
||||
|
||||
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
|
||||
#if defined(_DEBUG)
|
||||
if (!pc)
|
||||
lwsl_wsi_parser(wsi, "lws_client_int_s_hs: no protocol list");
|
||||
else
|
||||
lwsl_wsi_parser(wsi, "lws_client_int_s_hs: protocol list '%s'", pc);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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_wsi_info(wsi, "WSI_TOKEN_PROTOCOL is null");
|
||||
/*
|
||||
* no protocol name to work from, if we don't already have one
|
||||
* default to first protocol
|
||||
*/
|
||||
|
||||
if (wsi->a.protocol) {
|
||||
p = (char *)wsi->a.protocol->name;
|
||||
goto identify_protocol;
|
||||
}
|
||||
|
||||
/* no choice but to use the default protocol */
|
||||
|
||||
n = 0;
|
||||
wsi->a.protocol = &wsi->a.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, (unsigned int)len) &&
|
||||
(pc[len] == ',' || pc[len] == '\0')) {
|
||||
okay = 1;
|
||||
continue;
|
||||
}
|
||||
while (*pc && *pc++ != ',')
|
||||
;
|
||||
while (*pc == ' ')
|
||||
pc++;
|
||||
}
|
||||
|
||||
if (!okay) {
|
||||
lwsl_wsi_info(wsi, "got bad protocol %s", p);
|
||||
*cce = "HS: PROTOCOL malformed";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
identify_protocol:
|
||||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
lws_strncpy(wsi->ws->actual_protocol, p,
|
||||
sizeof(wsi->ws->actual_protocol));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* identify the selected protocol struct and set it
|
||||
*/
|
||||
n = 0;
|
||||
/* keep client connection pre-bound protocol */
|
||||
if (!lwsi_role_client(wsi))
|
||||
wsi->a.protocol = NULL;
|
||||
|
||||
while (n < wsi->a.vhost->count_protocols) {
|
||||
if (!wsi->a.protocol &&
|
||||
strcmp(p, wsi->a.vhost->protocols[n].name) == 0) {
|
||||
wsi->a.protocol = &wsi->a.vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == wsi->a.vhost->count_protocols) { /* no match */
|
||||
/* if server, that's already fatal */
|
||||
if (!lwsi_role_client(wsi)) {
|
||||
lwsl_wsi_info(wsi, "fail protocol %s", p);
|
||||
*cce = "HS: Cannot match protocol";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* for client, find the index of our pre-bound protocol */
|
||||
|
||||
n = 0;
|
||||
while (wsi->a.vhost->protocols[n].callback) {
|
||||
if (wsi->a.protocol && strcmp(wsi->a.protocol->name,
|
||||
wsi->a.vhost->protocols[n].name) == 0) {
|
||||
wsi->a.protocol = &wsi->a.vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!wsi->a.vhost->protocols[n].callback) {
|
||||
if (wsi->a.protocol)
|
||||
lwsl_wsi_err(wsi, "Failed to match protocol %s",
|
||||
wsi->a.protocol->name);
|
||||
else
|
||||
lwsl_wsi_err(wsi, "No protocol on client");
|
||||
*cce = "ws protocol no match";
|
||||
goto bail2;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_wsi_debug(wsi, "Selected protocol %s", wsi->a.protocol ?
|
||||
wsi->a.protocol->name : "no pcol");
|
||||
|
||||
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_wsi_ext(wsi, "no client extensions allowed by server");
|
||||
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, (int)context->pt_serv_buf_size,
|
||||
WSI_TOKEN_EXTENSIONS) < 0) {
|
||||
lwsl_wsi_warn(wsi, "ext list from server failed to copy");
|
||||
*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_wsi_notice(wsi, "checking client ext %s", ext_name);
|
||||
|
||||
n = 0;
|
||||
ext = wsi->a.vhost->ws.extensions;
|
||||
while (ext && ext->callback) {
|
||||
if (strcmp(ext_name, ext->name)) {
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
||||
n = 1;
|
||||
lwsl_wsi_notice(wsi, "instantiating client ext %s", 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_wsi_info(wsi, " ext %s failed construction",
|
||||
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->a.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_wsi_err(wsi, "unable to parse user defaults '%s'",
|
||||
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_wsi_err(wsi, "unable to parse remote def '%s'", 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_wsi_err(wsi, "ext %s rejects server options %s",
|
||||
ext->name, a);
|
||||
*cce = "HS: EXT: Rejects server options";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
wsi->ws->count_act_ext++;
|
||||
|
||||
ext++;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
lwsl_wsi_warn(wsi, "Unknown ext '%s'!", 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_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", 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_wsi_err(wsi, "Problem allocating wsi user mem");
|
||||
*cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/*
|
||||
* we seem to be good to go, give client last chance to check
|
||||
* headers and OK it
|
||||
*/
|
||||
if (wsi->a.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_validity_confirmed(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->a.protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = (int)context->pt_serv_buf_size;
|
||||
n += LWS_PRE;
|
||||
wsi->ws->rx_ubuf = lws_malloc((unsigned int)n + 4 /* 0x0000ffff zlib */,
|
||||
"client frame buffer");
|
||||
if (!wsi->ws->rx_ubuf) {
|
||||
lwsl_wsi_err(wsi, "OOM allocating rx buffer %d", n);
|
||||
*cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
wsi->ws->rx_ubuf_alloc = (unsigned int)n;
|
||||
|
||||
lwsl_wsi_debug(wsi, "handshake OK for protocol %s", wsi->a.protocol->name);
|
||||
|
||||
/* call him back to inform him he is up */
|
||||
|
||||
if (wsi->a.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;
|
||||
}
|
@ -0,0 +1,559 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
#include "extension-permessage-deflate.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define LWS_ZLIB_MEMLEVEL 8
|
||||
|
||||
const struct lws_ext_options lws_ext_pm_deflate_options[] = {
|
||||
/* public RFC7692 settings */
|
||||
{ "server_no_context_takeover", EXTARG_NONE },
|
||||
{ "client_no_context_takeover", EXTARG_NONE },
|
||||
{ "server_max_window_bits", EXTARG_OPT_DEC },
|
||||
{ "client_max_window_bits", EXTARG_OPT_DEC },
|
||||
/* ones only user code can set */
|
||||
{ "rx_buf_size", EXTARG_DEC },
|
||||
{ "tx_buf_size", EXTARG_DEC },
|
||||
{ "compression_level", EXTARG_DEC },
|
||||
{ "mem_level", EXTARG_DEC },
|
||||
{ NULL, 0 }, /* sentinel */
|
||||
};
|
||||
|
||||
static void
|
||||
lws_extension_pmdeflate_restrict_args(struct lws *wsi,
|
||||
struct lws_ext_pm_deflate_priv *priv)
|
||||
{
|
||||
int n, extra;
|
||||
|
||||
/* cap the RX buf at the nearest power of 2 to protocol rx buf */
|
||||
|
||||
n = (int)wsi->a.context->pt_serv_buf_size;
|
||||
if (wsi->a.protocol->rx_buffer_size)
|
||||
n = (int)wsi->a.protocol->rx_buffer_size;
|
||||
|
||||
extra = 7;
|
||||
while (n >= 1 << (extra + 1))
|
||||
extra++;
|
||||
|
||||
if (extra < priv->args[PMD_RX_BUF_PWR2]) {
|
||||
priv->args[PMD_RX_BUF_PWR2] = (unsigned char)extra;
|
||||
lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_extension_callback_pm_deflate(struct lws_context *context,
|
||||
const struct lws_extension *ext,
|
||||
struct lws *wsi,
|
||||
enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct lws_ext_pm_deflate_priv *priv =
|
||||
(struct lws_ext_pm_deflate_priv *)user;
|
||||
struct lws_ext_pm_deflate_rx_ebufs *pmdrx =
|
||||
(struct lws_ext_pm_deflate_rx_ebufs *)in;
|
||||
struct lws_ext_option_arg *oa;
|
||||
int n, ret = 0, was_fin = 0, m;
|
||||
unsigned int pen = 0;
|
||||
int penbits = 0;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_EXT_CB_NAMED_OPTION_SET:
|
||||
oa = in;
|
||||
if (!oa->option_name)
|
||||
break;
|
||||
lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
|
||||
n++)
|
||||
if (!strcmp(lws_ext_pm_deflate_options[n].name,
|
||||
oa->option_name))
|
||||
break;
|
||||
|
||||
if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
|
||||
break;
|
||||
oa->option_index = n;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWS_EXT_CB_OPTION_SET:
|
||||
oa = in;
|
||||
lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
|
||||
oa->option_index, oa->start, oa->len);
|
||||
if (oa->start)
|
||||
priv->args[oa->option_index] = (unsigned char)atoi(oa->start);
|
||||
else
|
||||
priv->args[oa->option_index] = 1;
|
||||
|
||||
if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
|
||||
|
||||
lws_extension_pmdeflate_restrict_args(wsi, priv);
|
||||
break;
|
||||
|
||||
case LWS_EXT_CB_OPTION_CONFIRM:
|
||||
if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
|
||||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_EXT_CB_CLIENT_CONSTRUCT:
|
||||
case LWS_EXT_CB_CONSTRUCT:
|
||||
|
||||
n = (int)context->pt_serv_buf_size;
|
||||
if (wsi->a.protocol->rx_buffer_size)
|
||||
n = (int)wsi->a.protocol->rx_buffer_size;
|
||||
|
||||
if (n < 128) {
|
||||
lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
|
||||
"(%s) to have an RX buffer >= 128",
|
||||
wsi->a.protocol->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* fill in **user */
|
||||
priv = lws_zalloc(sizeof(*priv), "pmd priv");
|
||||
*((void **)user) = priv;
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
|
||||
/* fill in pointer to options list */
|
||||
if (in)
|
||||
*((const struct lws_ext_options **)in) =
|
||||
lws_ext_pm_deflate_options;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWS_EXT_CB_OPTION_DEFAULT:
|
||||
|
||||
/* set the public, RFC7692 defaults... */
|
||||
|
||||
priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
|
||||
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
|
||||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
|
||||
|
||||
/* ...and the ones the user code can override */
|
||||
|
||||
priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
|
||||
priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
|
||||
priv->args[PMD_COMP_LEVEL] = 1;
|
||||
priv->args[PMD_MEM_LEVEL] = 8;
|
||||
|
||||
lws_extension_pmdeflate_restrict_args(wsi, priv);
|
||||
break;
|
||||
|
||||
case LWS_EXT_CB_DESTROY:
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
|
||||
lws_free(priv->buf_rx_inflated);
|
||||
lws_free(priv->buf_tx_deflated);
|
||||
if (priv->rx_init)
|
||||
(void)inflateEnd(&priv->rx);
|
||||
if (priv->tx_init)
|
||||
(void)deflateEnd(&priv->tx);
|
||||
lws_free(priv);
|
||||
|
||||
return ret;
|
||||
|
||||
|
||||
case LWS_EXT_CB_PAYLOAD_RX:
|
||||
/*
|
||||
* ie, we are INFLATING
|
||||
*/
|
||||
lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
|
||||
pmdrx->eb_in.len, priv->rx.avail_in);
|
||||
|
||||
/*
|
||||
* If this frame is not marked as compressed,
|
||||
* there is nothing we should do with it
|
||||
*/
|
||||
|
||||
if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
|
||||
/*
|
||||
* This is a bit different than DID_NOTHING... we have
|
||||
* identified using ext-private bits in the packet, or
|
||||
* by it being a control fragment that we SHOULD not do
|
||||
* anything to it, parent should continue as if we
|
||||
* processed it
|
||||
*/
|
||||
return PMDR_NOTHING_WE_SHOULD_DO;
|
||||
|
||||
/*
|
||||
* we shouldn't come back in here if we already applied the
|
||||
* trailer for this compressed packet
|
||||
*/
|
||||
if (!wsi->ws->pmd_trailer_application)
|
||||
return PMDR_DID_NOTHING;
|
||||
|
||||
pmdrx->eb_out.len = 0;
|
||||
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
|
||||
"existing avail in %d, pkt fin: %d",
|
||||
pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
|
||||
|
||||
/* if needed, initialize the inflator */
|
||||
|
||||
if (!priv->rx_init) {
|
||||
if (inflateInit2(&priv->rx,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
|
||||
lwsl_wsi_err(wsi, "iniflateInit failed");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
priv->rx_init = 1;
|
||||
if (!priv->buf_rx_inflated)
|
||||
priv->buf_rx_inflated = lws_malloc(
|
||||
(unsigned int)(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_RX_BUF_PWR2])),
|
||||
"pmd rx inflate buf");
|
||||
if (!priv->buf_rx_inflated) {
|
||||
lwsl_wsi_err(wsi, "OOM");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* don't give us new input while we still work through
|
||||
* the last input
|
||||
*/
|
||||
|
||||
if (priv->rx.avail_in && pmdrx->eb_in.token &&
|
||||
pmdrx->eb_in.len) {
|
||||
lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
|
||||
priv->rx.avail_in);
|
||||
// assert(0);
|
||||
}
|
||||
#endif
|
||||
if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
|
||||
priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
||||
priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
|
||||
}
|
||||
|
||||
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
|
||||
pmdrx->eb_out.token = priv->rx.next_out;
|
||||
priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
|
||||
|
||||
/* so... if...
|
||||
*
|
||||
* - he has no remaining input content for this message, and
|
||||
*
|
||||
* - and this is the final fragment, and
|
||||
*
|
||||
* - we used everything that could be drained on the input side
|
||||
*
|
||||
* ...then put back the 00 00 FF FF the sender stripped as our
|
||||
* input to zlib
|
||||
*/
|
||||
if (!priv->rx.avail_in &&
|
||||
wsi->ws->final &&
|
||||
!wsi->ws->rx_packet_length &&
|
||||
wsi->ws->pmd_trailer_application) {
|
||||
lwsl_wsi_ext(wsi, "trailer apply 1");
|
||||
was_fin = 1;
|
||||
wsi->ws->pmd_trailer_application = 0;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
}
|
||||
|
||||
/*
|
||||
* if after all that there's nothing pending and nothing to give
|
||||
* him right now, bail without having done anything
|
||||
*/
|
||||
|
||||
if (!priv->rx.avail_in)
|
||||
return PMDR_DID_NOTHING;
|
||||
|
||||
n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
|
||||
lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
|
||||
priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
|
||||
n, priv->rx.msg);
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* track how much input was used, and advance it
|
||||
*/
|
||||
|
||||
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
||||
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
|
||||
pmdrx->eb_in.len = (int)priv->rx.avail_in;
|
||||
|
||||
lwsl_wsi_debug(wsi, "%d %d %d %d %d",
|
||||
priv->rx.avail_in,
|
||||
wsi->ws->final,
|
||||
(int)wsi->ws->rx_packet_length,
|
||||
was_fin,
|
||||
wsi->ws->pmd_trailer_application);
|
||||
|
||||
if (!priv->rx.avail_in &&
|
||||
wsi->ws->final &&
|
||||
!wsi->ws->rx_packet_length &&
|
||||
!was_fin &&
|
||||
wsi->ws->pmd_trailer_application) {
|
||||
lwsl_wsi_ext(wsi, "RX trailer apply 2");
|
||||
|
||||
/* we overallocated just for this situation where
|
||||
* we might issue something */
|
||||
priv->rx.avail_out += 5;
|
||||
|
||||
was_fin = 1;
|
||||
wsi->ws->pmd_trailer_application = 0;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
n = inflate(&priv->rx, Z_SYNC_FLUSH);
|
||||
lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
|
||||
n, priv->rx.avail_in, priv->rx.avail_out);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_wsi_info(wsi, "zlib error inflate %d: %s",
|
||||
n, priv->rx.msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(priv->rx.avail_out);
|
||||
}
|
||||
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
|
||||
|
||||
lwsl_wsi_ext(wsi, " RX leaving with new effbuff len %d, "
|
||||
"rx.avail_in=%d, TOTAL RX since FIN %lu",
|
||||
pmdrx->eb_out.len, priv->rx.avail_in,
|
||||
(unsigned long)priv->count_rx_between_fin);
|
||||
|
||||
if (was_fin) {
|
||||
lwsl_wsi_ext(wsi, "was_fin");
|
||||
priv->count_rx_between_fin = 0;
|
||||
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
|
||||
(void)inflateEnd(&priv->rx);
|
||||
priv->rx_init = 0;
|
||||
}
|
||||
|
||||
return PMDR_EMPTY_FINAL;
|
||||
}
|
||||
|
||||
if (priv->rx.avail_in)
|
||||
return PMDR_HAS_PENDING;
|
||||
|
||||
return PMDR_EMPTY_NONFINAL;
|
||||
|
||||
case LWS_EXT_CB_PAYLOAD_TX:
|
||||
|
||||
/*
|
||||
* ie, we are DEFLATING
|
||||
*
|
||||
* initialize us if needed
|
||||
*/
|
||||
|
||||
if (!priv->tx_init) {
|
||||
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
|
||||
Z_DEFLATED,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
|
||||
(wsi->a.vhost->listen_port <= 0)],
|
||||
priv->args[PMD_MEM_LEVEL],
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (n != Z_OK) {
|
||||
lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
priv->tx_init = 1;
|
||||
}
|
||||
|
||||
if (!priv->buf_tx_deflated)
|
||||
priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_TX_BUF_PWR2])),
|
||||
"pmd tx deflate buf");
|
||||
if (!priv->buf_tx_deflated) {
|
||||
lwsl_wsi_err(wsi, "OOM");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
/* hook us up with any deflated input that the caller has */
|
||||
|
||||
if (pmdrx->eb_in.token) {
|
||||
|
||||
assert(!priv->tx.avail_in);
|
||||
|
||||
priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
|
||||
lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
|
||||
"TOTAL TX since FIN: %d",
|
||||
pmdrx->eb_in.len,
|
||||
(int)priv->count_tx_between_fin);
|
||||
priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
||||
priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
|
||||
}
|
||||
|
||||
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
|
||||
pmdrx->eb_out.token = priv->tx.next_out;
|
||||
priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
|
||||
|
||||
pen = 0;
|
||||
penbits = 0;
|
||||
deflatePending(&priv->tx, &pen, &penbits);
|
||||
pen = pen | (unsigned int)penbits;
|
||||
|
||||
if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
|
||||
lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
|
||||
|
||||
if (!pen)
|
||||
return PMDR_DID_NOTHING;
|
||||
}
|
||||
|
||||
m = Z_NO_FLUSH;
|
||||
if (!(len & LWS_WRITE_NO_FIN)) {
|
||||
lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
|
||||
(int)wsi->ws->rx_packet_length);
|
||||
m = Z_SYNC_FLUSH;
|
||||
}
|
||||
|
||||
n = deflate(&priv->tx, m);
|
||||
if (n == Z_STREAM_ERROR) {
|
||||
lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
|
||||
|
||||
lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
|
||||
(unsigned int)len);
|
||||
|
||||
if ((len & 0xf) == LWS_WRITE_TEXT)
|
||||
priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
|
||||
if ((len & 0xf) == LWS_WRITE_BINARY)
|
||||
priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
|
||||
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
|
||||
if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
|
||||
pmdrx->eb_out.len < 4) {
|
||||
lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
|
||||
(int)pmdrx->eb_out.len);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (!(len & LWS_WRITE_NO_FIN) &&
|
||||
m == Z_SYNC_FLUSH &&
|
||||
!pen &&
|
||||
pmdrx->eb_out.len >= 4) {
|
||||
// lwsl_wsi_err(wsi, "Trimming 4 from end of write");
|
||||
priv->tx.next_out -= 4;
|
||||
priv->tx.avail_out += 4;
|
||||
priv->count_tx_between_fin = 0;
|
||||
|
||||
assert(priv->tx.next_out[0] == 0x00 &&
|
||||
priv->tx.next_out[1] == 0x00 &&
|
||||
priv->tx.next_out[2] == 0xff &&
|
||||
priv->tx.next_out[3] == 0xff);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* track how much input was used and advance it
|
||||
*/
|
||||
|
||||
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
||||
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
|
||||
pmdrx->eb_in.len = (int)priv->tx.avail_in;
|
||||
|
||||
priv->compressed_out = 1;
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
|
||||
lwsl_wsi_ext(wsi, " TX rewritten with new eb_in len %d, "
|
||||
"eb_out len %d, deflatePending %d",
|
||||
pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
|
||||
|
||||
if (pmdrx->eb_in.len || pen)
|
||||
return PMDR_HAS_PENDING;
|
||||
|
||||
if (!(len & LWS_WRITE_NO_FIN))
|
||||
return PMDR_EMPTY_FINAL;
|
||||
|
||||
return PMDR_EMPTY_NONFINAL;
|
||||
|
||||
case LWS_EXT_CB_PACKET_TX_PRESEND:
|
||||
if (!priv->compressed_out)
|
||||
break;
|
||||
priv->compressed_out = 0;
|
||||
|
||||
/*
|
||||
* we may have not produced any output for the actual "first"
|
||||
* write... in that case, we need to fix up the inappropriate
|
||||
* use of CONTINUATION when the first real write does come.
|
||||
*/
|
||||
if (priv->tx_first_frame_type & 0xf) {
|
||||
*pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
|
||||
((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
|
||||
/*
|
||||
* We have now written the "first" fragment, only
|
||||
* do that once
|
||||
*/
|
||||
priv->tx_first_frame_type = 0;
|
||||
}
|
||||
|
||||
n = *(pmdrx->eb_in.token) & 15;
|
||||
|
||||
/* set RSV1, but not on CONTINUATION */
|
||||
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
|
||||
*pmdrx->eb_in.token |= 0x40;
|
||||
|
||||
lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
|
||||
((*pmdrx->eb_in.token) & 0xff),
|
||||
pmdrx->eb_in.len);
|
||||
|
||||
if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
|
||||
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
|
||||
(void)deflateEnd(&priv->tx);
|
||||
priv->tx_init = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_MINIZ)
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
|
||||
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
|
||||
|
||||
enum arg_indexes {
|
||||
PMD_SERVER_NO_CONTEXT_TAKEOVER,
|
||||
PMD_CLIENT_NO_CONTEXT_TAKEOVER,
|
||||
PMD_SERVER_MAX_WINDOW_BITS,
|
||||
PMD_CLIENT_MAX_WINDOW_BITS,
|
||||
PMD_RX_BUF_PWR2,
|
||||
PMD_TX_BUF_PWR2,
|
||||
PMD_COMP_LEVEL,
|
||||
PMD_MEM_LEVEL,
|
||||
|
||||
PMD_ARG_COUNT
|
||||
};
|
||||
|
||||
struct lws_ext_pm_deflate_priv {
|
||||
z_stream rx;
|
||||
z_stream tx;
|
||||
|
||||
unsigned char *buf_rx_inflated; /* RX inflated output buffer */
|
||||
unsigned char *buf_tx_deflated; /* TX deflated output buffer */
|
||||
|
||||
unsigned char *buf_tx_holding;
|
||||
|
||||
size_t count_rx_between_fin;
|
||||
size_t count_tx_between_fin;
|
||||
|
||||
size_t len_tx_holding;
|
||||
|
||||
unsigned char args[PMD_ARG_COUNT];
|
||||
|
||||
unsigned char tx_first_frame_type;
|
||||
|
||||
unsigned char tx_init:1;
|
||||
unsigned char rx_init:1;
|
||||
unsigned char compressed_out:1;
|
||||
};
|
||||
|
382
Kinc/Sources/kinc/libs/roles/ws/ext/extension.c
Normal file
382
Kinc/Sources/kinc/libs/roles/ws/ext/extension.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#include "extension-permessage-deflate.h"
|
||||
|
||||
void
|
||||
lws_context_init_extensions(const struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
{
|
||||
lwsl_cx_info(context, " LWS_MAX_EXTENSIONS_ACTIVE: %u", LWS_MAX_EXTENSIONS_ACTIVE);
|
||||
}
|
||||
|
||||
enum lws_ext_option_parser_states {
|
||||
LEAPS_SEEK_NAME,
|
||||
LEAPS_EAT_NAME,
|
||||
LEAPS_SEEK_VAL,
|
||||
LEAPS_EAT_DEC,
|
||||
LEAPS_SEEK_ARG_TERM
|
||||
};
|
||||
|
||||
int
|
||||
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
|
||||
void *ext_user, const struct lws_ext_options *opts,
|
||||
const char *in, int len)
|
||||
{
|
||||
enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
|
||||
unsigned int match_map = 0, n, m, w = 0, count_options = 0,
|
||||
pending_close_quote = 0;
|
||||
struct lws_ext_option_arg oa;
|
||||
|
||||
oa.option_name = NULL;
|
||||
|
||||
while (opts[count_options].name)
|
||||
count_options++;
|
||||
while (len) {
|
||||
lwsl_wsi_ext(wsi, "'%c' %d", *in, leap);
|
||||
switch (leap) {
|
||||
case LEAPS_SEEK_NAME:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
match_map = (unsigned int)(1 << count_options) - 1;
|
||||
leap = LEAPS_EAT_NAME;
|
||||
w = 0;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LEAPS_EAT_NAME:
|
||||
oa.start = NULL;
|
||||
oa.len = 0;
|
||||
m = match_map;
|
||||
n = 0;
|
||||
pending_close_quote = 0;
|
||||
while (m) {
|
||||
if (!(m & 1)) {
|
||||
m >>= 1;
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
lwsl_wsi_ext(wsi, " m=%d, n=%d, w=%d", m, n, w);
|
||||
|
||||
if (*in == opts[n].name[w]) {
|
||||
if (!opts[n].name[w + 1]) {
|
||||
oa.option_index = (int)n;
|
||||
lwsl_wsi_ext(wsi, "hit %d",
|
||||
oa.option_index);
|
||||
leap = LEAPS_SEEK_VAL;
|
||||
if (len == 1)
|
||||
goto set_arg;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
match_map &= (unsigned int)~(1 << n);
|
||||
if (!match_map) {
|
||||
lwsl_wsi_ext(wsi, "empty match map");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
m >>= 1;
|
||||
n++;
|
||||
}
|
||||
w++;
|
||||
break;
|
||||
case LEAPS_SEEK_VAL:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
if (*in == ';' || len == 1) { /* ie,nonoptional */
|
||||
if (opts[oa.option_index].type == EXTARG_DEC)
|
||||
return -1;
|
||||
leap = LEAPS_SEEK_NAME;
|
||||
goto set_arg;
|
||||
}
|
||||
if (*in == '=') {
|
||||
w = 0;
|
||||
pending_close_quote = 0;
|
||||
if (opts[oa.option_index].type == EXTARG_NONE)
|
||||
return -1;
|
||||
|
||||
leap = LEAPS_EAT_DEC;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
|
||||
case LEAPS_EAT_DEC:
|
||||
if (*in >= '0' && *in <= '9') {
|
||||
if (!w)
|
||||
oa.start = in;
|
||||
w++;
|
||||
if (len != 1)
|
||||
break;
|
||||
}
|
||||
if (!w && *in =='"') {
|
||||
pending_close_quote = 1;
|
||||
break;
|
||||
}
|
||||
if (!w)
|
||||
return -1;
|
||||
if (pending_close_quote && *in != '"' && len != 1)
|
||||
return -1;
|
||||
leap = LEAPS_SEEK_ARG_TERM;
|
||||
if (oa.start)
|
||||
oa.len = lws_ptr_diff(in, oa.start);
|
||||
if (len == 1)
|
||||
oa.len++;
|
||||
|
||||
set_arg:
|
||||
ext->callback(lws_get_context(wsi),
|
||||
ext, wsi, LWS_EXT_CB_OPTION_SET,
|
||||
ext_user, (char *)&oa, 0);
|
||||
if (len == 1)
|
||||
break;
|
||||
if (pending_close_quote && *in == '"')
|
||||
break;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LEAPS_SEEK_ARG_TERM:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ';') {
|
||||
leap = LEAPS_SEEK_NAME;
|
||||
break;
|
||||
}
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
in++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
|
||||
|
||||
int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
|
||||
{
|
||||
int n, m, handled = 0;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < wsi->ws->count_act_ext; n++) {
|
||||
m = wsi->ws->active_extensions[n]->callback(
|
||||
lws_get_context(wsi), wsi->ws->active_extensions[n],
|
||||
wsi, (enum lws_extension_callback_reasons)reason, wsi->ws->act_ext_user[n], arg, (size_t)len);
|
||||
if (m < 0) {
|
||||
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
|
||||
wsi->ws->active_extensions[n]->name, reason);
|
||||
return -1;
|
||||
}
|
||||
/* valgrind... */
|
||||
if (reason == LWS_EXT_CB_DESTROY)
|
||||
wsi->ws->act_ext_user[n] = NULL;
|
||||
if (m > handled)
|
||||
handled = m;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
|
||||
int reason, void *arg, int len)
|
||||
{
|
||||
int n = 0, m, handled = 0;
|
||||
const struct lws_extension *ext;
|
||||
|
||||
if (!wsi || !wsi->a.vhost || !wsi->ws)
|
||||
return 0;
|
||||
|
||||
ext = wsi->a.vhost->ws.extensions;
|
||||
|
||||
while (ext && ext->callback && !handled) {
|
||||
m = ext->callback(context, ext, wsi, (enum lws_extension_callback_reasons)reason,
|
||||
(void *)(lws_intptr_t)n, arg, (size_t)len);
|
||||
if (m < 0) {
|
||||
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
|
||||
wsi->ws->active_extensions[n]->name, reason);
|
||||
return -1;
|
||||
}
|
||||
if (m)
|
||||
handled = 1;
|
||||
|
||||
ext++;
|
||||
n++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
|
||||
{
|
||||
struct lws_tokens ebuf;
|
||||
int ret, m, n = 0;
|
||||
|
||||
ebuf.token = buf;
|
||||
ebuf.len = (int)len;
|
||||
|
||||
/*
|
||||
* while we have original buf to spill ourselves, or extensions report
|
||||
* more in their pipeline
|
||||
*/
|
||||
|
||||
ret = 1;
|
||||
while (ret == 1) {
|
||||
|
||||
/* default to nobody has more to spill */
|
||||
|
||||
ret = 0;
|
||||
|
||||
/* show every extension the new incoming data */
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
|
||||
&ebuf, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */
|
||||
ret = 1;
|
||||
|
||||
if (buf != ebuf.token)
|
||||
/*
|
||||
* extension recreated it:
|
||||
* need to buffer this if not all sent
|
||||
*/
|
||||
wsi->ws->clean_buffer = 0;
|
||||
|
||||
/* assuming they left us something to send, send it */
|
||||
|
||||
if (ebuf.len) {
|
||||
n = lws_issue_raw(wsi, ebuf.token, (size_t)ebuf.len);
|
||||
if (n < 0) {
|
||||
lwsl_wsi_info(wsi, "closing from ext access");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* always either sent it all or privately buffered */
|
||||
if (wsi->ws->clean_buffer)
|
||||
len = (size_t)n;
|
||||
|
||||
lwsl_wsi_ext(wsi, "written %d bytes to client", n);
|
||||
}
|
||||
|
||||
/* no extension has more to spill? Then we can go */
|
||||
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
/* we used up what we had */
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
/*
|
||||
* Did that leave the pipe choked?
|
||||
* Or we had to hold on to some of it?
|
||||
*/
|
||||
|
||||
if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
|
||||
/* no we could add more, lets's do that */
|
||||
continue;
|
||||
|
||||
lwsl_wsi_debug(wsi, "choked");
|
||||
|
||||
/*
|
||||
* Yes, he's choked. Don't spill the rest now get a callback
|
||||
* when he is ready to send and take care of it there
|
||||
*/
|
||||
lws_callback_on_writable(wsi);
|
||||
wsi->ws->extension_data_pending = 1;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
int
|
||||
lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
|
||||
void *v, size_t len)
|
||||
{
|
||||
struct lws_context *context = wsi->a.context;
|
||||
int n, handled = 0;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
/* maybe an extension will take care of it for us */
|
||||
|
||||
for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
|
||||
if (!wsi->ws->active_extensions[n]->callback)
|
||||
continue;
|
||||
|
||||
handled |= wsi->ws->active_extensions[n]->callback(context,
|
||||
wsi->ws->active_extensions[n], wsi,
|
||||
r, wsi->ws->act_ext_user[n], v, len);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
int
|
||||
lws_set_extension_option(struct lws *wsi, const char *ext_name,
|
||||
const char *opt_name, const char *opt_val)
|
||||
{
|
||||
struct lws_ext_option_arg oa;
|
||||
int idx = 0;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
/* first identify if the ext is active on this wsi */
|
||||
while (idx < wsi->ws->count_act_ext &&
|
||||
strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
|
||||
idx++;
|
||||
|
||||
if (idx == wsi->ws->count_act_ext)
|
||||
return -1; /* request ext not active on this wsi */
|
||||
|
||||
oa.option_name = opt_name;
|
||||
oa.option_index = 0;
|
||||
oa.start = opt_val;
|
||||
oa.len = 0;
|
||||
|
||||
return wsi->ws->active_extensions[idx]->callback(wsi->a.context,
|
||||
wsi->ws->active_extensions[idx], wsi,
|
||||
LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
|
||||
&oa, 0);
|
||||
}
|
2144
Kinc/Sources/kinc/libs/roles/ws/ops-ws.c
Normal file
2144
Kinc/Sources/kinc/libs/roles/ws/ops-ws.c
Normal file
File diff suppressed because it is too large
Load Diff
202
Kinc/Sources/kinc/libs/roles/ws/private-lib-roles-ws.h
Normal file
202
Kinc/Sources/kinc/libs/roles/ws/private-lib-roles-ws.h
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_ROLE_WS
|
||||
*/
|
||||
|
||||
extern const 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
|
||||
|
||||
#define PAYLOAD_BUF_SIZE 128 - 3 + LWS_PRE
|
||||
|
||||
struct _lws_websocket_related {
|
||||
unsigned 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
|
||||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
struct lws_dll2_owner proxy_owner;
|
||||
char actual_protocol[16];
|
||||
size_t proxy_buffered;
|
||||
#endif
|
||||
|
||||
/* Also used for close content... control opcode == < 128 */
|
||||
uint8_t ping_payload_buf[PAYLOAD_BUF_SIZE];
|
||||
uint8_t pong_payload_buf[PAYLOAD_BUF_SIZE];
|
||||
|
||||
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 pong_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;
|
||||
unsigned int pmd_trailer_application:1;
|
||||
#endif
|
||||
|
||||
uint8_t mask[4];
|
||||
|
||||
size_t rx_packet_length;
|
||||
uint32_t rx_ubuf_head;
|
||||
uint32_t rx_ubuf_alloc;
|
||||
|
||||
uint8_t pong_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;
|
||||
uint8_t allow_reserved_bits;
|
||||
uint8_t allow_unknown_opcode;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
uint8_t count_act_ext;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* we need to separately track what's happening with both compressed rx in
|
||||
* and with inflated rx out that will be passed to the user code
|
||||
*/
|
||||
|
||||
struct lws_ext_pm_deflate_rx_ebufs {
|
||||
struct lws_tokens eb_in;
|
||||
struct lws_tokens eb_out;
|
||||
};
|
||||
|
||||
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);
|
||||
LWS_EXTERN int
|
||||
lws_extension_server_handshake(struct lws *wsi, char **p, int budget);
|
||||
#endif
|
||||
|
||||
int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi);
|
||||
int
|
||||
lws_process_ws_upgrade(struct lws *wsi);
|
||||
|
||||
int
|
||||
lws_process_ws_upgrade2(struct lws *wsi);
|
||||
|
||||
extern const struct lws_protocols lws_ws_proxy;
|
||||
|
||||
int
|
||||
lws_server_init_wsi_for_ws(struct lws *wsi);
|
||||
|
||||
void
|
||||
lws_sul_wsping_cb(lws_sorted_usec_list_t *sul);
|
1108
Kinc/Sources/kinc/libs/roles/ws/server-ws.c
Normal file
1108
Kinc/Sources/kinc/libs/roles/ws/server-ws.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user