1
0
Fork 0
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:
Andy Green 2018-04-11 13:39:42 +08:00
parent 16e2f09710
commit 126be3ccf3
67 changed files with 6418 additions and 5094 deletions

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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,

View file

@ -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

View file

@ -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)
{

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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
View 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
View 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
View 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 },
};

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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 */

View file

@ -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;
}

View file

@ -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)))

View file

@ -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;

View file

@ -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

View 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
View 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
View 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 },
};

View file

@ -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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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;
}

View file

@ -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
```

View file

@ -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);

View file

@ -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

View file

@ -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)) {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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