diff --git a/lib/context.c b/lib/context.c index f577e26c..39ecdeca 100644 --- a/lib/context.c +++ b/lib/context.c @@ -757,7 +757,7 @@ lws_create_context(struct lws_context_creation_info *info) #ifndef LWS_NO_DAEMONIZE int pid_daemon = get_daemonize_pid(); #endif - int n, m; + int n; #if defined(__ANDROID__) struct rlimit rt; #endif @@ -908,7 +908,8 @@ lws_create_context(struct lws_context_creation_info *info) * and header data pool */ for (n = 0; n < context->count_threads; n++) { - context->pt[n].serv_buf = lws_zalloc(context->pt_serv_buf_size, "pt_serv_buf"); + context->pt[n].serv_buf = lws_malloc(context->pt_serv_buf_size, + "pt_serv_buf"); if (!context->pt[n].serv_buf) { lwsl_err("OOM\n"); return NULL; @@ -918,19 +919,8 @@ lws_create_context(struct lws_context_creation_info *info) context->pt[n].context = context; #endif context->pt[n].tid = n; - context->pt[n].http_header_data = lws_malloc(context->max_http_header_data * - context->max_http_header_pool, "context ah hdr data"); - if (!context->pt[n].http_header_data) - goto bail; - - context->pt[n].ah_pool = lws_zalloc(sizeof(struct allocated_headers) * - context->max_http_header_pool, "context ah hdr pool"); - for (m = 0; m < context->max_http_header_pool; m++) - context->pt[n].ah_pool[m].data = - (char *)context->pt[n].http_header_data + - (m * context->max_http_header_data); - if (!context->pt[n].ah_pool) - goto bail; + context->pt[n].ah_list = NULL; + context->pt[n].ah_pool_length = 0; lws_pt_mutex_init(&context->pt[n]); } @@ -1457,10 +1447,9 @@ lws_context_destroy(struct lws_context *context) lws_libevent_destroyloop(context, n); lws_free_set_NULL(context->pt[n].serv_buf); - if (pt->ah_pool) - lws_free(pt->ah_pool); - if (pt->http_header_data) - lws_free(pt->http_header_data); + + while (pt->ah_list) + _lws_destroy_ah(pt, pt->ah_list); } lws_plat_context_early_destroy(context); diff --git a/lib/hpack.c b/lib/hpack.c index cdc089ed..a07d13e4 100644 --- a/lib/hpack.c +++ b/lib/hpack.c @@ -289,11 +289,11 @@ lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len) return 1; wsi->u.http2.hpack_dyn_table = dyn; - dyn->args = lws_malloc(1024); + dyn->args = lws_malloc(1024, "hpack"); if (!dyn->args) goto bail1; dyn->args_length = 1024; - dyn->entries = lws_malloc(sizeof(dyn->entries[0]) * 20); + dyn->entries = lws_malloc(sizeof(dyn->entries[0]) * 20, "hpack dyn entries"); if (!dyn->entries) goto bail2; dyn->num_entries = 20; diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index d8a739c1..79ed3b3e 100755 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -66,7 +66,7 @@ void lws_free_wsi(struct lws *wsi) { struct lws_context_per_thread *pt; - int n; + struct allocated_headers *ah; if (!wsi) return; @@ -94,14 +94,16 @@ lws_free_wsi(struct lws *wsi) wsi->vhost->lserv_wsi = NULL; lws_pt_lock(pt); - for (n = 0; n < wsi->context->max_http_header_pool; n++) { - if (pt->ah_pool[n].in_use && - pt->ah_pool[n].wsi == wsi) { + ah = pt->ah_list; + while (ah) { + if (ah->in_use && ah->wsi == wsi) { lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); - pt->ah_pool[n].in_use = 0; - pt->ah_pool[n].wsi = NULL; + ah->in_use = 0; + ah->wsi = NULL; pt->ah_count_in_use--; + break; } + ah = ah->next; } #if defined(LWS_WITH_PEER_LIMITS) diff --git a/lib/parsers.c b/lib/parsers.c index b99fb26b..99119e13 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -60,6 +60,51 @@ lextable_decode(int pos, char c) } } +static struct allocated_headers * +_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size) +{ + struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct"); + + if (!ah) + return NULL; + + ah->data = lws_malloc(data_size, "ah data"); + if (!ah->data) { + lws_free(ah); + + return NULL; + } + ah->next = pt->ah_list; + pt->ah_list = ah; + ah->data_length = data_size; + pt->ah_pool_length++; + + lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__, + ah, (int)data_size, pt->ah_pool_length); + + return ah; +} + +int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah) +{ + lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) { + if ((*a) == ah) { + *a = ah->next; + pt->ah_pool_length--; + lwsl_info("%s: freed ah %p : pool length %d\n", + __func__, ah, pt->ah_pool_length); + if (ah->data) + lws_free(ah->data); + lws_free(ah); + + return 0; + } + } lws_end_foreach_llp(a, next); + + return 1; +} + void _lws_header_table_reset(struct allocated_headers *ah) { @@ -214,16 +259,15 @@ lws_header_table_attach(struct lws *wsi, int autoservice) __lws_remove_from_ah_waiting_list(wsi); - for (n = 0; n < context->max_http_header_pool; n++) - if (!pt->ah_pool[n].in_use) - break; + wsi->u.hdr.ah = _lws_create_ah(pt, context->max_http_header_data); + if (!wsi->u.hdr.ah) { /* we could not create an ah */ + _lws_header_ensure_we_are_on_waiting_list(wsi); - /* if the count of in use said something free... */ - assert(n != context->max_http_header_pool); + goto bail; + } - wsi->u.hdr.ah = &pt->ah_pool[n]; wsi->u.hdr.ah->in_use = 1; - pt->ah_pool[n].wsi = wsi; /* mark our owner */ + wsi->u.hdr.ah->wsi = wsi; /* mark our owner */ pt->ah_count_in_use++; #if defined(LWS_WITH_PEER_LIMITS) @@ -431,7 +475,7 @@ bail: nobody_usable_waiting: lwsl_info("%s: nobody usable waiting\n", __func__); - ah->in_use = 0; + _lws_destroy_ah(pt, ah); pt->ah_count_in_use--; goto bail; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index e03a7d45..7d30078f 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -731,10 +731,17 @@ struct lws_fd_hashtable { * other APIs to get information out of it. */ +#if defined(LWS_WITH_ESP32) +typedef uint16_t ah_data_idx_t; +#else +typedef uint32_t ah_data_idx_t; +#endif + struct lws_fragments { - unsigned int offset; - unsigned short len; - unsigned char nfrag; /* which ah->frag[] continues this content, or 0 */ + ah_data_idx_t offset; + uint16_t len; + uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */ + uint8_t flags; /* only http2 cares */ }; /* @@ -743,34 +750,39 @@ struct lws_fragments { */ struct allocated_headers { + struct allocated_headers *next; /* linked list */ struct lws *wsi; /* owner */ char *data; /* prepared by context init to point to dedicated storage */ + ah_data_idx_t data_length; /* * the randomly ordered fragments, indexed by frag_index and * lws_fragments->nfrag for continuation. */ - struct lws_fragments frags[WSI_TOKEN_COUNT * 2]; + struct lws_fragments frags[WSI_TOKEN_COUNT]; time_t assigned; /* * for each recognized token, frag_index says which frag[] his data * starts in (0 means the token did not appear) * the actual header data gets dumped as it comes in, into data[] */ - unsigned char frag_index[WSI_TOKEN_COUNT]; - unsigned char rx[2048]; + uint8_t frag_index[WSI_TOKEN_COUNT]; +#if defined(LWS_WITH_ESP32) + uint8_t rx[256]; +#else + uint8_t rx[2048]; +#endif - unsigned int rxpos; - unsigned int rxlen; - unsigned int pos; - - unsigned int http_response; + int16_t rxpos; + int16_t rxlen; + uint32_t pos; + uint32_t http_response; #ifndef LWS_NO_CLIENT char initial_handshake_hash_base64[30]; #endif - unsigned char in_use; - unsigned char nfrag; + uint8_t in_use; + uint8_t nfrag; }; /* @@ -796,7 +808,7 @@ struct lws_context_per_thread { struct lws_cgi *cgi_list; #endif void *http_header_data; - struct allocated_headers *ah_pool; + struct allocated_headers *ah_list; struct lws *ah_wait_list; int ah_wait_list_length; #ifdef LWS_OPENSSL_SUPPORT @@ -835,6 +847,7 @@ struct lws_context_per_thread { lws_sockfd_type dummy_pipe_fds[2]; #endif unsigned int fds_count; + uint32_t ah_pool_length; short ah_count_in_use; unsigned char tid; @@ -1901,6 +1914,9 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); LWS_EXTERN struct lws * lws_client_connect_via_info2(struct lws *wsi); +LWS_EXTERN int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah); + /* * EXTENSIONS */ diff --git a/lib/service.c b/lib/service.c index cf514603..23d51bbf 100644 --- a/lib/service.c +++ b/lib/service.c @@ -73,7 +73,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) int write_type = LWS_WRITE_PONG; struct lws_tokens eff_buf; #ifdef LWS_WITH_HTTP2 - struct lws *wsi2, *wsi2a; + struct lws *wsi2; #endif int ret, m, n; @@ -530,7 +530,7 @@ LWS_VISIBLE LWS_EXTERN int lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) { struct lws_context_per_thread *pt = &context->pt[tsi]; - int n; + struct allocated_headers *ah; /* Figure out if we really want to wait in poll() * We only need to wait if really nothing already to do and we have @@ -550,15 +550,16 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) #endif /* 3) if any ah has pending rx, do not wait in poll */ - for (n = 0; n < context->max_http_header_pool; n++) - if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen) { - /* any ah with pending rx must be attached to someone */ - if (!pt->ah_pool[n].wsi) { - lwsl_err("%s: ah with no wsi\n", __func__); + ah = pt->ah_list; + while (ah) { + if (ah->rxpos != ah->rxlen) { + if (!ah->wsi) { assert(0); } return 0; } + ah = ah->next; + } return timeout_ms; } @@ -573,12 +574,12 @@ int lws_service_flag_pending(struct lws_context *context, int tsi) { struct lws_context_per_thread *pt = &context->pt[tsi]; + struct allocated_headers *ah; #ifdef LWS_OPENSSL_SUPPORT struct lws *wsi_next; #endif struct lws *wsi; int forced = 0; - int n; /* POLLIN faking */ @@ -629,16 +630,20 @@ lws_service_flag_pending(struct lws_context *context, int tsi) * fake their POLLIN status so they will be able to drain the * rx buffered in the ah */ - for (n = 0; n < context->max_http_header_pool; n++) - if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen && - !pt->ah_pool[n].wsi->hdr_parsing_completed) { - pt->fds[pt->ah_pool[n].wsi->position_in_fds_table].revents |= - pt->fds[pt->ah_pool[n].wsi->position_in_fds_table].events & + ah = pt->ah_list; + while (ah) { + if (ah->rxpos != ah->rxlen && !ah->wsi->hdr_parsing_completed) { + pt->fds[ah->wsi->position_in_fds_table].revents |= + pt->fds[ah->wsi->position_in_fds_table].events & LWS_POLLIN; - if (pt->fds[pt->ah_pool[n].wsi->position_in_fds_table].revents & - LWS_POLLIN) + if (pt->fds[ah->wsi->position_in_fds_table].revents & + LWS_POLLIN) { forced = 1; + break; + } } + ah = ah->next; + } return forced; } @@ -809,6 +814,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t { struct lws_context_per_thread *pt = &context->pt[tsi]; lws_sockfd_type our_fd = 0, tmp_fd; + struct allocated_headers *ah; struct lws_tokens eff_buf; unsigned int pending = 0; struct lws *wsi, *wsi1; @@ -888,62 +894,66 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t * timeout status */ - for (n = 0; n < context->max_http_header_pool; n++) - if (pt->ah_pool[n].in_use && pt->ah_pool[n].wsi && - pt->ah_pool[n].assigned && - now - pt->ah_pool[n].assigned > 60) { - int len; - char buf[256]; - const unsigned char *c; + ah = pt->ah_list; + while (ah) { + int len; + char buf[256]; + const unsigned char *c; - /* - * a single ah session somehow got held for - * an unreasonable amount of time. - * - * Dump info on the connection... - */ - - wsi = pt->ah_pool[n].wsi; - buf[0] = '\0'; - lws_get_peer_simple(wsi, buf, sizeof(buf)); - lwsl_notice("ah excessive hold: wsi %p\n" - " peer address: %s\n" - " ah rxpos %u, rxlen %u, pos %u\n", - wsi, buf, pt->ah_pool[n].rxpos, - pt->ah_pool[n].rxlen, - pt->ah_pool[n].pos); - buf[0] = '\0'; - m = 0; - do { - c = lws_token_to_string(m); - if (!c) - break; - - len = lws_hdr_total_length(wsi, m); - if (!len || len > sizeof(buf) - 1) { - m++; - continue; - } - - if (lws_hdr_copy(wsi, buf, - sizeof buf, m) > 0) { - buf[sizeof(buf) - 1] = '\0'; - - lwsl_notice(" %s = %s\n", - (const char *)c, buf); - } - m++; - } while (1); - - /* ... and then drop the connection */ - - if (wsi->desc.sockfd == our_fd) - /* it was the guy we came to service! */ - timed_out = 1; - - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + if (!ah->in_use || !ah->wsi || !ah->assigned || + now - ah->assigned < 60) { + ah = ah->next; + continue; } + /* + * a single ah session somehow got held for + * an unreasonable amount of time. + * + * Dump info on the connection... + */ + wsi = ah->wsi; + buf[0] = '\0'; + lws_get_peer_simple(wsi, buf, sizeof(buf)); + lwsl_notice("ah excessive hold: wsi %p\n" + " peer address: %s\n" + " ah rxpos %u, rxlen %u, pos %u\n", + wsi, buf, ah->rxpos, ah->rxlen, + ah->pos); + buf[0] = '\0'; + m = 0; + do { + c = lws_token_to_string(m); + if (!c) + break; + + len = lws_hdr_total_length(wsi, m); + if (!len || len > sizeof(buf) - 1) { + m++; + continue; + } + + if (lws_hdr_copy(wsi, buf, + sizeof buf, m) > 0) { + buf[sizeof(buf) - 1] = '\0'; + + lwsl_notice(" %s = %s\n", + (const char *)c, buf); + } + m++; + } while (1); + + /* ... and then drop the connection */ + + if (wsi->desc.sockfd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + ah = ah->next; + } + #ifdef LWS_WITH_CGI /* * Phase 3: handle cgi timeouts diff --git a/lib/ssl-http2.c b/lib/ssl-http2.c index 428e7887..9c25646f 100644 --- a/lib/ssl-http2.c +++ b/lib/ssl-http2.c @@ -52,7 +52,7 @@ #ifndef LWS_NO_SERVER #ifdef LWS_OPENSSL_SUPPORT -#if OPENSSL_VERSION_NUMBER >= 0x10002000L +#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L) struct alpn_ctx { unsigned char *data; @@ -89,7 +89,7 @@ alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, LWS_VISIBLE void lws_context_init_http2_ssl(struct lws_vhost *vhost) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L +#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L) static struct alpn_ctx protos = { (unsigned char *)"\x02h2" "\x08http/1.1", 6 + 9 }; @@ -107,7 +107,7 @@ lws_context_init_http2_ssl(struct lws_vhost *vhost) void lws_http2_configure_if_upgraded(struct lws *wsi) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L +#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L) struct allocated_headers *ah; const char *method = "alpn"; const unsigned char *name; diff --git a/lib/ssl-server.c b/lib/ssl-server.c index e352f84c..a7590bb0 100644 --- a/lib/ssl-server.c +++ b/lib/ssl-server.c @@ -137,7 +137,7 @@ lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info, return 0; } -#if defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) +#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) static int lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) { diff --git a/lwsws/main.c b/lwsws/main.c index a68ea1af..de800970 100644 --- a/lwsws/main.c +++ b/lwsws/main.c @@ -119,7 +119,7 @@ context_creation(void) memset(&info, 0, sizeof(info)); info.external_baggage_free_on_destroy = config_strings; - info.max_http_header_pool = 16; + info.max_http_header_pool = 256; info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_LIBUV;