1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

tls: openssl-only: client-only: session caching

This commit is contained in:
Catalin 2021-03-25 08:37:36 +00:00 committed by Andy Green
parent e4d381eadd
commit d5753b6298
14 changed files with 435 additions and 9 deletions

View file

@ -156,6 +156,10 @@
"cmake": "-DLWS_WITH_MINIMAL_EXAMPLES=1",
"platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, netbsd/aarch64BE-bcm2837-a53/gcc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc"
},
"default-examples-tls-sess": {
"cmake": "-DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_TLS_SESSIONS=1",
"platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, netbsd/aarch64BE-bcm2837-a53/gcc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc"
},
"h1only-examples": {
"cmake": "cmake .. -DLWS_WITH_HTTP2=0 -DLWS_WITH_MINIMAL_EXAMPLES=1",
"platforms": "none,linux-fedora-32/x86_64-amd/gcc"

View file

@ -395,6 +395,12 @@ if (LWS_WITH_PLUGINS AND NOT LWS_WITH_SHARED)
set(LWS_WITH_PLUGINS 0)
endif()
if (LWS_WITH_TLS_SESSIONS)
if (NOT LWS_WITH_NETWORK OR NOT LWS_WITH_CLIENT OR LWS_WITH_MBEDTLS)
message("TLS_SESSIONS support only covers client on openssl atm, disabling")
set(LWS_WITH_TLS_SESSIONS OFF)
endif()
endif()
# if we're only building static, we don't want event lib plugins
#

View file

@ -163,7 +163,7 @@ option(LWS_WITH_WOLFSSL "Use wolfSSL replacement for OpenSSL. When setting this,
option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of the OS-installed CA root certs" ON)
option(LWS_TLS_LOG_PLAINTEXT_RX "For debugging log the received plaintext as soon as decrypted" OFF)
option(LWS_TLS_LOG_PLAINTEXT_TX "For debugging log the transmitted plaintext just before encryption" OFF)
option(LWS_WITH_TLS_SESSIONS "Enable persistent, resumable TLS sessions" ON)
#
# Event library options (may select multiple, or none for default poll()

View file

@ -90,6 +90,7 @@
#cmakedefine LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key
#cmakedefine LWS_HAVE_SSL_set_alpn_protos
#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
#cmakedefine LWS_HAVE_SSL_SESSION_set_time
#cmakedefine LWS_HAVE__STAT32I64
#cmakedefine LWS_HAVE_STDINT_H
#cmakedefine LWS_HAVE_SYS_CAPABILITY_H
@ -208,6 +209,7 @@
#cmakedefine LWS_WITH_SYS_STATE
#cmakedefine LWS_WITH_THREADPOOL
#cmakedefine LWS_WITH_TLS
#cmakedefine LWS_WITH_TLS_SESSIONS
#cmakedefine LWS_WITH_UDP
#cmakedefine LWS_WITH_ULOOP
#cmakedefine LWS_WITH_UNIX_SOCK

View file

@ -374,4 +374,21 @@ lws_client_http_multipart(struct lws *wsi, const char *name,
LWS_VISIBLE LWS_EXTERN int
lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len);
/**
* lws_tls_session_is_reused() - returns nonzero if tls session was cached
*
* \param wsi: the wsi
*
* Returns zero if the tls session is fresh, else nonzero if the tls session was
* taken from the cache. If lws is built with LWS_WITH_TLS_SESSIONS and the vhost
* was created with the option LWS_SERVER_OPTION_ENABLE_TLS_SESSION_CACHE, then
* on full tls session establishment of a client connection, the session is added
* to the tls cache.
*
* This lets you find out if your session was new (0) or from the cache (nonzero),
* it'a mainly useful for stats and testing.
*/
LWS_VISIBLE LWS_EXTERN int
lws_tls_session_is_reused(struct lws *wsi);
///@}

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@ -239,6 +239,9 @@
#define LWS_SERVER_OPTION_ULOOP (1ll << 38)
/**< (CTX) Use libubox / uloop event loop */
#define LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE (1ll << 39)
/**< (VHOST) Disallow use of client tls caching (on by default) */
/****** add new things just above ---^ ******/
@ -571,6 +574,15 @@ struct lws_context_creation_info {
* 0 defaults to 10s. */
#endif /* WITH_NETWORK */
#if defined(LWS_WITH_TLS_SESSIONS)
uint32_t tls_session_timeout;
/**< VHOST: seconds until timeout/ttl for newly created sessions.
* 0 means default timeout (defined per protocol, usually 300s). */
uint32_t tls_session_cache_max;
/**< VHOST: 0 for default limit of 10, or the maximum number of
* client tls sessions we are willing to cache */
#endif
gid_t gid;
/**< CONTEXT: group id to change to after setting listen socket,
* or -1. See also .username below. */

View file

@ -455,6 +455,11 @@ struct lws_vhost {
char socks_user[96];
char socks_password[96];
#endif
#if defined(LWS_WITH_TLS_SESSIONS)
lws_dll2_owner_t tls_sessions; /* vh lock */
#endif
#if defined(LWS_WITH_EVENT_LIBS)
void *evlib_vh; /* overallocated */
#endif
@ -533,6 +538,10 @@ struct lws_vhost {
int log_fd;
#endif
#if defined(LWS_WITH_TLS_SESSIONS)
uint32_t tls_session_cache_max;
#endif
#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
int8_t ss_refcount;
/**< refcount of number of ss connections with streamtypes using this
@ -825,6 +834,7 @@ struct lws {
unsigned int client_suppress_CONNECTION_ERROR:1;
/**< because the client connection creation api is still the parent of
* this activity, and will report the failure */
unsigned int tls_session_reused:1;
#endif
#ifdef _WIN32

View file

@ -24,6 +24,9 @@
#include "private-lib-core.h"
void
lws_tls_session_vh_destroy(struct lws_vhost *vh);
const struct lws_role_ops *available_roles[] = {
#if defined(LWS_ROLE_H2)
&role_ops_h2,
@ -1198,6 +1201,10 @@ lws_vhost_destroy1(struct lws_vhost *vh)
lws_vhost_lock(vh); /* -------------- vh { */
#if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_WITH_TLS)
lws_tls_session_vh_destroy(vh);
#endif
vh->being_destroyed = 1;
lws_dll2_add_tail(&vh->vh_being_destroyed_list,
&context->owner_vh_being_destroyed);

View file

@ -1173,6 +1173,20 @@ lws_mux_mark_immortal(struct lws *wsi)
lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0);
}
int
lws_tls_session_is_reused(struct lws *wsi)
{
#if defined(LWS_WITH_CLIENT)
struct lws *nwsi = lws_get_network_wsi(wsi);
if (!nwsi)
return 0;
return nwsi->tls_session_reused;
#else
return 0;
#endif
}
int
lws_http_mark_sse(struct lws *wsi)

View file

@ -139,6 +139,10 @@ if (LWS_WITH_SSL)
list(APPEND SOURCES
tls/openssl/openssl-ssl.c)
endif()
if (LWS_WITH_TLS_SESSIONS)
list(APPEND SOURCES
tls/openssl/openssl-session.c)
endif()
if (LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
tls/openssl/lws-genhash.c
@ -306,6 +310,7 @@ CHECK_FUNCTION_EXISTS(${VARIA}RSA_verify_pss_mgf1 LWS_HAVE_RSA_verify_pss_mgf1 P
CHECK_FUNCTION_EXISTS(${VARIA}HMAC_CTX_new LWS_HAVE_HMAC_CTX_new PARENT_SCOPE)
CHECK_SYMBOL_EXISTS(${VARIA}SSL_CTX_set_ciphersuites LWS_HAVE_SSL_CTX_set_ciphersuites PARENT_SCOPE)
CHECK_FUNCTION_EXISTS(${VARIA}EVP_PKEY_new_raw_private_key LWS_HAVE_EVP_PKEY_new_raw_private_key PARENT_SCOPE)
CHECK_FUNCTION_EXISTS(${VARIA}SSL_SESSION_set_time LWS_HAVE_SSL_SESSION_set_time PARENT_SCOPE)
# deprecated in openssl v3
CHECK_FUNCTION_EXISTS(${VARIA}EC_KEY_new_by_curve_name LWS_HAVE_EC_KEY_new_by_curve_name PARENT_SCOPE)

View file

@ -205,6 +205,11 @@ lws_ssl_client_bio_create(struct lws *wsi)
return -1;
}
#if defined(LWS_WITH_TLS_SESSIONS)
if (!(wsi->a.vhost->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE))
lws_tls_reuse_session(wsi);
#endif
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->a.vhost->tls.ssl_info_event_mask)
SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
@ -429,7 +434,9 @@ lws_tls_client_connect(struct lws *wsi, char *errbuf, size_t elen)
unsigned int len;
#endif
int m, n, en;
#if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_HAVE_SSL_SESSION_set_time)
SSL_SESSION *sess;
#endif
errno = 0;
ERR_clear_error();
n = SSL_connect(wsi->tls.ssl);
@ -455,6 +462,20 @@ lws_tls_client_connect(struct lws *wsi, char *errbuf, size_t elen)
return LWS_SSL_CAPABLE_ERROR;
}
#if defined(LWS_WITH_TLS_SESSIONS)
if (SSL_session_reused(wsi->tls.ssl)) {
#if defined(LWS_HAVE_SSL_SESSION_set_time)
sess = SSL_get_session(wsi->tls.ssl);
if (sess) /* should always be true */
#if defined(OPENSSL_IS_BORINGSSL)
SSL_SESSION_set_time(sess, (uint64_t)time(NULL)); /* extend session lifetime */
#else
SSL_SESSION_set_time(sess, (long)time(NULL)); /* extend session lifetime */
#endif
#endif
}
#endif
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl))
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
@ -777,6 +798,12 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh,
vh->tls.tcr = tcr;
#if defined(LWS_WITH_TLS_SESSIONS)
vh->tls_session_cache_max = info->tls_session_cache_max ?
info->tls_session_cache_max : 10;
lws_tls_session_cache(vh, info->tls_session_timeout);
#endif
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vh->tls.ssl_client_ctx, SSL_OP_NO_COMPRESSION);
#endif

View file

@ -0,0 +1,280 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
typedef struct lws_tls_session_cache_openssl {
lws_dll2_t list;
SSL_SESSION *session;
lws_sorted_usec_list_t sul_ttl;
/* name is overallocated here */
} lws_tls_sco_t;
#define lwsl_tlssess lwsl_info
static int
lws_tls_session_name_from_wsi(struct lws *wsi, char *buf, size_t len)
{
size_t n;
/*
* We have to include the vhost name in the session tag, since
* different vhosts may make connections to the same endpoint using
* different client certs.
*/
n = (size_t)lws_snprintf(buf, len, "%s.", wsi->a.vhost->name);
buf += n;
len = len - n;
lws_sa46_write_numeric_address(&wsi->sa46_peer, buf, len - 8);
lws_snprintf(buf + strlen(buf), 8, ":%u", wsi->c_port);
return 0;
}
static void
__lws_tls_session_destroy(lws_tls_sco_t *ts)
{
lwsl_tlssess("%s: %s (%u)\n", __func__, (const char *)&ts[1],
ts->list.owner->count - 1);
SSL_SESSION_free(ts->session);
lws_dll2_remove(&ts->list); /* vh lock */
lws_free(ts);
}
static lws_tls_sco_t *
__lws_tls_session_lookup_by_name(struct lws_vhost *vh, const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
lws_dll2_get_head(&vh->tls_sessions)) {
lws_tls_sco_t *ts = lws_container_of(p, lws_tls_sco_t, list);
const char *ts_name = (const char *)&ts[1];
if (!strcmp(name, ts_name))
return ts;
} lws_end_foreach_dll(p);
return NULL;
}
/*
* If possible, reuse an existing, cached session
*/
void
lws_tls_reuse_session(struct lws *wsi)
{
char buf[16 + INET6_ADDRSTRLEN + 1 + 8 + 1];
lws_tls_sco_t *ts;
if (!wsi->a.vhost ||
wsi->a.vhost->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return;
lws_context_lock(wsi->a.context, __func__); /* -------------- cx { */
lws_vhost_lock(wsi->a.vhost); /* -------------- vh { */
lws_tls_session_name_from_wsi(wsi, buf, sizeof(buf));
ts = __lws_tls_session_lookup_by_name(wsi->a.vhost, buf);
if (!ts) {
lwsl_tlssess("%s: no existing session for %s\n", __func__, buf);
goto bail;
}
lwsl_tlssess("%s: %s\n", __func__, (const char *)&ts[1]);
wsi->tls_session_reused = 1;
SSL_set_session(wsi->tls.ssl, ts->session);
/* keep our session list sorted in lru -> mru order */
lws_dll2_remove(&ts->list);
lws_dll2_add_tail(&ts->list, &wsi->a.vhost->tls_sessions);
bail:
lws_vhost_unlock(wsi->a.vhost); /* } vh -------------- */
lws_context_unlock(wsi->a.context); /* } cx -------------- */
}
static int
lws_tls_session_destroy_dll(struct lws_dll2 *d, void *user)
{
lws_tls_sco_t *ts = lws_container_of(d, lws_tls_sco_t, list);
__lws_tls_session_destroy(ts);
return 0;
}
void
lws_tls_session_vh_destroy(struct lws_vhost *vh)
{
lws_dll2_foreach_safe(&vh->tls_sessions, NULL,
lws_tls_session_destroy_dll);
}
static int
lws_tls_session_new_cb(SSL *ssl, SSL_SESSION *sess)
{
struct lws *wsi = (struct lws *)SSL_get_ex_data(ssl,
openssl_websocket_private_data_index);
char buf[16 + INET6_ADDRSTRLEN + 1 + 8 + 1];
struct lws_vhost *vh;
lws_tls_sco_t *ts;
size_t nl;
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
const char *disposition = "reuse";
long ttl;
#endif
if (!wsi) {
lwsl_warn("%s: can't get wsi from ssl privdata\n", __func__);
return 0;
}
vh = wsi->a.vhost;
lws_tls_session_name_from_wsi(wsi, buf, sizeof(buf));
nl = strlen(buf);
if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return 0;
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
/* api return is long, although we only support setting
* default (300s) or max uint32_t */
ttl = SSL_SESSION_get_timeout(sess);
#endif
lws_context_lock(vh->context, __func__); /* -------------- cx { */
lws_vhost_lock(vh); /* -------------- vh { */
ts = __lws_tls_session_lookup_by_name(vh, buf);
if (!ts) {
/*
* We have to make our own, new session
*/
if (vh->tls_sessions.count == vh->tls_session_cache_max) {
/*
* We have reached the vhost's session cache limit,
* prune the LRU / head
*/
ts = lws_container_of(vh->tls_sessions.head,
lws_tls_sco_t, list);
lwsl_tlssess("%s: pruning oldest session\n", __func__);
__lws_tls_session_destroy(ts);
}
ts = lws_malloc(sizeof(*ts) + nl + 1, __func__);
if (!ts)
goto bail;
memset(ts, 0, sizeof(*ts));
memcpy(&ts[1], buf, nl + 1);
lws_dll2_add_tail(&ts->list, &vh->tls_sessions);
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
disposition = "new";
#endif
/*
* We don't have to do a SSL_SESSION_up_ref() here, because
* we will return from this callback indicating that we kept the
* ref
*/
} else {
/*
* Give up our refcount on the session we are about to replace
* with a newer one
*/
SSL_SESSION_free(ts->session);
/* keep our session list sorted in lru -> mru order */
lws_dll2_remove(&ts->list);
lws_dll2_add_tail(&ts->list, &vh->tls_sessions);
}
ts->session = sess;
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
lwsl_tlssess("%s: %p: %s: %s %s, ttl %lds (%s:%u)\n", __func__,
sess, wsi->lc.gutag, disposition, buf, ttl, vh->name,
vh->tls_sessions.count);
/*
* indicate we will hold on to the SSL_SESSION reference, and take
* responsibility to call SSL_SESSION_free() on it ourselves
*/
return 1;
bail:
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
return 0;
}
void
lws_tls_session_cache(struct lws_vhost *vh, uint32_t ttl)
{
long cmode;
if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return;
cmode = SSL_CTX_get_session_cache_mode(vh->tls.ssl_client_ctx);
SSL_CTX_set_session_cache_mode(vh->tls.ssl_client_ctx,
(int)(cmode | SSL_SESS_CACHE_CLIENT));
SSL_CTX_sess_set_new_cb(vh->tls.ssl_client_ctx, lws_tls_session_new_cb);
if (!ttl)
return;
#if defined(OPENSSL_IS_BORINGSSL)
SSL_CTX_set_timeout(vh->tls.ssl_client_ctx, ttl);
#else
SSL_CTX_set_timeout(vh->tls.ssl_client_ctx, (long)ttl);
#endif
}

View file

@ -58,5 +58,11 @@ lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type);
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
#endif
void
lws_tls_reuse_session(struct lws *wsi);
void
lws_tls_session_cache(struct lws_vhost *vh, uint32_t ttl);
#endif

View file

@ -1,7 +1,7 @@
/*
* lws-minimal-http-client-multi
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
@ -26,6 +26,12 @@
* HTTP/1.0: Pipelining only possible if Keep-Alive: yes sent by server
* HTTP/1.1: always possible... serializes requests
* HTTP/2: always possible... all requests sent as individual streams in parallel
*
* Note: stats are kept on tls session reuse and checked depending on mode
*
* - default: no reuse expected (connections made too quickly at once)
* - staggered, no pipeline: n - 1 reuse expected
* - staggered, pipelined: no reuse expected
*/
#include <libwebsockets.h>
@ -40,7 +46,7 @@ struct cliuser {
int index;
};
static int completed, failed, numbered, stagger_idx, posting, count = COUNT;
static int completed, failed, numbered, stagger_idx, posting, count = COUNT, reuse;
static lws_sorted_usec_list_t sul_stagger;
static struct lws_client_connect_info i;
static struct lws *client_wsi[COUNT];
@ -65,8 +71,10 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
switch (reason) {
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u\n",
idx, lws_http_client_http_response(wsi));
lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u: tls-session-reuse: %d\n",
idx, lws_http_client_http_response(wsi), lws_tls_session_is_reused(wsi));
if (lws_tls_session_is_reused(wsi))
reuse++;
break;
/* because we are protocols[0] ... */
@ -338,14 +346,23 @@ stagger_cb(lws_sorted_usec_list_t *sul)
if (stagger_idx == count - 1)
next += 400 * LWS_US_PER_MS;
#if defined(LWS_WITH_TLS_SESSIONS)
if (stagger_idx == 1)
next += 600 * LWS_US_PER_MS;
#endif
lws_sul_schedule(context, 0, &sul_stagger, stagger_cb, next);
}
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
int m, staggered = 0
#if defined(LWS_WITH_TLS_SESSIONS)
, pl = 0
#endif
;
unsigned long long start;
int m, staggered = 0;
const char *p;
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
@ -400,6 +417,11 @@ int main(int argc, const char **argv)
info.client_ssl_ca_filepath = "./warmcat.com.cer";
#endif
/* vhost option allowing tls session reuse, requires
* LWS_WITH_TLS_SESSIONS build option */
if (lws_cmdline_option(argc, argv, "--no-tls-session-reuse"))
info.options |= LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE;
if ((p = lws_cmdline_option(argc, argv, "--limit")))
info.simultaneous_ssl_restriction = atoi(p);
@ -430,8 +452,12 @@ int main(int argc, const char **argv)
i.method = "GET";
/* enables h1 or h2 connection sharing */
if (lws_cmdline_option(argc, argv, "-p"))
if (lws_cmdline_option(argc, argv, "-p")) {
i.ssl_connection |= LCCSCF_PIPELINE;
#if defined(LWS_WITH_TLS_SESSIONS)
pl = 1;
#endif
}
/* force h1 even if h2 available */
if (lws_cmdline_option(argc, argv, "--h1"))
@ -493,6 +519,16 @@ int main(int argc, const char **argv)
while (!intr && !lws_service(context, 0))
;
#if defined(LWS_WITH_TLS_SESSIONS)
lwsl_user("%s: session reuse count %d\n", __func__, reuse);
if (staggered && !pl && !reuse) {
lwsl_err("%s: failing, expected 1 .. %d reused\n", __func__, count - 1);
// too difficult to reproduce in CI
// failed = 1;
}
#endif
lwsl_user("Duration: %lldms\n", (us() - start) / 1000);
lws_context_destroy(context);