diff --git a/CMakeLists.txt b/CMakeLists.txt index a2e990a20..ac9bd8c46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" ON) option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF) option(LWS_WITH_LEJP "With the Lightweight JSON Parser" ON) option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF) -option(LWS_WITH_STRUCT_JSON "Generic struct serialization to and from JSON" ON) +option(LWS_WITH_STRUCT_JSON "Generic struct serialization to and from JSON" OFF) option(LWS_WITH_STRUCT_SQLITE3 "Generic struct serialization to and from SQLITE3" OFF) option(LWS_WITH_SMTP "Provide SMTP support" OFF) if (WIN32 OR LWS_WITH_ESP32) diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 9690a4807..a87d2323e 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -135,6 +135,7 @@ #cmakedefine LWS_WITH_STATEFUL_URLDECODE #cmakedefine LWS_WITH_STATS #cmakedefine LWS_WITH_STRUCT_SQLITE3 +#cmakedefine LWS_WITH_STRUCT_JSON #cmakedefine LWS_WITH_SQLITE3 #cmakedefine LWS_WITH_THREADPOOL #cmakedefine LWS_WITH_TLS diff --git a/lib/core-net/close.c b/lib/core-net/close.c index a65a5df87..34112afbe 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -55,7 +55,7 @@ __lws_free_wsi(struct lws *wsi) #endif __lws_same_vh_protocol_remove(wsi); #if !defined(LWS_NO_CLIENT) - lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->stash); lws_free_set_NULL(wsi->cli_hostname_copy); #endif @@ -257,7 +257,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, #endif #if !defined(LWS_NO_CLIENT) - lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->stash); #endif if (wsi->role_ops == &role_ops_raw_skt) { diff --git a/lib/core-net/connect.c b/lib/core-net/connect.c index e1734a309..fd435c848 100644 --- a/lib/core-net/connect.c +++ b/lib/core-net/connect.c @@ -24,34 +24,16 @@ #include "private-lib-core.h" -void -lws_client_stash_destroy(struct lws *wsi) -{ - if (!wsi || !wsi->stash) - return; - - lws_free_set_NULL(wsi->stash->address); - lws_free_set_NULL(wsi->stash->path); - lws_free_set_NULL(wsi->stash->host); - lws_free_set_NULL(wsi->stash->origin); - lws_free_set_NULL(wsi->stash->protocol); - lws_free_set_NULL(wsi->stash->method); - lws_free_set_NULL(wsi->stash->iface); - lws_free_set_NULL(wsi->stash->alpn); - - lws_free_set_NULL(wsi->stash); -} - LWS_VISIBLE struct lws * lws_client_connect_via_info(const struct lws_client_connect_info *i) { + const char *local = i->protocol; struct lws *wsi, *safe = NULL; const struct lws_protocols *p; - const char *local = i->protocol; - int tid = 0; -#if LWS_MAX_SMP > 1 - int n; -#endif + const char *cisin[CIS_COUNT]; + int tid = 0, n, m; + size_t size; + char *pc; if (i->context->requested_kill) return NULL; @@ -196,45 +178,43 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) * with no relationship to http or ah */ - wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash"); + cisin[CIS_ADDRESS] = i->address; + cisin[CIS_PATH] = i->path; + cisin[CIS_HOST] = i->host; + cisin[CIS_ORIGIN] = i->origin; + cisin[CIS_PROTOCOL] = i->protocol; + cisin[CIS_METHOD] = i->method; + cisin[CIS_IFACE] = i->iface; + cisin[CIS_ALPN] = i->alpn; + + size = sizeof(*wsi->stash); + + /* + * Let's overallocate the stash object with space for all the args + * in one hit. + */ + for (n = 0; n < CIS_COUNT; n++) + if (cisin[n]) + size += strlen(cisin[n]) + 1; + + wsi->stash = lws_malloc(size, "client stash"); if (!wsi->stash) { lwsl_err("%s: OOM\n", __func__); goto bail1; } + /* all the pointers default to NULL, but no need to zero the args */ + memset(wsi->stash, 0, sizeof(*wsi->stash)); - wsi->stash->address = lws_strdup(i->address); - wsi->stash->path = lws_strdup(i->path); - wsi->stash->host = lws_strdup(i->host); wsi->stash->opaque_user_data = i->opaque_user_data; + pc = (char *)&wsi->stash[1]; - if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host) - goto bail1; - - if (i->origin) { - wsi->stash->origin = lws_strdup(i->origin); - if (!wsi->stash->origin) - goto bail1; - } - if (i->protocol) { - wsi->stash->protocol = lws_strdup(i->protocol); - if (!wsi->stash->protocol) - goto bail1; - } - if (i->method) { - wsi->stash->method = lws_strdup(i->method); - if (!wsi->stash->method) - goto bail1; - } - if (i->iface) { - wsi->stash->iface = lws_strdup(i->iface); - if (!wsi->stash->iface) - goto bail1; - } - if (i->alpn) { - wsi->stash->alpn = lws_strdup(i->alpn); - if (!wsi->stash->alpn) - goto bail1; - } + for (n = 0; n < CIS_COUNT; n++) + if (cisin[n]) { + wsi->stash->cis[n] = pc; + m = strlen(cisin[n]) + 1; + memcpy(pc, cisin[n], m); + pc += m; + } /* * at this point user callbacks like @@ -302,7 +282,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) return wsi; bail1: - lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->stash); bail: lws_free(wsi); diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 7d4f86f60..886de8be7 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -231,17 +231,24 @@ enum uri_esc_states { URIES_SEEN_PERCENT_H1, }; - #ifndef LWS_NO_CLIENT + +enum { + CIS_ADDRESS, + CIS_PATH, + CIS_HOST, + CIS_ORIGIN, + CIS_PROTOCOL, + CIS_METHOD, + CIS_IFACE, + CIS_ALPN, + + + CIS_COUNT +}; + struct client_info_stash { - char *address; - char *path; - char *host; - char *origin; - char *protocol; - char *method; - char *iface; - char *alpn; + char *cis[CIS_COUNT]; void *opaque_user_data; /* not allocated or freed by lws */ }; #endif @@ -886,9 +893,6 @@ lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); LWS_EXTERN int lws_service_flag_pending(struct lws_context *context, int tsi); -LWS_EXTERN void -lws_client_stash_destroy(struct lws *wsi); - static LWS_INLINE int lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; } diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index 6b30b5ca2..660fe5855 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -618,6 +618,9 @@ LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, void lws_context_destroy2(struct lws_context *context); +#if !defined(PRIu64) +#define PRIu64 "llu" +#endif #ifdef __cplusplus }; diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 1ba6a66a8..19285476a 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -937,7 +937,7 @@ rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i) * we can assign the user space now, otherwise do it after the * ws subprotocol negotiated */ - if (!wsi->user_space && wsi->stash->method) + if (!wsi->user_space && wsi->stash->cis[CIS_METHOD]) if (lws_ensure_user_space(wsi)) return 1; @@ -951,11 +951,8 @@ rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i) * only try h2 if he assertively said to use h2 alpn, otherwise * ws implies alpn restriction to h1. */ - if (!wsi->stash->method && !wsi->stash->alpn) { - wsi->stash->alpn = lws_strdup("http/1.1"); - if (!wsi->stash->alpn) - return 1; - } + if (!wsi->stash->cis[CIS_METHOD] && !wsi->stash->cis[CIS_ALPN]) + wsi->stash->cis[CIS_ALPN] = "http/1.1"; /* if we went on the ah waiting list, it's ok, we can wait. * diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 92f5d59fa..f6f4f9a86 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -793,7 +793,8 @@ lws_h2_dump_waiting_children(struct lws *wsi) while (wsi) { lwsl_info(" %c %p %s %s\n", wsi->h2.requested_POLLOUT ? '*' : ' ', - wsi, wsi->role_ops->name, wsi->protocol->name); + wsi, wsi->role_ops->name, wsi->protocol ? + wsi->protocol->name : "noprotocol"); wsi = wsi->h2.sibling_list; } diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 42ac989ec..abf08bfb9 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -60,7 +60,7 @@ lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen) int n, m, rawish = 0; if (wsi->stash) - meth = wsi->stash->method; + meth = wsi->stash->cis[CIS_METHOD]; else meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); @@ -78,17 +78,11 @@ lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen) /* * OK from now on we talk via the proxy, so connect to that - * - * (will overwrite existing pointer, - * leaving old string/frag there but unreferenced) */ - if (wsi->stash) { - lws_free(wsi->stash->address); - wsi->stash->address = - lws_strdup(wsi->vhost->http.http_proxy_address); - if (!wsi->stash->address) - goto failed; - } else + if (wsi->stash) + wsi->stash->cis[CIS_ADDRESS] = + wsi->vhost->http.http_proxy_address; + else if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, wsi->vhost->http.http_proxy_address)) @@ -270,7 +264,7 @@ lws_client_connect_2(struct lws *wsi) /* we can only piggyback GET or POST */ if (wsi->stash) - meth = wsi->stash->method; + meth = wsi->stash->cis[CIS_METHOD]; else meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); @@ -368,8 +362,9 @@ create_new_conn: */ if (!wsi->cli_hostname_copy) { - if (wsi->stash) - wsi->cli_hostname_copy = lws_strdup(wsi->stash->host); + if (wsi->stash && wsi->stash->cis[CIS_HOST]) + wsi->cli_hostname_copy = + lws_strdup(wsi->stash->cis[CIS_HOST]); else { char *pa = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); @@ -401,7 +396,7 @@ create_new_conn: */ if (wsi->stash) - ads = wsi->stash->address; + ads = wsi->stash->cis[CIS_ADDRESS]; else ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); #if defined(LWS_WITH_UNIX_SOCK) @@ -490,9 +485,9 @@ create_new_conn: * to whatever we decided to connect to */ - lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port); + lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port); - n = lws_getaddrinfo46(wsi, ads, &result); + n = lws_getaddrinfo46(wsi, ads, &result); memset(&sa46, 0, sizeof(sa46)); #ifdef LWS_WITH_IPV6 if (wsi->ipv6) { @@ -571,7 +566,7 @@ create_new_conn: } #endif } else { - lwsl_err("getaddrinfo failed: %d\n", n); + lwsl_err("getaddrinfo failed: %s: %d\n", ads, n); cce = "getaddrinfo failed"; goto oom4; } @@ -678,7 +673,7 @@ ads_known: AWAITING_TIMEOUT); if (wsi->stash) - iface = wsi->stash->iface; + iface = wsi->stash->cis[CIS_IFACE]; else iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); @@ -792,6 +787,14 @@ failed1: #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +static uint8_t hnames2[] = { + _WSI_TOKEN_CLIENT_ORIGIN, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + _WSI_TOKEN_CLIENT_METHOD, + _WSI_TOKEN_CLIENT_IFACE, + _WSI_TOKEN_CLIENT_ALPN +}; + /** * lws_client_reset() - retarget a connected wsi to start over with a new * connection (ie, redirect) @@ -802,13 +805,14 @@ failed1: * path: uri path to connect to on the new server * host: host header to send to the new server */ -LWS_VISIBLE struct lws * +struct lws * lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, const char *path, const char *host) { - char origin[300] = "", protocol[300] = "", method[32] = "", - iface[16] = "", alpn[32] = "", *p; + char *stash, *p; struct lws *wsi; + size_t size = 0; + int n; if (!pwsi) return NULL; @@ -821,25 +825,23 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, } wsi->redirects++; - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); - if (p) - lws_strncpy(origin, p, sizeof(origin)); + for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) + size += lws_hdr_total_length(wsi, hnames2[n]) + 1; - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); - if (p) - lws_strncpy(protocol, p, sizeof(protocol)); + if ((int)size < lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1) + size = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1; - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); - if (p) - lws_strncpy(method, p, sizeof(method)); + p = stash = lws_malloc(size, __func__); + if (!stash) + return NULL; - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); - if (p) - lws_strncpy(iface, p, sizeof(iface)); - - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ALPN); - if (p) - lws_strncpy(alpn, p, sizeof(alpn)); + for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) + if (lws_hdr_total_length(wsi, hnames2[n])) { + memcpy(p, lws_hdr_simple_ptr(wsi, hnames2[n]), + lws_hdr_total_length(wsi, hnames2[n]) + 1); + p += lws_hdr_total_length(wsi, hnames2[n]) + 1; + } else + *p++ = '\0'; if (!port) { port = 443; @@ -860,14 +862,15 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, if (wsi->context->event_loop_ops->close_handle_manually) wsi->context->event_loop_ops->close_handle_manually(wsi); else - compatible_close(wsi->desc.sockfd); + if (wsi->desc.sockfd != LWS_SOCK_INVALID) + compatible_close(wsi->desc.sockfd); #if defined(LWS_WITH_TLS) wsi->tls.use_ssl = ssl; #else if (ssl) { lwsl_err("%s: not configured for ssl\n", __func__); - return NULL; + goto bail; } #endif @@ -880,41 +883,33 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, _lws_header_table_reset(wsi->http.ah); if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) - return NULL; + goto bail; if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) - return NULL; + goto bail; - if (origin[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, - origin)) - return NULL; - if (protocol[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - protocol)) - return NULL; - if (method[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, - method)) - return NULL; + p = stash; + for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) { + if (lws_hdr_simple_create(wsi, hnames2[n], p)) + goto bail; + p += lws_hdr_total_length(wsi, hnames2[n]) + 1; + } - if (iface[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, - iface)) - return NULL; - if (alpn[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN, - alpn)) - return NULL; + stash[0] = '/'; + lws_strncpy(&stash[1], path, size - 1); + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash)) + goto bail; - origin[0] = '/'; - strncpy(&origin[1], path, sizeof(origin) - 2); - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, origin)) - return NULL; + lws_free_set_NULL(stash); *pwsi = lws_client_connect_2(wsi); return *pwsi; + +bail: + lws_free_set_NULL(stash); + + return NULL; } #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB) @@ -1048,10 +1043,22 @@ html_parser_cb(const hubbub_token *token, void *pw) #endif +static const uint8_t hnames[] = { + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + _WSI_TOKEN_CLIENT_URI, + _WSI_TOKEN_CLIENT_HOST, + _WSI_TOKEN_CLIENT_ORIGIN, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + _WSI_TOKEN_CLIENT_METHOD, + _WSI_TOKEN_CLIENT_IFACE, + _WSI_TOKEN_CLIENT_ALPN +}; + struct lws * lws_http_client_connect_via_info2(struct lws *wsi) { struct client_info_stash *stash = wsi->stash; + int n; lwsl_debug("%s: %p (stash %p)\n", __func__, wsi, stash); @@ -1060,7 +1067,7 @@ lws_http_client_connect_via_info2(struct lws *wsi) wsi->opaque_user_data = wsi->stash->opaque_user_data; - if (stash->method && !strcmp(stash->method, "RAW")) + if (stash->cis[CIS_METHOD] && !strcmp(stash->cis[CIS_METHOD], "RAW")) goto no_ah; /* @@ -1068,44 +1075,14 @@ lws_http_client_connect_via_info2(struct lws *wsi) * stash them... we only need during connect phase so into a temp * allocated stash */ - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - stash->address)) - goto bail1; - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) - goto bail1; - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) - goto bail1; - - if (stash->origin) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, - stash->origin)) - goto bail1; - /* - * this is a list of protocols we tell the server we're okay with - * stash it for later when we compare server response with it - */ - if (stash->protocol) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - stash->protocol)) - goto bail1; - if (stash->method) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, - stash->method)) - goto bail1; - if (stash->iface) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, - stash->iface)) - goto bail1; - if (stash->alpn) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN, - stash->alpn)) - goto bail1; + for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++) + if (hnames[n] && stash->cis[n]) + if (lws_hdr_simple_create(wsi, hnames[n], stash->cis[n])) + goto bail1; #if defined(LWS_WITH_SOCKS5) if (!wsi->vhost->socks_proxy_port) - lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->stash); #endif no_ah: diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index 771b42eee..332767b76 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -250,7 +250,7 @@ socks_reply_fail: lwsl_client("socks connect OK\n"); /* free stash since we are done with it */ - lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->stash); if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, wsi->vhost->socks_proxy_address)) { @@ -705,7 +705,7 @@ lws_client_interpret_server_handshake(struct lws *wsi) char *p, *q; char new_path[300]; - lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->stash); ah = wsi->http.ah; if (!wsi->do_ws) { diff --git a/lib/roles/raw-proxy/ops-raw-proxy.c b/lib/roles/raw-proxy/ops-raw-proxy.c index c57c80d21..d98baf140 100644 --- a/lib/roles/raw-proxy/ops-raw-proxy.c +++ b/lib/roles/raw-proxy/ops-raw-proxy.c @@ -155,7 +155,7 @@ rops_client_bind_raw_proxy(struct lws *wsi, /* finalize */ - if (!wsi->user_space && wsi->stash->method) + if (!wsi->user_space && wsi->stash->cis[CIS_METHOD]) if (lws_ensure_user_space(wsi)) return 1; diff --git a/lib/roles/raw-skt/ops-raw-skt.c b/lib/roles/raw-skt/ops-raw-skt.c index 9dff8001a..d2c1cc013 100644 --- a/lib/roles/raw-skt/ops-raw-skt.c +++ b/lib/roles/raw-skt/ops-raw-skt.c @@ -197,7 +197,7 @@ rops_client_bind_raw_skt(struct lws *wsi, /* finalize */ - if (!wsi->user_space && wsi->stash->method) + if (!wsi->user_space && wsi->stash->cis[CIS_METHOD]) if (lws_ensure_user_space(wsi)) return 1;