diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index f1c4afd9a..8e1fed53e 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -453,6 +453,9 @@ struct lws_context_creation_info { int simultaneous_ssl_restriction; /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions * possible.*/ + int ssl_handshake_serialize; + /**< CONTEXT: 0 disables ssl handshake serialization (default). + * 1 enables ssl handshake serialization. */ int ssl_info_event_mask; /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO * callback for connections on this vhost. The mask values are of diff --git a/lib/core/context.c b/lib/core/context.c index 0b9a22489..c7e84b41b 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -910,6 +910,7 @@ lws_create_context(const struct lws_context_creation_info *info) #if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) context->simultaneous_ssl_restriction = info->simultaneous_ssl_restriction; + context->ssl_handshake_serialize = info->ssl_handshake_serialize; #endif context->options = info->options; diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index d98d8386b..e5fb25a5b 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -699,6 +699,7 @@ struct lws_context { unsigned int max_http_header_pool; int simultaneous_ssl_restriction; int simultaneous_ssl; + int ssl_handshake_serialize; #if defined(LWS_WITH_TLS_JIT_TRUST) int vh_idle_grace_ms; #endif diff --git a/lib/tls/tls-client.c b/lib/tls/tls-client.c index 9e8443c4c..6009e4352 100644 --- a/lib/tls/tls-client.c +++ b/lib/tls/tls-client.c @@ -24,6 +24,43 @@ #include "private-lib-core.h" +static int +lws_ssl_handshake_serialize(struct lws_context *ctx, struct lws *wsi) +{ + struct lws_vhost *vh = ctx->vhost_list; +#if LWS_MAX_SMP > 1 + int tsi = lws_pthread_self_to_tsi(ctx); +#else + int tsi = 0; +#endif + struct lws_context_per_thread *pt = &ctx->pt[tsi]; + unsigned int n; + + while (vh) { + for (n = 0; n < pt->fds_count; n++) { + struct lws *w = wsi_from_fd(ctx, pt->fds[n].fd); + + if (!w || w->tsi != tsi || w->a.vhost != vh || wsi == w) + continue; + + /* Now we found other vhost's wsi in process */ + if (lwsi_role_mqtt(w)) { + /* MQTT TLS connection not established yet. + * Let it finish. + */ + if (lwsi_state(w) != LRS_ESTABLISHED) + return 1; + } else { + /* H1/H2 not finished yet. Let it finish. */ + if (lwsi_state(w) != LRS_DEAD_SOCKET) + return 1; + } + } + vh = vh->vhost_next; + } + return 0; +} + static int lws_ssl_client_connect1(struct lws *wsi, char *errbuf, size_t len) { @@ -190,6 +227,14 @@ lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1) return CCTLS_RETURN_ERROR; } wsi->tls_borrowed = 1; + if (wsi->a.context->ssl_handshake_serialize) { + if (lws_ssl_handshake_serialize(wsi->a.context, wsi)) { + lws_tls_restrict_return(wsi->a.context); + wsi->tls_borrowed = 0; + *pcce = "ssl handshake serialization"; + return CCTLS_RETURN_ERROR; + } + } } #endif diff --git a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c index cbbbf9ed9..d8a95603f 100644 --- a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -583,6 +583,13 @@ int main(int argc, const char **argv) if ((p = lws_cmdline_option(argc, argv, "--limit"))) info.simultaneous_ssl_restriction = atoi(p); + if (lws_cmdline_option(argc, argv, "--ssl-handshake-serialize")) + /* We only consider simultaneous_ssl_restriction > 1 use cases. + * If ssl isn't limited or only 1 is allowed, we don't care. + */ + if (info.simultaneous_ssl_restriction > 1) + info.ssl_handshake_serialize = 1; + context = lws_create_context(&info); if (!context) { lwsl_err("lws init failed\n");