mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
refactor role ops
This only refactors internal architecture and representations, the user api is unaffected.
This commit is contained in:
parent
16e2f09710
commit
126be3ccf3
67 changed files with 6418 additions and 5094 deletions
101
CMakeLists.txt
101
CMakeLists.txt
|
@ -196,6 +196,18 @@ if(GIT_EXECUTABLE)
|
|||
message("Git commit hash: ${LWS_BUILD_HASH}")
|
||||
endif()
|
||||
|
||||
# translate old functionality enables to set up ROLE enables so nothing changes
|
||||
|
||||
set(LWS_ROLE_H1 1)
|
||||
set(LWS_ROLE_WS 1)
|
||||
set(LWS_ROLE_RAW 1)
|
||||
if (LWS_WITH_HTTP2)
|
||||
set(LWS_ROLE_H2 1)
|
||||
endif()
|
||||
if (LWS_WITH_CGI)
|
||||
set(LWS_ROLE_CGI 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP2 AND LWS_WITHOUT_SERVER)
|
||||
message(FATAL_ERROR "HTTP2 can only be used with server at the moment")
|
||||
endif()
|
||||
|
@ -449,8 +461,10 @@ else()
|
|||
set(LWS_OPENSSL_CLIENT_CERTS /etc/pki/tls/certs/ CACHE PATH "Client SSL certificate directory")
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SSL)
|
||||
# LWS_OPENSSL_SUPPORT deprecated... use LWS_WITH_TLS
|
||||
if (LWS_WITH_SSL OR LWS_WITH_MBEDTLS)
|
||||
set(LWS_OPENSSL_SUPPORT 1)
|
||||
set(LWS_WITH_TLS 1)
|
||||
endif()
|
||||
|
||||
if (LWS_SSL_CLIENT_USE_OS_CA_CERTS)
|
||||
|
@ -673,37 +687,70 @@ set(HDR_PUBLIC
|
|||
|
||||
set(SOURCES
|
||||
lib/misc/base64-decode.c
|
||||
lib/handshake.c
|
||||
lib/libwebsockets.c
|
||||
lib/service.c
|
||||
lib/pollfd.c
|
||||
lib/output.c
|
||||
lib/server/parsers.c
|
||||
lib/roles/http/server/parsers.c
|
||||
lib/context.c
|
||||
lib/alloc.c
|
||||
lib/header.c
|
||||
lib/roles/http/header.c
|
||||
lib/roles/pipe/ops-pipe.c
|
||||
lib/misc/lws-ring.c)
|
||||
|
||||
if (LWS_WITH_CGI)
|
||||
if (LWS_ROLE_H1)
|
||||
list(APPEND SOURCES
|
||||
lib/server/cgi.c)
|
||||
lib/roles/h1/ops-h1.c)
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/h1/client-h1.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_WS)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/ws/ops-ws.c)
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/ws/client-ws.c
|
||||
lib/roles/ws/client-parser.c)
|
||||
endif()
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/ws/server-ws.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_RAW)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/raw/ops-raw.c)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_CGI)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/cgi/cgi-server.c
|
||||
lib/roles/cgi/ops-cgi.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_ACCESS_LOG)
|
||||
list(APPEND SOURCES
|
||||
lib/server/access-log.c)
|
||||
lib/roles/http/server/access-log.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_PEER_LIMITS)
|
||||
list(APPEND SOURCES
|
||||
lib/server/peer-limits.c)
|
||||
lib/misc/peer-limits.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/client/client.c
|
||||
lib/client/client-handshake.c
|
||||
lib/client/client-parser.c)
|
||||
lib/roles/http/client/client.c
|
||||
lib/roles/http/client/client-handshake.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/listen/ops-listen.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
|
@ -785,7 +832,7 @@ if (LWS_WITH_SSL)
|
|||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/server/ssl-server.c)
|
||||
lib/tls/tls-server.c)
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/mbedtls-server.c)
|
||||
|
@ -796,7 +843,7 @@ if (LWS_WITH_SSL)
|
|||
endif()
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/client/ssl-client.c)
|
||||
lib/tls/tls-client.c)
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/mbedtls-client.c)
|
||||
|
@ -815,9 +862,10 @@ endif()
|
|||
|
||||
if (LWS_WITH_HTTP2 AND NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/http2/http2.c
|
||||
lib/http2/hpack.c
|
||||
lib/http2/ssl-http2.c)
|
||||
lib/roles/h2/http2.c
|
||||
lib/roles/h2/hpack.c
|
||||
lib/roles/h2/ssl-http2.c
|
||||
lib/roles/h2/ops-h2.c)
|
||||
endif()
|
||||
# select the active platform files
|
||||
|
||||
|
@ -843,22 +891,21 @@ endif()
|
|||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/server/server.c
|
||||
lib/server/lws-spa.c
|
||||
lib/server/server-handshake.c)
|
||||
lib/roles/http/server/server.c
|
||||
lib/roles/http/server/lws-spa.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_EXTENSIONS)
|
||||
list(APPEND HDR_PRIVATE
|
||||
lib/ext/extension-permessage-deflate.h)
|
||||
lib/roles/ws/ext/extension-permessage-deflate.h)
|
||||
list(APPEND SOURCES
|
||||
lib/ext/extension.c
|
||||
lib/ext/extension-permessage-deflate.c)
|
||||
lib/roles/ws/ext/extension.c
|
||||
lib/roles/ws/ext/extension-permessage-deflate.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP_PROXY)
|
||||
list(APPEND SOURCES
|
||||
lib/server/rewrite.c)
|
||||
lib/roles/http/server/rewrite.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_LIBEV)
|
||||
|
@ -882,7 +929,7 @@ if (LWS_WITH_LEJP)
|
|||
endif()
|
||||
if (LWS_WITH_LEJP_CONF)
|
||||
list(APPEND SOURCES
|
||||
"lib/server/lejp-conf.c"
|
||||
"lib/roles/http/server/lejp-conf.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -893,13 +940,13 @@ endif()
|
|||
|
||||
if (LWS_WITH_RANGES)
|
||||
list(APPEND SOURCES
|
||||
lib/server/ranges.c)
|
||||
lib/roles/http/server/ranges.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_ZIP_FOPS)
|
||||
if (LWS_WITH_ZLIB)
|
||||
list(APPEND SOURCES
|
||||
lib/server/fops-zip.c)
|
||||
lib/roles/http/server/fops-zip.c)
|
||||
else()
|
||||
message(FATAL_ERROR "Pre-zipped file support (LWS_WITH_ZIP_FOPS) requires ZLIB (LWS_WITH_ZLIB)")
|
||||
endif()
|
||||
|
@ -930,7 +977,7 @@ else()
|
|||
# Unix.
|
||||
if (NOT LWS_WITHOUT_DAEMONIZE)
|
||||
list(APPEND SOURCES
|
||||
lib/server/daemonize.c)
|
||||
lib/misc/daemonize.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
|
||||
#define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share"
|
||||
|
||||
#cmakedefine LWS_ROLE_H1
|
||||
#cmakedefine LWS_ROLE_WS
|
||||
#cmakedefine LWS_ROLE_RAW
|
||||
#cmakedefine LWS_ROLE_H2
|
||||
#cmakedefine LWS_ROLE_CGI
|
||||
|
||||
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
|
||||
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
|
||||
#cmakedefine USE_WOLFSSL
|
||||
|
@ -36,8 +42,9 @@
|
|||
/* The current git commit hash that we're building from */
|
||||
#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}"
|
||||
|
||||
/* Build with OpenSSL support */
|
||||
/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
|
||||
#cmakedefine LWS_OPENSSL_SUPPORT
|
||||
#cmakedefine LWS_WITH_TLS
|
||||
|
||||
/* The client should load and trust CA root certs it finds in the OS */
|
||||
#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS
|
||||
|
|
|
@ -50,70 +50,6 @@ static const char * const mount_protocols[] = {
|
|||
"callback://"
|
||||
};
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
/*
|
||||
* These are the standardized defaults.
|
||||
* Override what actually goes in the vhost settings in platform or user code.
|
||||
* Leave these alone because they are used to determine "what is different
|
||||
* from the protocol defaults".
|
||||
*/
|
||||
const struct http2_settings lws_h2_defaults = { {
|
||||
1,
|
||||
/* H2SET_HEADER_TABLE_SIZE */ 4096,
|
||||
/* *** This controls how many entries in the dynamic table ***
|
||||
* Allows the sender to inform the remote endpoint of the maximum
|
||||
* size of the header compression table used to decode header
|
||||
* blocks, in octets. The encoder can select any size equal to or
|
||||
* less than this value by using signaling specific to the header
|
||||
* compression format inside a header block (see [COMPRESSION]).
|
||||
* The initial value is 4,096 octets.
|
||||
*/
|
||||
/* H2SET_ENABLE_PUSH */ 1,
|
||||
/* H2SET_MAX_CONCURRENT_STREAMS */ 0x7fffffff,
|
||||
/* H2SET_INITIAL_WINDOW_SIZE */ 65535,
|
||||
/* H2SET_MAX_FRAME_SIZE */ 16384,
|
||||
/* H2SET_MAX_HEADER_LIST_SIZE */ 0x7fffffff,
|
||||
/*< This advisory setting informs a peer of the maximum size of
|
||||
* header list that the sender is prepared to accept, in octets.
|
||||
* The value is based on the uncompressed size of header fields,
|
||||
* including the length of the name and value in octets plus an
|
||||
* overhead of 32 octets for each header field.
|
||||
*/
|
||||
/* H2SET_RESERVED7 */ 0,
|
||||
/* H2SET_ENABLE_CONNECT_PROTOCOL */ 0,
|
||||
}};
|
||||
|
||||
/* these are the "lws defaults"... they can be overridden in plat */
|
||||
|
||||
const struct http2_settings lws_h2_stock_settings = { {
|
||||
1,
|
||||
/* H2SET_HEADER_TABLE_SIZE */ 65536, /* ffox */
|
||||
/* *** This controls how many entries in the dynamic table ***
|
||||
* Allows the sender to inform the remote endpoint of the maximum
|
||||
* size of the header compression table used to decode header
|
||||
* blocks, in octets. The encoder can select any size equal to or
|
||||
* less than this value by using signaling specific to the header
|
||||
* compression format inside a header block (see [COMPRESSION]).
|
||||
* The initial value is 4,096 octets.
|
||||
*
|
||||
* Can't pass h2spec with less than 4096 here...
|
||||
*/
|
||||
/* H2SET_ENABLE_PUSH */ 1,
|
||||
/* H2SET_MAX_CONCURRENT_STREAMS */ 24,
|
||||
/* H2SET_INITIAL_WINDOW_SIZE */ 65535,
|
||||
/* H2SET_MAX_FRAME_SIZE */ 16384,
|
||||
/* H2SET_MAX_HEADER_LIST_SIZE */ 4096,
|
||||
/*< This advisory setting informs a peer of the maximum size of
|
||||
* header list that the sender is prepared to accept, in octets.
|
||||
* The value is based on the uncompressed size of header fields,
|
||||
* including the length of the name and value in octets plus an
|
||||
* overhead of 32 octets for each header field.
|
||||
*/
|
||||
/* H2SET_RESERVED7 */ 0,
|
||||
/* H2SET_ENABLE_CONNECT_PROTOCOL */ 1,
|
||||
}};
|
||||
#endif
|
||||
|
||||
LWS_VISIBLE void *
|
||||
lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost,
|
||||
const struct lws_protocols *prot, int size)
|
||||
|
@ -265,7 +201,7 @@ lws_protocol_init(struct lws_context *context)
|
|||
pvo = pvo1->options;
|
||||
}
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
any |= !!vh->ssl_ctx;
|
||||
#endif
|
||||
|
||||
|
@ -617,10 +553,11 @@ lws_create_vhost(struct lws_context *context,
|
|||
vh->pvo = info->pvo;
|
||||
vh->headers = info->headers;
|
||||
vh->user = info->user;
|
||||
if (!info->h2_rx_scratch_size)
|
||||
vh->h2_rx_scratch_size = LWS_H2_RX_SCRATCH_SIZE;
|
||||
else
|
||||
vh->h2_rx_scratch_size = info->h2_rx_scratch_size;
|
||||
|
||||
#if defined(LWS_ROLE_H2)
|
||||
role_ops_h2.init_vhost(vh, info);
|
||||
#endif
|
||||
|
||||
vh->ssl_info_event_mask = info->ssl_info_event_mask;
|
||||
if (info->keepalive_timeout)
|
||||
vh->keepalive_timeout = info->keepalive_timeout;
|
||||
|
@ -632,7 +569,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
else
|
||||
vh->timeout_secs_ah_idle = 10;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (info->ecdh_curve)
|
||||
lws_strncpy(vh->ecdh_curve, info->ecdh_curve,
|
||||
sizeof(vh->ecdh_curve));
|
||||
|
@ -966,7 +903,7 @@ lws_create_event_pipes(struct lws_context *context)
|
|||
return 1;
|
||||
}
|
||||
wsi->context = context;
|
||||
lwsi_set_role(wsi, LWSI_ROLE_EVENT_PIPE);
|
||||
lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_pipe);
|
||||
wsi->protocol = NULL;
|
||||
wsi->tsi = n;
|
||||
wsi->vhost = NULL;
|
||||
|
@ -1023,7 +960,7 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
#if defined(GCC_VER)
|
||||
lwsl_info("Compiled with %s\n", GCC_VER);
|
||||
#endif
|
||||
#if LWS_POSIX
|
||||
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6))
|
||||
lwsl_info("IPV6 compiled in and enabled\n");
|
||||
|
@ -1035,7 +972,6 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_ESP32)
|
||||
lws_feature_status_libev(info);
|
||||
lws_feature_status_libuv(info);
|
||||
#endif
|
||||
#endif
|
||||
lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN);
|
||||
lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS);
|
||||
|
@ -1044,9 +980,7 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
#if defined(LWS_WITH_STATS)
|
||||
lwsl_info(" LWS_WITH_STATS : on\n");
|
||||
#endif
|
||||
#if LWS_POSIX
|
||||
lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
|
||||
#endif
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
lwsl_info(" HTTP2 support : available\n");
|
||||
#else
|
||||
|
@ -1065,8 +999,8 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
else
|
||||
context->pt_serv_buf_size = 4096;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
context->set = lws_h2_stock_settings;
|
||||
#if defined(LWS_ROLE_H2)
|
||||
role_ops_h2.init_context(context, info);
|
||||
#endif
|
||||
|
||||
#if LWS_MAX_SMP > 1
|
||||
|
|
|
@ -185,7 +185,7 @@ lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
|
|||
if (!LWS_LIBEV_ENABLED(context))
|
||||
return;
|
||||
|
||||
if (lwsi_role(new_wsi) == LWSI_ROLE_RAW_FILE)
|
||||
if (new_wsi->role_ops == &role_ops_raw_file)
|
||||
fd = desc.filefd;
|
||||
else
|
||||
fd = desc.sockfd;
|
||||
|
|
|
@ -174,7 +174,7 @@ lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
|
|||
// Initialize the event
|
||||
pt = &context->pt[(int)new_wsi->tsi];
|
||||
|
||||
if (lwsi_role(new_wsi) == LWSI_ROLE_RAW_FILE)
|
||||
if (new_wsi->role_ops == role_ops_raw_file)
|
||||
fd = desc.filefd;
|
||||
else
|
||||
fd = desc.sockfd;
|
||||
|
|
|
@ -429,7 +429,7 @@ lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc)
|
|||
return;
|
||||
|
||||
wsi->w_read.context = context;
|
||||
if (lwsi_role(wsi) == LWSI_ROLE_RAW_FILE || wsi->event_pipe)
|
||||
if (wsi->role_ops == &role_ops_raw_file || wsi->event_pipe)
|
||||
uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
|
||||
(int)(long long)desc.filefd);
|
||||
else
|
||||
|
@ -543,8 +543,7 @@ lws_libuv_closewsi(uv_handle_t* handle)
|
|||
* We get called back here for every wsi that closes
|
||||
*/
|
||||
|
||||
if (lwsi_role(wsi) == LWSI_ROLE_LISTEN_SOCKET &&
|
||||
wsi->context->deprecated) {
|
||||
if (wsi->role_ops == &role_ops_listen && wsi->context->deprecated) {
|
||||
lspd = 1;
|
||||
context->deprecation_pending_listen_close_count--;
|
||||
if (!context->deprecation_pending_listen_close_count)
|
||||
|
@ -637,6 +636,13 @@ lws_libuv_stop(struct lws_context *context)
|
|||
void
|
||||
lws_libuv_closehandle(struct lws *wsi)
|
||||
{
|
||||
if (wsi->told_event_loop_closed) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
wsi->told_event_loop_closed = 1;
|
||||
|
||||
/* required to defer actual deletion until libuv has processed it */
|
||||
uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi);
|
||||
}
|
||||
|
|
357
lib/handshake.c
357
lib/handshake.c
|
@ -1,357 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
/*
|
||||
* -04 of the protocol (actually the 80th version) has a radically different
|
||||
* handshake. The 04 spec gives the following idea
|
||||
*
|
||||
* The handshake from the client looks as follows:
|
||||
*
|
||||
* GET /chat HTTP/1.1
|
||||
* Host: server.example.com
|
||||
* Upgrade: websocket
|
||||
* Connection: Upgrade
|
||||
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||
* Sec-WebSocket-Origin: http://example.com
|
||||
* Sec-WebSocket-Protocol: chat, superchat
|
||||
* Sec-WebSocket-Version: 4
|
||||
*
|
||||
* The handshake from the server looks as follows:
|
||||
*
|
||||
* HTTP/1.1 101 Switching Protocols
|
||||
* Upgrade: websocket
|
||||
* Connection: Upgrade
|
||||
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
|
||||
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
|
||||
* Sec-WebSocket-Protocol: chat
|
||||
*/
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We have to take care about parsing because the headers may be split
|
||||
* into multiple fragments. They may contain unknown headers with arbitrary
|
||||
* argument lengths. So, we parse using a single-character at a time state
|
||||
* machine that is completely independent of packet size.
|
||||
*
|
||||
* Returns <0 for error or length of chars consumed from buf (up to len)
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
||||
{
|
||||
unsigned char *last_char, *oldbuf = buf;
|
||||
lws_filepos_t body_chunk_len;
|
||||
size_t n;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
|
||||
if (lwsi_role_h2(wsi) &&
|
||||
!lwsi_role_ws(wsi) &&
|
||||
lwsi_state(wsi) != LRS_BODY) {
|
||||
int m;
|
||||
|
||||
// lwsl_notice("%s: h2 path: wsistate 0x%x len %d\n", __func__,
|
||||
// wsi->wsistate, (int)len);
|
||||
|
||||
/*
|
||||
* wsi here is always the network connection wsi, not a stream
|
||||
* wsi. Once we unpicked the framing we will find the right
|
||||
* swsi and make it the target of the frame.
|
||||
*
|
||||
* If it's ws over h2, the nwsi will get us here to do the h2
|
||||
* processing, and that will call us back with the swsi +
|
||||
* ESTABLISHED state for the inner payload, handled in a later
|
||||
* case.
|
||||
*/
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lws_rxflow_cache(wsi, buf, 0, (int)len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* lws_h2_parser() may send something; when it gets the
|
||||
* whole frame, it will want to perform some action
|
||||
* involving a reply. But we may be in a partial send
|
||||
* situation on the network wsi...
|
||||
*
|
||||
* Even though we may be in a partial send and unable to
|
||||
* send anything new, we still have to parse the network
|
||||
* wsi in order to gain tx credit to send, which is
|
||||
* potentially necessary to clear the old partial send.
|
||||
*
|
||||
* ALL network wsi-specific frames are sent by PPS
|
||||
* already, these are sent as a priority on the writable
|
||||
* handler, and so respect partial sends. The only
|
||||
* problem is when a stream wsi wants to send an, eg,
|
||||
* reply headers frame in response to the parsing
|
||||
* we will do now... the *stream wsi* must stall in a
|
||||
* different state until it is able to do so from a
|
||||
* priority on the WRITABLE callback, same way that
|
||||
* file transfers operate.
|
||||
*/
|
||||
|
||||
m = lws_h2_parser(wsi, buf, len, &body_chunk_len);
|
||||
if (m && m != 2) {
|
||||
lwsl_debug("%s: http2_parser bailed\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
if (m && m == 2) {
|
||||
/* swsi has been closed */
|
||||
buf += body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
goto read_ok;
|
||||
}
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer) {
|
||||
wsi->rxflow_pos += (int)body_chunk_len;
|
||||
assert(wsi->rxflow_pos <= wsi->rxflow_len);
|
||||
}
|
||||
|
||||
buf += body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
}
|
||||
// lwsl_debug("%s: used up block\n", __func__);
|
||||
goto read_ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
// lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
|
||||
case LRS_ISSUING_FILE:
|
||||
return 0;
|
||||
|
||||
case LRS_ESTABLISHED:
|
||||
|
||||
if (lwsi_role_non_ws_client(wsi))
|
||||
break;
|
||||
|
||||
if (lwsi_role_ws(wsi))
|
||||
goto ws_mode;
|
||||
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LRS_HEADERS:
|
||||
if (!wsi->ah) {
|
||||
lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
|
||||
assert(0);
|
||||
}
|
||||
lwsl_parser("issuing %d bytes to parser\n", (int)len);
|
||||
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
|
||||
last_char = buf;
|
||||
if (lws_handshake_server(wsi, &buf, (size_t)len))
|
||||
/* Handshake indicates this session is done. */
|
||||
goto bail;
|
||||
|
||||
/* we might have transitioned to RAW */
|
||||
if (lwsi_role_raw(wsi))
|
||||
/* we gave the read buffer to RAW handler already */
|
||||
goto read_ok;
|
||||
|
||||
/*
|
||||
* It's possible that we've exhausted our data already, or
|
||||
* rx flow control has stopped us dealing with this early,
|
||||
* but lws_handshake_server doesn't update len for us.
|
||||
* Figure out how much was read, so that we can proceed
|
||||
* appropriately:
|
||||
*/
|
||||
len -= (buf - last_char);
|
||||
// lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
|
||||
|
||||
if (!wsi->hdr_parsing_completed)
|
||||
/* More header content on the way */
|
||||
goto read_ok;
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
case LRS_ESTABLISHED:
|
||||
case LRS_HEADERS:
|
||||
goto read_ok;
|
||||
case LRS_ISSUING_FILE:
|
||||
goto read_ok;
|
||||
case LRS_BODY:
|
||||
wsi->http.rx_content_remain =
|
||||
wsi->http.rx_content_length;
|
||||
if (wsi->http.rx_content_remain)
|
||||
goto http_postbody;
|
||||
|
||||
/* there is no POST content */
|
||||
goto postbody_completion;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_BODY:
|
||||
http_postbody:
|
||||
//lwsl_notice("http post body\n");
|
||||
while (len && wsi->http.rx_content_remain) {
|
||||
/* Copy as much as possible, up to the limit of:
|
||||
* what we have in the read buffer (len)
|
||||
* remaining portion of the POST body (content_remain)
|
||||
*/
|
||||
body_chunk_len = min(wsi->http.rx_content_remain, len);
|
||||
wsi->http.rx_content_remain -= body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (wsi->cgi) {
|
||||
struct lws_cgi_args args;
|
||||
|
||||
args.ch = LWS_STDIN;
|
||||
args.stdwsi = &wsi->cgi->stdwsi[0];
|
||||
args.data = buf;
|
||||
args.len = body_chunk_len;
|
||||
|
||||
/* returns how much used */
|
||||
n = user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
|
||||
wsi->user_space,
|
||||
(void *)&args, 0);
|
||||
if ((int)n < 0)
|
||||
goto bail;
|
||||
} else {
|
||||
#endif
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
|
||||
buf, (size_t)body_chunk_len);
|
||||
if (n)
|
||||
goto bail;
|
||||
n = (size_t)body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
}
|
||||
#endif
|
||||
buf += n;
|
||||
|
||||
if (wsi->http.rx_content_remain) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
wsi->context->timeout_secs);
|
||||
break;
|
||||
}
|
||||
/* he sent all the content in time */
|
||||
postbody_completion:
|
||||
#ifdef LWS_WITH_CGI
|
||||
/*
|
||||
* If we're running a cgi, we can't let him off the
|
||||
* hook just because he sent his POST data
|
||||
*/
|
||||
if (wsi->cgi)
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
|
||||
wsi->context->timeout_secs);
|
||||
else
|
||||
#endif
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (!wsi->cgi)
|
||||
#endif
|
||||
{
|
||||
lwsl_info("HTTP_BODY_COMPLETION\n");
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY_COMPLETION,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n)
|
||||
goto bail;
|
||||
|
||||
if (wsi->http2_substream)
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_AWAITING_CLOSE_ACK:
|
||||
case LRS_WAITING_TO_SEND_CLOSE:
|
||||
case LRS_SHUTDOWN:
|
||||
|
||||
ws_mode:
|
||||
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
|
||||
switch (lwsi_role(wsi)) {
|
||||
case LWSI_ROLE_WS1_SERVER:
|
||||
case LWSI_ROLE_WS2_SERVER:
|
||||
/*
|
||||
* for h2 we are on the swsi
|
||||
*/
|
||||
if (lws_interpret_incoming_packet(wsi, &buf,
|
||||
(size_t)len) < 0) {
|
||||
lwsl_info("interpret_incoming_packet bailed\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_DEFERRING_ACTION:
|
||||
lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
|
||||
break;
|
||||
|
||||
case LRS_SSL_ACK_PENDING:
|
||||
break;
|
||||
|
||||
case LRS_DEAD_SOCKET:
|
||||
lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
|
||||
assert(0);
|
||||
/* fallthru */
|
||||
|
||||
default:
|
||||
lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
read_ok:
|
||||
/* Nothing more to do for now */
|
||||
// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
|
||||
// wsi, (long)(buf - oldbuf), (int)len, wsi->state);
|
||||
|
||||
return lws_ptr_diff(buf, oldbuf);
|
||||
|
||||
bail:
|
||||
/*
|
||||
* h2 / h2-ws calls us recursively in lws_read()->lws_h2_parser()->
|
||||
* lws_read() pattern, having stripped the h2 framing in the middle.
|
||||
*
|
||||
* When taking down the whole connection, make sure that only the
|
||||
* outer lws_read() does the wsi close.
|
||||
*/
|
||||
if (!wsi->outer_will_close)
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "lws_read bail");
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -124,14 +124,8 @@ __lws_free_wsi(struct lws *wsi)
|
|||
wsi->peer = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->upgraded_to_http2 || wsi->http2_substream) {
|
||||
lws_hpack_destroy_dynamic_header(wsi);
|
||||
|
||||
if (wsi->h2.h2n)
|
||||
lws_free_set_NULL(wsi->h2.h2n);
|
||||
}
|
||||
#endif
|
||||
if (wsi->role_ops->destroy_role)
|
||||
wsi->role_ops->destroy_role(wsi);
|
||||
|
||||
/* since we will destroy the wsi, make absolutely sure now */
|
||||
|
||||
|
@ -604,10 +598,10 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
wsi->child_list = NULL;
|
||||
}
|
||||
|
||||
if (lwsi_role(wsi) == LWSI_ROLE_RAW_FILE) {
|
||||
if (wsi->role_ops == &role_ops_raw_file) {
|
||||
lws_remove_child_from_any_parent(wsi);
|
||||
__remove_wsi_socket_from_fds(wsi);
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_RAW_CLOSE_FILE,
|
||||
wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0],
|
||||
wsi->user_space, NULL, 0);
|
||||
goto async_close;
|
||||
}
|
||||
|
@ -615,7 +609,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
wsi->wsistate_pre_close = wsi->wsistate;
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (lwsi_role(wsi) == LWSI_ROLE_CGI) {
|
||||
if (wsi->role_ops == &role_ops_cgi) {
|
||||
/* we are not a network connection, but a handler for CGI io */
|
||||
if (wsi->parent && wsi->parent->cgi) {
|
||||
|
||||
|
@ -638,25 +632,21 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
lws_client_stash_destroy(wsi);
|
||||
#endif
|
||||
|
||||
if (lwsi_role(wsi) == LWSI_ROLE_RAW_SOCKET) {
|
||||
wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0);
|
||||
if (wsi->role_ops == &role_ops_raw_skt) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
goto just_kill_connection;
|
||||
}
|
||||
|
||||
if (lwsi_role_http_server(wsi) && wsi->http.fop_fd != NULL) {
|
||||
if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
|
||||
wsi->http.fop_fd != NULL)
|
||||
lws_vfs_file_close(&wsi->http.fop_fd);
|
||||
wsi->vhost->protocols->callback(wsi,
|
||||
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
|
||||
wsi->told_user_closed = 1;
|
||||
}
|
||||
|
||||
if (wsi->socket_is_permanently_unusable ||
|
||||
reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
|
||||
lwsi_state(wsi) == LRS_SHUTDOWN)
|
||||
goto just_kill_connection;
|
||||
|
||||
switch (wsi->wsistate_pre_close & LRS_MASK) {
|
||||
switch (lwsi_state_PRE_CLOSE(wsi)) {
|
||||
case LRS_DEAD_SOCKET:
|
||||
return;
|
||||
|
||||
|
@ -687,16 +677,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
|
||||
goto just_kill_connection;
|
||||
|
||||
if (!wsi->told_user_closed && lwsi_role_http_server(wsi)) {
|
||||
if (!wsi->told_user_closed && lwsi_role_http(wsi) &&
|
||||
lwsi_role_server(wsi)) {
|
||||
if (wsi->user_space && wsi->protocol_bind_balance) {
|
||||
wsi->vhost->protocols->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->protocol_bind_balance = 0;
|
||||
}
|
||||
wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->told_user_closed = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -750,116 +738,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
* LRS_AWAITING_CLOSE_ACK and will skip doing this a second time.
|
||||
*/
|
||||
|
||||
if ((wsi->wsistate_pre_close & LWSIFR_WS) &&
|
||||
(wsi->ws->close_in_ping_buffer_len || /* already a reason */
|
||||
(reason != LWS_CLOSE_STATUS_NOSTATUS &&
|
||||
(reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
|
||||
lwsl_debug("sending close indication...\n");
|
||||
|
||||
/* if no prepared close reason, use 1000 and no aux data */
|
||||
if (!wsi->ws->close_in_ping_buffer_len) {
|
||||
wsi->ws->close_in_ping_buffer_len = 2;
|
||||
wsi->ws->ping_payload_buf[LWS_PRE] =
|
||||
(reason >> 8) & 0xff;
|
||||
wsi->ws->ping_payload_buf[LWS_PRE + 1] =
|
||||
reason & 0xff;
|
||||
}
|
||||
|
||||
lwsl_debug("waiting for chance to send close\n");
|
||||
wsi->waiting_to_send_close_frame = 1;
|
||||
lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
|
||||
__lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
if (wsi->role_ops->close_via_role_protocol &&
|
||||
wsi->role_ops->close_via_role_protocol(wsi, reason))
|
||||
return;
|
||||
}
|
||||
|
||||
just_kill_connection:
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
|
||||
if (wsi->http2_substream && wsi->h2_stream_carries_ws)
|
||||
lws_h2_rst_stream(wsi, 0, "none");
|
||||
|
||||
if (wsi->h2.parent_wsi) {
|
||||
lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi,
|
||||
wsi->h2.parent_wsi);
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
wsi->h2.parent_wsi->h2.child_list) {
|
||||
lwsl_info(" \\---- child %p\n", *w);
|
||||
} lws_end_foreach_llp(w, h2.sibling_list);
|
||||
}
|
||||
|
||||
if (wsi->upgraded_to_http2 || wsi->http2_substream || wsi->client_h2_substream) {
|
||||
lwsl_info("closing %p: parent %p\n", wsi, wsi->h2.parent_wsi);
|
||||
|
||||
if (wsi->h2.child_list) {
|
||||
lwsl_info(" parent %p: closing children: list:\n", wsi);
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
wsi->h2.child_list) {
|
||||
lwsl_info(" \\---- child %p\n", *w);
|
||||
} lws_end_foreach_llp(w, h2.sibling_list);
|
||||
/* trigger closing of all of our http2 children first */
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
wsi->h2.child_list) {
|
||||
lwsl_info(" closing child %p\n", *w);
|
||||
/* disconnect from siblings */
|
||||
wsi2 = (*w)->h2.sibling_list;
|
||||
(*w)->h2.sibling_list = NULL;
|
||||
(*w)->socket_is_permanently_unusable = 1;
|
||||
__lws_close_free_wsi(*w, reason, "h2 child recurse");
|
||||
*w = wsi2;
|
||||
continue;
|
||||
} lws_end_foreach_llp(w, h2.sibling_list);
|
||||
}
|
||||
}
|
||||
|
||||
if (wsi->upgraded_to_http2) {
|
||||
/* remove pps */
|
||||
struct lws_h2_protocol_send *w = wsi->h2.h2n->pps, *w1;
|
||||
while (w) {
|
||||
w1 = w->next;
|
||||
free(w);
|
||||
w = w1;
|
||||
}
|
||||
wsi->h2.h2n->pps = NULL;
|
||||
}
|
||||
|
||||
if ((wsi->client_h2_substream || wsi->http2_substream) &&
|
||||
wsi->h2.parent_wsi) {
|
||||
lwsl_info(" %p: disentangling from siblings\n", wsi);
|
||||
lws_start_foreach_llp(struct lws **, w,
|
||||
wsi->h2.parent_wsi->h2.child_list) {
|
||||
/* disconnect from siblings */
|
||||
if (*w == wsi) {
|
||||
wsi2 = (*w)->h2.sibling_list;
|
||||
(*w)->h2.sibling_list = NULL;
|
||||
*w = wsi2;
|
||||
lwsl_info(" %p disentangled from sibling %p\n",
|
||||
wsi, wsi2);
|
||||
break;
|
||||
}
|
||||
} lws_end_foreach_llp(w, h2.sibling_list);
|
||||
wsi->h2.parent_wsi->h2.child_count--;
|
||||
wsi->h2.parent_wsi = NULL;
|
||||
if (wsi->h2.pending_status_body)
|
||||
lws_free_set_NULL(wsi->h2.pending_status_body);
|
||||
}
|
||||
|
||||
if (wsi->h2_stream_carries_ws) {
|
||||
struct lws *nwsi = lws_get_network_wsi(wsi);
|
||||
|
||||
nwsi->ws_over_h2_count++;
|
||||
/* if no ws, then put a timeout on the parent wsi */
|
||||
if (!nwsi->ws_over_h2_count)
|
||||
__lws_set_timeout(nwsi,
|
||||
PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
|
||||
}
|
||||
|
||||
if (wsi->upgraded_to_http2 && wsi->h2.h2n &&
|
||||
wsi->h2.h2n->rx_scratch)
|
||||
lws_free_set_NULL(wsi->h2.h2n->rx_scratch);
|
||||
#endif
|
||||
if (wsi->role_ops->close_kill_connection)
|
||||
wsi->role_ops->close_kill_connection(wsi, reason);
|
||||
|
||||
lws_remove_child_from_any_parent(wsi);
|
||||
n = 0;
|
||||
|
@ -868,8 +754,7 @@ just_kill_connection:
|
|||
wsi->protocol_bind_balance) {
|
||||
lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
|
||||
wsi->protocol->name);
|
||||
wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->protocol_bind_balance = 0;
|
||||
}
|
||||
|
@ -880,18 +765,6 @@ just_kill_connection:
|
|||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
if (lwsi_role_client(wsi) && !(lwsi_state(wsi) & LWSIFS_NOTEST)) {
|
||||
const struct lws_protocols *pro = wsi->protocol;
|
||||
|
||||
if (!wsi->protocol)
|
||||
pro = &wsi->vhost->protocols[0];
|
||||
pro->callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->told_user_closed = 1;
|
||||
}
|
||||
|
||||
|
||||
#if LWS_POSIX
|
||||
/*
|
||||
* Testing with ab shows that we have to stage the socket close when
|
||||
* the system is under stress... shutdown any further TX, change the
|
||||
|
@ -899,14 +772,13 @@ just_kill_connection:
|
|||
* for the POLLIN to show a zero-size rx before coming back and doing
|
||||
* the actual close.
|
||||
*/
|
||||
if (lwsi_role(wsi) != LWSI_ROLE_RAW_SOCKET &&
|
||||
!lwsi_role_client(wsi) &&
|
||||
if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) &&
|
||||
lwsi_state(wsi) != LRS_SHUTDOWN &&
|
||||
lwsi_state(wsi) != LRS_UNCONNECTED &&
|
||||
reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
|
||||
!wsi->socket_is_permanently_unusable) {
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (lws_is_ssl(wsi) && wsi->ssl) {
|
||||
n = 0;
|
||||
switch (__lws_tls_shutdown(wsi)) {
|
||||
|
@ -952,7 +824,6 @@ just_kill_connection:
|
|||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
|
||||
wsi, wsi->desc.sockfd);
|
||||
|
@ -980,66 +851,22 @@ just_kill_connection:
|
|||
lwsi_set_state(wsi, LRS_DEAD_SOCKET);
|
||||
lws_free_set_NULL(wsi->rxflow_buffer);
|
||||
|
||||
if ((wsi->wsistate_pre_close & LWSIFR_WS) || lwsi_role_ws(wsi)) {
|
||||
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
struct lws **w = &pt->rx_draining_ext_list;
|
||||
|
||||
wsi->ws->rx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->ws->rx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->ws->rx_draining_ext_list);
|
||||
}
|
||||
wsi->ws->rx_draining_ext_list = NULL;
|
||||
}
|
||||
|
||||
if (wsi->ws->tx_draining_ext) {
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->ws->tx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->ws->tx_draining_ext_list);
|
||||
}
|
||||
wsi->ws->tx_draining_ext_list = NULL;
|
||||
}
|
||||
lws_free_set_NULL(wsi->ws->rx_ubuf);
|
||||
|
||||
if (wsi->trunc_alloc)
|
||||
/* not going to be completed... nuke it */
|
||||
lws_free_set_NULL(wsi->trunc_alloc);
|
||||
|
||||
wsi->ws->ping_payload_len = 0;
|
||||
wsi->ws->ping_pending_flag = 0;
|
||||
}
|
||||
if (wsi->role_ops->close_role)
|
||||
wsi->role_ops->close_role(pt, wsi);
|
||||
|
||||
/* tell the user it's all over for this guy */
|
||||
|
||||
if (wsi->protocol && !wsi->told_user_closed &&
|
||||
wsi->protocol->callback &&
|
||||
lwsi_role(wsi) != LWSI_ROLE_RAW_SOCKET &&
|
||||
!(wsi->wsistate_pre_close & LWSIFS_NOTEST)) {
|
||||
if (lwsi_role_client(wsi))
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CLOSED,
|
||||
wsi->user_space, NULL, 0);
|
||||
else
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
|
||||
wsi->user_space, NULL, 0);
|
||||
} else if (lwsi_role_http_server(wsi)) {
|
||||
lwsl_debug("calling back CLOSED_HTTP\n");
|
||||
wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->user_space, NULL, 0 );
|
||||
} else
|
||||
lwsl_debug("not calling back closed role=0x%x state=0x%x\n",
|
||||
lwsi_role(wsi), wsi->wsistate_pre_close);
|
||||
if (lwsi_state_est_PRE_CLOSE(wsi) && !wsi->told_user_closed &&
|
||||
wsi->role_ops->close_cb[lwsi_role_server(wsi)]) {
|
||||
const struct lws_protocols *pro = wsi->protocol;
|
||||
|
||||
if (!wsi->protocol)
|
||||
pro = &wsi->vhost->protocols[0];
|
||||
pro->callback(wsi,
|
||||
wsi->role_ops->close_cb[lwsi_role_server(wsi)],
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->told_user_closed = 1;
|
||||
}
|
||||
|
||||
/* deallocate any active extension contexts */
|
||||
|
||||
|
@ -1084,15 +911,10 @@ __lws_close_free_wsi_final(struct lws *wsi)
|
|||
int n;
|
||||
|
||||
if (lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) {
|
||||
#if LWS_POSIX
|
||||
n = compatible_close(wsi->desc.sockfd);
|
||||
if (n)
|
||||
lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
|
||||
|
||||
#else
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
(void)n;
|
||||
#endif
|
||||
wsi->desc.sockfd = LWS_SOCK_INVALID;
|
||||
}
|
||||
|
||||
|
@ -1147,7 +969,7 @@ lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
LWS_VISIBLE int
|
||||
interface_to_sa(struct lws_vhost *vh, const char *ifname,
|
||||
struct sockaddr_in *addr, size_t addrlen)
|
||||
|
@ -1163,12 +985,10 @@ interface_to_sa(struct lws_vhost *vh, const char *ifname,
|
|||
#endif
|
||||
|
||||
#ifndef LWS_PLAT_OPTEE
|
||||
#if LWS_POSIX
|
||||
static int
|
||||
lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
|
||||
int name_len, char *rip, int rip_len)
|
||||
{
|
||||
#if LWS_POSIX
|
||||
struct addrinfo ai, *res;
|
||||
struct sockaddr_in addr4;
|
||||
|
||||
|
@ -1233,24 +1053,12 @@ lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
|
|||
return -1;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
(void)vh;
|
||||
(void)ads;
|
||||
(void)name;
|
||||
(void)name_len;
|
||||
(void)rip;
|
||||
(void)rip_len;
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
LWS_VISIBLE const char *
|
||||
lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
|
||||
{
|
||||
#if LWS_POSIX
|
||||
socklen_t len, olen;
|
||||
#ifdef LWS_WITH_IPV6
|
||||
struct sockaddr_in6 sin6;
|
||||
|
@ -1259,10 +1067,7 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
|
|||
int af = AF_INET;
|
||||
void *p, *q;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->http2_substream)
|
||||
wsi = wsi->h2.parent_wsi;
|
||||
#endif
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
if (wsi->parent_carries_io)
|
||||
wsi = wsi->parent;
|
||||
|
@ -1288,9 +1093,6 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
|
|||
}
|
||||
|
||||
return lws_plat_inet_ntop(af, q, name, namelen);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1299,7 +1101,6 @@ lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
|
|||
int name_len, char *rip, int rip_len)
|
||||
{
|
||||
#ifndef LWS_PLAT_OPTEE
|
||||
#if LWS_POSIX
|
||||
socklen_t len;
|
||||
#ifdef LWS_WITH_IPV6
|
||||
struct sockaddr_in6 sin6;
|
||||
|
@ -1334,7 +1135,6 @@ lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
|
|||
|
||||
bail:
|
||||
lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1);
|
||||
#endif
|
||||
#endif
|
||||
(void)wsi;
|
||||
(void)fd;
|
||||
|
@ -1631,9 +1431,6 @@ lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2)
|
|||
return (int)(t1 - t2);
|
||||
}
|
||||
|
||||
|
||||
#if LWS_POSIX
|
||||
|
||||
LWS_VISIBLE lws_sockfd_type
|
||||
lws_get_socket_fd(struct lws *wsi)
|
||||
{
|
||||
|
@ -1642,8 +1439,6 @@ lws_get_socket_fd(struct lws *wsi)
|
|||
return wsi->desc.sockfd;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LWS_LATENCY
|
||||
void
|
||||
lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
|
||||
|
@ -1961,27 +1756,6 @@ lws_get_protocol(struct lws *wsi)
|
|||
return wsi->protocol;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_is_final_fragment(struct lws *wsi)
|
||||
{
|
||||
lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
|
||||
wsi->ws->final, (long)wsi->ws->rx_packet_length,
|
||||
(long)wsi->ws->rx_draining_ext);
|
||||
return wsi->ws->final && !wsi->ws->rx_packet_length &&
|
||||
!wsi->ws->rx_draining_ext;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_is_first_fragment(struct lws *wsi)
|
||||
{
|
||||
return wsi->ws->first_fragment;
|
||||
}
|
||||
|
||||
LWS_VISIBLE unsigned char
|
||||
lws_get_reserved_bits(struct lws *wsi)
|
||||
{
|
||||
return wsi->ws->rsv;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ensure_user_space(struct lws *wsi)
|
||||
|
@ -2198,7 +1972,7 @@ lwsl_hexdump(const void *vbuf, size_t len)
|
|||
LWS_VISIBLE int
|
||||
lws_is_ssl(struct lws *wsi)
|
||||
{
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
return wsi->use_ssl & LCCSCF_USE_SSL;
|
||||
#else
|
||||
(void)wsi;
|
||||
|
@ -2206,7 +1980,7 @@ lws_is_ssl(struct lws *wsi)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
|
||||
LWS_VISIBLE lws_tls_conn*
|
||||
lws_get_ssl(struct lws *wsi)
|
||||
{
|
||||
|
@ -2223,24 +1997,28 @@ lws_partial_buffered(struct lws *wsi)
|
|||
LWS_VISIBLE size_t
|
||||
lws_get_peer_write_allowance(struct lws *wsi)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
/* only if we are using HTTP2 on this connection */
|
||||
if (!lwsi_role_h2(wsi))
|
||||
return -1;
|
||||
if (wsi->role_ops->tx_credit)
|
||||
return wsi->role_ops->tx_credit(wsi);
|
||||
|
||||
return lws_h2_tx_cr_get(wsi);
|
||||
#else
|
||||
(void)wsi;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state)
|
||||
lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state,
|
||||
struct lws_role_ops *ops)
|
||||
{
|
||||
lwsl_debug("%s: %p: role 0x%x, state 0x%x\n", __func__, wsi, role, state);
|
||||
lwsi_set_role(wsi, role);
|
||||
lwsi_set_state(wsi, state);
|
||||
#if defined(_DEBUG)
|
||||
const char *name = "(unset)";
|
||||
#endif
|
||||
wsi->wsistate = role | state;
|
||||
if (ops)
|
||||
wsi->role_ops = ops;
|
||||
#if defined(_DEBUG)
|
||||
if (wsi->role_ops)
|
||||
name = wsi->role_ops->name;
|
||||
lwsl_debug("%s: %p: wsistate 0x%x, ops %s\n", __func__, wsi,
|
||||
wsi->wsistate, name);
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE struct lws_plat_file_ops *
|
||||
|
@ -2319,38 +2097,6 @@ lws_clear_child_pending_on_writable(struct lws *wsi)
|
|||
wsi->parent_pending_cb_on_writable = 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_get_close_length(struct lws *wsi)
|
||||
{
|
||||
return wsi->ws->close_in_ping_buffer_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN unsigned char *
|
||||
lws_get_close_payload(struct lws *wsi)
|
||||
{
|
||||
return &wsi->ws->ping_payload_buf[LWS_PRE];
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_close_reason(struct lws *wsi, enum lws_close_status status,
|
||||
unsigned char *buf, size_t len)
|
||||
{
|
||||
unsigned char *p, *start;
|
||||
int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE;
|
||||
|
||||
assert(lwsi_role_ws(wsi));
|
||||
|
||||
start = p = &wsi->ws->ping_payload_buf[LWS_PRE];
|
||||
|
||||
*p++ = (((int)status) >> 8) & 0xff;
|
||||
*p++ = ((int)status) & 0xff;
|
||||
|
||||
if (buf)
|
||||
while (len-- && p < start + budget)
|
||||
*p++ = *buf++;
|
||||
|
||||
wsi->ws->close_in_ping_buffer_len = lws_ptr_diff(p, start);
|
||||
}
|
||||
|
||||
LWS_EXTERN int
|
||||
__lws_rx_flow_control(struct lws *wsi)
|
||||
|
@ -2538,7 +2284,6 @@ LWS_EXTERN int
|
|||
lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
|
||||
const char *iface)
|
||||
{
|
||||
#if LWS_POSIX
|
||||
#ifdef LWS_WITH_UNIX_SOCK
|
||||
struct sockaddr_un serv_unix;
|
||||
#endif
|
||||
|
@ -2656,7 +2401,6 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
|
|||
memcpy(&sain, &sin, sizeof(sain));
|
||||
port = ntohs(sain.sin_port);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return port;
|
||||
|
@ -2823,15 +2567,7 @@ bail:
|
|||
|
||||
#endif
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_restart_ws_ping_pong_timer(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->context->ws_ping_pong_interval ||
|
||||
!lwsi_role_ws(wsi))
|
||||
return;
|
||||
|
||||
wsi->ws->time_next_ping_check = (time_t)lws_now_secs();
|
||||
}
|
||||
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
|
||||
|
@ -3123,7 +2859,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
|
|||
" \"h2_subs\":\"%lu\""
|
||||
,
|
||||
vh->name, vh->listen_port,
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
vh->use_ssl & LCCSCF_USE_SSL,
|
||||
#else
|
||||
0,
|
||||
|
|
|
@ -39,8 +39,6 @@ extern "C" {
|
|||
* CARE: everything using cmake defines needs to be below here
|
||||
*/
|
||||
|
||||
#define LWS_POSIX 1
|
||||
|
||||
#if defined(LWS_HAS_INTPTR_T)
|
||||
#include <stdint.h>
|
||||
#define lws_intptr_t intptr_t
|
||||
|
@ -180,7 +178,7 @@ typedef unsigned long long lws_intptr_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
|
@ -1648,7 +1646,7 @@ struct lws_vhost;
|
|||
*/
|
||||
///@{
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
#include <mbedtls/sha1.h>
|
||||
|
@ -2808,7 +2806,7 @@ struct lws_context_creation_info {
|
|||
int ka_interval;
|
||||
/**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes
|
||||
* attempt */
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_CTX *provided_client_ssl_ctx;
|
||||
/**< CONTEXT: If non-null, swap out libwebsockets ssl
|
||||
* implementation for the one provided by provided_ssl_ctx.
|
||||
|
@ -5931,14 +5929,6 @@ lws_get_close_payload(struct lws *wsi);
|
|||
LWS_VISIBLE LWS_EXTERN
|
||||
struct lws *lws_get_network_wsi(struct lws *wsi);
|
||||
|
||||
/*
|
||||
* \deprecated DEPRECATED Note: this is not normally needed as a user api.
|
||||
* It's provided in case it is
|
||||
* useful when integrating with other app poll loop service code.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
|
||||
|
||||
/**
|
||||
* lws_set_allocator() - custom allocator support
|
||||
*
|
||||
|
@ -6047,7 +6037,7 @@ struct lws_wifi_scan { /* generic wlan scan item */
|
|||
uint8_t authmode;
|
||||
};
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
|
||||
/**
|
||||
* lws_get_ssl() - Return wsi's SSL context structure
|
||||
* \param wsi: websocket connection
|
||||
|
|
434
lib/output.c
434
lib/output.c
|
@ -21,25 +21,6 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
static int
|
||||
lws_0405_frame_mask_generate(struct lws *wsi)
|
||||
{
|
||||
int n;
|
||||
/* fetch the per-frame nonce */
|
||||
|
||||
n = lws_get_random(lws_get_context(wsi), wsi->ws->mask, 4);
|
||||
if (n != 4) {
|
||||
lwsl_parser("Unable to read from random device %s %d\n",
|
||||
SYSTEM_RANDOM_FILEPATH, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start masking from first byte of masking key buffer */
|
||||
wsi->ws->mask_idx = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* notice this returns number of bytes consumed, or -1
|
||||
*/
|
||||
|
@ -63,11 +44,12 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
*/
|
||||
if (wsi->could_have_pending) {
|
||||
lwsl_hexdump_level(LLL_ERR, buf, len);
|
||||
lwsl_err("** %p: vh: %s, prot: %s, "
|
||||
lwsl_err("** %p: vh: %s, prot: %s, role %s: "
|
||||
"Illegal back-to-back write of %lu detected...\n",
|
||||
wsi, wsi->vhost->name, wsi->protocol->name,
|
||||
wsi->role_ops->name,
|
||||
(unsigned long)len);
|
||||
// assert(0);
|
||||
assert(0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -215,12 +197,6 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
enum lws_write_protocol wp)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int masked7 = lwsi_role_client(wsi);
|
||||
unsigned char is_masked_bit = 0;
|
||||
unsigned char *dropmask = NULL;
|
||||
struct lws_tokens eff_buf;
|
||||
size_t orig_len = len;
|
||||
int pre = 0, n, wp1f = wp & 0x1f;
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
struct lws_write_passthru pas;
|
||||
|
@ -255,361 +231,11 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.tx += len;
|
||||
|
||||
if (wsi->ws && wsi->ws->tx_draining_ext && lwsi_role_ws(wsi)) {
|
||||
/* remove us from the list */
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
assert(wsi->role_ops);
|
||||
if (!wsi->role_ops->write_role_protocol)
|
||||
return lws_issue_raw(wsi, buf, len);
|
||||
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->ws->tx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->ws->tx_draining_ext_list);
|
||||
}
|
||||
wsi->ws->tx_draining_ext_list = NULL;
|
||||
wp = (wsi->ws->tx_draining_stashed_wp & 0xc0) |
|
||||
LWS_WRITE_CONTINUATION;
|
||||
wp1f = wp & 0x1f;
|
||||
|
||||
lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
|
||||
}
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
if (wp1f == LWS_WRITE_HTTP ||
|
||||
wp1f == LWS_WRITE_HTTP_FINAL ||
|
||||
wp1f == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
|
||||
wp1f == LWS_WRITE_HTTP_HEADERS)
|
||||
goto send_raw;
|
||||
|
||||
/* if not in a state to send ws stuff, then just send nothing */
|
||||
|
||||
if (!lwsi_role_ws(wsi) &&
|
||||
((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK) ||
|
||||
wp1f != LWS_WRITE_CLOSE)) {
|
||||
//assert(0);
|
||||
lwsl_debug("binning %d %d\n", lwsi_state(wsi), wp1f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if we are continuing a frame that already had its header done */
|
||||
|
||||
if (wsi->ws->inside_frame) {
|
||||
lwsl_debug("INSIDE FRAME\n");
|
||||
goto do_more_inside_frame;
|
||||
}
|
||||
|
||||
wsi->ws->clean_buffer = 1;
|
||||
|
||||
/*
|
||||
* give a chance to the extensions to modify payload
|
||||
* the extension may decide to produce unlimited payload erratically
|
||||
* (eg, compression extension), so we require only that if he produces
|
||||
* something, it will be a complete fragment of the length known at
|
||||
* the time (just the fragment length known), and if he has
|
||||
* more we will come back next time he is writeable and allow him to
|
||||
* produce more fragments until he's drained.
|
||||
*
|
||||
* This allows what is sent each time it is writeable to be limited to
|
||||
* a size that can be sent without partial sends or blocking, allows
|
||||
* interleaving of control frames and other connection service.
|
||||
*/
|
||||
eff_buf.token = (char *)buf;
|
||||
eff_buf.token_len = (int)len;
|
||||
|
||||
switch ((int)wp) {
|
||||
case LWS_WRITE_PING:
|
||||
case LWS_WRITE_PONG:
|
||||
case LWS_WRITE_CLOSE:
|
||||
break;
|
||||
default:
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
if (n && eff_buf.token_len) {
|
||||
lwsl_debug("drain len %d\n", (int)eff_buf.token_len);
|
||||
/* extension requires further draining */
|
||||
wsi->ws->tx_draining_ext = 1;
|
||||
wsi->ws->tx_draining_ext_list =
|
||||
pt->tx_draining_ext_list;
|
||||
pt->tx_draining_ext_list = wsi;
|
||||
/* we must come back to do more */
|
||||
lws_callback_on_writable(wsi);
|
||||
/*
|
||||
* keep a copy of the write type for the overall
|
||||
* action that has provoked generation of these
|
||||
* fragments, so the last guy can use its FIN state.
|
||||
*/
|
||||
wsi->ws->tx_draining_stashed_wp = wp;
|
||||
/* this is definitely not actually the last fragment
|
||||
* because the extension asserted he has more coming
|
||||
* So make sure this intermediate one doesn't go out
|
||||
* with a FIN.
|
||||
*/
|
||||
wp |= LWS_WRITE_NO_FIN;
|
||||
}
|
||||
#endif
|
||||
if (eff_buf.token_len && wsi->ws->stashed_write_pending) {
|
||||
wsi->ws->stashed_write_pending = 0;
|
||||
wp = (wp &0xc0) | (int)wsi->ws->stashed_write_type;
|
||||
wp1f = wp & 0x1f;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* an extension did something we need to keep... for example, if
|
||||
* compression extension, it has already updated its state according
|
||||
* to this being issued
|
||||
*/
|
||||
if ((char *)buf != eff_buf.token) {
|
||||
/*
|
||||
* ext might eat it, but not have anything to issue yet.
|
||||
* In that case we have to follow his lead, but stash and
|
||||
* replace the write type that was lost here the first time.
|
||||
*/
|
||||
if (len && !eff_buf.token_len) {
|
||||
if (!wsi->ws->stashed_write_pending)
|
||||
wsi->ws->stashed_write_type = (char)wp & 0x3f;
|
||||
wsi->ws->stashed_write_pending = 1;
|
||||
return (int)len;
|
||||
}
|
||||
/*
|
||||
* extension recreated it:
|
||||
* need to buffer this if not all sent
|
||||
*/
|
||||
wsi->ws->clean_buffer = 0;
|
||||
}
|
||||
|
||||
buf = (unsigned char *)eff_buf.token;
|
||||
len = eff_buf.token_len;
|
||||
|
||||
if (!buf) {
|
||||
lwsl_err("null buf (%d)\n", (int)len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (wsi->ws->ietf_spec_revision) {
|
||||
case 13:
|
||||
if (masked7) {
|
||||
pre += 4;
|
||||
dropmask = &buf[0 - pre];
|
||||
is_masked_bit = 0x80;
|
||||
}
|
||||
|
||||
switch (wp & 0xf) {
|
||||
case LWS_WRITE_TEXT:
|
||||
n = LWSWSOPC_TEXT_FRAME;
|
||||
break;
|
||||
case LWS_WRITE_BINARY:
|
||||
n = LWSWSOPC_BINARY_FRAME;
|
||||
break;
|
||||
case LWS_WRITE_CONTINUATION:
|
||||
n = LWSWSOPC_CONTINUATION;
|
||||
break;
|
||||
|
||||
case LWS_WRITE_CLOSE:
|
||||
n = LWSWSOPC_CLOSE;
|
||||
break;
|
||||
case LWS_WRITE_PING:
|
||||
n = LWSWSOPC_PING;
|
||||
break;
|
||||
case LWS_WRITE_PONG:
|
||||
n = LWSWSOPC_PONG;
|
||||
break;
|
||||
default:
|
||||
lwsl_warn("lws_write: unknown write opc / wp\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
n |= 1 << 7;
|
||||
|
||||
if (len < 126) {
|
||||
pre += 2;
|
||||
buf[-pre] = n;
|
||||
buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
|
||||
} else {
|
||||
if (len < 65536) {
|
||||
pre += 4;
|
||||
buf[-pre] = n;
|
||||
buf[-pre + 1] = 126 | is_masked_bit;
|
||||
buf[-pre + 2] = (unsigned char)(len >> 8);
|
||||
buf[-pre + 3] = (unsigned char)len;
|
||||
} else {
|
||||
pre += 10;
|
||||
buf[-pre] = n;
|
||||
buf[-pre + 1] = 127 | is_masked_bit;
|
||||
#if defined __LP64__
|
||||
buf[-pre + 2] = (len >> 56) & 0x7f;
|
||||
buf[-pre + 3] = len >> 48;
|
||||
buf[-pre + 4] = len >> 40;
|
||||
buf[-pre + 5] = len >> 32;
|
||||
#else
|
||||
buf[-pre + 2] = 0;
|
||||
buf[-pre + 3] = 0;
|
||||
buf[-pre + 4] = 0;
|
||||
buf[-pre + 5] = 0;
|
||||
#endif
|
||||
buf[-pre + 6] = (unsigned char)(len >> 24);
|
||||
buf[-pre + 7] = (unsigned char)(len >> 16);
|
||||
buf[-pre + 8] = (unsigned char)(len >> 8);
|
||||
buf[-pre + 9] = (unsigned char)len;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
do_more_inside_frame:
|
||||
|
||||
/*
|
||||
* Deal with masking if we are in client -> server direction and
|
||||
* the wp demands it
|
||||
*/
|
||||
|
||||
if (masked7) {
|
||||
if (!wsi->ws->inside_frame)
|
||||
if (lws_0405_frame_mask_generate(wsi)) {
|
||||
lwsl_err("frame mask generation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* in v7, just mask the payload
|
||||
*/
|
||||
if (dropmask) { /* never set if already inside frame */
|
||||
for (n = 4; n < (int)len + 4; n++)
|
||||
dropmask[n] = dropmask[n] ^ wsi->ws->mask[
|
||||
(wsi->ws->mask_idx++) & 3];
|
||||
|
||||
/* copy the frame nonce into place */
|
||||
memcpy(dropmask, wsi->ws->mask, 4);
|
||||
}
|
||||
}
|
||||
|
||||
send_raw:
|
||||
switch (wp1f) {
|
||||
case LWS_WRITE_TEXT:
|
||||
case LWS_WRITE_BINARY:
|
||||
case LWS_WRITE_CONTINUATION:
|
||||
if (!wsi->h2_stream_carries_ws)
|
||||
break;
|
||||
/* fallthru */
|
||||
case LWS_WRITE_CLOSE:
|
||||
/* lwsl_hexdump(&buf[-pre], len); */
|
||||
case LWS_WRITE_HTTP:
|
||||
case LWS_WRITE_HTTP_FINAL:
|
||||
case LWS_WRITE_HTTP_HEADERS:
|
||||
case LWS_WRITE_HTTP_HEADERS_CONTINUATION:
|
||||
case LWS_WRITE_PONG:
|
||||
case LWS_WRITE_PING:
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
/*
|
||||
* ws-over-h2 also ends up here after the ws framing applied
|
||||
*/
|
||||
if (lwsi_role_h2(wsi)) {
|
||||
unsigned char flags = 0;
|
||||
|
||||
n = LWS_H2_FRAME_TYPE_DATA;
|
||||
if (wp1f == LWS_WRITE_HTTP_HEADERS) {
|
||||
n = LWS_H2_FRAME_TYPE_HEADERS;
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
flags = LWS_H2_FLAG_END_HEADERS;
|
||||
if (wsi->h2.send_END_STREAM ||
|
||||
(wp & LWS_WRITE_H2_STREAM_END)) {
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->h2.send_END_STREAM = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (wp1f == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
|
||||
n = LWS_H2_FRAME_TYPE_CONTINUATION;
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
flags = LWS_H2_FLAG_END_HEADERS;
|
||||
if (wsi->h2.send_END_STREAM ||
|
||||
(wp & LWS_WRITE_H2_STREAM_END)) {
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->h2.send_END_STREAM = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((wp1f == LWS_WRITE_HTTP ||
|
||||
wp1f == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->http.tx_content_length) {
|
||||
wsi->http.tx_content_remain -= len;
|
||||
lwsl_info("%s: wsi %p: tx_content_remain = %llu\n",
|
||||
__func__, wsi,
|
||||
(unsigned long long)wsi->http.tx_content_remain);
|
||||
if (!wsi->http.tx_content_remain) {
|
||||
lwsl_info("%s: selecting final write mode\n",
|
||||
__func__);
|
||||
wp = LWS_WRITE_HTTP_FINAL;
|
||||
wp1f = wp & 0x1f;
|
||||
}
|
||||
}
|
||||
|
||||
if (wp1f == LWS_WRITE_HTTP_FINAL ||
|
||||
(wp & LWS_WRITE_H2_STREAM_END)) {
|
||||
//lws_get_network_wsi(wsi)->h2.END_STREAM) {
|
||||
lwsl_info("%s: setting END_STREAM\n", __func__);
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->h2.send_END_STREAM = 1;
|
||||
}
|
||||
|
||||
/* if any ws framing, account for that too */
|
||||
return lws_h2_frame_write(wsi, n, flags, wsi->h2.my_sid,
|
||||
(int)len + pre, buf - pre);
|
||||
}
|
||||
#endif
|
||||
return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* give any active extensions a chance to munge the buffer
|
||||
* before send. We pass in a pointer to an lws_tokens struct
|
||||
* prepared with the default buffer and content length that's in
|
||||
* there. Rather than rewrite the default buffer, extensions
|
||||
* that expect to grow the buffer can adapt .token to
|
||||
* point to their own per-connection buffer in the extension
|
||||
* user allocation. By default with no extensions or no
|
||||
* extension callback handling, just the normal input buffer is
|
||||
* used then so it is efficient.
|
||||
*
|
||||
* callback returns 1 in case it wants to spill more buffers
|
||||
*
|
||||
* This takes care of holding the buffer if send is incomplete, ie,
|
||||
* if wsi->ws->clean_buffer is 0 (meaning an extension meddled with
|
||||
* the buffer). If wsi->ws->clean_buffer is 1, it will instead
|
||||
* return to the user code how much OF THE USER BUFFER was consumed.
|
||||
*/
|
||||
|
||||
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
|
||||
wsi->ws->inside_frame = 1;
|
||||
if (n <= 0)
|
||||
return n;
|
||||
|
||||
if (n == (int)len + pre) {
|
||||
/* everything in the buffer was handled (or rebuffered...) */
|
||||
wsi->ws->inside_frame = 0;
|
||||
return (int)orig_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* it is how many bytes of user buffer got sent... may be < orig_len
|
||||
* in which case callback when writable has already been arranged
|
||||
* and user code can call lws_write() again with the rest
|
||||
* later.
|
||||
*/
|
||||
|
||||
return n - pre;
|
||||
return wsi->role_ops->write_role_protocol(wsi, buf, len, &wp);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
||||
|
@ -688,26 +314,29 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
poss = wsi->http.tx_content_remain;
|
||||
|
||||
/*
|
||||
* if there is a hint about how much we will do well to send at one time,
|
||||
* restrict ourselves to only trying to send that.
|
||||
* if there is a hint about how much we will do well to send at
|
||||
* one time, restrict ourselves to only trying to send that.
|
||||
*/
|
||||
if (wsi->protocol->tx_packet_size &&
|
||||
poss > wsi->protocol->tx_packet_size)
|
||||
poss = wsi->protocol->tx_packet_size;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
m = lws_h2_tx_cr_get(wsi);
|
||||
if (!m) {
|
||||
lwsl_info("%s: came here with no tx credit\n", __func__);
|
||||
return 0;
|
||||
if (wsi->role_ops->tx_credit) {
|
||||
lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
|
||||
|
||||
if (!txc) {
|
||||
lwsl_info("%s: came here with no tx credit\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
if (txc < poss)
|
||||
poss = txc;
|
||||
|
||||
/*
|
||||
* consumption of the actual payload amount sent will be
|
||||
* handled when the role data frame is sent
|
||||
*/
|
||||
}
|
||||
if ((lws_filepos_t)m < poss)
|
||||
poss = m;
|
||||
/*
|
||||
* consumption of the actual payload amount sent will be handled
|
||||
* when the http2 data frame is sent
|
||||
*/
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges) {
|
||||
|
@ -854,7 +483,6 @@ file_had_it:
|
|||
return -1;
|
||||
}
|
||||
|
||||
#if LWS_POSIX
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
|
@ -879,12 +507,12 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
|||
|
||||
return n;
|
||||
}
|
||||
#if LWS_POSIX
|
||||
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK ||
|
||||
LWS_ERRNO == LWS_EINTR)
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
#endif
|
||||
|
||||
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
@ -894,7 +522,6 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
|||
{
|
||||
int n = 0;
|
||||
|
||||
#if LWS_POSIX
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
if (wsi->trunc_len)
|
||||
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
|
||||
|
@ -915,20 +542,13 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
|||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
#else
|
||||
(void)n;
|
||||
(void)wsi;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
// !!!
|
||||
#endif
|
||||
|
||||
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
|
||||
len, wsi->desc.sockfd, n, LWS_ERRNO);
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_pending_no_ssl(struct lws *wsi)
|
||||
{
|
||||
|
|
|
@ -217,7 +217,7 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!pt->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
|
||||
#else
|
||||
|
|
|
@ -140,7 +140,7 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
#if 1
|
||||
n = poll(pt->fds, pt->fds_count, timeout_ms);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!pt->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
|
||||
#else
|
||||
|
|
|
@ -254,7 +254,7 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!n && !pt->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
|
||||
#else
|
||||
|
|
71
lib/pollfd.c
71
lib/pollfd.c
|
@ -172,9 +172,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
* ... and the service thread is waiting ...
|
||||
* then cancel it to force a restart with our changed events
|
||||
*/
|
||||
#if LWS_POSIX
|
||||
pa_events = pa->prev_events != pa->events;
|
||||
#endif
|
||||
|
||||
if (pa_events) {
|
||||
if (lws_plat_change_pollfd(context, wsi, pfd)) {
|
||||
|
@ -264,11 +262,7 @@ __insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
wsi->position_in_fds_table = pt->fds_count;
|
||||
|
||||
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
|
||||
#if LWS_POSIX
|
||||
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
|
||||
#else
|
||||
pt->fds[wsi->position_in_fds_table].events = 0;
|
||||
#endif
|
||||
pa.events = pt->fds[pt->fds_count].events;
|
||||
|
||||
lws_plat_insert_socket_into_fds(context, wsi);
|
||||
|
@ -422,10 +416,6 @@ LWS_VISIBLE int
|
|||
lws_callback_on_writable(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
struct lws *network_wsi, *wsi2;
|
||||
int already;
|
||||
#endif
|
||||
int n;
|
||||
|
||||
if (lwsi_state(wsi) == LRS_SHUTDOWN)
|
||||
|
@ -461,66 +451,13 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
lwsl_info("%s: %p (role/state 0x%x)\n", __func__, wsi, wsi->wsistate);
|
||||
|
||||
if (!lwsi_role_h2(wsi))
|
||||
goto network_sock;
|
||||
|
||||
if (wsi->h2.requested_POLLOUT
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
&& !wsi->client_h2_alpn
|
||||
#endif
|
||||
) {
|
||||
lwsl_info("already pending writable\n");
|
||||
return 1;
|
||||
if (wsi->role_ops->callback_on_writable) {
|
||||
if (wsi->role_ops->callback_on_writable(wsi))
|
||||
return 1;
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
}
|
||||
|
||||
/* is this for DATA or for control messages? */
|
||||
if (wsi->upgraded_to_http2 && !wsi->h2.h2n->pps &&
|
||||
!lws_h2_tx_cr_get(wsi)) {
|
||||
/*
|
||||
* other side is not able to cope with us sending DATA
|
||||
* anything so no matter if we have POLLOUT on our side if it's
|
||||
* DATA we want to send.
|
||||
*
|
||||
* Delay waiting for our POLLOUT until peer indicates he has
|
||||
* space for more using tx window command in http2 layer
|
||||
*/
|
||||
lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi,
|
||||
wsi->h2.tx_cr);
|
||||
wsi->h2.skint = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
wsi->h2.skint = 0;
|
||||
network_wsi = lws_get_network_wsi(wsi);
|
||||
already = network_wsi->h2.requested_POLLOUT;
|
||||
|
||||
/* mark everybody above him as requesting pollout */
|
||||
|
||||
wsi2 = wsi;
|
||||
while (wsi2) {
|
||||
wsi2->h2.requested_POLLOUT = 1;
|
||||
lwsl_info("mark %p pending writable\n", wsi2);
|
||||
wsi2 = wsi2->h2.parent_wsi;
|
||||
}
|
||||
|
||||
/* for network action, act only on the network wsi */
|
||||
|
||||
wsi = network_wsi;
|
||||
if (already && !wsi->client_h2_alpn
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
&& !wsi->client_h2_substream
|
||||
#endif
|
||||
)
|
||||
return 1;
|
||||
network_sock:
|
||||
#endif
|
||||
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
|
||||
return 1;
|
||||
|
||||
if (wsi->position_in_fds_table < 0) {
|
||||
lwsl_debug("%s: failed to find socket %d\n", __func__,
|
||||
wsi->desc.sockfd);
|
||||
|
|
|
@ -210,7 +210,7 @@ int fork(void);
|
|||
#define strerror(x) ""
|
||||
#endif
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
|
@ -413,7 +413,7 @@ extern "C" {
|
|||
* Choose the SSL backend
|
||||
*/
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
#if defined(LWS_WITH_MBEDTLS________)
|
||||
struct lws_tls_mbed_ctx {
|
||||
|
||||
|
@ -482,107 +482,89 @@ enum lws_websocket_opcodes_07 {
|
|||
typedef uint32_t lws_wsi_state_t;
|
||||
|
||||
/*
|
||||
* 31 16 15 0
|
||||
* [ role ] [ state ]
|
||||
* The wsi->role_ops pointer decides almost everything about what role the wsi
|
||||
* will play, h2, raw, ws, etc.
|
||||
*
|
||||
* The role part is generally invariant for the lifetime of the wsi, although
|
||||
* it can change if the connection role itself does, eg, if the connection
|
||||
* upgrades from H1 -> WS1 the role is changed at that point.
|
||||
* However there are a few additional flags needed that vary, such as if the
|
||||
* role is a client or server side, if it has that concept. And the connection
|
||||
* fulfilling the role, has a separate dynamic state.
|
||||
*
|
||||
* 31 16 15 0
|
||||
* [ role flags ] [ state ]
|
||||
*
|
||||
* The role flags part is generally invariant for the lifetime of the wsi,
|
||||
* although it can change if the connection role itself does, eg, if the
|
||||
* connection upgrades from H1 -> WS1 the role flags may be changed at that
|
||||
* point.
|
||||
*
|
||||
* The state part reflects the dynamic connection state, and the states are
|
||||
* reused between roles.
|
||||
*
|
||||
* None of the internal role or state representations are made available outside
|
||||
* of lws internals.
|
||||
* of lws internals. Even for lws internals, if you add stuff here, please keep
|
||||
* the constants inside this header only by adding necessary helpers here and
|
||||
* use the helpers in the actual code. This is to ease any future refactors.
|
||||
*
|
||||
* Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our
|
||||
* data as a stream inside a different protocol.
|
||||
*/
|
||||
|
||||
#define _RS 16
|
||||
|
||||
#define LWSIFR_HTTP (0x008 << _RS)
|
||||
#define LWSIFR_H2 (0x010 << _RS)
|
||||
#define LWSIFR_CLIENT (0x020 << _RS)
|
||||
#define LWSIFR_SERVER (0x040 << _RS)
|
||||
#define LWSIFR_WS (0x080 << _RS)
|
||||
#define LWSIFR_RAW (0x100 << _RS)
|
||||
#define LWSIFR_CLIENT (0x1000 << _RS) /* client side */
|
||||
#define LWSIFR_SERVER (0x2000 << _RS) /* server side */
|
||||
|
||||
#define LWSIFR_P_ENCAP_H2 (0x0100 << _RS) /* we are encapsulated by h2 */
|
||||
|
||||
enum lwsi_role {
|
||||
LWSI_ROLE_UNSET = (0 << _RS),
|
||||
|
||||
LWSI_ROLE_H1_SERVER = (LWSIFR_SERVER | LWSIFR_HTTP ),
|
||||
LWSI_ROLE_H2_SERVER = (LWSIFR_SERVER | LWSIFR_HTTP | LWSIFR_H2),
|
||||
LWSI_ROLE_H1_CLIENT = (LWSIFR_CLIENT | LWSIFR_HTTP ),
|
||||
LWSI_ROLE_H2_CLIENT = (LWSIFR_CLIENT | LWSIFR_HTTP | LWSIFR_H2),
|
||||
LWSI_ROLE_WS1_SERVER = (LWSIFR_SERVER | LWSIFR_WS ),
|
||||
LWSI_ROLE_WS2_SERVER = (LWSIFR_SERVER | LWSIFR_WS | LWSIFR_H2),
|
||||
LWSI_ROLE_WS1_CLIENT = (LWSIFR_CLIENT | LWSIFR_WS ),
|
||||
LWSI_ROLE_WS2_CLIENT = (LWSIFR_CLIENT | LWSIFR_WS | LWSIFR_H2),
|
||||
|
||||
LWSI_ROLE_CGI = (1 << _RS),
|
||||
LWSI_ROLE_LISTEN_SOCKET = (2 << _RS),
|
||||
LWSI_ROLE_EVENT_PIPE = (3 << _RS),
|
||||
LWSI_ROLE_RAW_FILE = (LWSIFR_RAW | (4 << _RS)),
|
||||
LWSI_ROLE_RAW_SOCKET = (LWSIFR_RAW | (5 << _RS)),
|
||||
|
||||
LWSI_ROLE_MASK = (0xffff << _RS),
|
||||
LWSI_ROLE_MASK = (0xffff << _RS),
|
||||
LWSI_ROLE_ENCAP_MASK = (0x0f00 << _RS),
|
||||
};
|
||||
|
||||
#define lwsi_role(wsi) \
|
||||
(wsi->wsistate & LWSI_ROLE_MASK)
|
||||
#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK)
|
||||
#if !defined (_DEBUG)
|
||||
#define lwsi_set_role(wsi, role) wsi->wsistate = \
|
||||
(wsi->wsistate & (~LWSI_ROLE_MASK)) | role
|
||||
#else
|
||||
void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
|
||||
#endif
|
||||
#define lwsi_role_ws(wsi) (!!(wsi->wsistate & LWSIFR_WS))
|
||||
#define lwsi_role_ws_client(wsi) \
|
||||
((wsi->wsistate & (LWSIFR_CLIENT | LWSIFR_WS))\
|
||||
== (LWSIFR_CLIENT | LWSIFR_WS))
|
||||
#define lwsi_role_non_ws_client(wsi) \
|
||||
((wsi->wsistate & (LWSIFR_CLIENT | LWSIFR_WS))\
|
||||
== (LWSIFR_CLIENT))
|
||||
|
||||
#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
|
||||
#define lwsi_role_raw(wsi) (!!(wsi->wsistate & LWSIFR_RAW))
|
||||
#define lwsi_role_http_server(wsi) \
|
||||
((wsi->wsistate & (LWSIFR_SERVER | LWSIFR_HTTP))\
|
||||
== (LWSIFR_SERVER | LWSIFR_HTTP))
|
||||
#define lwsi_role_http_client(wsi) \
|
||||
((wsi->wsistate & (LWSIFR_CLIENT | LWSIFR_HTTP))\
|
||||
== (LWSIFR_CLIENT | LWSIFR_HTTP))
|
||||
#define lwsi_role_http(wsi) (!!(wsi->wsistate & LWSIFR_HTTP))
|
||||
#define lwsi_role_h2(wsi) (!!(wsi->wsistate & LWSIFR_H2))
|
||||
#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER))
|
||||
#define lwsi_role_h2_ENCAPSULATION(wsi) \
|
||||
((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2)
|
||||
|
||||
/* Pollout wants a callback in this state */
|
||||
#define LWSIFS_POCB (0x100)
|
||||
/* Before any protocol connection was established */
|
||||
#define LWSIFS_NOTEST (0x200)
|
||||
#define LWSIFS_NOT_EST (0x200)
|
||||
|
||||
enum lwsi_state {
|
||||
|
||||
/* Phase 1: pre-transport */
|
||||
|
||||
LRS_UNCONNECTED = LWSIFS_NOTEST | 0,
|
||||
LRS_WAITING_CONNECT = LWSIFS_NOTEST | 1,
|
||||
LRS_UNCONNECTED = LWSIFS_NOT_EST | 0,
|
||||
LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 1,
|
||||
|
||||
/* Phase 2: establishing intermediaries on top of transport */
|
||||
|
||||
LRS_WAITING_PROXY_REPLY = LWSIFS_NOTEST | 2,
|
||||
LRS_WAITING_SSL = LWSIFS_NOTEST | 3,
|
||||
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOTEST | 4,
|
||||
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOTEST | 5,
|
||||
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOTEST | 6,
|
||||
LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 2,
|
||||
LRS_WAITING_SSL = LWSIFS_NOT_EST | 3,
|
||||
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 4,
|
||||
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 5,
|
||||
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 6,
|
||||
|
||||
/* Phase 3: establishing tls tunnel */
|
||||
|
||||
LRS_SSL_INIT = LWSIFS_NOTEST | 7,
|
||||
LRS_SSL_ACK_PENDING = LWSIFS_NOTEST | 8,
|
||||
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOTEST | 9,
|
||||
LRS_SSL_INIT = LWSIFS_NOT_EST | 7,
|
||||
LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 8,
|
||||
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 9,
|
||||
|
||||
/* Phase 4: connected */
|
||||
|
||||
LRS_WAITING_SERVER_REPLY = LWSIFS_NOTEST | 10,
|
||||
LRS_H2_AWAIT_PREFACE = LWSIFS_NOTEST | 11,
|
||||
LRS_H2_AWAIT_SETTINGS = LWSIFS_NOTEST |
|
||||
LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 10,
|
||||
LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 11,
|
||||
LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST |
|
||||
LWSIFS_POCB | 12,
|
||||
|
||||
/* Phase 5: protocol logically established */
|
||||
|
@ -590,31 +572,35 @@ enum lwsi_state {
|
|||
LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 13,
|
||||
LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 14,
|
||||
LRS_DEFERRING_ACTION = LWSIFS_POCB | 15,
|
||||
LRS_H1C_ISSUE_HANDSHAKE = 16,
|
||||
LRS_H1C_ISSUE_HANDSHAKE2 = 17,
|
||||
LRS_ISSUE_HTTP_BODY = 18,
|
||||
LRS_ISSUING_FILE = 19,
|
||||
LRS_HEADERS = 20,
|
||||
LRS_BODY = 21,
|
||||
LRS_ESTABLISHED = LWSIFS_POCB | 22,
|
||||
LRS_IDLING = 16,
|
||||
LRS_H1C_ISSUE_HANDSHAKE = 17,
|
||||
LRS_H1C_ISSUE_HANDSHAKE2 = 18,
|
||||
LRS_ISSUE_HTTP_BODY = 19,
|
||||
LRS_ISSUING_FILE = 20,
|
||||
LRS_HEADERS = 21,
|
||||
LRS_BODY = 22,
|
||||
LRS_ESTABLISHED = LWSIFS_POCB | 23,
|
||||
|
||||
/* Phase 6: finishing */
|
||||
|
||||
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 23,
|
||||
LRS_RETURNED_CLOSE = LWSIFS_POCB | 24,
|
||||
LRS_AWAITING_CLOSE_ACK = 25,
|
||||
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 26,
|
||||
LRS_SHUTDOWN = 27,
|
||||
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 24,
|
||||
LRS_RETURNED_CLOSE = LWSIFS_POCB | 25,
|
||||
LRS_AWAITING_CLOSE_ACK = 26,
|
||||
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 27,
|
||||
LRS_SHUTDOWN = 28,
|
||||
|
||||
/* Phase 7: dead */
|
||||
|
||||
LRS_DEAD_SOCKET = 28,
|
||||
LRS_DEAD_SOCKET = 29,
|
||||
|
||||
LRS_MASK = 0xffff
|
||||
};
|
||||
|
||||
#define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK))
|
||||
#define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOTEST))
|
||||
#define lwsi_state_PRE_CLOSE(wsi) ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK))
|
||||
#define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOT_EST))
|
||||
#define lwsi_state_est_PRE_CLOSE(wsi) (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST))
|
||||
#define lwsi_state_can_handle_POLLOUT(wsi) (wsi->wsistate & LWSIFS_POCB)
|
||||
#if !defined (_DEBUG)
|
||||
#define lwsi_set_state(wsi, lrs) wsi->wsistate = \
|
||||
(wsi->wsistate & (~LRS_MASK)) | lrs
|
||||
|
@ -622,6 +608,100 @@ enum lwsi_state {
|
|||
void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* internal role-specific ops
|
||||
*/
|
||||
struct lws_context_per_thread;
|
||||
struct lws_role_ops {
|
||||
const char *name;
|
||||
/*
|
||||
* After http headers have parsed, this is the last chance for a role
|
||||
* to upgrade the connection to something else using the headers.
|
||||
* ws-over-h2 is upgraded from h2 like this.
|
||||
*/
|
||||
int (*check_upgrades)(struct lws *wsi);
|
||||
/* role-specific context init during vhost creation */
|
||||
int (*init_context)(struct lws_context *context,
|
||||
struct lws_context_creation_info *info);
|
||||
/* role-specific per-vhost init during vhost creation */
|
||||
int (*init_vhost)(struct lws_vhost *vh,
|
||||
struct lws_context_creation_info *info);
|
||||
/* generic 1Hz callback for the role itself */
|
||||
int (*periodic_checks)(struct lws_context *context, int tsi,
|
||||
time_t now);
|
||||
/* chance for the role to force POLLIN without network activity */
|
||||
int (*service_flag_pending)(struct lws_context *context, int tsi);
|
||||
/* an fd using this role has POLLIN signalled */
|
||||
int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd);
|
||||
/* an fd using the role wanted a POLLOUT callback and now has it */
|
||||
int (*handle_POLLOUT)(struct lws *wsi);
|
||||
/* perform user pollout */
|
||||
int (*perform_user_POLLOUT)(struct lws *wsi);
|
||||
/* do effective callback on writeable */
|
||||
int (*callback_on_writable)(struct lws *wsi);
|
||||
/* connection-specific tx credit in bytes */
|
||||
lws_filepos_t (*tx_credit)(struct lws *wsi);
|
||||
/* role-specific write formatting */
|
||||
int (*write_role_protocol)(struct lws *wsi, unsigned char *buf,
|
||||
size_t len, enum lws_write_protocol *wp);
|
||||
|
||||
/* cache rx due to rx flow control */
|
||||
int (*rxflow_cache)(struct lws *wsi, unsigned char *buf, int n,
|
||||
int len);
|
||||
/* get encapsulation parent */
|
||||
struct lws * (*encapsulation_parent)(struct lws *wsi);
|
||||
|
||||
/* chance for the role to handle close in the protocol */
|
||||
int (*close_via_role_protocol)(struct lws *wsi,
|
||||
enum lws_close_status reason);
|
||||
/* role-specific close processing */
|
||||
int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi);
|
||||
/* role-specific connection close processing */
|
||||
int (*close_kill_connection)(struct lws *wsi,
|
||||
enum lws_close_status reason);
|
||||
/* role-specific destructor */
|
||||
int (*destroy_role)(struct lws *wsi);
|
||||
|
||||
/*
|
||||
* the callback reasons for WRITEABLE for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint16_t writeable_cb[2];
|
||||
/*
|
||||
* the callback reasons for CLOSE for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint16_t close_cb[2];
|
||||
};
|
||||
|
||||
extern struct lws_role_ops role_ops_h1, role_ops_h2, role_ops_raw_skt,
|
||||
role_ops_raw_file, role_ops_ws, role_ops_cgi,
|
||||
role_ops_listen, role_ops_pipe;
|
||||
|
||||
#define lwsi_role_ws(wsi) (wsi->role_ops == &role_ops_ws)
|
||||
#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)
|
||||
#if defined(LWS_ROLE_H2)
|
||||
#define lwsi_role_h2(wsi) (wsi->role_ops == &role_ops_h2)
|
||||
#else
|
||||
#define lwsi_role_h2(_a) (0)
|
||||
#endif
|
||||
#define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi))
|
||||
|
||||
enum {
|
||||
LWS_HP_RET_BAIL_OK,
|
||||
LWS_HP_RET_BAIL_DIE,
|
||||
LWS_HP_RET_USER_SERVICE,
|
||||
|
||||
LWS_HPI_RET_DIE,
|
||||
LWS_HPI_RET_HANDLED,
|
||||
LWS_HPI_RET_CLOSE_HANDLED,
|
||||
|
||||
LWS_UPG_RET_DONE,
|
||||
LWS_UPG_RET_CONTINUE,
|
||||
LWS_UPG_RET_BAIL
|
||||
};
|
||||
|
||||
enum http_version {
|
||||
HTTP_VERSION_1_0,
|
||||
HTTP_VERSION_1_1,
|
||||
|
@ -888,7 +968,7 @@ struct lws_context_per_thread {
|
|||
const char *last_lock_reason;
|
||||
#endif
|
||||
int ah_wait_list_length;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
struct lws *pending_read_list; /* linked list */
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBEV)
|
||||
|
@ -1030,7 +1110,7 @@ struct lws_vhost {
|
|||
struct lws_dll_lws dll_active_client_conns;
|
||||
#endif
|
||||
const char *error_document_404;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
lws_tls_ctx *ssl_ctx;
|
||||
lws_tls_ctx *ssl_client_ctx;
|
||||
struct lws_tls_ss_pieces *ss; /* for acme tls certs */
|
||||
|
@ -1063,7 +1143,7 @@ struct lws_vhost {
|
|||
int log_fd;
|
||||
#endif
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
int use_ssl;
|
||||
int allow_non_ssl_on_ssl_port;
|
||||
unsigned int user_supplied_ssl_ctx:1;
|
||||
|
@ -1334,7 +1414,7 @@ LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info)
|
|||
#define lws_libev_run(_a, _b) ((void) 0)
|
||||
#define lws_libev_destroyloop(_a, _b) ((void) 0)
|
||||
#define LWS_LIBEV_ENABLED(context) (0)
|
||||
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
#define lws_feature_status_libev(_a) \
|
||||
lwsl_info("libev support not compiled in\n")
|
||||
#else
|
||||
|
@ -1364,7 +1444,7 @@ LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info)
|
|||
#define lws_libuv_run(_a, _b) ((void) 0)
|
||||
#define lws_libuv_destroyloop(_a, _b) ((void) 0)
|
||||
#define LWS_LIBUV_ENABLED(context) (0)
|
||||
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
#define lws_feature_status_libuv(_a) \
|
||||
lwsl_info("libuv support not compiled in\n")
|
||||
#else
|
||||
|
@ -1395,7 +1475,7 @@ LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *in
|
|||
#define lws_libevent_run(_a, _b) ((void) 0)
|
||||
#define lws_libevent_destroyloop(_a, _b) ((void) 0)
|
||||
#define LWS_LIBEVENT_ENABLED(context) (0)
|
||||
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
#define lws_feature_status_libevent(_a) \
|
||||
lwsl_info("libevent support not compiled in\n")
|
||||
#else
|
||||
|
@ -1988,7 +2068,7 @@ struct lws {
|
|||
const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
#endif
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
lws_tls_conn *ssl;
|
||||
lws_tls_bio *client_bio;
|
||||
struct lws *pending_read_list_prev, *pending_read_list_next;
|
||||
|
@ -2003,10 +2083,13 @@ struct lws {
|
|||
lws_sock_file_fd_type desc; /* .filefd / .sockfd */
|
||||
#if defined(LWS_WITH_STATS)
|
||||
uint64_t active_writable_req_us;
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
uint64_t accept_start_us;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct lws_role_ops *role_ops;
|
||||
|
||||
lws_usec_t pending_timer;
|
||||
|
||||
time_t pending_timeout_set;
|
||||
|
@ -2045,6 +2128,7 @@ struct lws {
|
|||
unsigned int interpreting:1;
|
||||
unsigned int already_did_cce:1;
|
||||
unsigned int told_user_closed:1;
|
||||
unsigned int told_event_loop_closed:1;
|
||||
unsigned int waiting_to_send_close_frame:1;
|
||||
unsigned int ipv6:1;
|
||||
unsigned int parent_carries_io:1;
|
||||
|
@ -2081,13 +2165,13 @@ struct lws {
|
|||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
unsigned int extension_data_pending:1;
|
||||
#endif
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
unsigned int use_ssl;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
unsigned int sock_send_blocking:1;
|
||||
#endif
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
unsigned int redirect_to_https:1;
|
||||
#endif
|
||||
|
||||
|
@ -2118,7 +2202,7 @@ struct lws {
|
|||
#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
|
||||
char reason_bf; /* internal writeable callback reason bitfield */
|
||||
#endif
|
||||
#if defined(LWS_WITH_STATS) && defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS)
|
||||
char seen_rx;
|
||||
#endif
|
||||
uint8_t ws_over_h2_count;
|
||||
|
@ -2273,7 +2357,7 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
|||
lws_client_interpret_server_handshake(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_rx_sm(struct lws *wsi, unsigned char c);
|
||||
lws_ws_rx_sm(struct lws *wsi, unsigned char c);
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len);
|
||||
|
@ -2282,7 +2366,8 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
|||
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state);
|
||||
lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state,
|
||||
struct lws_role_ops *ops);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
|
||||
|
@ -2346,6 +2431,10 @@ LWS_EXTERN int
|
|||
lws_h2_client_handshake(struct lws *wsi);
|
||||
LWS_EXTERN struct lws *
|
||||
lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi);
|
||||
int
|
||||
lws_handle_POLLOUT_event_h2(struct lws *wsi);
|
||||
int
|
||||
lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
|
||||
#else
|
||||
#define lws_h2_configure_if_upgraded(x)
|
||||
#endif
|
||||
|
@ -2396,8 +2485,6 @@ int lws_context_init_server(struct lws_context_creation_info *info,
|
|||
struct lws_vhost *vhost);
|
||||
LWS_EXTERN struct lws_vhost *
|
||||
lws_select_vhost(struct lws_context *context, int port, const char *servername);
|
||||
LWS_EXTERN int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
LWS_EXTERN void
|
||||
|
@ -2420,7 +2507,7 @@ interface_to_sa(struct lws_vhost *vh, const char *ifname,
|
|||
struct sockaddr_in *addr, size_t addrlen);
|
||||
LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
|
||||
|
||||
#ifndef LWS_OPENSSL_SUPPORT
|
||||
#if !defined(LWS_WITH_TLS)
|
||||
#define LWS_SSL_ENABLED(context) (0)
|
||||
#define lws_context_init_server_ssl(_a, _b) (0)
|
||||
#define lws_ssl_destroy(_a)
|
||||
|
@ -2704,7 +2791,7 @@ LWS_EXTERN struct lws *
|
|||
lws_client_wsi_effective(struct lws *wsi);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_http_transaction_completed_client(struct lws *wsi);
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
LWS_EXTERN int
|
||||
lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost);
|
||||
|
@ -2732,12 +2819,9 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa);
|
|||
|
||||
#ifndef LWS_NO_SERVER
|
||||
LWS_EXTERN int
|
||||
lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd);
|
||||
LWS_EXTERN int
|
||||
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
#else
|
||||
#define lws_server_socket_service(_a, _b, _c) (0)
|
||||
#define lws_server_socket_service(_b, _c) (0)
|
||||
#define lws_handshake_server(_a, _b, _c) (0)
|
||||
#endif
|
||||
|
||||
|
@ -2900,6 +2984,24 @@ __lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
|||
int
|
||||
__lws_change_pollfd(struct lws *wsi, int _and, int _or);
|
||||
|
||||
int
|
||||
lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
|
||||
int
|
||||
lws_callback_as_writeable(struct lws *wsi);
|
||||
int
|
||||
lws_read_or_use_preamble(struct lws_context_per_thread *pt, struct lws *wsi);
|
||||
int
|
||||
lws_process_ws_upgrade(struct lws *wsi);
|
||||
int
|
||||
lws_server_init_wsi_for_ws(struct lws *wsi);
|
||||
int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi);
|
||||
char *
|
||||
lws_generate_client_ws_handshake(struct lws *wsi, char *p);
|
||||
int
|
||||
lws_client_ws_upgrade(struct lws *wsi, const char **cce);
|
||||
int
|
||||
lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi);
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -88,7 +88,7 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
|
|||
|
||||
/* initialize the instance struct */
|
||||
|
||||
lws_role_transition(new_wsi, LWSI_ROLE_CGI, LRS_ESTABLISHED);
|
||||
lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, &role_ops_cgi);
|
||||
|
||||
new_wsi->hdr_parsing_completed = 0;
|
||||
new_wsi->position_in_fds_table = -1;
|
99
lib/roles/cgi/ops-cgi.c
Normal file
99
lib/roles/cgi/ops-cgi.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_cgi(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_cgi_args args;
|
||||
|
||||
assert(wsi->role_ops == &role_ops_cgi);
|
||||
|
||||
if (wsi->cgi_channel >= LWS_STDOUT &&
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (wsi->cgi_channel == LWS_STDIN &&
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (wsi->cgi_channel == LWS_STDIN &&
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
return LWS_HPI_RET_DIE;
|
||||
}
|
||||
|
||||
args.ch = wsi->cgi_channel;
|
||||
args.stdwsi = &wsi->parent->cgi->stdwsi[0];
|
||||
args.hdr_state = wsi->hdr_state;
|
||||
|
||||
lwsl_debug("CGI LWS_STDOUT %p wsistate 0x%x\n",
|
||||
wsi->parent, wsi->wsistate);
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->parent->protocol->callback,
|
||||
wsi->parent, LWS_CALLBACK_CGI,
|
||||
wsi->parent->user_space,
|
||||
(void *)&args, 0))
|
||||
return 1;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLOUT_cgi(struct lws *wsi)
|
||||
{
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_periodic_checks_cgi(struct lws_context *context, int tsi, time_t now)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
|
||||
lws_cgi_kill_terminated(pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_cgi = {
|
||||
"cgi",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* periodic_checks */ rops_periodic_checks_cgi,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_cgi,
|
||||
/* handle_POLLOUT */ rops_handle_POLLOUT_cgi,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ NULL,
|
||||
/* rxflow_cache */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
};
|
69
lib/roles/h1/client-h1.c
Normal file
69
lib/roles/h1/client-h1.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
int
|
||||
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
return 0;
|
||||
}
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (lwsi_role_client(wsi))
|
||||
m = lws_client_rx_sm(wsi, 0);
|
||||
else
|
||||
#endif
|
||||
m = lws_ws_rx_sm(wsi, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos++;
|
||||
|
||||
if (lws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_debug("client_rx_sm exited\n");
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
720
lib/roles/h1/ops-h1.c
Normal file
720
lib/roles/h1/ops-h1.c
Normal file
|
@ -0,0 +1,720 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* We have to take care about parsing because the headers may be split
|
||||
* into multiple fragments. They may contain unknown headers with arbitrary
|
||||
* argument lengths. So, we parse using a single-character at a time state
|
||||
* machine that is completely independent of packet size.
|
||||
*
|
||||
* Returns <0 for error or length of chars consumed from buf (up to len)
|
||||
*/
|
||||
|
||||
int
|
||||
lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
||||
{
|
||||
unsigned char *last_char, *oldbuf = buf;
|
||||
lws_filepos_t body_chunk_len;
|
||||
size_t n;
|
||||
|
||||
// lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
|
||||
case LRS_ISSUING_FILE:
|
||||
return 0;
|
||||
|
||||
case LRS_ESTABLISHED:
|
||||
|
||||
if (lwsi_role_ws(wsi))
|
||||
goto ws_mode;
|
||||
|
||||
if (lwsi_role_client(wsi))
|
||||
break;
|
||||
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LRS_HEADERS:
|
||||
if (!wsi->ah) {
|
||||
lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
|
||||
assert(0);
|
||||
}
|
||||
lwsl_parser("issuing %d bytes to parser\n", (int)len);
|
||||
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
|
||||
last_char = buf;
|
||||
if (lws_handshake_server(wsi, &buf, (size_t)len))
|
||||
/* Handshake indicates this session is done. */
|
||||
goto bail;
|
||||
|
||||
/* we might have transitioned to RAW */
|
||||
if (wsi->role_ops == &role_ops_raw_skt ||
|
||||
wsi->role_ops == &role_ops_raw_file)
|
||||
/* we gave the read buffer to RAW handler already */
|
||||
goto read_ok;
|
||||
|
||||
/*
|
||||
* It's possible that we've exhausted our data already, or
|
||||
* rx flow control has stopped us dealing with this early,
|
||||
* but lws_handshake_server doesn't update len for us.
|
||||
* Figure out how much was read, so that we can proceed
|
||||
* appropriately:
|
||||
*/
|
||||
len -= (buf - last_char);
|
||||
// lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
|
||||
|
||||
if (!wsi->hdr_parsing_completed)
|
||||
/* More header content on the way */
|
||||
goto read_ok;
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
case LRS_ESTABLISHED:
|
||||
case LRS_HEADERS:
|
||||
goto read_ok;
|
||||
case LRS_ISSUING_FILE:
|
||||
goto read_ok;
|
||||
case LRS_BODY:
|
||||
wsi->http.rx_content_remain =
|
||||
wsi->http.rx_content_length;
|
||||
if (wsi->http.rx_content_remain)
|
||||
goto http_postbody;
|
||||
|
||||
/* there is no POST content */
|
||||
goto postbody_completion;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_BODY:
|
||||
http_postbody:
|
||||
//lwsl_notice("http post body\n");
|
||||
while (len && wsi->http.rx_content_remain) {
|
||||
/* Copy as much as possible, up to the limit of:
|
||||
* what we have in the read buffer (len)
|
||||
* remaining portion of the POST body (content_remain)
|
||||
*/
|
||||
body_chunk_len = min(wsi->http.rx_content_remain, len);
|
||||
wsi->http.rx_content_remain -= body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (wsi->cgi) {
|
||||
struct lws_cgi_args args;
|
||||
|
||||
args.ch = LWS_STDIN;
|
||||
args.stdwsi = &wsi->cgi->stdwsi[0];
|
||||
args.data = buf;
|
||||
args.len = body_chunk_len;
|
||||
|
||||
/* returns how much used */
|
||||
n = user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
|
||||
wsi->user_space,
|
||||
(void *)&args, 0);
|
||||
if ((int)n < 0)
|
||||
goto bail;
|
||||
} else {
|
||||
#endif
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
|
||||
buf, (size_t)body_chunk_len);
|
||||
if (n)
|
||||
goto bail;
|
||||
n = (size_t)body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
}
|
||||
#endif
|
||||
buf += n;
|
||||
|
||||
if (wsi->http.rx_content_remain) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
wsi->context->timeout_secs);
|
||||
break;
|
||||
}
|
||||
/* he sent all the content in time */
|
||||
postbody_completion:
|
||||
#ifdef LWS_WITH_CGI
|
||||
/*
|
||||
* If we're running a cgi, we can't let him off the
|
||||
* hook just because he sent his POST data
|
||||
*/
|
||||
if (wsi->cgi)
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
|
||||
wsi->context->timeout_secs);
|
||||
else
|
||||
#endif
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (!wsi->cgi)
|
||||
#endif
|
||||
{
|
||||
lwsl_info("HTTP_BODY_COMPLETION\n");
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY_COMPLETION,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n)
|
||||
goto bail;
|
||||
|
||||
if (wsi->http2_substream)
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_AWAITING_CLOSE_ACK:
|
||||
case LRS_WAITING_TO_SEND_CLOSE:
|
||||
case LRS_SHUTDOWN:
|
||||
|
||||
ws_mode:
|
||||
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
#if defined(LWS_ROLE_WS)
|
||||
if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) &&
|
||||
/*
|
||||
* for h2 we are on the swsi
|
||||
*/
|
||||
lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
|
||||
lwsl_info("interpret_incoming_packet bailed\n");
|
||||
goto bail;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case LRS_DEFERRING_ACTION:
|
||||
lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
|
||||
break;
|
||||
|
||||
case LRS_SSL_ACK_PENDING:
|
||||
break;
|
||||
|
||||
case LRS_DEAD_SOCKET:
|
||||
lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
|
||||
assert(0);
|
||||
/* fallthru */
|
||||
|
||||
default:
|
||||
lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
|
||||
assert(0);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
read_ok:
|
||||
/* Nothing more to do for now */
|
||||
// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
|
||||
// wsi, (long)(buf - oldbuf), (int)len, wsi->state);
|
||||
|
||||
return lws_ptr_diff(buf, oldbuf);
|
||||
|
||||
bail:
|
||||
/*
|
||||
* h2 / h2-ws calls us recursively in lws_read()->lws_h2_parser()->
|
||||
* lws_read() pattern, having stripped the h2 framing in the middle.
|
||||
*
|
||||
* When taking down the whole connection, make sure that only the
|
||||
* outer lws_read() does the wsi close.
|
||||
*/
|
||||
if (!wsi->outer_will_close)
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "lws_read bail");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct allocated_headers *ah;
|
||||
int n, len;
|
||||
|
||||
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
|
||||
goto try_pollout;
|
||||
|
||||
/* any incoming data ready? */
|
||||
|
||||
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
goto try_pollout;
|
||||
|
||||
/*
|
||||
* If we previously just did POLLIN when IN and OUT were
|
||||
* signalled (because POLLIN processing may have used up
|
||||
* the POLLOUT), don't let that happen twice in a row...
|
||||
* next time we see the situation favour POLLOUT
|
||||
*/
|
||||
if (wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT)) {
|
||||
// lwsl_notice("favouring pollout\n");
|
||||
wsi->favoured_pollin = 0;
|
||||
goto try_pollout;
|
||||
}
|
||||
|
||||
/*
|
||||
* We haven't processed that the tunnel is set up yet, so
|
||||
* defer reading
|
||||
*/
|
||||
if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* these states imply we MUST have an ah attached */
|
||||
|
||||
if ((lwsi_state(wsi) == LRS_ESTABLISHED ||
|
||||
lwsi_state(wsi) == LRS_ISSUING_FILE ||
|
||||
lwsi_state(wsi) == LRS_HEADERS)) {
|
||||
if (!wsi->ah) {
|
||||
/* no autoservice beacuse we will do it next */
|
||||
if (lws_header_table_attach(wsi, 0)) {
|
||||
lwsl_info("wsi %p: ah get fail\n", wsi);
|
||||
goto try_pollout;
|
||||
}
|
||||
}
|
||||
ah = wsi->ah;
|
||||
|
||||
assert(ah->rxpos <= ah->rxlen);
|
||||
/* if nothing in ah rx buffer, get some fresh rx */
|
||||
if (ah->rxpos == ah->rxlen) {
|
||||
|
||||
if (wsi->preamble_rx) {
|
||||
memcpy(ah->rx, wsi->preamble_rx, wsi->preamble_rx_len);
|
||||
lws_free_set_NULL(wsi->preamble_rx);
|
||||
ah->rxlen = wsi->preamble_rx_len;
|
||||
wsi->preamble_rx_len = 0;
|
||||
} else {
|
||||
ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
|
||||
sizeof(ah->rx));
|
||||
}
|
||||
|
||||
ah->rxpos = 0;
|
||||
switch (ah->rxlen) {
|
||||
case 0:
|
||||
lwsl_info("%s: read 0 len a\n",
|
||||
__func__);
|
||||
wsi->seen_zero_length_recv = 1;
|
||||
lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
goto try_pollout;
|
||||
//goto fail;
|
||||
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
goto fail;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
ah->rxlen = ah->rxpos = 0;
|
||||
goto try_pollout;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ah->rxpos != ah->rxlen && ah->rxlen)) {
|
||||
lwsl_err("%s: assert: rxpos %d, rxlen %d\n",
|
||||
__func__, ah->rxpos, ah->rxlen);
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* just ignore incoming if waiting for close */
|
||||
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_ISSUING_FILE)
|
||||
goto try_pollout;
|
||||
|
||||
/*
|
||||
* otherwise give it to whoever wants it
|
||||
* according to the connection state
|
||||
*/
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
|
||||
n = lws_read_h2(wsi, ah->rx + ah->rxpos,
|
||||
ah->rxlen - ah->rxpos);
|
||||
else
|
||||
#endif
|
||||
n = lws_read_h1(wsi, ah->rx + ah->rxpos,
|
||||
ah->rxlen - ah->rxpos);
|
||||
if (n < 0) /* we closed wsi */
|
||||
return LWS_HPI_RET_DIE;
|
||||
|
||||
if (!wsi->ah)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
if (wsi->ah->rxlen)
|
||||
wsi->ah->rxpos += n;
|
||||
|
||||
lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
|
||||
__func__, wsi, wsi->ah->rxpos,
|
||||
wsi->ah->rxlen);
|
||||
|
||||
if (lws_header_table_is_in_detachable_state(wsi) &&
|
||||
(wsi->role_ops == &role_ops_raw_skt ||
|
||||
wsi->role_ops == &role_ops_raw_file)) // ???
|
||||
lws_header_table_detach(wsi, 1);
|
||||
|
||||
/* during the parsing we upgraded to ws */
|
||||
|
||||
if (wsi->ah && wsi->ah->rxpos == wsi->ah->rxlen &&
|
||||
lwsi_role_ws(wsi)) {
|
||||
lwsl_info("%s: %p: dropping ah on ws post-upgrade\n",
|
||||
__func__, wsi);
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
lws_header_table_detach(wsi, 0);
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
len = lws_read_or_use_preamble(pt, wsi);
|
||||
if (len < 0)
|
||||
goto fail;
|
||||
|
||||
if (!len)
|
||||
goto try_pollout;
|
||||
|
||||
/* just ignore incoming if waiting for close */
|
||||
if (lwsi_state(wsi) != LRS_FLUSHING_BEFORE_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_ISSUING_FILE) {
|
||||
/*
|
||||
* this may want to send
|
||||
* (via HTTP callback for example)
|
||||
*
|
||||
* returns number of bytes used
|
||||
*/
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
|
||||
n = lws_read_h2(wsi, pt->serv_buf, len);
|
||||
else
|
||||
#endif
|
||||
n = lws_read_h1(wsi, pt->serv_buf, len);
|
||||
if (n < 0) /* we closed wsi */
|
||||
return LWS_HPI_RET_DIE;
|
||||
|
||||
if (n != len) {
|
||||
if (wsi->preamble_rx) {
|
||||
lwsl_err("DISCARDING %d (ah %p)\n", len - n, wsi->ah);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
assert(n < len);
|
||||
wsi->preamble_rx = lws_malloc(len - n, "preamble_rx");
|
||||
if (!wsi->preamble_rx) {
|
||||
lwsl_err("OOM\n");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(wsi->preamble_rx, pt->serv_buf + n, len - n);
|
||||
wsi->preamble_rx_len = (int)len - n;
|
||||
lwsl_debug("stashed %d\n", (int)wsi->preamble_rx_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* he may have used up the
|
||||
* writability above, if we will defer POLLOUT
|
||||
* processing in favour of POLLIN, note it
|
||||
*/
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
wsi->favoured_pollin = 1;
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
/*
|
||||
* he may have used up the
|
||||
* writability above, if we will defer POLLOUT
|
||||
* processing in favour of POLLIN, note it
|
||||
*/
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
wsi->favoured_pollin = 1;
|
||||
|
||||
try_pollout:
|
||||
|
||||
/* this handles POLLOUT for http serving fragments */
|
||||
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* one shot */
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_notice("%s a\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* clear back-to-back write detection */
|
||||
wsi->could_have_pending = 0;
|
||||
|
||||
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
|
||||
lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n",
|
||||
__func__);
|
||||
|
||||
if (wsi->ah)
|
||||
lwsl_debug(" existing ah rxpos %d / rxlen %d\n",
|
||||
wsi->ah->rxpos, wsi->ah->rxlen);
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wsi->hdr_parsing_completed)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_WRITEABLE_CB, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (wsi->active_writable_req_us) {
|
||||
uint64_t ul = time_in_microseconds() -
|
||||
wsi->active_writable_req_us;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_WRITABLE_DELAY, ul);
|
||||
lws_stats_atomic_max(wsi->context, pt,
|
||||
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
|
||||
wsi->active_writable_req_us = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_HTTP_WRITEABLE,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n < 0) {
|
||||
lwsl_info("writeable_fail\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
/* >0 == completion, <0 == error
|
||||
*
|
||||
* We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
|
||||
* it's done. That's the case even if we just completed the
|
||||
* send, so wait for that.
|
||||
*/
|
||||
n = lws_serve_http_file_fragment(wsi);
|
||||
if (n < 0)
|
||||
goto fail;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
|
||||
fail:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "server socket svc fail");
|
||||
|
||||
return LWS_HPI_RET_DIE;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
|
||||
// lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", __func__, wsi,
|
||||
// wsi->wsistate, wsi->role_ops->name, pollfd->revents);
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) {
|
||||
if (lws_handle_POLLOUT_event(wsi, pollfd))
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
|
||||
/*
|
||||
* we stopped caring about anything except control
|
||||
* packets. Force flow control off, defeat tx
|
||||
* draining.
|
||||
*/
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
if (wsi->ws)
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
}
|
||||
|
||||
if (lws_is_flowcontrolled(wsi))
|
||||
/* We cannot deal with any kind of new RX because we are
|
||||
* RX-flowcontrolled.
|
||||
*/
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
#if !defined(LWS_NO_SERVER)
|
||||
if (!lwsi_role_client(wsi)) {
|
||||
int n;
|
||||
|
||||
lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, wsi->wsistate);
|
||||
n = lws_h1_server_socket_service(wsi, pollfd);
|
||||
if (n != LWS_HPI_RET_HANDLED)
|
||||
return n;
|
||||
if (lwsi_state(wsi) != LRS_SSL_INIT)
|
||||
if (lws_server_socket_service_ssl(wsi, LWS_SOCK_INVALID))
|
||||
return LWS_HPI_RET_DIE;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if ((pollfd->revents & LWS_POLLIN) &&
|
||||
wsi->hdr_parsing_completed && !wsi->told_user_closed) {
|
||||
|
||||
/*
|
||||
* In SSL mode we get POLLIN notification about
|
||||
* encrypted data in.
|
||||
*
|
||||
* But that is not necessarily related to decrypted
|
||||
* data out becoming available; in may need to perform
|
||||
* other in or out before that happens.
|
||||
*
|
||||
* simply mark ourselves as having readable data
|
||||
* and turn off our POLLIN
|
||||
*/
|
||||
wsi->client_rx_avail = 1;
|
||||
lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
|
||||
//lwsl_notice("calling back %s\n", wsi->protocol->name);
|
||||
|
||||
/* let user code know, he'll usually ask for writeable
|
||||
* callback and drain / re-enable it there
|
||||
*/
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if (lwsi_state(wsi) == LRS_ESTABLISHED)
|
||||
// return LWS_HPI_RET_HANDLED;
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if ((pollfd->revents & LWS_POLLOUT) &&
|
||||
lws_handle_POLLOUT_event(wsi, pollfd)) {
|
||||
lwsl_debug("POLLOUT event closed it\n");
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
}
|
||||
|
||||
if (lws_client_socket_service(wsi, pollfd, NULL))
|
||||
return LWS_HPI_RET_DIE;
|
||||
#endif
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
int rops_handle_POLLOUT_h1(struct lws *wsi)
|
||||
{
|
||||
if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
if (lwsi_role_client(wsi))
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_service_flag_pending_h1(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct allocated_headers *ah;
|
||||
int forced = 0;
|
||||
|
||||
/* POLLIN faking (the pt lock is taken by the parent) */
|
||||
|
||||
/*
|
||||
* 3) For any wsi who have an ah with pending RX who did not
|
||||
* complete their current headers, and are not flowcontrolled,
|
||||
* fake their POLLIN status so they will be able to drain the
|
||||
* rx buffered in the ah
|
||||
*/
|
||||
ah = pt->ah_list;
|
||||
while (ah) {
|
||||
if ((ah->rxpos != ah->rxlen &&
|
||||
!ah->wsi->hdr_parsing_completed) || ah->wsi->preamble_rx) {
|
||||
pt->fds[ah->wsi->position_in_fds_table].revents |=
|
||||
pt->fds[ah->wsi->position_in_fds_table].events &
|
||||
LWS_POLLIN;
|
||||
if (pt->fds[ah->wsi->position_in_fds_table].revents &
|
||||
LWS_POLLIN) {
|
||||
forced = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ah = ah->next;
|
||||
}
|
||||
|
||||
return forced;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol *wp)
|
||||
{
|
||||
#if 0
|
||||
/* if not in a state to send stuff, then just send nothing */
|
||||
|
||||
if ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)) {
|
||||
//assert(0);
|
||||
lwsl_debug("binning %d %d\n", lwsi_state(wsi), *wp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return lws_issue_raw(wsi, (unsigned char *)buf, len);
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_h1 = {
|
||||
"h1",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ rops_service_flag_pending_h1,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_h1,
|
||||
/* handle_POLLOUT */ rops_handle_POLLOUT_h1,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ rops_write_role_protocol_h1,
|
||||
/* rxflow_cache */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
|
||||
LWS_CALLBACK_HTTP_WRITEABLE },
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
|
||||
LWS_CALLBACK_CLOSED_HTTP },
|
||||
};
|
|
@ -187,6 +187,7 @@ lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
|
|||
wsi->seen_nonpseudoheader = 0;
|
||||
|
||||
wsi->h2.parent_wsi = parent_wsi;
|
||||
wsi->role_ops = parent_wsi->role_ops;
|
||||
/* new guy's sibling is whoever was the first child before */
|
||||
wsi->h2.sibling_list = parent_wsi->h2.child_list;
|
||||
/* first child is now the new guy */
|
||||
|
@ -258,8 +259,8 @@ lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi)
|
|||
if (lws_ensure_user_space(wsi))
|
||||
goto bail1;
|
||||
|
||||
lwsi_set_role(wsi, LWSI_ROLE_H2_CLIENT);
|
||||
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_H2_WAITING_TO_SEND_HEADERS,
|
||||
&role_ops_h2);
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
|
@ -290,8 +291,8 @@ int lws_h2_issue_preface(struct lws *wsi)
|
|||
(int)strlen(preface))
|
||||
return 1;
|
||||
|
||||
lwsi_set_role(wsi, LWSI_ROLE_H2_CLIENT);
|
||||
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_H2_WAITING_TO_SEND_HEADERS,
|
||||
&role_ops_h2);
|
||||
|
||||
h2n->count = 0;
|
||||
wsi->h2.tx_cr = 65535;
|
||||
|
@ -1090,6 +1091,7 @@ lws_h2_parse_frame_header(struct lws *wsi)
|
|||
if (w->h2.my_sid < h2n->sid &&
|
||||
w->h2.h2_state == LWS_H2_STATE_IDLE)
|
||||
lws_close_free_wsi(w, 0, "h2 sid close");
|
||||
assert(w->h2.sibling_list != w);
|
||||
} lws_end_foreach_ll(w, h2.sibling_list);
|
||||
|
||||
|
||||
|
@ -1200,11 +1202,13 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
|
|||
|
||||
assert(lws_h2_wsi_from_id(wsi, 1) == h2n->swsi);
|
||||
|
||||
lwsi_set_role(wsi, LWSI_ROLE_H2_CLIENT);
|
||||
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT,
|
||||
LRS_H2_WAITING_TO_SEND_HEADERS,
|
||||
&role_ops_h2);
|
||||
|
||||
lwsi_set_role(h2n->swsi, LWSI_ROLE_H2_CLIENT);
|
||||
lwsi_set_state(h2n->swsi, LRS_H2_WAITING_TO_SEND_HEADERS);
|
||||
lws_role_transition(h2n->swsi, LWSIFR_CLIENT,
|
||||
LRS_H2_WAITING_TO_SEND_HEADERS,
|
||||
&role_ops_h2);
|
||||
|
||||
/* pass on the initial headers to SID 1 */
|
||||
h2n->swsi->ah = wsi->ah;
|
||||
|
@ -1772,17 +1776,21 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
* more waiting leave it for next time around
|
||||
*/
|
||||
|
||||
n = lws_read(h2n->swsi, in - 1, n);
|
||||
n = lws_read_h1(h2n->swsi, in - 1, n);
|
||||
h2n->swsi->outer_will_close = 0;
|
||||
/*
|
||||
* can return 0 in POST body with content len
|
||||
* exhausted somehow.
|
||||
* can return 0 in POST body with
|
||||
* content len exhausted somehow.
|
||||
*/
|
||||
if (n <= 0) {
|
||||
lwsl_debug("%s: lws_read_h1 told %d %d / %d\n",
|
||||
__func__, n, h2n->count, h2n->length);
|
||||
in += h2n->length - h2n->count;
|
||||
h2n->inside = h2n->length;
|
||||
h2n->count = h2n->length - 1;
|
||||
lwsl_debug("%s: lws_read told %d\n", __func__, n);
|
||||
|
||||
if (n < 0)
|
||||
goto already_closed_swsi;
|
||||
goto close_swsi_and_return;
|
||||
}
|
||||
|
||||
|
@ -1944,6 +1952,7 @@ close_swsi_and_return:
|
|||
h2n->frame_state = 0;
|
||||
h2n->count = 0;
|
||||
|
||||
already_closed_swsi:
|
||||
*inused = in - oldin;
|
||||
|
||||
return 2;
|
||||
|
@ -2108,3 +2117,84 @@ lws_h2_ws_handshake(struct lws *wsi)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
||||
{
|
||||
unsigned char *oldbuf = buf;
|
||||
lws_filepos_t body_chunk_len;
|
||||
int m;
|
||||
|
||||
// lwsl_notice("%s: h2 path: wsistate 0x%x len %d\n", __func__,
|
||||
// wsi->wsistate, (int)len);
|
||||
|
||||
/*
|
||||
* wsi here is always the network connection wsi, not a stream
|
||||
* wsi. Once we unpicked the framing we will find the right
|
||||
* swsi and make it the target of the frame.
|
||||
*
|
||||
* If it's ws over h2, the nwsi will get us here to do the h2
|
||||
* processing, and that will call us back with the swsi +
|
||||
* ESTABLISHED state for the inner payload, handled in a later
|
||||
* case.
|
||||
*/
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lws_rxflow_cache(wsi, buf, 0, (int)len);
|
||||
buf += len;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* lws_h2_parser() may send something; when it gets the
|
||||
* whole frame, it will want to perform some action
|
||||
* involving a reply. But we may be in a partial send
|
||||
* situation on the network wsi...
|
||||
*
|
||||
* Even though we may be in a partial send and unable to
|
||||
* send anything new, we still have to parse the network
|
||||
* wsi in order to gain tx credit to send, which is
|
||||
* potentially necessary to clear the old partial send.
|
||||
*
|
||||
* ALL network wsi-specific frames are sent by PPS
|
||||
* already, these are sent as a priority on the writable
|
||||
* handler, and so respect partial sends. The only
|
||||
* problem is when a stream wsi wants to send an, eg,
|
||||
* reply headers frame in response to the parsing
|
||||
* we will do now... the *stream wsi* must stall in a
|
||||
* different state until it is able to do so from a
|
||||
* priority on the WRITABLE callback, same way that
|
||||
* file transfers operate.
|
||||
*/
|
||||
|
||||
m = lws_h2_parser(wsi, buf, len, &body_chunk_len);
|
||||
if (m && m != 2) {
|
||||
lwsl_debug("%s: http2_parser bailed\n", __func__);
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"lws_read_h2 bail");
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (m == 2) {
|
||||
/* swsi has been closed */
|
||||
buf += body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
break;
|
||||
}
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer) {
|
||||
wsi->rxflow_pos += (int)body_chunk_len;
|
||||
assert(wsi->rxflow_pos <= wsi->rxflow_len);
|
||||
}
|
||||
|
||||
buf += body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
}
|
||||
|
||||
return lws_ptr_diff(buf, oldbuf);
|
||||
}
|
||||
|
1082
lib/roles/h2/ops-h2.c
Normal file
1082
lib/roles/h2/ops-h2.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -50,7 +50,7 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
#if !defined(LWS_NO_SERVER)
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
|
||||
OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||
|
@ -132,7 +132,8 @@ int lws_h2_configure_if_upgraded(struct lws *wsi)
|
|||
|
||||
ah = wsi->ah;
|
||||
|
||||
lws_role_transition(wsi, LWSI_ROLE_H2_SERVER, LRS_H2_AWAIT_PREFACE);
|
||||
lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
|
||||
&role_ops_h2);
|
||||
|
||||
/* http2 union member has http union struct at start */
|
||||
wsi->ah = ah;
|
|
@ -31,7 +31,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
const char *cce = "", *iface, *adsin, *meth;
|
||||
struct lws *wsi_piggy = NULL;
|
||||
struct lws *wsi_piggyback = NULL;
|
||||
struct addrinfo *result;
|
||||
struct lws_pollfd pfd;
|
||||
ssize_t plen = 0;
|
||||
|
@ -48,7 +48,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
lwsl_client("%s\n", __func__);
|
||||
lwsl_client("%s: %p\n", __func__, wsi);
|
||||
|
||||
if (!wsi->ah) {
|
||||
cce = "ah was NULL at cc2";
|
||||
|
@ -79,11 +79,13 @@ lws_client_connect_2(struct lws *wsi)
|
|||
struct lws *w = lws_container_of(d, struct lws,
|
||||
dll_active_client_conns);
|
||||
|
||||
if (w->client_hostname_copy &&
|
||||
lwsl_debug("%s: check %s %s %d %d\n", __func__, adsin, w->client_hostname_copy, wsi->c_port, w->c_port);
|
||||
|
||||
if (w != wsi && w->client_hostname_copy &&
|
||||
!strcmp(adsin, w->client_hostname_copy) &&
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
(wsi->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) ==
|
||||
(w->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) &&
|
||||
(w->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) &&
|
||||
#endif
|
||||
wsi->c_port == w->c_port) {
|
||||
|
||||
|
@ -91,7 +93,8 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
/* do we know for a fact pipelining won't fly? */
|
||||
if (w->keepalive_rejected) {
|
||||
lwsl_notice("defeating pipelining due to no KA on server\n");
|
||||
lwsl_info("defeating pipelining due to no "
|
||||
"keepalive on server\n");
|
||||
goto create_new_conn;
|
||||
}
|
||||
#if defined (LWS_WITH_HTTP2)
|
||||
|
@ -113,8 +116,8 @@ lws_client_connect_2(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
lwsl_info("applying %p to txn queue on %p (%d)\n", wsi, w,
|
||||
lwsi_state(w));
|
||||
lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n", wsi, w,
|
||||
w->wsistate);
|
||||
/*
|
||||
* ...let's add ourselves to his transaction queue...
|
||||
*/
|
||||
|
@ -127,7 +130,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
* to take over parsing the rx.
|
||||
*/
|
||||
|
||||
wsi_piggy = w;
|
||||
wsi_piggyback = w;
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
goto send_hs;
|
||||
|
@ -149,6 +152,23 @@ create_new_conn:
|
|||
strdup(lws_hdr_simple_ptr(wsi,
|
||||
_WSI_TOKEN_CLIENT_PEER_ADDRESS));
|
||||
|
||||
/*
|
||||
* If we made our own connection, and we're doing a method that can take
|
||||
* a pipeline, we are an "active client connection".
|
||||
*
|
||||
* Add ourselves to the vhost list of those so that others can
|
||||
* piggyback on our transaction queue
|
||||
*/
|
||||
|
||||
if (meth && !strcmp(meth, "GET") &&
|
||||
lws_dll_is_null(&wsi->dll_client_transaction_queue) &&
|
||||
lws_dll_is_null(&wsi->dll_active_client_conns)) {
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
lws_dll_lws_add_front(&wsi->dll_active_client_conns,
|
||||
&wsi->vhost->dll_active_client_conns);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
|
||||
/*
|
||||
* start off allowing ipv6 on connection if vhost allows it
|
||||
*/
|
||||
|
@ -383,7 +403,7 @@ create_new_conn:
|
|||
sa46.sa4.sin_port = htons(port);
|
||||
n = sizeof(struct sockaddr);
|
||||
}
|
||||
|
||||
lwsl_notice("%s: CONNECT\n", __func__);
|
||||
if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 ||
|
||||
LWS_ERRNO == LWS_EISCONN) {
|
||||
if (LWS_ERRNO == LWS_EALREADY ||
|
||||
|
@ -474,7 +494,7 @@ create_new_conn:
|
|||
#endif
|
||||
|
||||
send_hs:
|
||||
if (wsi_piggy &&
|
||||
if (wsi_piggyback &&
|
||||
!lws_dll_is_null(&wsi->dll_client_transaction_queue)) {
|
||||
/*
|
||||
* We are pipelining on an already-established connection...
|
||||
|
@ -492,9 +512,11 @@ send_hs:
|
|||
* If we are trying to do this too early, before the master
|
||||
* connection has written his own headers,
|
||||
*/
|
||||
lws_callback_on_writable(wsi_piggy);
|
||||
lwsl_debug("wsi %p: waiting to send headers\n", wsi);
|
||||
lws_callback_on_writable(wsi_piggyback);
|
||||
lwsl_info("wsi %p: waiting to send headers\n", wsi);
|
||||
} else {
|
||||
lwsl_info("wsi %p: client creating own connection\n", wsi);
|
||||
|
||||
/* we are making our own connection */
|
||||
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
|
||||
|
||||
|
@ -525,29 +547,13 @@ send_hs:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we made our own connection, and we're doing a method that can take
|
||||
* a pipeline, we are an "active client connection".
|
||||
*
|
||||
* Add ourselves to the vhost list of those so that others can
|
||||
* piggyback on our transaction queue
|
||||
*/
|
||||
|
||||
if (meth && !strcmp(meth, "GET") &&
|
||||
lws_dll_is_null(&wsi->dll_client_transaction_queue)) {
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
lws_dll_lws_add_front(&wsi->dll_active_client_conns,
|
||||
&wsi->vhost->dll_active_client_conns);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
|
||||
return wsi;
|
||||
|
||||
oom4:
|
||||
/* we're closing, losing some rx is OK */
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
|
||||
if (lwsi_role_client(wsi) && !(lwsi_state(wsi) & LWSIFS_NOTEST)) {
|
||||
if (lwsi_role_client(wsi) && lwsi_state_est(wsi)) {
|
||||
wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, (void *)cce, strlen(cce));
|
||||
|
@ -619,7 +625,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
|
|||
|
||||
/* close the connection by hand */
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
lws_ssl_close(wsi);
|
||||
#endif
|
||||
|
||||
|
@ -641,7 +647,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
|
|||
|
||||
__remove_wsi_socket_from_fds(wsi);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
wsi->use_ssl = ssl;
|
||||
#else
|
||||
if (ssl) {
|
||||
|
@ -852,7 +858,6 @@ LWS_VISIBLE struct lws *
|
|||
lws_client_connect_via_info(struct lws_client_connect_info *i)
|
||||
{
|
||||
struct lws *wsi;
|
||||
int v = SPEC_LATEST_SUPPORTED;
|
||||
const struct lws_protocols *p;
|
||||
const char *local = i->protocol;
|
||||
|
||||
|
@ -875,7 +880,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
|
||||
wsi->context = i->context;
|
||||
/* assert the mode and union status (hdr) clearly */
|
||||
lws_role_transition(wsi, LWSI_ROLE_H1_CLIENT, LRS_UNCONNECTED);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
|
||||
wsi->desc.sockfd = LWS_SOCK_INVALID;
|
||||
|
||||
/* 1) fill up the wsi with stuff from the connect_info as far as it
|
||||
|
@ -883,21 +888,13 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
* not even be able to get ahold of an ah at this point.
|
||||
*/
|
||||
|
||||
if (!i->method) { /* ie, ws */
|
||||
/* allocate the ws struct for the wsi */
|
||||
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
|
||||
if (!wsi->ws) {
|
||||
lwsl_notice("OOM\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* -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;
|
||||
}
|
||||
if (!i->method) /* ie, ws */
|
||||
#if defined(LWS_ROLE_WS)
|
||||
if (lws_create_client_ws_object(i, wsi))
|
||||
return NULL;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
wsi->user_space = NULL;
|
||||
wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
||||
|
@ -917,7 +914,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
|
||||
|
||||
/* reasonable place to start */
|
||||
lwsi_set_role(wsi, LWSI_ROLE_H1_CLIENT);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
|
||||
|
||||
/*
|
||||
* 1) for http[s] connection, allow protocol selection by name
|
||||
|
@ -947,7 +944,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
if (lws_ensure_user_space(wsi))
|
||||
goto bail;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
wsi->use_ssl = i->ssl_connection;
|
||||
|
||||
if (!i->method) /* !!! disallow ws for h2 right now */
|
|
@ -21,52 +21,6 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
int
|
||||
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
return 0;
|
||||
}
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (lwsi_role_client(wsi))
|
||||
m = lws_client_rx_sm(wsi, 0);
|
||||
else
|
||||
#endif
|
||||
m = lws_rx_sm(wsi, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos++;
|
||||
|
||||
if (lws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_debug("client_rx_sm exited\n");
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
|
||||
{
|
||||
|
@ -134,7 +88,7 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
|||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *p = (char *)&pt->serv_buf[0];
|
||||
struct lws *w;
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
char ebuf[128];
|
||||
#endif
|
||||
const char *cce = NULL;
|
||||
|
@ -344,7 +298,7 @@ start_ws_handshake:
|
|||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
|
||||
return -1;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
/* we can retry this... just cook the SSL BIO the first time */
|
||||
|
||||
if ((wsi->use_ssl & LCCSCF_USE_SSL) && !wsi->ssl &&
|
||||
|
@ -391,8 +345,9 @@ start_ws_handshake:
|
|||
lwsl_info("client connection upgraded to h2\n");
|
||||
lws_h2_configure_if_upgraded(wsi);
|
||||
|
||||
lws_role_transition(wsi, LWSI_ROLE_H2_CLIENT,
|
||||
LRS_H2_CLIENT_SEND_SETTINGS);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT,
|
||||
LRS_H2_CLIENT_SEND_SETTINGS,
|
||||
&role_ops_h2);
|
||||
|
||||
/* send the H2 preface to legitimize the connection */
|
||||
if (lws_h2_issue_preface(wsi)) {
|
||||
|
@ -412,7 +367,8 @@ start_ws_handshake:
|
|||
case LRS_H1C_ISSUE_HANDSHAKE2:
|
||||
p = lws_generate_client_handshake(wsi, p);
|
||||
if (p == NULL) {
|
||||
if (lwsi_role_raw(wsi))
|
||||
if (wsi->role_ops == &role_ops_raw_skt ||
|
||||
wsi->role_ops == &role_ops_raw_file)
|
||||
return 0;
|
||||
|
||||
lwsl_err("Failed to generate handshake for client\n");
|
||||
|
@ -424,8 +380,8 @@ start_ws_handshake:
|
|||
lws_latency_pre(context, wsi);
|
||||
|
||||
w = lws_client_wsi_master(wsi);
|
||||
lwsl_debug("%s: HANDSHAKE2: %p: sending headers on %p\n",
|
||||
__func__, wsi, w);
|
||||
lwsl_debug("%s: HANDSHAKE2: %p: sending headers on %p (wsistate 0x%x 0x%x)\n",
|
||||
__func__, wsi, w, wsi->wsistate, w->wsistate);
|
||||
|
||||
n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb));
|
||||
lws_latency(context, wsi, "send lws_issue_raw", n,
|
||||
|
@ -449,6 +405,22 @@ start_ws_handshake:
|
|||
break;
|
||||
}
|
||||
|
||||
lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
if (lwsi_state(w) == LRS_IDLING) {
|
||||
lwsi_set_state(w, LRS_WAITING_SERVER_REPLY);
|
||||
w->hdr_parsing_completed = 0;
|
||||
|
||||
w->ah->parser_state = WSI_TOKEN_NAME_PART;
|
||||
w->ah->lextable_pos = 0;
|
||||
/* If we're (re)starting on headers, need other implied init */
|
||||
wsi->ah->ues = URIES_IDLE;
|
||||
}
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
|
||||
wsi->context->timeout_secs);
|
||||
|
||||
lws_callback_on_writable(w);
|
||||
|
||||
goto client_http_body_sent;
|
||||
|
@ -534,6 +506,8 @@ client_http_body_sent:
|
|||
if (wsi->ah->parser_state != WSI_PARSING_COMPLETE)
|
||||
break;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* otherwise deal with the handshake. If there's any
|
||||
* packet traffic already arrived we'll trigger poll() again
|
||||
|
@ -559,23 +533,7 @@ bail3:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_WARN_UNUSED_RESULT
|
||||
lws_http_transaction_completed_client(struct lws *wsi)
|
||||
|
@ -616,6 +574,9 @@ lws_http_transaction_completed_client(struct lws *wsi)
|
|||
/* after the first one, they can only be coming from the queue */
|
||||
wsi->transaction_from_pipeline_queue = 1;
|
||||
|
||||
wsi->http.rx_content_length = 0;
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
/* is there a new tail after removing that one? */
|
||||
wsi_eff = lws_client_wsi_effective(wsi);
|
||||
|
||||
|
@ -630,24 +591,20 @@ lws_http_transaction_completed_client(struct lws *wsi)
|
|||
* in case something turns up...
|
||||
*/
|
||||
lwsl_info("%s: nothing pipelined waiting\n", __func__);
|
||||
if (wsi->ah) {
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
lws_header_table_detach(wsi, 0);
|
||||
}
|
||||
lwsi_set_state(wsi, LRS_IDLING);
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* H1: we can serialize the queued guys into into the same ah
|
||||
* H1: we can serialize the queued guys into the same ah
|
||||
* H2: everybody needs their own ah until their own STREAM_END
|
||||
*/
|
||||
|
||||
/* otherwise set ourselves up ready to go again */
|
||||
lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
|
||||
wsi->http.rx_content_length = 0;
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
wsi->ah->parser_state = WSI_TOKEN_NAME_PART;
|
||||
wsi->ah->lextable_pos = 0;
|
||||
|
@ -691,37 +648,28 @@ strrchr(const char *s, int c)
|
|||
int
|
||||
lws_client_interpret_server_handshake(struct lws *wsi)
|
||||
{
|
||||
int n, len, okay = 0, port = 0, ssl = 0;
|
||||
int n, port = 0, ssl = 0;
|
||||
int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
|
||||
struct lws_context *context = wsi->context;
|
||||
const char *pc, *prot, *ads = NULL, *path, *cce = NULL;
|
||||
const char *prot, *ads = NULL, *path, *cce = NULL;
|
||||
struct allocated_headers *ah = NULL;
|
||||
struct lws *w = lws_client_wsi_effective(wsi);
|
||||
char *p, *q;
|
||||
char new_path[300];
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *sb = (char *)&pt->serv_buf[0];
|
||||
const struct lws_ext_options *opts;
|
||||
const struct lws_extension *ext;
|
||||
char ext_name[128];
|
||||
const char *c, *a;
|
||||
char ignore;
|
||||
int more = 1;
|
||||
void *v;
|
||||
#endif
|
||||
|
||||
lws_client_stash_destroy(wsi);
|
||||
|
||||
ah = wsi->ah;
|
||||
if (!wsi->do_ws) {
|
||||
/* we are being an http client...
|
||||
*/
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (wsi->client_h2_alpn)
|
||||
lws_role_transition(wsi, LWSI_ROLE_H2_CLIENT,
|
||||
LRS_ESTABLISHED);
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT,
|
||||
LRS_ESTABLISHED, &role_ops_h2);
|
||||
else
|
||||
lws_role_transition(wsi, LWSI_ROLE_H1_CLIENT,
|
||||
LRS_ESTABLISHED);
|
||||
#endif
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT,
|
||||
LRS_ESTABLISHED, &role_ops_h1);
|
||||
|
||||
wsi->ah = ah;
|
||||
ah->http_response = 0;
|
||||
|
@ -778,7 +726,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
|
||||
/* Relative reference absolute path */
|
||||
if (p[0] == '/') {
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
ssl = wsi->use_ssl & LCCSCF_USE_SSL;
|
||||
#endif
|
||||
ads = lws_hdr_simple_ptr(wsi,
|
||||
|
@ -801,7 +749,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
else {
|
||||
/* This doesn't try to calculate an absolute path,
|
||||
* that will be left to the server */
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
ssl = wsi->use_ssl & LCCSCF_USE_SSL;
|
||||
#endif
|
||||
ads = lws_hdr_simple_ptr(wsi,
|
||||
|
@ -819,7 +767,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
path = p;
|
||||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if ((wsi->use_ssl & LCCSCF_USE_SSL) && !ssl) {
|
||||
cce = "HS: Redirect attempted SSL downgrade";
|
||||
goto bail3;
|
||||
|
@ -879,9 +827,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
ww->client_pipeline = 0;
|
||||
|
||||
/* go back to "trying to connect" state */
|
||||
lws_role_transition(ww,
|
||||
LWSI_ROLE_H1_CLIENT,
|
||||
LRS_UNCONNECTED);
|
||||
lws_role_transition(ww, LWSIFR_CLIENT,
|
||||
LRS_UNCONNECTED,
|
||||
&role_ops_h1);
|
||||
ww->user_space = NULL;
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
@ -972,425 +920,16 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
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 (p && !strncmp(p, "401", 3)) {
|
||||
lwsl_warn(
|
||||
"lws_client_handshake: got bad HTTP response '%s'\n", p);
|
||||
cce = "HS: ws upgrade unauthorized";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (p && strncmp(p, "101", 3)) {
|
||||
lwsl_warn(
|
||||
"lws_client_handshake: got bad HTTP response '%s'\n", p);
|
||||
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;
|
||||
}
|
||||
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
|
||||
if (!p) {
|
||||
lwsl_info("no Connection hdr\n");
|
||||
cce = "HS: CONNECTION missing";
|
||||
goto bail3;
|
||||
}
|
||||
strtolower(p);
|
||||
if (strcmp(p, "upgrade")) {
|
||||
lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
|
||||
cce = "HS: UPGRADE malformed";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
|
||||
if (!pc) {
|
||||
lwsl_parser("lws_client_int_s_hs: no protocol list\n");
|
||||
} else
|
||||
lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
|
||||
|
||||
/*
|
||||
* confirm the protocol the server wants to talk was in the list
|
||||
* of protocols we offered
|
||||
*/
|
||||
|
||||
len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
|
||||
if (!len) {
|
||||
lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
|
||||
/*
|
||||
* no protocol name to work from,
|
||||
* default to first protocol
|
||||
*/
|
||||
n = 0;
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
goto check_extensions;
|
||||
}
|
||||
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
|
||||
len = (int)strlen(p);
|
||||
|
||||
while (pc && *pc && !okay) {
|
||||
if (!strncmp(pc, p, len) &&
|
||||
(pc[len] == ',' || pc[len] == '\0')) {
|
||||
okay = 1;
|
||||
continue;
|
||||
}
|
||||
while (*pc && *pc++ != ',')
|
||||
;
|
||||
while (*pc && *pc == ' ')
|
||||
pc++;
|
||||
}
|
||||
|
||||
if (!okay) {
|
||||
lwsl_info("%s: got bad protocol %s\n", __func__, p);
|
||||
cce = "HS: PROTOCOL malformed";
|
||||
#if defined(LWS_ROLE_WS)
|
||||
switch (lws_client_ws_upgrade(wsi, &cce)) {
|
||||
case 2:
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/*
|
||||
* identify the selected protocol struct and set it
|
||||
*/
|
||||
n = 0;
|
||||
/* keep client connection pre-bound protocol */
|
||||
if (!lwsi_role_client(wsi))
|
||||
wsi->protocol = NULL;
|
||||
|
||||
while (wsi->vhost->protocols[n].callback) {
|
||||
if (!wsi->protocol &&
|
||||
strcmp(p, wsi->vhost->protocols[n].name) == 0) {
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!wsi->vhost->protocols[n].callback) { /* no match */
|
||||
/* if server, that's already fatal */
|
||||
if (!lwsi_role_client(wsi)) {
|
||||
lwsl_info("%s: fail protocol %s\n", __func__, p);
|
||||
cce = "HS: Cannot match protocol";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* for client, find the index of our pre-bound protocol */
|
||||
|
||||
n = 0;
|
||||
while (wsi->vhost->protocols[n].callback) {
|
||||
if (wsi->protocol && strcmp(wsi->protocol->name,
|
||||
wsi->vhost->protocols[n].name) == 0) {
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!wsi->vhost->protocols[n].callback) {
|
||||
if (wsi->protocol)
|
||||
lwsl_err("Failed to match protocol %s\n",
|
||||
wsi->protocol->name);
|
||||
else
|
||||
lwsl_err("No protocol on client\n");
|
||||
goto bail2;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
|
||||
|
||||
check_extensions:
|
||||
/*
|
||||
* stitch protocol choice into the vh protocol linked list
|
||||
* We always insert ourselves at the start of the list
|
||||
*
|
||||
* X <-> B
|
||||
* X <-> pAn <-> pB
|
||||
*/
|
||||
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
|
||||
wsi->same_vh_protocol_prev = /* guy who points to us */
|
||||
&wsi->vhost->same_vh_protocol_list[n];
|
||||
wsi->same_vh_protocol_next = /* old first guy is our next */
|
||||
wsi->vhost->same_vh_protocol_list[n];
|
||||
/* we become the new first guy */
|
||||
wsi->vhost->same_vh_protocol_list[n] = wsi;
|
||||
|
||||
if (wsi->same_vh_protocol_next)
|
||||
/* old first guy points back to us now */
|
||||
wsi->same_vh_protocol_next->same_vh_protocol_prev =
|
||||
&wsi->same_vh_protocol_next;
|
||||
wsi->on_same_vh_list = 1;
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
||||
#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->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->active_extensions[wsi->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->act_ext_user[wsi->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->act_ext_user[
|
||||
wsi->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->act_ext_user[wsi->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->act_ext_user[wsi->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->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->ah->initial_handshake_hash_base64)) {
|
||||
lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
|
||||
wsi->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, LWSI_ROLE_H1_CLIENT, LRS_ESTABLISHED);
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
||||
|
||||
/*
|
||||
* create the frame buffer for this connection according to the
|
||||
* size mentioned in the protocol definition. If 0 there, then
|
||||
* use a big default for compatibility
|
||||
*/
|
||||
n = (int)wsi->protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = context->pt_serv_buf_size;
|
||||
n += LWS_PRE;
|
||||
wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
|
||||
"client frame buffer");
|
||||
if (!wsi->ws->rx_ubuf) {
|
||||
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
|
||||
cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
wsi->ws->rx_ubuf_alloc = n;
|
||||
lwsl_info("Allocating client RX buffer %d\n", n);
|
||||
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
|
||||
(const char *)&n, sizeof n)) {
|
||||
lwsl_warn("Failed to set SNDBUF to %d", n);
|
||||
cce = "HS: SO_SNDBUF failed";
|
||||
case 3:
|
||||
goto bail3;
|
||||
}
|
||||
#endif
|
||||
|
||||
lwsi_set_role(wsi, LWSI_ROLE_WS1_CLIENT);
|
||||
|
||||
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;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* inform all extensions, not just active ones since they
|
||||
* already know
|
||||
*/
|
||||
ext = wsi->vhost->extensions;
|
||||
|
||||
while (ext && ext->callback) {
|
||||
v = NULL;
|
||||
for (n = 0; n < wsi->count_act_ext; n++)
|
||||
if (wsi->active_extensions[n] == ext)
|
||||
v = wsi->act_ext_user[n];
|
||||
|
||||
ext->callback(context, ext, wsi,
|
||||
LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0);
|
||||
ext++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
bail3:
|
||||
close_reason = LWS_CLOSE_STATUS_NOSTATUS;
|
||||
|
@ -1419,14 +958,8 @@ bail2:
|
|||
char *
|
||||
lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
||||
{
|
||||
char buf[128], hash[20], key_b64[40], *p = pkt;
|
||||
struct lws_context *context = wsi->context;
|
||||
char *p = pkt;
|
||||
const char *meth;
|
||||
int n;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
const struct lws_extension *ext;
|
||||
int ext_count = 0;
|
||||
#endif
|
||||
const char *pp = lws_hdr_simple_ptr(wsi,
|
||||
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
|
||||
|
||||
|
@ -1456,32 +989,17 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
lws_bind_protocol(wsi, pr);
|
||||
}
|
||||
|
||||
if ((wsi->protocol->callback)(wsi,
|
||||
LWS_CALLBACK_RAW_ADOPT,
|
||||
wsi->user_space, NULL, 0))
|
||||
if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT,
|
||||
wsi->user_space, NULL, 0))
|
||||
return NULL;
|
||||
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
lws_role_transition(wsi, LWSI_ROLE_RAW_SOCKET, LRS_ESTABLISHED);
|
||||
lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt);
|
||||
lws_header_table_detach(wsi, 1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wsi->do_ws) {
|
||||
/*
|
||||
* create the random key
|
||||
*/
|
||||
n = lws_get_random(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));
|
||||
}
|
||||
|
||||
/*
|
||||
* 04 example client handshake
|
||||
*
|
||||
|
@ -1505,7 +1023,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
|
||||
|
||||
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
|
||||
if (lws_check_opt(context->options,
|
||||
if (lws_check_opt(wsi->context->options,
|
||||
LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
|
||||
p += sprintf(p, "Origin: %s\x0d\x0a",
|
||||
lws_hdr_simple_ptr(wsi,
|
||||
|
@ -1516,87 +1034,169 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
_WSI_TOKEN_CLIENT_ORIGIN));
|
||||
}
|
||||
|
||||
if (wsi->do_ws) {
|
||||
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
|
||||
"Connection: Upgrade\x0d\x0a"
|
||||
"Sec-WebSocket-Key: ");
|
||||
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->extensions;
|
||||
while (ext && ext->callback) {
|
||||
n = lws_ext_cb_all_exts(context, wsi,
|
||||
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
|
||||
(char *)ext->name, 0);
|
||||
if (n) { /* an extension vetos us */
|
||||
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
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->ah->initial_handshake_hash_base64,
|
||||
sizeof(wsi->ah->initial_handshake_hash_base64));
|
||||
}
|
||||
if (wsi->do_ws)
|
||||
p = lws_generate_client_ws_handshake(wsi, p);
|
||||
|
||||
/* give userland a chance to append, eg, cookies */
|
||||
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
|
||||
wsi->user_space, &p,
|
||||
(pkt + context->pt_serv_buf_size) - p - 12))
|
||||
(pkt + wsi->context->pt_serv_buf_size) - p - 12))
|
||||
return NULL;
|
||||
|
||||
p += sprintf(p, "\x0d\x0a");
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_http_client_read(struct lws *wsi, char **buf, int *len)
|
||||
{
|
||||
int rlen, n;
|
||||
|
||||
rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len);
|
||||
*len = 0;
|
||||
|
||||
// lwsl_notice("%s: rlen %d\n", __func__, rlen);
|
||||
|
||||
/* allow the source to signal he has data again next time */
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
|
||||
if (rlen == LWS_SSL_CAPABLE_ERROR) {
|
||||
lwsl_notice("%s: SSL capable error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rlen == 0)
|
||||
return -1;
|
||||
|
||||
if (rlen < 0)
|
||||
return 0;
|
||||
|
||||
*len = rlen;
|
||||
wsi->client_rx_avail = 0;
|
||||
|
||||
/*
|
||||
* server may insist on transfer-encoding: chunked,
|
||||
* so http client must deal with it
|
||||
*/
|
||||
spin_chunks:
|
||||
while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
|
||||
switch (wsi->chunk_parser) {
|
||||
case ELCP_HEX:
|
||||
if ((*buf)[0] == '\x0d') {
|
||||
wsi->chunk_parser = ELCP_CR;
|
||||
break;
|
||||
}
|
||||
n = char_to_hex((*buf)[0]);
|
||||
if (n < 0) {
|
||||
lwsl_debug("chunking failure\n");
|
||||
return -1;
|
||||
}
|
||||
wsi->chunk_remaining <<= 4;
|
||||
wsi->chunk_remaining |= n;
|
||||
break;
|
||||
case ELCP_CR:
|
||||
if ((*buf)[0] != '\x0a') {
|
||||
lwsl_debug("chunking failure\n");
|
||||
return -1;
|
||||
}
|
||||
wsi->chunk_parser = ELCP_CONTENT;
|
||||
lwsl_info("chunk %d\n", wsi->chunk_remaining);
|
||||
if (wsi->chunk_remaining)
|
||||
break;
|
||||
lwsl_info("final chunk\n");
|
||||
goto completed;
|
||||
|
||||
case ELCP_CONTENT:
|
||||
break;
|
||||
|
||||
case ELCP_POST_CR:
|
||||
if ((*buf)[0] != '\x0d') {
|
||||
lwsl_debug("chunking failure\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
wsi->chunk_parser = ELCP_POST_LF;
|
||||
break;
|
||||
|
||||
case ELCP_POST_LF:
|
||||
if ((*buf)[0] != '\x0a')
|
||||
return -1;
|
||||
|
||||
wsi->chunk_parser = ELCP_HEX;
|
||||
wsi->chunk_remaining = 0;
|
||||
break;
|
||||
}
|
||||
(*buf)++;
|
||||
(*len)--;
|
||||
}
|
||||
|
||||
if (wsi->chunked && !wsi->chunk_remaining)
|
||||
return 0;
|
||||
|
||||
if (wsi->http.rx_content_remain &&
|
||||
wsi->http.rx_content_remain < (unsigned int)*len)
|
||||
n = (int)wsi->http.rx_content_remain;
|
||||
else
|
||||
n = *len;
|
||||
|
||||
if (wsi->chunked && wsi->chunk_remaining &&
|
||||
wsi->chunk_remaining < n)
|
||||
n = wsi->chunk_remaining;
|
||||
|
||||
#ifdef LWS_WITH_HTTP_PROXY
|
||||
/* hubbub */
|
||||
if (wsi->perform_rewrite)
|
||||
lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
|
||||
|
||||
if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
|
||||
wsi_eff, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
|
||||
wsi_eff->user_space, *buf, n)) {
|
||||
lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ returned -1\n",
|
||||
__func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (wsi->chunked && wsi->chunk_remaining) {
|
||||
(*buf) += n;
|
||||
wsi->chunk_remaining -= n;
|
||||
*len -= n;
|
||||
}
|
||||
|
||||
if (wsi->chunked && !wsi->chunk_remaining)
|
||||
wsi->chunk_parser = ELCP_POST_CR;
|
||||
|
||||
if (wsi->chunked && *len)
|
||||
goto spin_chunks;
|
||||
|
||||
if (wsi->chunked)
|
||||
return 0;
|
||||
|
||||
/* if we know the content length, decrement the content remaining */
|
||||
if (wsi->http.rx_content_length > 0)
|
||||
wsi->http.rx_content_remain -= n;
|
||||
|
||||
// lwsl_notice("rx_content_remain %lld, rx_content_length %lld\n",
|
||||
// wsi->http.rx_content_remain, wsi->http.rx_content_length);
|
||||
|
||||
if (wsi->http.rx_content_remain || !wsi->http.rx_content_length)
|
||||
return 0;
|
||||
|
||||
completed:
|
||||
|
||||
if (lws_http_transaction_completed_client(wsi)) {
|
||||
lwsl_notice("%s: transaction completed says -1\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -38,7 +38,7 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
|
|||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lwsi_role_h2(wsi))
|
||||
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
|
||||
return lws_add_http2_header_by_name(wsi, name,
|
||||
value, length, p, end);
|
||||
#else
|
||||
|
@ -66,7 +66,7 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
|
|||
unsigned char *end)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lwsi_role_h2(wsi))
|
||||
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
|
||||
return 0;
|
||||
#else
|
||||
(void)wsi;
|
||||
|
@ -105,7 +105,7 @@ lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
|
|||
{
|
||||
const unsigned char *name;
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lwsi_role_h2(wsi))
|
||||
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
|
||||
return lws_add_http2_header_by_token(wsi, token, value,
|
||||
length, p, end);
|
||||
#endif
|
||||
|
@ -201,7 +201,7 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
|
|||
#endif
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lwsi_role_h2(wsi))
|
||||
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
|
||||
return lws_add_http2_header_status(wsi, code, p, end);
|
||||
#endif
|
||||
if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
|
|
@ -349,7 +349,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
|
||||
a->info->protocols = a->protocols;
|
||||
a->info->extensions = a->extensions;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
|
||||
"ECDHE-RSA-AES256-GCM-SHA384:"
|
||||
"DHE-RSA-AES256-GCM-SHA384:"
|
||||
|
@ -449,7 +449,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
}
|
||||
a->any_vhosts = 1;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (a->enable_client_ssl) {
|
||||
const char *cert_filepath = a->info->client_ssl_cert_filepath;
|
||||
const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
|
||||
|
@ -593,7 +593,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
case LEJPVP_KEEPALIVE_TIMEOUT:
|
||||
a->info->keepalive_timeout = atoi(ctx->buf);
|
||||
return 0;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
case LEJPVP_CLIENT_CIPHERS:
|
||||
a->info->client_ssl_cipher_list = a->p;
|
||||
break;
|
||||
|
@ -673,7 +673,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
case LEJPVP_ENABLE_CLIENT_SSL:
|
||||
a->enable_client_ssl = arg_to_bool(ctx->buf);
|
||||
return 0;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_WITH_TLS)
|
||||
case LEJPVP_CLIENT_SSL_KEY:
|
||||
a->info->client_ssl_private_key_filepath = a->p;
|
||||
break;
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -22,7 +22,7 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
static const unsigned char lextable[] = {
|
||||
#include "lextable.h"
|
||||
#include "../lextable.h"
|
||||
};
|
||||
|
||||
#define FAIL_CHAR 0x08
|
||||
|
@ -1040,8 +1040,8 @@ nope:
|
|||
* If it's h1, server needs to look out for unknown
|
||||
* methods...
|
||||
*/
|
||||
if (ah->lextable_pos < 0 &&
|
||||
lwsi_role(wsi) == LWSI_ROLE_H1_SERVER) {
|
||||
if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
|
||||
lwsi_role_server(wsi)) {
|
||||
/* this is not a header we know about */
|
||||
for (m = 0; m < ARRAY_SIZE(methods); m++)
|
||||
if (ah->frag_index[methods[m]]) {
|
||||
|
@ -1197,642 +1197,3 @@ forbid:
|
|||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
|
||||
{
|
||||
return wsi->ws->frame_is_binary;
|
||||
}
|
||||
|
||||
void
|
||||
lws_add_wsi_to_draining_ext_list(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
return;
|
||||
|
||||
lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
|
||||
|
||||
wsi->ws->rx_draining_ext = 1;
|
||||
wsi->ws->rx_draining_ext_list = pt->rx_draining_ext_list;
|
||||
pt->rx_draining_ext_list = wsi;
|
||||
}
|
||||
|
||||
void
|
||||
lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct lws **w = &pt->rx_draining_ext_list;
|
||||
|
||||
if (!wsi->ws->rx_draining_ext)
|
||||
return;
|
||||
|
||||
lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__);
|
||||
|
||||
wsi->ws->rx_draining_ext = 0;
|
||||
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
/* if us, point it instead to who we were pointing to */
|
||||
*w = wsi->ws->rx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->ws->rx_draining_ext_list);
|
||||
}
|
||||
wsi->ws->rx_draining_ext_list = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* client-parser.c: lws_client_rx_sm() needs to be roughly kept in
|
||||
* sync with changes here, esp related to ext draining
|
||||
*/
|
||||
|
||||
int
|
||||
lws_rx_sm(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
int callback_action = LWS_CALLBACK_RECEIVE;
|
||||
int ret = 0, rx_draining_ext = 0;
|
||||
struct lws_tokens eff_buf;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
int n;
|
||||
#endif
|
||||
|
||||
eff_buf.token = NULL;
|
||||
eff_buf.token_len = 0;
|
||||
if (wsi->socket_is_permanently_unusable)
|
||||
return -1;
|
||||
|
||||
switch (wsi->lws_rx_parse_state) {
|
||||
case LWS_RXPS_NEW:
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
eff_buf.token = NULL;
|
||||
eff_buf.token_len = 0;
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
rx_draining_ext = 1;
|
||||
lwsl_debug("%s: doing draining flow\n", __func__);
|
||||
|
||||
goto drain_extension;
|
||||
}
|
||||
switch (wsi->ws->ietf_spec_revision) {
|
||||
case 13:
|
||||
/*
|
||||
* no prepended frame key any more
|
||||
*/
|
||||
wsi->ws->all_zero_nonce = 1;
|
||||
goto handle_first;
|
||||
|
||||
default:
|
||||
lwsl_warn("lws_rx_sm: unknown spec version %d\n",
|
||||
wsi->ws->ietf_spec_revision);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LWS_RXPS_04_mask_1:
|
||||
wsi->ws->mask[1] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
|
||||
break;
|
||||
case LWS_RXPS_04_mask_2:
|
||||
wsi->ws->mask[2] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
|
||||
break;
|
||||
case LWS_RXPS_04_mask_3:
|
||||
wsi->ws->mask[3] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
|
||||
/*
|
||||
* start from the zero'th byte in the XOR key buffer since
|
||||
* this is the start of a frame with a new key
|
||||
*/
|
||||
|
||||
wsi->ws->mask_idx = 0;
|
||||
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
|
||||
break;
|
||||
|
||||
/*
|
||||
* 04 logical framing from the spec (all this is masked when incoming
|
||||
* and has to be unmasked)
|
||||
*
|
||||
* We ignore the possibility of extension data because we don't
|
||||
* negotiate any extensions at the moment.
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
* |F|R|R|R| opcode|R| Payload len | Extended payload length |
|
||||
* |I|S|S|S| (4) |S| (7) | (16/63) |
|
||||
* |N|V|V|V| |V| | (if payload len==126/127) |
|
||||
* | |1|2|3| |4| | |
|
||||
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
* | Extended payload length continued, if payload len == 127 |
|
||||
* + - - - - - - - - - - - - - - - +-------------------------------+
|
||||
* | | Extension data |
|
||||
* +-------------------------------+ - - - - - - - - - - - - - - - +
|
||||
* : :
|
||||
* +---------------------------------------------------------------+
|
||||
* : Application data :
|
||||
* +---------------------------------------------------------------+
|
||||
*
|
||||
* We pass payload through to userland as soon as we get it, ignoring
|
||||
* FIN. It's up to userland to buffer it up if it wants to see a
|
||||
* whole unfragmented block of the original size (which may be up to
|
||||
* 2^63 long!)
|
||||
*/
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_1:
|
||||
handle_first:
|
||||
|
||||
wsi->ws->opcode = c & 0xf;
|
||||
wsi->ws->rsv = c & 0x70;
|
||||
wsi->ws->final = !!((c >> 7) & 1);
|
||||
|
||||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
wsi->ws->rsv_first_msg = (c & 0x70);
|
||||
wsi->ws->frame_is_binary =
|
||||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME;
|
||||
wsi->ws->first_fragment = 1;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 0xb:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
lwsl_info("illegal opcode\n");
|
||||
return -1;
|
||||
}
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN:
|
||||
|
||||
wsi->ws->this_frame_masked = !!(c & 0x80);
|
||||
|
||||
switch (c & 0x7f) {
|
||||
case 126:
|
||||
/* control frames are not allowed to have big lengths */
|
||||
if (wsi->ws->opcode & 8)
|
||||
goto illegal_ctl_length;
|
||||
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
|
||||
break;
|
||||
case 127:
|
||||
/* control frames are not allowed to have big lengths */
|
||||
if (wsi->ws->opcode & 8)
|
||||
goto illegal_ctl_length;
|
||||
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
|
||||
break;
|
||||
default:
|
||||
wsi->ws->rx_packet_length = c & 0x7f;
|
||||
if (wsi->ws->this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else
|
||||
if (wsi->ws->rx_packet_length)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
else {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
|
||||
wsi->ws->rx_packet_length = c << 8;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
|
||||
wsi->ws->rx_packet_length |= c;
|
||||
if (wsi->ws->this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
|
||||
if (c & 0x80) {
|
||||
lwsl_warn("b63 of length must be zero\n");
|
||||
/* kill the connection */
|
||||
return -1;
|
||||
}
|
||||
#if defined __LP64__
|
||||
wsi->ws->rx_packet_length = ((size_t)c) << 56;
|
||||
#else
|
||||
wsi->ws->rx_packet_length = 0;
|
||||
#endif
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
|
||||
#if defined __LP64__
|
||||
wsi->ws->rx_packet_length |= ((size_t)c) << 48;
|
||||
#endif
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
|
||||
#if defined __LP64__
|
||||
wsi->ws->rx_packet_length |= ((size_t)c) << 40;
|
||||
#endif
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
|
||||
#if defined __LP64__
|
||||
wsi->ws->rx_packet_length |= ((size_t)c) << 32;
|
||||
#endif
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
|
||||
wsi->ws->rx_packet_length |= ((size_t)c) << 24;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
|
||||
wsi->ws->rx_packet_length |= ((size_t)c) << 16;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
|
||||
wsi->ws->rx_packet_length |= ((size_t)c) << 8;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
|
||||
wsi->ws->rx_packet_length |= ((size_t)c);
|
||||
if (wsi->ws->this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
|
||||
wsi->ws->mask[0] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
|
||||
wsi->ws->mask[1] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
|
||||
wsi->ws->mask[2] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
|
||||
wsi->ws->mask[3] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
wsi->ws->mask_idx = 0;
|
||||
if (wsi->ws->rx_packet_length == 0) {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
|
||||
assert(wsi->ws->rx_ubuf);
|
||||
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
goto drain_extension;
|
||||
|
||||
if (wsi->ws->rx_ubuf_head + LWS_PRE >=
|
||||
wsi->ws->rx_ubuf_alloc) {
|
||||
lwsl_err("Attempted overflow \n");
|
||||
return -1;
|
||||
}
|
||||
if (wsi->ws->all_zero_nonce)
|
||||
wsi->ws->rx_ubuf[LWS_PRE +
|
||||
(wsi->ws->rx_ubuf_head++)] = c;
|
||||
else
|
||||
wsi->ws->rx_ubuf[LWS_PRE +
|
||||
(wsi->ws->rx_ubuf_head++)] =
|
||||
c ^ wsi->ws->mask[
|
||||
(wsi->ws->mask_idx++) & 3];
|
||||
|
||||
if (--wsi->ws->rx_packet_length == 0) {
|
||||
/* spill because we have the whole frame */
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
|
||||
/*
|
||||
* if there's no protocol max frame size given, we are
|
||||
* supposed to default to context->pt_serv_buf_size
|
||||
*/
|
||||
if (!wsi->protocol->rx_buffer_size &&
|
||||
wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
|
||||
break;
|
||||
|
||||
if (wsi->protocol->rx_buffer_size &&
|
||||
wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
|
||||
break;
|
||||
|
||||
/* spill because we filled our rx buffer */
|
||||
spill:
|
||||
/*
|
||||
* is this frame a control packet we should take care of at this
|
||||
* layer? If so service it and hide it from the user callback
|
||||
*/
|
||||
|
||||
lwsl_parser("spill on %s\n", wsi->protocol->name);
|
||||
|
||||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_CLOSE:
|
||||
|
||||
/* is this an acknowledgment of our close? */
|
||||
if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
|
||||
/*
|
||||
* fine he has told us he is closing too, let's
|
||||
* finish our close
|
||||
*/
|
||||
lwsl_parser("seen client close ack\n");
|
||||
return -1;
|
||||
}
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
|
||||
/* if he sends us 2 CLOSE, kill him */
|
||||
return -1;
|
||||
|
||||
if (lws_partial_buffered(wsi)) {
|
||||
/*
|
||||
* if we're in the middle of something,
|
||||
* we can't do a normal close response and
|
||||
* have to just close our end.
|
||||
*/
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
lwsl_parser("Closing on peer close due to Pending tx\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
|
||||
wsi->user_space,
|
||||
&wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head))
|
||||
return -1;
|
||||
|
||||
lwsl_parser("server sees client close packet\n");
|
||||
lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
|
||||
/* deal with the close packet contents as a PONG */
|
||||
wsi->ws->payload_is_close = 1;
|
||||
goto process_as_ping;
|
||||
|
||||
case LWSWSOPC_PING:
|
||||
lwsl_info("received %d byte ping, sending pong\n",
|
||||
wsi->ws->rx_ubuf_head);
|
||||
|
||||
if (wsi->ws->ping_pending_flag) {
|
||||
/*
|
||||
* there is already a pending ping payload
|
||||
* we should just log and drop
|
||||
*/
|
||||
lwsl_parser("DROP PING since one pending\n");
|
||||
goto ping_drop;
|
||||
}
|
||||
process_as_ping:
|
||||
/* control packets can only be < 128 bytes long */
|
||||
if (wsi->ws->rx_ubuf_head > 128 - 3) {
|
||||
lwsl_parser("DROP PING payload too large\n");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* stash the pong payload */
|
||||
memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
|
||||
&wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head);
|
||||
|
||||
wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
|
||||
wsi->ws->ping_pending_flag = 1;
|
||||
|
||||
/* get it sent as soon as possible */
|
||||
lws_callback_on_writable(wsi);
|
||||
ping_drop:
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
return 0;
|
||||
|
||||
case LWSWSOPC_PONG:
|
||||
lwsl_info("received pong\n");
|
||||
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head);
|
||||
|
||||
if (wsi->pending_timeout ==
|
||||
PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
|
||||
lwsl_info("received expected PONG on wsi %p\n",
|
||||
wsi);
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
}
|
||||
|
||||
/* issue it */
|
||||
callback_action = LWS_CALLBACK_RECEIVE_PONG;
|
||||
break;
|
||||
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
case LWSWSOPC_CONTINUATION:
|
||||
break;
|
||||
|
||||
default:
|
||||
lwsl_parser("passing opc %x up to exts\n",
|
||||
wsi->ws->opcode);
|
||||
/*
|
||||
* It's something special we can't understand here.
|
||||
* Pass the payload up to the extension's parsing
|
||||
* state machine.
|
||||
*/
|
||||
|
||||
eff_buf.token = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
eff_buf.token_len = wsi->ws->rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
|
||||
&eff_buf, 0) <= 0)
|
||||
/* not handle or fail */
|
||||
lwsl_ext("ext opc opcode 0x%x unknown\n",
|
||||
wsi->ws->opcode);
|
||||
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* No it's real payload, pass it up to the user callback.
|
||||
* It's nicely buffered with the pre-padding taken care of
|
||||
* so it can be sent straight out again using lws_write
|
||||
*/
|
||||
|
||||
eff_buf.token = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
eff_buf.token_len = wsi->ws->rx_ubuf_head;
|
||||
|
||||
if (wsi->ws->opcode == LWSWSOPC_PONG && !eff_buf.token_len)
|
||||
goto already_done;
|
||||
|
||||
drain_extension:
|
||||
lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
|
||||
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
|
||||
goto already_done;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
|
||||
#endif
|
||||
/*
|
||||
* eff_buf may be pointing somewhere completely different now,
|
||||
* it's the output
|
||||
*/
|
||||
wsi->ws->first_fragment = 0;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (n < 0) {
|
||||
/*
|
||||
* we may rely on this to get RX, just drop connection
|
||||
*/
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if (rx_draining_ext && eff_buf.token_len == 0)
|
||||
goto already_done;
|
||||
|
||||
if (
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
n &&
|
||||
#endif
|
||||
eff_buf.token_len)
|
||||
/* extension had more... main loop will come back */
|
||||
lws_add_wsi_to_draining_ext_list(wsi);
|
||||
else
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
|
||||
if (eff_buf.token_len > 0 ||
|
||||
callback_action == LWS_CALLBACK_RECEIVE_PONG) {
|
||||
eff_buf.token[eff_buf.token_len] = '\0';
|
||||
|
||||
if (wsi->protocol->callback) {
|
||||
if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
|
||||
lwsl_info("Doing pong callback\n");
|
||||
|
||||
ret = user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, (enum lws_callback_reasons)
|
||||
callback_action,
|
||||
wsi->user_space,
|
||||
eff_buf.token,
|
||||
eff_buf.token_len);
|
||||
}
|
||||
else
|
||||
lwsl_err("No callback on payload spill!\n");
|
||||
}
|
||||
|
||||
already_done:
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
illegal_ctl_length:
|
||||
|
||||
lwsl_warn("Control frame with xtended length is illegal\n");
|
||||
/* kill the connection */
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE size_t
|
||||
lws_remaining_packet_payload(struct lws *wsi)
|
||||
{
|
||||
return wsi->ws->rx_packet_length;
|
||||
}
|
||||
|
||||
/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much
|
||||
* to expect in that state and can deal with it in bulk more efficiently.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
|
||||
size_t *len)
|
||||
{
|
||||
unsigned char *buffer = *buf, mask[4];
|
||||
int buffer_size, n;
|
||||
unsigned int avail;
|
||||
char *rx_ubuf;
|
||||
|
||||
if (wsi->protocol->rx_buffer_size)
|
||||
buffer_size = (int)wsi->protocol->rx_buffer_size;
|
||||
else
|
||||
buffer_size = wsi->context->pt_serv_buf_size;
|
||||
avail = buffer_size - wsi->ws->rx_ubuf_head;
|
||||
|
||||
/* do not consume more than we should */
|
||||
if (avail > wsi->ws->rx_packet_length)
|
||||
avail = (unsigned int)wsi->ws->rx_packet_length;
|
||||
|
||||
/* do not consume more than what is in the buffer */
|
||||
if (avail > *len)
|
||||
avail = (unsigned int)(*len);
|
||||
|
||||
/* we want to leave 1 byte for the parser to handle properly */
|
||||
if (avail <= 1)
|
||||
return 0;
|
||||
|
||||
avail--;
|
||||
rx_ubuf = wsi->ws->rx_ubuf + LWS_PRE + wsi->ws->rx_ubuf_head;
|
||||
if (wsi->ws->all_zero_nonce)
|
||||
memcpy(rx_ubuf, buffer, avail);
|
||||
else {
|
||||
|
||||
for (n = 0; n < 4; n++)
|
||||
mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
|
||||
|
||||
/* deal with 4-byte chunks using unwrapped loop */
|
||||
n = avail >> 2;
|
||||
while (n--) {
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[0];
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[1];
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[2];
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[3];
|
||||
}
|
||||
/* and the remaining bytes bytewise */
|
||||
for (n = 0; n < (int)(avail & 3); n++)
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[n];
|
||||
|
||||
wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
|
||||
}
|
||||
|
||||
(*buf) += avail;
|
||||
wsi->ws->rx_ubuf_head += avail;
|
||||
wsi->ws->rx_packet_length -= avail;
|
||||
*len -= avail;
|
||||
|
||||
return avail;
|
||||
}
|
File diff suppressed because it is too large
Load diff
171
lib/roles/listen/ops-listen.c
Normal file
171
lib/roles/listen/ops-listen.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
|
||||
lws_sock_file_fd_type fd;
|
||||
int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
|
||||
struct sockaddr_storage cli_addr;
|
||||
socklen_t clilen;
|
||||
|
||||
/* pollin means a client has connected to us then
|
||||
*
|
||||
* pollout is a hack on esp32 for background accepts signalling
|
||||
* they completed
|
||||
*/
|
||||
|
||||
do {
|
||||
struct lws *cwsi;
|
||||
|
||||
if (!(pollfd->revents & (LWS_POLLIN | LWS_POLLOUT)) ||
|
||||
!(pollfd->events & LWS_POLLIN))
|
||||
break;
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
/*
|
||||
* can we really accept it, with regards to SSL limit?
|
||||
* another vhost may also have had POLLIN on his
|
||||
* listener this round and used it up already
|
||||
*/
|
||||
if (wsi->vhost->use_ssl &&
|
||||
context->simultaneous_ssl_restriction &&
|
||||
context->simultaneous_ssl ==
|
||||
context->simultaneous_ssl_restriction)
|
||||
/*
|
||||
* no... ignore it, he won't come again until
|
||||
* we are below the simultaneous_ssl_restriction
|
||||
* limit and POLLIN is enabled on him again
|
||||
*/
|
||||
break;
|
||||
#endif
|
||||
/* listen socket got an unencrypted connection... */
|
||||
|
||||
clilen = sizeof(cli_addr);
|
||||
lws_latency_pre(context, wsi);
|
||||
|
||||
/*
|
||||
* We cannot identify the peer who is in the listen
|
||||
* socket connect queue before we accept it; even if
|
||||
* we could, not accepting it due to PEER_LIMITS would
|
||||
* block the connect queue for other legit peers.
|
||||
*/
|
||||
|
||||
accept_fd = accept((int)pollfd->fd,
|
||||
(struct sockaddr *)&cli_addr, &clilen);
|
||||
lws_latency(context, wsi, "listener accept",
|
||||
(int)accept_fd, accept_fd != LWS_SOCK_INVALID);
|
||||
if (accept_fd == LWS_SOCK_INVALID) {
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK) {
|
||||
break;
|
||||
}
|
||||
lwsl_err("ERROR on accept: %s\n",
|
||||
strerror(LWS_ERRNO));
|
||||
break;
|
||||
}
|
||||
|
||||
lws_plat_set_socket_options(wsi->vhost, accept_fd);
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
||||
((cli_addr.ss_family == AF_INET6) ?
|
||||
ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
|
||||
ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
|
||||
accept_fd);
|
||||
#else
|
||||
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
||||
ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
|
||||
accept_fd);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* look at who we connected to and give user code a
|
||||
* chance to reject based on client IP. There's no
|
||||
* protocol selected yet so we issue this to
|
||||
* protocols[0]
|
||||
*/
|
||||
if ((wsi->vhost->protocols[0].callback)(wsi,
|
||||
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
|
||||
NULL,
|
||||
(void *)(lws_intptr_t)accept_fd, 0)) {
|
||||
lwsl_debug("Callback denied net connection\n");
|
||||
compatible_close(accept_fd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW))
|
||||
opts |= LWS_ADOPT_HTTP;
|
||||
else
|
||||
opts = LWS_ADOPT_SOCKET;
|
||||
|
||||
fd.sockfd = accept_fd;
|
||||
cwsi = lws_adopt_descriptor_vhost(wsi->vhost, opts, fd,
|
||||
NULL, NULL);
|
||||
if (!cwsi)
|
||||
/* already closed cleanly as necessary */
|
||||
return LWS_HPI_RET_DIE;
|
||||
|
||||
if (lws_server_socket_service_ssl(cwsi, accept_fd))
|
||||
lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"listen svc fail");
|
||||
|
||||
lwsl_info("%s: new wsi %p: wsistate 0x%x, role_ops %s\n",
|
||||
__func__, cwsi, cwsi->wsistate, cwsi->role_ops->name);
|
||||
|
||||
} while (pt->fds_count < context->fd_limit_per_thread - 1 &&
|
||||
lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
int rops_handle_POLLOUT_listen(struct lws *wsi)
|
||||
{
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_listen = {
|
||||
"listen",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_listen,
|
||||
/* handle_POLLOUT */ rops_handle_POLLOUT_listen,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ NULL,
|
||||
/* rxflow_cache */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
};
|
78
lib/roles/pipe/ops-pipe.c
Normal file
78
lib/roles/pipe/ops-pipe.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
#if !defined(WIN32) && !defined(_WIN32)
|
||||
char s[10];
|
||||
int n;
|
||||
|
||||
/*
|
||||
* discard the byte(s) that signaled us
|
||||
* We really don't care about the number of bytes, but coverity
|
||||
* thinks we should.
|
||||
*/
|
||||
n = read(wsi->desc.sockfd, s, sizeof(s));
|
||||
(void)n;
|
||||
if (n < 0)
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
#endif
|
||||
/*
|
||||
* the poll() wait, or the event loop for libuv etc is a
|
||||
* process-wide resource that we interrupted. So let every
|
||||
* protocol that may be interested in the pipe event know that
|
||||
* it happened.
|
||||
*/
|
||||
if (lws_broadcast(wsi->context, LWS_CALLBACK_EVENT_WAIT_CANCELLED,
|
||||
NULL, 0)) {
|
||||
lwsl_info("closed in event cancel\n");
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_pipe = {
|
||||
"pipe",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_pipe,
|
||||
/* handle_POLLOUT */ NULL,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ NULL,
|
||||
/* rxflow_cache */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
};
|
199
lib/roles/raw/ops-raw.c
Normal file
199
lib/roles/raw/ops-raw.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
int len, n;
|
||||
|
||||
/* pending truncated sends have uber priority */
|
||||
|
||||
if (wsi->trunc_len) {
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
|
||||
wsi->trunc_len) < 0)
|
||||
goto fail;
|
||||
/*
|
||||
* we can't afford to allow input processing to send
|
||||
* something new, so spin around he event loop until
|
||||
* he doesn't have any partials
|
||||
*/
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
|
||||
/* any tunnel has to have been established... */
|
||||
lwsi_state(wsi) != LRS_SSL_ACK_PENDING &&
|
||||
!(wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT))) {
|
||||
|
||||
len = lws_read_or_use_preamble(pt, wsi);
|
||||
if (len < 0)
|
||||
goto fail;
|
||||
|
||||
if (!len)
|
||||
goto try_pollout;
|
||||
|
||||
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RAW_RX,
|
||||
wsi->user_space, pt->serv_buf,
|
||||
len);
|
||||
if (n < 0) {
|
||||
lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
if (wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT))
|
||||
/* we balanced the last favouring of pollin */
|
||||
wsi->favoured_pollin = 0;
|
||||
|
||||
try_pollout:
|
||||
|
||||
/* this handles POLLOUT for http serving fragments */
|
||||
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* one shot */
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_notice("%s a\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* clear back-to-back write detection */
|
||||
wsi->could_have_pending = 0;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_WRITEABLE_CB, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (wsi->active_writable_req_us) {
|
||||
uint64_t ul = time_in_microseconds() -
|
||||
wsi->active_writable_req_us;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_WRITABLE_DELAY, ul);
|
||||
lws_stats_atomic_max(wsi->context, pt,
|
||||
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
|
||||
wsi->active_writable_req_us = 0;
|
||||
}
|
||||
#endif
|
||||
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RAW_WRITEABLE,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n < 0) {
|
||||
lwsl_info("writeable_fail\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
fail:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail");
|
||||
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (pollfd->revents & LWS_POLLOUT) {
|
||||
n = lws_callback_as_writeable(wsi);
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
return LWS_HPI_RET_DIE;
|
||||
}
|
||||
if (n)
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
}
|
||||
|
||||
if (pollfd->revents & LWS_POLLIN) {
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RAW_RX_FILE,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
lwsl_debug("raw rx callback closed it\n");
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (pollfd->revents & LWS_POLLHUP)
|
||||
return LWS_HPI_RET_CLOSE_HANDLED;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
struct lws_role_ops role_ops_raw_skt = {
|
||||
"raw-skt",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_raw_skt,
|
||||
/* handle_POLLOUT */ NULL,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ NULL,
|
||||
/* rxflow_cache */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 },
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 },
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct lws_role_ops role_ops_raw_file = {
|
||||
"raw-file",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_raw_file,
|
||||
/* handle_POLLOUT */ NULL,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ NULL,
|
||||
/* rxflow_cache */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
|
||||
};
|
|
@ -22,7 +22,7 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
/*
|
||||
* parsers.c: lws_rx_sm() needs to be roughly kept in
|
||||
* parsers.c: lws_ws_rx_sm() needs to be roughly kept in
|
||||
* sync with changes here, esp related to ext draining
|
||||
*/
|
||||
|
604
lib/roles/ws/client-ws.c
Normal file
604
lib/roles/ws/client-ws.c
Normal file
|
@ -0,0 +1,604 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <private-libwebsockets.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(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;
|
||||
}
|
||||
|
||||
char *
|
||||
lws_generate_client_ws_handshake(struct lws *wsi, char *p)
|
||||
{
|
||||
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: Upgrade\x0d\x0a"
|
||||
"Sec-WebSocket-Key: ");
|
||||
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->extensions;
|
||||
while (ext && ext->callback) {
|
||||
n = lws_ext_cb_all_exts(wsi->context, wsi,
|
||||
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
|
||||
(char *)ext->name, 0);
|
||||
if (n) { /* an extension vetos us */
|
||||
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
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->ah->initial_handshake_hash_base64,
|
||||
sizeof(wsi->ah->initial_handshake_hash_base64));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
lws_client_ws_upgrade(struct lws *wsi, const char **cce)
|
||||
{
|
||||
int n, len, okay = 0;
|
||||
struct lws_context *context = wsi->context;
|
||||
const char *pc;
|
||||
char *p;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *sb = (char *)&pt->serv_buf[0];
|
||||
const struct lws_ext_options *opts;
|
||||
const struct lws_extension *ext;
|
||||
char ext_name[128];
|
||||
const char *c, *a;
|
||||
char ignore;
|
||||
int more = 1;
|
||||
void *v;
|
||||
#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->ah->http_response != 401) {
|
||||
lwsl_warn(
|
||||
"lws_client_handshake: got bad HTTP response '%d'\n",
|
||||
wsi->ah->http_response);
|
||||
*cce = "HS: ws upgrade unauthorized";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (wsi->ah->http_response != 101) {
|
||||
lwsl_warn(
|
||||
"lws_client_handshake: got bad HTTP response '%d'\n",
|
||||
wsi->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;
|
||||
}
|
||||
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
|
||||
if (!p) {
|
||||
lwsl_info("no Connection hdr\n");
|
||||
*cce = "HS: CONNECTION missing";
|
||||
goto bail3;
|
||||
}
|
||||
strtolower(p);
|
||||
if (strcmp(p, "upgrade")) {
|
||||
lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
|
||||
*cce = "HS: UPGRADE malformed";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
|
||||
if (!pc) {
|
||||
lwsl_parser("lws_client_int_s_hs: no protocol list\n");
|
||||
} else
|
||||
lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
|
||||
|
||||
/*
|
||||
* confirm the protocol the server wants to talk was in the list
|
||||
* of protocols we offered
|
||||
*/
|
||||
|
||||
len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
|
||||
if (!len) {
|
||||
lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
|
||||
/*
|
||||
* no protocol name to work from,
|
||||
* default to first protocol
|
||||
*/
|
||||
n = 0;
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
goto check_extensions;
|
||||
}
|
||||
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
|
||||
len = (int)strlen(p);
|
||||
|
||||
while (pc && *pc && !okay) {
|
||||
if (!strncmp(pc, p, len) &&
|
||||
(pc[len] == ',' || pc[len] == '\0')) {
|
||||
okay = 1;
|
||||
continue;
|
||||
}
|
||||
while (*pc && *pc++ != ',')
|
||||
;
|
||||
while (*pc && *pc == ' ')
|
||||
pc++;
|
||||
}
|
||||
|
||||
if (!okay) {
|
||||
lwsl_info("%s: got bad protocol %s\n", __func__, p);
|
||||
*cce = "HS: PROTOCOL malformed";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/*
|
||||
* identify the selected protocol struct and set it
|
||||
*/
|
||||
n = 0;
|
||||
/* keep client connection pre-bound protocol */
|
||||
if (!lwsi_role_client(wsi))
|
||||
wsi->protocol = NULL;
|
||||
|
||||
while (wsi->vhost->protocols[n].callback) {
|
||||
if (!wsi->protocol &&
|
||||
strcmp(p, wsi->vhost->protocols[n].name) == 0) {
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!wsi->vhost->protocols[n].callback) { /* no match */
|
||||
/* if server, that's already fatal */
|
||||
if (!lwsi_role_client(wsi)) {
|
||||
lwsl_info("%s: fail protocol %s\n", __func__, p);
|
||||
*cce = "HS: Cannot match protocol";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* for client, find the index of our pre-bound protocol */
|
||||
|
||||
n = 0;
|
||||
while (wsi->vhost->protocols[n].callback) {
|
||||
if (wsi->protocol && strcmp(wsi->protocol->name,
|
||||
wsi->vhost->protocols[n].name) == 0) {
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!wsi->vhost->protocols[n].callback) {
|
||||
if (wsi->protocol)
|
||||
lwsl_err("Failed to match protocol %s\n",
|
||||
wsi->protocol->name);
|
||||
else
|
||||
lwsl_err("No protocol on client\n");
|
||||
goto bail2;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
|
||||
|
||||
check_extensions:
|
||||
/*
|
||||
* stitch protocol choice into the vh protocol linked list
|
||||
* We always insert ourselves at the start of the list
|
||||
*
|
||||
* X <-> B
|
||||
* X <-> pAn <-> pB
|
||||
*/
|
||||
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
|
||||
wsi->same_vh_protocol_prev = /* guy who points to us */
|
||||
&wsi->vhost->same_vh_protocol_list[n];
|
||||
wsi->same_vh_protocol_next = /* old first guy is our next */
|
||||
wsi->vhost->same_vh_protocol_list[n];
|
||||
/* we become the new first guy */
|
||||
wsi->vhost->same_vh_protocol_list[n] = wsi;
|
||||
|
||||
if (wsi->same_vh_protocol_next)
|
||||
/* old first guy points back to us now */
|
||||
wsi->same_vh_protocol_next->same_vh_protocol_prev =
|
||||
&wsi->same_vh_protocol_next;
|
||||
wsi->on_same_vh_list = 1;
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
||||
#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->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->active_extensions[wsi->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->act_ext_user[wsi->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->act_ext_user[
|
||||
wsi->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->act_ext_user[wsi->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->act_ext_user[wsi->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->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->ah->initial_handshake_hash_base64)) {
|
||||
lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
|
||||
wsi->ah->initial_handshake_hash_base64);
|
||||
*cce = "HS: Accept hash wrong";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* allocate the per-connection user memory (if any) */
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Problem allocating wsi user mem\n");
|
||||
*cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/*
|
||||
* we seem to be good to go, give client last chance to check
|
||||
* headers and OK it
|
||||
*/
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
*cce = "HS: Rejected by filter cb";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* clear his proxy connection timeout */
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
|
||||
/* free up his parsing allocations */
|
||||
lws_header_table_detach(wsi, 0);
|
||||
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
|
||||
&role_ops_ws);
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
||||
|
||||
/*
|
||||
* create the frame buffer for this connection according to the
|
||||
* size mentioned in the protocol definition. If 0 there, then
|
||||
* use a big default for compatibility
|
||||
*/
|
||||
n = (int)wsi->protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = context->pt_serv_buf_size;
|
||||
n += LWS_PRE;
|
||||
wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
|
||||
"client frame buffer");
|
||||
if (!wsi->ws->rx_ubuf) {
|
||||
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
|
||||
*cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
wsi->ws->rx_ubuf_alloc = n;
|
||||
lwsl_info("Allocating client RX buffer %d\n", n);
|
||||
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
|
||||
(const char *)&n, sizeof n)) {
|
||||
lwsl_warn("Failed to set SNDBUF to %d", n);
|
||||
*cce = "HS: SO_SNDBUF failed";
|
||||
goto bail3;
|
||||
}
|
||||
#endif
|
||||
|
||||
lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
|
||||
|
||||
/* call him back to inform him he is up */
|
||||
|
||||
if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
*cce = "HS: Rejected at CLIENT_ESTABLISHED";
|
||||
goto bail3;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* inform all extensions, not just active ones since they
|
||||
* already know
|
||||
*/
|
||||
ext = wsi->vhost->extensions;
|
||||
|
||||
while (ext && ext->callback) {
|
||||
v = NULL;
|
||||
for (n = 0; n < wsi->count_act_ext; n++)
|
||||
if (wsi->active_extensions[n] == ext)
|
||||
v = wsi->act_ext_user[n];
|
||||
|
||||
ext->callback(context, ext, wsi,
|
||||
LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0);
|
||||
ext++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
bail3:
|
||||
return 3;
|
||||
|
||||
bail2:
|
||||
return 2;
|
||||
}
|
1872
lib/roles/ws/ops-ws.c
Normal file
1872
lib/roles/ws/ops-ws.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -19,7 +19,7 @@
|
|||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
#include <private-libwebsockets.h>
|
||||
|
||||
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
|
||||
|
||||
|
@ -226,6 +226,199 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int
|
||||
lws_process_ws_upgrade(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
char protocol_list[128], protocol_name[64], *p;
|
||||
int protocol_len, hit, n = 0, non_space_char_found = 0;
|
||||
|
||||
if (!wsi->protocol)
|
||||
lwsl_err("NULL protocol at lws_read\n");
|
||||
|
||||
/*
|
||||
* It's either websocket or h2->websocket
|
||||
*
|
||||
* Select the first protocol we support from the list
|
||||
* the client sent us.
|
||||
*
|
||||
* Copy it to remove header fragmentation
|
||||
*/
|
||||
|
||||
if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
|
||||
WSI_TOKEN_PROTOCOL) < 0) {
|
||||
lwsl_err("protocol list too long");
|
||||
return 1;
|
||||
}
|
||||
|
||||
protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
|
||||
protocol_list[protocol_len] = '\0';
|
||||
p = protocol_list;
|
||||
hit = 0;
|
||||
|
||||
while (*p && !hit) {
|
||||
n = 0;
|
||||
non_space_char_found = 0;
|
||||
while (n < (int)sizeof(protocol_name) - 1 &&
|
||||
*p && *p != ',') {
|
||||
/* ignore leading spaces */
|
||||
if (!non_space_char_found && *p == ' ') {
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
non_space_char_found = 1;
|
||||
protocol_name[n++] = *p++;
|
||||
}
|
||||
protocol_name[n] = '\0';
|
||||
if (*p)
|
||||
p++;
|
||||
|
||||
lwsl_debug("checking %s\n", protocol_name);
|
||||
|
||||
n = 0;
|
||||
while (wsi->vhost->protocols[n].callback) {
|
||||
lwsl_debug("try %s\n",
|
||||
wsi->vhost->protocols[n].name);
|
||||
|
||||
if (wsi->vhost->protocols[n].name &&
|
||||
!strcmp(wsi->vhost->protocols[n].name,
|
||||
protocol_name)) {
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
hit = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
/* we didn't find a protocol he wanted? */
|
||||
|
||||
if (!hit) {
|
||||
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
|
||||
lwsl_notice("No protocol from \"%s\" supported\n",
|
||||
protocol_list);
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* some clients only have one protocol and
|
||||
* do not send the protocol list header...
|
||||
* allow it and match to the vhost's default
|
||||
* protocol (which itself defaults to zero)
|
||||
*/
|
||||
lwsl_info("defaulting to prot handler %d\n",
|
||||
wsi->vhost->default_protocol_index);
|
||||
n = wsi->vhost->default_protocol_index;
|
||||
wsi->protocol = &wsi->vhost->protocols[
|
||||
(int)wsi->vhost->default_protocol_index];
|
||||
}
|
||||
|
||||
/* allocate the ws struct for the wsi */
|
||||
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
|
||||
if (!wsi->ws) {
|
||||
lwsl_notice("OOM\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
|
||||
wsi->ws->ietf_spec_revision =
|
||||
atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
|
||||
|
||||
/* allocate wsi->user storage */
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_notice("problem with user space\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the user code a chance to study the request and
|
||||
* have the opportunity to deny it
|
||||
*/
|
||||
if ((wsi->protocol->callback)(wsi,
|
||||
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
|
||||
wsi->user_space,
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
|
||||
lwsl_warn("User code denied connection\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform the handshake according to the protocol version the
|
||||
* client announced
|
||||
*/
|
||||
|
||||
switch (wsi->ws->ietf_spec_revision) {
|
||||
default:
|
||||
lwsl_notice("Unknown client spec version %d\n",
|
||||
wsi->ws->ietf_spec_revision);
|
||||
wsi->ws->ietf_spec_revision = 13;
|
||||
//return 1;
|
||||
/* fallthru */
|
||||
case 13:
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->h2_stream_carries_ws) {
|
||||
if (lws_h2_ws_handshake(wsi)) {
|
||||
lwsl_notice("h2 ws handshake failed\n");
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
lwsl_parser("lws_parse calling handshake_04\n");
|
||||
if (handshake_0405(wsi->context, wsi)) {
|
||||
lwsl_notice("hs0405 has failed the connection\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
lws_same_vh_protocol_insert(wsi, n);
|
||||
|
||||
/*
|
||||
* We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined
|
||||
* header considerations about keeping the ah around no longer apply.
|
||||
*
|
||||
* However it's common for the first ws protocol data to have been
|
||||
* coalesced with the browser upgrade request and to already be in the
|
||||
* ah rx buffer.
|
||||
*/
|
||||
|
||||
lwsl_debug("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n",
|
||||
__func__, wsi, wsi->ah->rxpos, wsi->ah->rxlen);
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
if (wsi->h2_stream_carries_ws)
|
||||
lws_role_transition(wsi, LWSIFR_SERVER | LWSIFR_P_ENCAP_H2,
|
||||
LRS_ESTABLISHED, &role_ops_ws);
|
||||
else
|
||||
lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED,
|
||||
&role_ops_ws);
|
||||
/*
|
||||
* Because rxpos/rxlen shows something in the ah, we will get
|
||||
* service guaranteed next time around the event loop
|
||||
*/
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
lws_server_init_wsi_for_ws(wsi);
|
||||
lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision);
|
||||
|
||||
/* !!! drop ah unreservedly after ESTABLISHED */
|
||||
if (wsi->ah->rxpos == wsi->ah->rxlen) {
|
||||
lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi);
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
lws_header_table_detach(wsi, 1);
|
||||
} else
|
||||
lwsl_info("%s: %p: unable to drop ah at ws upgrade %d vs %d\n",
|
||||
__func__, wsi, wsi->ah->rxpos, wsi->ah->rxlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
|
@ -238,7 +431,7 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
|
||||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
|
||||
lwsl_parser("handshake_04 missing pieces\n");
|
||||
lwsl_info("handshake_04 missing pieces\n");
|
||||
/* completed header processing, but missing some bits */
|
||||
goto bail;
|
||||
}
|
||||
|
@ -328,10 +521,10 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
#if defined(DEBUG)
|
||||
fwrite(response, 1, p - response, stderr);
|
||||
#endif
|
||||
n = lws_write(wsi, (unsigned char *)response,
|
||||
p - response, LWS_WRITE_HTTP_HEADERS);
|
||||
n = lws_write(wsi, (unsigned char *)response, p - response,
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
if (n != (p - response)) {
|
||||
lwsl_debug("handshake_0405: ERROR writing to socket\n");
|
||||
lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
|
@ -362,3 +555,67 @@ bail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m;
|
||||
|
||||
lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
|
||||
|
||||
/* let the rx protocol state machine have as much as it needs */
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (wsi->rxflow_bitmap) {
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
lwsl_parser("%s: cached %ld\n", __func__, (long)len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
m = lws_ws_rx_sm(wsi, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer) {
|
||||
wsi->rxflow_pos++;
|
||||
if (wsi->rxflow_pos > wsi->rxflow_len)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* consume payload bytes efficiently */
|
||||
if (wsi->lws_rx_parse_state ==
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
|
||||
m = lws_payload_until_length_exhausted(wsi, buf, &len);
|
||||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos += m;
|
||||
}
|
||||
|
||||
/* process the byte */
|
||||
m = lws_ws_rx_sm(wsi, *(*buf)++);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
len--;
|
||||
|
||||
if (wsi->rxflow_buffer && wsi->rxflow_pos == wsi->rxflow_len) {
|
||||
lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi);
|
||||
lws_free_set_NULL(wsi->rxflow_buffer);
|
||||
/* having drained the rxflow buffer, can rearm POLLIN */
|
||||
#ifdef LWS_NO_SERVER
|
||||
m =
|
||||
#endif
|
||||
__lws_rx_flow_control(wsi);
|
||||
/* m ignored, needed for NO_SERVER case */
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
|
||||
|
||||
return 0;
|
||||
}
|
1772
lib/service.c
1772
lib/service.c
File diff suppressed because it is too large
Load diff
|
@ -13,3 +13,12 @@ same as minimal http client.
|
|||
|
||||
However it does it for 8 client connections concurrently.
|
||||
|
||||
## Commandline Options
|
||||
|
||||
Option|Meaning
|
||||
---|---
|
||||
-s|Stagger the connections by 100ms, the last by 1s
|
||||
-p|Use http/1.1 pipelining or h2 simultaneous streams
|
||||
--h1|Force http/1 only
|
||||
-l|Connect to server on https://localhost:7681 instead of https://warmcat.com:443
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include <time.h>
|
||||
|
||||
#define COUNT 8
|
||||
//#define STAGGERED_CONNECTIONS
|
||||
|
||||
struct user {
|
||||
int index;
|
||||
|
@ -136,36 +135,15 @@ unsigned long long us(void)
|
|||
}
|
||||
|
||||
static void
|
||||
lws_try_client_connection(struct lws_context *context, int m)
|
||||
lws_try_client_connection(struct lws_client_connect_info *i, int m)
|
||||
{
|
||||
struct lws_client_connect_info i;
|
||||
i->path = "/";
|
||||
|
||||
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
|
||||
i.context = context;
|
||||
|
||||
#if 0
|
||||
i.port = 7681;
|
||||
i.address = "localhost";
|
||||
#else
|
||||
i.port = 443;
|
||||
i.address = "warmcat.com";
|
||||
#endif
|
||||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.ssl_connection = LCCSCF_PIPELINE | /* enables h1 or h2 connection sharing */
|
||||
// LCCSCF_NOT_H2 | /* forces http/1 */
|
||||
LCCSCF_ALLOW_SELFSIGNED | /* allow selfsigned cert */
|
||||
LCCSCF_USE_SSL;
|
||||
i.method = "GET";
|
||||
|
||||
i.protocol = protocols[0].name;
|
||||
|
||||
i.pwsi = &client_wsi[m];
|
||||
i->pwsi = &client_wsi[m];
|
||||
user[m].index = m;
|
||||
i.userdata = &user[m];
|
||||
i->userdata = &user[m];
|
||||
|
||||
if (!lws_client_connect_via_info(&i)) {
|
||||
if (!lws_client_connect_via_info(i)) {
|
||||
failed++;
|
||||
if (++completed == COUNT) {
|
||||
lwsl_user("Done: failed: %d\n", failed);
|
||||
|
@ -175,27 +153,45 @@ lws_try_client_connection(struct lws_context *context, int m)
|
|||
lwsl_user("started connection %d\n", m);
|
||||
}
|
||||
|
||||
static int commandline_option(int argc, char **argv, const char *val)
|
||||
{
|
||||
int n = strlen(val);
|
||||
|
||||
while (--argc > 0) {
|
||||
if (!strncmp(argv[argc], val, n))
|
||||
return argc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_client_connect_info i;
|
||||
struct lws_context *context;
|
||||
unsigned long long start
|
||||
#if defined(STAGGERED_CONNECTIONS)
|
||||
, next
|
||||
#endif
|
||||
;
|
||||
int n = 0, m;
|
||||
unsigned long long start, next;
|
||||
int n = 0, m, staggered = 0, logs =
|
||||
LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
|
||||
/* for LLL_ verbosity above NOTICE to be built into lws,
|
||||
* lws must have been configured and built with
|
||||
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
|
||||
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
|
||||
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
|
||||
/* | LLL_DEBUG */;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
|
||||
/* for LLL_ verbosity above NOTICE to be built into lws,
|
||||
* lws must have been configured and built with
|
||||
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
|
||||
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
|
||||
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
|
||||
/* | LLL_DEBUG */, NULL);
|
||||
|
||||
lwsl_user("LWS minimal http client\n");
|
||||
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
|
||||
|
||||
staggered = !!commandline_option(argc, argv, "-s");
|
||||
m = commandline_option(argc, argv, "-d");
|
||||
if (m && m + 1 < argc)
|
||||
logs = atoi(argv[m + 1]);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS minimal http client [-s (staggered)] [-p (pipeline)]\n");
|
||||
lwsl_user(" [--h1 (http/1 only)] [-l (localhost)]\n");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
|
@ -218,43 +214,67 @@ int main(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(STAGGERED_CONNECTIONS)
|
||||
/*
|
||||
* just pile on all the connections at once, testing the queueing
|
||||
*/
|
||||
for (m = 0; m < (int)LWS_ARRAY_SIZE(client_wsi); m++)
|
||||
lws_try_client_connection(context, m);
|
||||
#else
|
||||
next =
|
||||
#endif
|
||||
start = us();
|
||||
i.context = context;
|
||||
i.ssl_connection = LCCSCF_USE_SSL;
|
||||
|
||||
/* enables h1 or h2 connection sharing */
|
||||
if (commandline_option(argc, argv, "-p"))
|
||||
i.ssl_connection |= LCCSCF_PIPELINE;
|
||||
|
||||
/* force h1 even if h2 available */
|
||||
if (commandline_option(argc, argv, "--h1"))
|
||||
i.ssl_connection |= LCCSCF_NOT_H2;
|
||||
|
||||
if (commandline_option(argc, argv, "-l")) {
|
||||
i.port = 7681;
|
||||
i.address = "localhost";
|
||||
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
|
||||
} else {
|
||||
i.port = 443;
|
||||
i.address = "warmcat.com";
|
||||
}
|
||||
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.method = "GET";
|
||||
i.protocol = protocols[0].name;
|
||||
|
||||
if (!staggered)
|
||||
/*
|
||||
* just pile on all the connections at once, testing the
|
||||
* pipeline queueing before the first is connected
|
||||
*/
|
||||
for (m = 0; m < (int)LWS_ARRAY_SIZE(client_wsi); m++)
|
||||
lws_try_client_connection(&i, m);
|
||||
|
||||
next = start = us();
|
||||
m = 0;
|
||||
while (n >= 0 && !interrupted) {
|
||||
|
||||
#if defined(STAGGERED_CONNECTIONS)
|
||||
/*
|
||||
* open the connections at 100ms intervals, with the last
|
||||
* one being after 1s, testing queueing, and direct H2 stream
|
||||
* addition stability
|
||||
*/
|
||||
if (us() > next && m < (int)LWS_ARRAY_SIZE(client_wsi)) {
|
||||
if (staggered) {
|
||||
/*
|
||||
* open the connections at 100ms intervals, with the
|
||||
* last one being after 1s, testing both queueing, and
|
||||
* direct H2 stream addition stability
|
||||
*/
|
||||
if (us() > next && m < (int)LWS_ARRAY_SIZE(client_wsi)) {
|
||||
|
||||
lws_try_client_connection(context, m++);
|
||||
lws_try_client_connection(&i, m++);
|
||||
|
||||
if (m == (int)LWS_ARRAY_SIZE(client_wsi) - 1)
|
||||
next = us() + 1000000;
|
||||
else
|
||||
next = us() + 100000;
|
||||
if (m == (int)LWS_ARRAY_SIZE(client_wsi) - 1)
|
||||
next = us() + 1000000;
|
||||
else
|
||||
next = us() + 100000;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
n = lws_service(context, 1000);
|
||||
n = lws_service(context, 100);
|
||||
}
|
||||
|
||||
lwsl_user("Duration: %lldms\n", (us() - start) / 1000);
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed\n");
|
||||
|
||||
return 0;
|
||||
lwsl_user("Exiting with %d\n", failed || completed != COUNT);
|
||||
|
||||
return failed || completed != COUNT;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ The example connects a socket itself to libwebsockets.org:80, and then
|
|||
has lws adopt it as a raw wsi. The lws protocol writes "GET / HTTP/1.1"
|
||||
to the socket and hexdumps what was sent back.
|
||||
|
||||
The socket won't close until the server side times it out, since it's
|
||||
a raw socket that doesn't understand it's looking at http.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
|
|
|
@ -111,7 +111,7 @@ int main(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
info.port = 7681;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
|
||||
info.protocols = protocols;
|
||||
|
||||
vhost = lws_create_vhost(context, &info);
|
||||
|
|
|
@ -54,7 +54,7 @@ static struct lws_poly_gen tx = { { 0xabcde, 0x23456789 } },
|
|||
rx = { { 0xabcde, 0x23456789 } }
|
||||
;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
@ -116,7 +116,7 @@ static int
|
|||
callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
union lws_tls_cert_info_results ci;
|
||||
#endif
|
||||
const char *which = "http";
|
||||
|
@ -177,7 +177,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
lwsl_notice("lws_http_client_http_response %d\n",
|
||||
lws_http_client_http_response(wsi));
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME,
|
||||
&ci, sizeof(ci.ns.name)))
|
||||
lwsl_notice(" Peer Cert CN : %s\n", ci.ns.name);
|
||||
|
@ -272,7 +272,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
force_exit = 1;
|
||||
break;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) && \
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param) && \
|
||||
!defined(LWS_WITH_MBEDTLS)
|
||||
case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
|
||||
if (crl_path[0]) {
|
||||
|
@ -419,7 +419,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
lws_callback_on_writable(wsi);
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
usleep(250);
|
||||
usleep(50);
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -545,7 +545,7 @@ static struct option options[] = {
|
|||
{ "ssl-cert", required_argument, NULL, 'C' },
|
||||
{ "ssl-key", required_argument, NULL, 'K' },
|
||||
{ "ssl-ca", required_argument, NULL, 'A' },
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
{ "ssl-crl", required_argument, NULL, 'R' },
|
||||
#endif
|
||||
{ NULL, 0, 0, 0 }
|
||||
|
@ -650,7 +650,7 @@ int main(int argc, char **argv)
|
|||
lws_strncpy(ca_path, optarg, sizeof(ca_path));
|
||||
break;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
case 'R':
|
||||
lws_strncpy(crl_path, optarg, sizeof(crl_path));
|
||||
break;
|
||||
|
@ -697,7 +697,7 @@ int main(int argc, char **argv)
|
|||
info.ws_ping_pong_interval = pp_secs;
|
||||
info.extensions = exts;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
#endif
|
||||
|
||||
|
@ -718,7 +718,7 @@ int main(int argc, char **argv)
|
|||
if (ca_path[0])
|
||||
info.client_ssl_ca_filepath = ca_path;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
else if (crl_path[0])
|
||||
lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
|
||||
#endif
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* using this protocol, including the sender
|
||||
*/
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
/* location of the certificate revocation list */
|
||||
extern char crl_path[1024];
|
||||
#endif
|
||||
|
@ -230,7 +230,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
|
||||
#if !defined(LWS_NO_CLIENT) && defined(LWS_WITH_TLS)
|
||||
if (!strncmp(in, "/proxytest", 10)) {
|
||||
struct lws_client_connect_info i;
|
||||
char *rootpath = "/git/";
|
||||
|
@ -773,7 +773,7 @@ bail:
|
|||
|
||||
break;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
|
||||
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
||||
/* Verify the client certificate */
|
||||
if (!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)) {
|
||||
|
|
|
@ -30,7 +30,7 @@ struct lws_plat_file_ops fops_plat;
|
|||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct lws_plat_file_ops fops_plat;
|
|||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ struct lws_plat_file_ops fops_plat;
|
|||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ int count_pollfds;
|
|||
volatile int force_exit = 0;
|
||||
struct lws_context *context;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ uv_timer_t timeout_watcher;
|
|||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
@ -296,7 +296,7 @@ static const struct option options[] = {
|
|||
{ "ssl-cert", required_argument, NULL, 'C' },
|
||||
{ "ssl-key", required_argument, NULL, 'K' },
|
||||
{ "ssl-ca", required_argument, NULL, 'A' },
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
{ "ssl-verify-client", no_argument, NULL, 'v' },
|
||||
#if defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
{ "ssl-crl", required_argument, NULL, 'R' },
|
||||
|
@ -363,7 +363,7 @@ int main(int argc, char **argv)
|
|||
use_ssl = 1;
|
||||
break;
|
||||
case 'S':
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
|
||||
info.ssl_info_event_mask |= SSL_CB_ALERT;
|
||||
#endif
|
||||
break;
|
||||
|
@ -390,7 +390,7 @@ int main(int argc, char **argv)
|
|||
case 'A':
|
||||
lws_strncpy(ca_path, optarg, sizeof(ca_path));
|
||||
break;
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
case 'v':
|
||||
use_ssl = 1;
|
||||
opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
|
||||
|
|
|
@ -37,7 +37,7 @@ struct lws_plat_file_ops fops_plat;
|
|||
/* http server gets files from this path */
|
||||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
char crl_path[1024] = "";
|
||||
#endif
|
||||
|
||||
|
@ -211,7 +211,7 @@ static struct option options[] = {
|
|||
{ "ssl-cert", required_argument, NULL, 'C' },
|
||||
{ "ssl-key", required_argument, NULL, 'K' },
|
||||
{ "ssl-ca", required_argument, NULL, 'A' },
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
{ "ssl-verify-client", no_argument, NULL, 'v' },
|
||||
#if defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
{ "ssl-crl", required_argument, NULL, 'R' },
|
||||
|
@ -339,7 +339,7 @@ int main(int argc, char **argv)
|
|||
pp_secs = atoi(optarg);
|
||||
lwsl_notice("Setting pingpong interval to %d\n", pp_secs);
|
||||
break;
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_TLS)
|
||||
case 'v':
|
||||
use_ssl = 1;
|
||||
opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
|
||||
|
@ -475,7 +475,7 @@ int main(int argc, char **argv)
|
|||
|
||||
info.port++;
|
||||
|
||||
#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
|
||||
#if !defined(LWS_NO_CLIENT) && defined(LWS_WITH_TLS)
|
||||
lws_init_vhost_client_ssl(&info, vhost);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ extern int count_pollfds;
|
|||
extern volatile int force_exit;
|
||||
extern struct lws_context *context;
|
||||
extern char *resource_path;
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
|
||||
extern char crl_path[1024];
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue