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

Protocol list is no longer a simple sentinel-terminated array but composed at vhost creation time in many cases. Use the vhost's count of how many protocols it has rather than seeking up to the sentinel.
690 lines
17 KiB
C
690 lines
17 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>
|
|
|
|
/*
|
|
* In-place str to lower case
|
|
*/
|
|
|
|
static void
|
|
strtolower(char *s)
|
|
{
|
|
while (*s) {
|
|
#ifdef LWS_PLAT_OPTEE
|
|
int tolower_optee(int c);
|
|
*s = tolower_optee((int)*s);
|
|
#else
|
|
*s = tolower((int)*s);
|
|
#endif
|
|
s++;
|
|
}
|
|
}
|
|
|
|
int
|
|
lws_create_client_ws_object(const struct lws_client_connect_info *i,
|
|
struct lws *wsi)
|
|
{
|
|
int v = SPEC_LATEST_SUPPORTED;
|
|
|
|
/* allocate the ws struct for the wsi */
|
|
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
|
|
if (!wsi->ws) {
|
|
lwsl_notice("OOM\n");
|
|
return 1;
|
|
}
|
|
|
|
/* -1 means just use latest supported */
|
|
if (i->ietf_version_or_minus_one != -1 &&
|
|
i->ietf_version_or_minus_one)
|
|
v = i->ietf_version_or_minus_one;
|
|
|
|
wsi->ws->ietf_spec_revision = v;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(LWS_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_debug("%s: hs client feels it has %d in\n", __func__, (int)len);
|
|
|
|
while (len) {
|
|
/*
|
|
* we were accepting input but now we stopped doing so
|
|
*/
|
|
if (lws_is_flowcontrolled(wsi)) {
|
|
lwsl_debug("%s: caching %ld\n", __func__, (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, (int)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_info("%s: trimming inside rxflow cache\n",
|
|
__func__);
|
|
*buf = bufin;
|
|
} else
|
|
*buf += len;
|
|
|
|
return 0;
|
|
}
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
if (wsi->ws->rx_draining_ext) {
|
|
int m;
|
|
|
|
lwsl_info("%s: draining ext\n", __func__);
|
|
if (lwsi_role_client(wsi))
|
|
m = lws_ws_client_rx_sm(wsi, 0);
|
|
else
|
|
m = lws_ws_rx_sm(wsi, 0, 0);
|
|
if (m < 0)
|
|
return -1;
|
|
continue;
|
|
}
|
|
#endif
|
|
/*
|
|
* caller will account for buflist usage by studying what
|
|
* happened to *buf
|
|
*/
|
|
|
|
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
|
|
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
|
|
__func__, (int)len);
|
|
return -1;
|
|
}
|
|
len--;
|
|
}
|
|
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
char *
|
|
lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1)
|
|
{
|
|
char buf[128], hash[20], key_b64[40];
|
|
int n;
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
const struct lws_extension *ext;
|
|
int ext_count = 0;
|
|
#endif
|
|
|
|
/*
|
|
* create the random key
|
|
*/
|
|
n = lws_get_random(wsi->context, hash, 16);
|
|
if (n != 16) {
|
|
lwsl_err("Unable to read from random dev %s\n",
|
|
SYSTEM_RANDOM_FILEPATH);
|
|
return NULL;
|
|
}
|
|
|
|
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
|
|
|
|
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
|
|
"Connection: %sUpgrade\x0d\x0a"
|
|
"Sec-WebSocket-Key: ", conn1);
|
|
strcpy(p, key_b64);
|
|
p += strlen(key_b64);
|
|
p += sprintf(p, "\x0d\x0a");
|
|
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
|
|
p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
|
|
lws_hdr_simple_ptr(wsi,
|
|
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
|
|
|
|
/* tell the server what extensions we could support */
|
|
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
ext = wsi->vhost->ws.extensions;
|
|
while (ext && ext->callback) {
|
|
|
|
n = wsi->vhost->protocols[0].callback(wsi,
|
|
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
|
|
wsi->user_space, (char *)ext->name, 0);
|
|
|
|
/*
|
|
* zero return from callback means go ahead and allow
|
|
* the extension, it's what we get if the callback is
|
|
* unhandled
|
|
*/
|
|
|
|
if (n) {
|
|
ext++;
|
|
continue;
|
|
}
|
|
|
|
/* apply it */
|
|
|
|
if (ext_count)
|
|
*p++ = ',';
|
|
else
|
|
p += sprintf(p, "Sec-WebSocket-Extensions: ");
|
|
p += sprintf(p, "%s", ext->client_offer);
|
|
ext_count++;
|
|
|
|
ext++;
|
|
}
|
|
if (ext_count)
|
|
p += sprintf(p, "\x0d\x0a");
|
|
#endif
|
|
|
|
if (wsi->ws->ietf_spec_revision)
|
|
p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
|
|
wsi->ws->ietf_spec_revision);
|
|
|
|
/* prepare the expected server accept response */
|
|
|
|
key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
|
|
n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
|
|
key_b64);
|
|
|
|
lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
|
|
|
|
lws_b64_encode_string(hash, 20,
|
|
wsi->http.ah->initial_handshake_hash_base64,
|
|
sizeof(wsi->http.ah->initial_handshake_hash_base64));
|
|
|
|
return p;
|
|
}
|
|
|
|
int
|
|
lws_client_ws_upgrade(struct lws *wsi, const char **cce)
|
|
{
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
|
struct lws_context *context = wsi->context;
|
|
struct lws_tokenize ts;
|
|
int n, len, okay = 0;
|
|
lws_tokenize_elem e;
|
|
char *p, buf[64];
|
|
const char *pc;
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
char *sb = (char *)&pt->serv_buf[0];
|
|
const struct lws_ext_options *opts;
|
|
const struct lws_extension *ext;
|
|
char ext_name[128];
|
|
const char *c, *a;
|
|
int more = 1;
|
|
char ignore;
|
|
#endif
|
|
|
|
if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
|
|
lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n",
|
|
__func__);
|
|
*cce = "HS: h2 / ws upgrade unsupported";
|
|
goto bail3;
|
|
}
|
|
|
|
if (wsi->http.ah->http_response == 401) {
|
|
lwsl_warn(
|
|
"lws_client_handshake: got bad HTTP response '%d'\n",
|
|
wsi->http.ah->http_response);
|
|
*cce = "HS: ws upgrade unauthorized";
|
|
goto bail3;
|
|
}
|
|
|
|
if (wsi->http.ah->http_response != 101) {
|
|
lwsl_warn(
|
|
"lws_client_handshake: got bad HTTP response '%d'\n",
|
|
wsi->http.ah->http_response);
|
|
*cce = "HS: ws upgrade response not 101";
|
|
goto bail3;
|
|
}
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
|
|
lwsl_info("no ACCEPT\n");
|
|
*cce = "HS: ACCEPT missing";
|
|
goto bail3;
|
|
}
|
|
|
|
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
|
|
if (!p) {
|
|
lwsl_info("no UPGRADE\n");
|
|
*cce = "HS: UPGRADE missing";
|
|
goto bail3;
|
|
}
|
|
strtolower(p);
|
|
if (strcmp(p, "websocket")) {
|
|
lwsl_warn(
|
|
"lws_client_handshake: got bad Upgrade header '%s'\n", p);
|
|
*cce = "HS: Upgrade to something other than websocket";
|
|
goto bail3;
|
|
}
|
|
|
|
/* connection: must have "upgrade" */
|
|
|
|
lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
|
|
LWS_TOKENIZE_F_MINUS_NONTERM);
|
|
ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
|
|
if (ts.len <= 0) /* won't fit, or absent */
|
|
goto bad_conn_format;
|
|
|
|
do {
|
|
e = lws_tokenize(&ts);
|
|
switch (e) {
|
|
case LWS_TOKZE_TOKEN:
|
|
if (!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_info("%s: malfored connection '%s'\n",
|
|
__func__, 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_parser("lws_client_int_s_hs: no protocol list\n");
|
|
} else
|
|
lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", 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_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
|
|
/*
|
|
* no protocol name to work from, if we don't already have one
|
|
* default to first protocol
|
|
*/
|
|
|
|
if (wsi->protocol) {
|
|
p = (char *)wsi->protocol->name;
|
|
goto identify_protocol;
|
|
}
|
|
|
|
/* no choice but to use the default protocol */
|
|
|
|
n = 0;
|
|
wsi->protocol = &wsi->vhost->protocols[0];
|
|
goto check_extensions;
|
|
}
|
|
|
|
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
|
|
len = (int)strlen(p);
|
|
|
|
while (pc && *pc && !okay) {
|
|
if (!strncmp(pc, p, len) &&
|
|
(pc[len] == ',' || pc[len] == '\0')) {
|
|
okay = 1;
|
|
continue;
|
|
}
|
|
while (*pc && *pc++ != ',')
|
|
;
|
|
while (*pc == ' ')
|
|
pc++;
|
|
}
|
|
|
|
if (!okay) {
|
|
lwsl_info("%s: got bad protocol %s\n", __func__, p);
|
|
*cce = "HS: PROTOCOL malformed";
|
|
goto bail2;
|
|
}
|
|
|
|
identify_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->protocol = NULL;
|
|
|
|
while (n < wsi->vhost->count_protocols) {
|
|
if (!wsi->protocol &&
|
|
strcmp(p, wsi->vhost->protocols[n].name) == 0) {
|
|
wsi->protocol = &wsi->vhost->protocols[n];
|
|
break;
|
|
}
|
|
n++;
|
|
}
|
|
|
|
if (n == wsi->vhost->count_protocols) { /* no match */
|
|
/* if server, that's already fatal */
|
|
if (!lwsi_role_client(wsi)) {
|
|
lwsl_info("%s: fail protocol %s\n", __func__, p);
|
|
*cce = "HS: Cannot match protocol";
|
|
goto bail2;
|
|
}
|
|
|
|
/* for client, find the index of our pre-bound protocol */
|
|
|
|
n = 0;
|
|
while (wsi->vhost->protocols[n].callback) {
|
|
if (wsi->protocol && strcmp(wsi->protocol->name,
|
|
wsi->vhost->protocols[n].name) == 0) {
|
|
wsi->protocol = &wsi->vhost->protocols[n];
|
|
break;
|
|
}
|
|
n++;
|
|
}
|
|
|
|
if (!wsi->vhost->protocols[n].callback) {
|
|
if (wsi->protocol)
|
|
lwsl_err("Failed to match protocol %s\n",
|
|
wsi->protocol->name);
|
|
else
|
|
lwsl_err("No protocol on client\n");
|
|
*cce = "ws protocol no match";
|
|
goto bail2;
|
|
}
|
|
}
|
|
|
|
lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
|
|
|
|
check_extensions:
|
|
/*
|
|
* stitch protocol choice into the vh protocol linked list
|
|
* We always insert ourselves at the start of the list
|
|
*
|
|
* X <-> B
|
|
* X <-> pAn <-> pB
|
|
*/
|
|
|
|
lws_same_vh_protocol_insert(wsi, n);
|
|
|
|
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
|
/* instantiate the accepted extensions */
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
|
|
lwsl_ext("no client extensions allowed by server\n");
|
|
goto check_accept;
|
|
}
|
|
|
|
/*
|
|
* break down the list of server accepted extensions
|
|
* and go through matching them or identifying bogons
|
|
*/
|
|
|
|
if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size,
|
|
WSI_TOKEN_EXTENSIONS) < 0) {
|
|
lwsl_warn("ext list from server failed to copy\n");
|
|
*cce = "HS: EXT: list too big";
|
|
goto bail2;
|
|
}
|
|
|
|
c = sb;
|
|
n = 0;
|
|
ignore = 0;
|
|
a = NULL;
|
|
while (more) {
|
|
|
|
if (*c && (*c != ',' && *c != '\t')) {
|
|
if (*c == ';') {
|
|
ignore = 1;
|
|
if (!a)
|
|
a = c + 1;
|
|
}
|
|
if (ignore || *c == ' ') {
|
|
c++;
|
|
continue;
|
|
}
|
|
|
|
ext_name[n] = *c++;
|
|
if (n < (int)sizeof(ext_name) - 1)
|
|
n++;
|
|
continue;
|
|
}
|
|
ext_name[n] = '\0';
|
|
ignore = 0;
|
|
if (!*c)
|
|
more = 0;
|
|
else {
|
|
c++;
|
|
if (!n)
|
|
continue;
|
|
}
|
|
|
|
/* check we actually support it */
|
|
|
|
lwsl_notice("checking client ext %s\n", ext_name);
|
|
|
|
n = 0;
|
|
ext = wsi->vhost->ws.extensions;
|
|
while (ext && ext->callback) {
|
|
if (strcmp(ext_name, ext->name)) {
|
|
ext++;
|
|
continue;
|
|
}
|
|
|
|
n = 1;
|
|
lwsl_notice("instantiating client ext %s\n", ext_name);
|
|
|
|
/* instantiate the extension on this conn */
|
|
|
|
wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
|
|
|
|
/* allow him to construct his ext instance */
|
|
|
|
if (ext->callback(lws_get_context(wsi), ext, wsi,
|
|
LWS_EXT_CB_CLIENT_CONSTRUCT,
|
|
(void *)&wsi->ws->act_ext_user[
|
|
wsi->ws->count_act_ext],
|
|
(void *)&opts, 0)) {
|
|
lwsl_info(" ext %s failed construction\n",
|
|
ext_name);
|
|
ext++;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* allow the user code to override ext defaults if it
|
|
* wants to
|
|
*/
|
|
ext_name[0] = '\0';
|
|
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
|
wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
|
|
(char *)ext->name, ext_name,
|
|
sizeof(ext_name))) {
|
|
*cce = "HS: EXT: failed setting defaults";
|
|
goto bail2;
|
|
}
|
|
|
|
if (ext_name[0] &&
|
|
lws_ext_parse_options(ext, wsi,
|
|
wsi->ws->act_ext_user[
|
|
wsi->ws->count_act_ext],
|
|
opts, ext_name,
|
|
(int)strlen(ext_name))) {
|
|
lwsl_err("%s: unable to parse user defaults '%s'",
|
|
__func__, ext_name);
|
|
*cce = "HS: EXT: failed parsing defaults";
|
|
goto bail2;
|
|
}
|
|
|
|
/*
|
|
* give the extension the server options
|
|
*/
|
|
if (a && lws_ext_parse_options(ext, wsi,
|
|
wsi->ws->act_ext_user[
|
|
wsi->ws->count_act_ext],
|
|
opts, a, lws_ptr_diff(c, a))) {
|
|
lwsl_err("%s: unable to parse remote def '%s'",
|
|
__func__, a);
|
|
*cce = "HS: EXT: failed parsing options";
|
|
goto bail2;
|
|
}
|
|
|
|
if (ext->callback(lws_get_context(wsi), ext, wsi,
|
|
LWS_EXT_CB_OPTION_CONFIRM,
|
|
wsi->ws->act_ext_user[wsi->ws->count_act_ext],
|
|
NULL, 0)) {
|
|
lwsl_err("%s: ext %s rejects server options %s",
|
|
__func__, ext->name, a);
|
|
*cce = "HS: EXT: Rejects server options";
|
|
goto bail2;
|
|
}
|
|
|
|
wsi->ws->count_act_ext++;
|
|
|
|
ext++;
|
|
}
|
|
|
|
if (n == 0) {
|
|
lwsl_warn("Unknown ext '%s'!\n", ext_name);
|
|
*cce = "HS: EXT: unknown ext";
|
|
goto bail2;
|
|
}
|
|
|
|
a = NULL;
|
|
n = 0;
|
|
}
|
|
|
|
check_accept:
|
|
#endif
|
|
|
|
/*
|
|
* Confirm his accept token is the one we precomputed
|
|
*/
|
|
|
|
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
|
|
if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
|
|
lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
|
|
wsi->http.ah->initial_handshake_hash_base64);
|
|
*cce = "HS: Accept hash wrong";
|
|
goto bail2;
|
|
}
|
|
|
|
/* allocate the per-connection user memory (if any) */
|
|
if (lws_ensure_user_space(wsi)) {
|
|
lwsl_err("Problem allocating wsi user mem\n");
|
|
*cce = "HS: OOM";
|
|
goto bail2;
|
|
}
|
|
|
|
/*
|
|
* we seem to be good to go, give client last chance to check
|
|
* headers and OK it
|
|
*/
|
|
if (wsi->protocol->callback(wsi,
|
|
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
|
|
wsi->user_space, NULL, 0)) {
|
|
*cce = "HS: Rejected by filter cb";
|
|
goto bail2;
|
|
}
|
|
|
|
/* clear his proxy connection timeout */
|
|
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
|
|
|
/* free up his parsing allocations */
|
|
lws_header_table_detach(wsi, 0);
|
|
|
|
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
|
|
&role_ops_ws);
|
|
|
|
if (wsi->context->ws_ping_pong_interval && !wsi->http2_substream ) {
|
|
wsi->sul_ping.cb = lws_sul_wsping_cb;
|
|
__lws_sul_insert(&pt->pt_sul_owner, &wsi->sul_ping,
|
|
(lws_usec_t)wsi->context->ws_ping_pong_interval *
|
|
LWS_USEC_PER_SEC);
|
|
}
|
|
|
|
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
|
|
|
/*
|
|
* create the frame buffer for this connection according to the
|
|
* size mentioned in the protocol definition. If 0 there, then
|
|
* use a big default for compatibility
|
|
*/
|
|
n = (int)wsi->protocol->rx_buffer_size;
|
|
if (!n)
|
|
n = context->pt_serv_buf_size;
|
|
n += LWS_PRE;
|
|
wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
|
|
"client frame buffer");
|
|
if (!wsi->ws->rx_ubuf) {
|
|
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
|
|
*cce = "HS: OOM";
|
|
goto bail2;
|
|
}
|
|
wsi->ws->rx_ubuf_alloc = n;
|
|
lwsl_info("Allocating client RX buffer %d\n", n);
|
|
|
|
#if !defined(LWS_PLAT_FREERTOS)
|
|
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
|
|
(const char *)&n, sizeof n)) {
|
|
lwsl_warn("Failed to set SNDBUF to %d", n);
|
|
*cce = "HS: SO_SNDBUF failed";
|
|
goto bail3;
|
|
}
|
|
#endif
|
|
|
|
lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
|
|
|
|
/* call him back to inform him he is up */
|
|
|
|
if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
|
|
wsi->user_space, NULL, 0)) {
|
|
*cce = "HS: Rejected at CLIENT_ESTABLISHED";
|
|
goto bail3;
|
|
}
|
|
|
|
return 0;
|
|
|
|
bail3:
|
|
return 3;
|
|
|
|
bail2:
|
|
return 2;
|
|
}
|