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

alpn: assemble defaults from roles and allow override

Since new roles may be incompatible with http, add support for
alpn names at the role struct, automatic generation of the
default list of alpn names that servers advertise, and the
ability to override the used alpn names per-vhost and per-
client connection.

This not only lets you modulate visibility or use of h2,
but also enables vhosts that only offer non-http roles,
as well as restricting http role vhosts to only alpn
identifiers related to http roles.
This commit is contained in:
Andy Green 2018-04-12 15:56:38 +08:00
parent f978ea8658
commit aa816e98a9
25 changed files with 497 additions and 334 deletions

View file

@ -864,7 +864,6 @@ if (LWS_WITH_HTTP2 AND NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
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

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
@ -25,6 +25,22 @@
#define LWS_BUILD_HASH "unknown-build-hash"
#endif
#if defined(LWS_WITH_TLS)
static const struct lws_role_ops * available_roles[] = {
#if defined(LWS_ROLE_H2)
&role_ops_h2,
#endif
#if defined(LWS_ROLE_H1)
&role_ops_h1,
#endif
#if defined(LWS_ROLE_WS)
&role_ops_ws,
#endif
};
static char alpn_discovered[32];
#endif
static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
/**
@ -40,6 +56,25 @@ lws_get_library_version(void)
return library_version;
}
int
lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
{
#if defined(LWS_WITH_TLS)
int n;
if (!alpn)
return 0;
lwsl_info("%s: '%s'\n", __func__, alpn);
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++)
if (!strcmp(available_roles[n]->alpn, alpn) &&
available_roles[n]->alpn_negotiated)
return available_roles[n]->alpn_negotiated(wsi, alpn);
#endif
return 0;
}
static const char * const mount_protocols[] = {
"http://",
"https://",
@ -553,6 +588,7 @@ lws_create_vhost(struct lws_context *context,
vh->pvo = info->pvo;
vh->headers = info->headers;
vh->user = info->user;
vh->alpn = info->alpn;
#if defined(LWS_ROLE_H2)
role_ops_h2.init_vhost(vh, info);
@ -1081,6 +1117,28 @@ lws_create_context(struct lws_context_creation_info *info)
context->options = info->options;
#if defined(LWS_WITH_TLS)
if (info->alpn)
context->alpn_default = info->alpn;
else {
char *p = alpn_discovered, first = 1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++) {
if (available_roles[n]->alpn) {
if (!first)
*p++ = ',';
p += lws_snprintf(p, alpn_discovered +
sizeof(alpn_discovered) - 2 - p,
"%s", available_roles[n]->alpn);
first = 0;
}
}
context->alpn_default = alpn_discovered;
}
lwsl_info("Default ALPN advertisment: %s\n", context->alpn_default);
#endif
if (info->timeout_secs)
context->timeout_secs = info->timeout_secs;
else

View file

@ -2294,7 +2294,10 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
#ifndef LWS_PLAT_OPTEE
socklen_t len = sizeof(struct sockaddr_storage);
#endif
int n, m;
int n;
#if !defined(LWS_WITH_ESP32)
int m;
#endif
struct sockaddr_storage sin;
struct sockaddr *v;
@ -2822,6 +2825,22 @@ lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs)
}
}
const char *
lws_cmdline_option(int argc, const char **argv, const char *val)
{
int n = strlen(val), c = argc;
while (--c > 0)
if (!strncmp(argv[c], val, n) && !*(argv[c] + n)) {
if (c != argc - 1)
return argv[c + 1];
return argv[c] + n;
}
return NULL;
}
#ifdef LWS_WITH_SERVER_STATUS
LWS_EXTERN int

View file

@ -2998,6 +2998,13 @@ struct lws_context_creation_info {
/**< VHOST: If non-NULL, when asked to serve a non-existent file,
* lws attempts to server this url path instead. Eg,
* "/404.html" */
const char *alpn;
/**< CONTEXT: If non-NULL, default list of advertised alpn, comma-
* separated
*
* VHOST: If non-NULL, per-vhost list of advertised alpn, comma-
* separated
*/
unsigned int h2_rx_scratch_size;
/**< VHOST: size of the rx scratch buffer for each stream. 0 =
* default (512 bytes). This affects the RX chunk size
@ -3404,7 +3411,6 @@ enum lws_client_connect_ssl_connection_flags {
LCCSCF_ALLOW_SELFSIGNED = (1 << 1),
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2),
LCCSCF_ALLOW_EXPIRED = (1 << 3),
LCCSCF_NOT_H2 = (1 << 4),
LCCSCF_PIPELINE = (1 << 16),
/**< Serialize / pipeline multiple client connections
@ -3487,6 +3493,12 @@ struct lws_client_connect_info {
* members added above will see 0 (default) even if the app
* was not built against the newer headers.
*/
const char *alpn;
/* NULL: allow lws default ALPN list, from vhost if present or from
* list of roles built into lws
* non-NULL: require one from provided comma-separated list of alpn
* tokens
*/
void *_unused[4]; /**< dummy */
};
@ -4088,6 +4100,7 @@ enum lws_token_indexes {
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_METHOD,
_WSI_TOKEN_CLIENT_IFACE,
_WSI_TOKEN_CLIENT_ALPN,
/* always last real token index*/
WSI_TOKEN_COUNT,
@ -4670,6 +4683,11 @@ LWS_VISIBLE LWS_EXTERN void
lws_libuv_static_refcount_del(uv_handle_t *);
#endif /* LWS_WITH_LIBUV */
#if defined(LWS_WITH_ESP32)
#define lws_libuv_static_refcount_add(_a, _b)
#define lws_libuv_static_refcount_del NULL
#endif
///@}
/*! \defgroup event libevent helpers
@ -5365,6 +5383,7 @@ typedef union {
lws_filefd_type filefd;
} lws_sock_file_fd_type;
#if !defined(LWS_WITH_ESP32)
struct lws_udp {
struct sockaddr sa;
socklen_t salen;
@ -5372,6 +5391,7 @@ struct lws_udp {
struct sockaddr sa_pending;
socklen_t salen_pending;
};
#endif
/*
* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
@ -5518,12 +5538,12 @@ lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
*/
LWS_VISIBLE LWS_EXTERN const char *
lws_get_peer_simple(struct lws *wsi, char *name, int namelen);
#if !defined(LWS_WITH_ESP32)
#define LWS_ITOSA_NOT_EXIST -1
#define LWS_ITOSA_NOT_USABLE -2
#define LWS_ITOSA_USABLE 0
#if !defined(LWS_WITH_ESP32)
/**
* lws_interface_to_sa() - Convert interface name or IP to sockaddr struct
*
@ -5809,6 +5829,25 @@ lws_set_wsi_user(struct lws *wsi, void *user);
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
const char **path);
/**
* lws_cmdline_option(): simple commandline parser
*
* \param argc: count of argument strings
* \param argv: argument strings
* \param val: string to find
*
* Returns NULL if the string \p val is not found in the arguments.
*
* If it is found, then it returns a pointer to the next character after \p val.
* So if \p val is "-d", then for the commandlines "myapp -d15" and
* "myapp -d 15", in both cases the return will point to the "15".
*
* In the case there is no argument, like "myapp -d", the return will
* either point to the '\\0' at the end of -d, or to the start of the
* next argument, ie, will be non-NULL.
*/
LWS_VISIBLE LWS_EXTERN const char *
lws_cmdline_option(int argc, const char **argv, const char *val);
/**
* lws_now_secs(): return seconds since 1970-1-1

View file

@ -181,11 +181,13 @@ handle_truncated_send:
wsi->trunc_len = (unsigned int)(real_len - n);
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
#if !defined(LWS_WITH_ESP32)
if (lws_wsi_is_udp(wsi)) {
/* stash original destination for fulfilling UDP partials */
wsi->udp->sa_pending = wsi->udp->sa;
wsi->udp->salen_pending = wsi->udp->salen;
}
#endif
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
@ -488,14 +490,16 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n;
int n = 0;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
if (lws_wsi_is_udp(wsi)) {
#if !defined(LWS_WITH_ESP32)
wsi->udp->salen = sizeof(wsi->udp->sa);
n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
&wsi->udp->sa, &wsi->udp->salen);
#endif
} else
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
@ -523,10 +527,12 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
int n = 0;
if (lws_wsi_is_udp(wsi)) {
#if !defined(LWS_WITH_ESP32)
if (wsi->trunc_len)
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
else
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
#endif
} else
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);

View file

@ -614,6 +614,7 @@ void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
struct lws_context_per_thread;
struct lws_role_ops {
const char *name;
const char *alpn;
/*
* After http headers have parsed, this is the last chance for a role
* to upgrade the connection to something else using the headers.
@ -652,6 +653,9 @@ struct lws_role_ops {
/* get encapsulation parent */
struct lws * (*encapsulation_parent)(struct lws *wsi);
/* role-specific destructor */
int (*alpn_negotiated)(struct lws *wsi, const char *alpn);
/* chance for the role to handle close in the protocol */
int (*close_via_role_protocol)(struct lws *wsi,
enum lws_close_status reason);
@ -693,9 +697,9 @@ enum {
LWS_HP_RET_BAIL_DIE,
LWS_HP_RET_USER_SERVICE,
LWS_HPI_RET_DIE,
LWS_HPI_RET_HANDLED,
LWS_HPI_RET_CLOSE_HANDLED,
LWS_HPI_RET_DIE, /* we closed it */
LWS_HPI_RET_HANDLED, /* no probs */
LWS_HPI_RET_CLOSE_HANDLED, /* close it for us */
LWS_UPG_RET_DONE,
LWS_UPG_RET_CONTINUE,
@ -1071,6 +1075,11 @@ struct lws_timed_vh_protocol {
struct lws_tls_ss_pieces;
struct alpn_ctx {
uint8_t data[23];
uint8_t len;
};
struct lws_vhost {
char http_proxy_address[128];
char proxy_basic_auth_token[128];
@ -1110,11 +1119,13 @@ struct lws_vhost {
struct lws_dll_lws dll_active_client_conns;
#endif
const char *error_document_404;
const char *alpn;
#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 */
char ecdh_curve[16];
struct alpn_ctx alpn_ctx;
#endif
#if defined(LWS_WITH_MBEDTLS)
lws_tls_x509 *x509_client_CA;
@ -1262,6 +1273,7 @@ struct lws_context {
void *user_space;
const char *server_string;
const struct lws_protocol_vhost_options *reject_service_keywords;
const char *alpn_default;
lws_reload_func deprecation_cb;
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
@ -1527,6 +1539,7 @@ struct client_info_stash {
char *protocol;
char *method;
char *iface;
char *alpn;
};
#endif
@ -2406,8 +2419,6 @@ LWS_EXTERN int
lws_add_http2_header_status(struct lws *wsi,
unsigned int code, unsigned char **p,
unsigned char *end);
LWS_EXTERN int
lws_h2_configure_if_upgraded(struct lws *wsi);
LWS_EXTERN void
lws_hpack_destroy_dynamic_header(struct lws *wsi);
LWS_EXTERN int
@ -2435,8 +2446,6 @@ 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
LWS_EXTERN int
@ -2511,7 +2520,7 @@ LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
#define LWS_SSL_ENABLED(context) (0)
#define lws_context_init_server_ssl(_a, _b) (0)
#define lws_ssl_destroy(_a)
#define lws_context_init_http2_ssl(_a)
#define lws_context_init_alpn(_a)
#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl
#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl
#define lws_ssl_pending lws_ssl_pending_no_ssl
@ -2533,6 +2542,8 @@ enum lws_tls_extant {
LWS_TLS_EXTANT_YES,
LWS_TLS_EXTANT_ALTERNATIVE
};
LWS_EXTERN void
lws_context_init_alpn(struct lws_vhost *vhost);
LWS_EXTERN enum lws_tls_extant
lws_tls_use_any_upgrade_check_extant(const char *name);
LWS_EXTERN int openssl_websocket_private_data_index;
@ -2641,17 +2652,9 @@ LWS_EXTERN lws_tls_ctx *
lws_tls_ctx_from_wsi(struct lws *wsi);
LWS_EXTERN int
lws_ssl_get_error(struct lws *wsi, int n);
#endif
/* HTTP2-related */
#ifdef LWS_WITH_HTTP2
LWS_EXTERN void
lws_context_init_http2_ssl(struct lws_vhost *vhost);
#else
#define lws_context_init_http2_ssl(_a)
#endif
#endif
#if LWS_MAX_SMP > 1
static LWS_INLINE void
@ -3002,6 +3005,13 @@ 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);
int
lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len);
int
lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn);
int
lws_tls_server_conn_alpn(struct lws *wsi);
#ifdef __cplusplus
};
#endif

View file

@ -76,7 +76,8 @@ rops_periodic_checks_cgi(struct lws_context *context, int tsi, time_t now)
}
struct lws_role_ops role_ops_cgi = {
"cgi",
/* role name */ "cgi",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -90,6 +91,7 @@ struct lws_role_ops role_ops_cgi = {
/* write_role_protocol */ NULL,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,

View file

@ -572,7 +572,7 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
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_CLOSE_HANDLED;
return LWS_HPI_RET_HANDLED;
}
@ -694,8 +694,29 @@ rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
return lws_issue_raw(wsi, (unsigned char *)buf, len);
}
static int
rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn)
{
lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
#if !defined(LWS_NO_CLIENT)
if (lwsi_role_client(wsi)) {
/*
* If alpn asserts it is http/1.1, server support for KA is
* mandatory.
*
* Knowing this lets us proceed with sending pipelined headers
* before we received the first response headers.
*/
wsi->keepalive_active = 1;
}
#endif
return 0;
}
struct lws_role_ops role_ops_h1 = {
"h1",
/* role name */ "h1",
/* alpn id */ "http/1.1",
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -709,6 +730,7 @@ struct lws_role_ops role_ops_h1 = {
/* write_role_protocol */ rops_write_role_protocol_h1,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ rops_alpn_negotiated_h1,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,

View file

@ -1056,8 +1056,54 @@ rops_encapsulation_parent_h2(struct lws *wsi)
return NULL;
}
static int
rops_alpn_negotiated_h2(struct lws *wsi, const char *alpn)
{
struct allocated_headers *ah;
lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
#if !defined(LWS_NO_CLIENT)
if (lwsi_role_client(wsi)) {
lwsl_info("%s: upgraded to H2\n", __func__);
wsi->client_h2_alpn = 1;
}
#endif
wsi->upgraded_to_http2 = 1;
wsi->vhost->conn_stats.h2_alpn++;
/* adopt the header info */
ah = wsi->ah;
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;
if (!wsi->h2.h2n)
wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n");
if (!wsi->h2.h2n)
return 1;
lws_h2_init(wsi);
/* HTTP2 union */
lws_hpack_dynamic_size(wsi,
wsi->h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
wsi->h2.tx_cr = 65535;
lwsl_info("%s: wsi %p: configured for h2\n", __func__, wsi);
return 0;
}
struct lws_role_ops role_ops_h2 = {
"h2",
/* role name */ "h2",
/* alpn id */ "h2",
/* check_upgrades */ rops_check_upgrades_h2,
/* init_context */ rops_init_context_h2,
/* init_vhost */ rops_init_vhost_h2,
@ -1071,6 +1117,7 @@ struct lws_role_ops role_ops_h2 = {
/* write_role_protocol */ rops_write_role_protocol_h2,
/* rxflow_cache */ rops_rxflow_cache_h2,
/* encapsulation_parent */ rops_encapsulation_parent_h2,
/* alpn_negotiated */ rops_alpn_negotiated_h2,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ rops_close_kill_connection_h2,

View file

@ -1,158 +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
*
* Some or all of this file is based on code from nghttp2, which has the
* following license. Since it's more liberal than lws license, you're also
* at liberty to get the original code from
* https://github.com/tatsuhiro-t/nghttp2 under his liberal terms alone.
*
* nghttp2 - HTTP/2.0 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* 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-libwebsockets.h"
#if !defined(LWS_NO_SERVER)
#if defined(LWS_WITH_TLS)
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
struct alpn_ctx {
unsigned char *data;
unsigned short len;
};
static int
alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
#if !defined(LWS_WITH_MBEDTLS)
struct alpn_ctx *alpn_ctx = arg;
if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
alpn_ctx->len, in, inlen) !=
OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_NOACK;
#endif
return SSL_TLSEXT_ERR_OK;
}
#endif
LWS_VISIBLE void
lws_context_init_http2_ssl(struct lws_vhost *vhost)
{
#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 };
SSL_CTX_set_alpn_select_cb(vhost->ssl_ctx, alpn_cb, &protos);
lwsl_notice(" HTTP2 / ALPN enabled\n");
#else
lwsl_notice(
" HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n",
OPENSSL_VERSION_NUMBER);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
int lws_h2_configure_if_upgraded(struct lws *wsi)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
struct allocated_headers *ah;
const unsigned char *name = NULL;
char cstr[10];
unsigned len;
if (!wsi->ssl)
return 0;
SSL_get0_alpn_selected(wsi->ssl, &name, &len);
if (!len) {
lwsl_info("no ALPN upgrade\n");
return 0;
}
if (len > sizeof(cstr) - 1)
len = sizeof(cstr) - 1;
memcpy(cstr, name, len);
cstr[len] = '\0';
lwsl_info("negotiated '%s' using ALPN\n", cstr);
wsi->use_ssl |= LCCSCF_USE_SSL;
if (strncmp((char *)name, "http/1.1", 8) == 0)
return 0;
/* http2 */
wsi->upgraded_to_http2 = 1;
wsi->vhost->conn_stats.h2_alpn++;
/* adopt the header info */
ah = wsi->ah;
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;
wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n");
if (!wsi->h2.h2n)
return 1;
lws_h2_init(wsi);
/* HTTP2 union */
lws_hpack_dynamic_size(wsi,
wsi->h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
wsi->h2.tx_cr = 65535;
lwsl_info("%s: wsi %p: configured for h2\n", __func__, wsi);
#endif
return 0;
}
#endif
#endif

View file

@ -84,8 +84,8 @@ lws_client_connect_2(struct lws *wsi)
if (w != wsi && w->client_hostname_copy &&
!strcmp(adsin, w->client_hostname_copy) &&
#if defined(LWS_WITH_TLS)
(wsi->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) ==
(w->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) &&
(wsi->use_ssl & LCCSCF_USE_SSL) ==
(w->use_ssl & LCCSCF_USE_SSL) &&
#endif
wsi->c_port == w->c_port) {
@ -403,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 ||
@ -595,7 +595,7 @@ 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] = "", *p;
iface[16] = "", alpn[32], *p;
struct lws *wsi = *pwsi;
if (wsi->redirects == 3) {
@ -618,7 +618,11 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (p)
lws_strncpy(method, p, sizeof(iface));
lws_strncpy(iface, p, sizeof(iface));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ALPN);
if (p)
lws_strncpy(alpn, p, sizeof(alpn));
lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n",
address, port, path, ssl);
@ -687,6 +691,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
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;
origin[0] = '/';
strncpy(&origin[1], path, sizeof(origin) - 2);
@ -850,6 +858,7 @@ lws_client_stash_destroy(struct lws *wsi)
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);
}
@ -946,9 +955,6 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
#if defined(LWS_WITH_TLS)
wsi->use_ssl = i->ssl_connection;
if (!i->method) /* !!! disallow ws for h2 right now */
wsi->use_ssl |= LCCSCF_NOT_H2;
#else
if (i->ssl_connection & LCCSCF_USE_SSL) {
lwsl_err("libwebsockets not configured for ssl\n");
@ -995,6 +1001,26 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
if (!wsi->stash->iface)
goto bail1;
}
/*
* For ws, default to http/1.1 only. If i->alpn is set, defer to
* whatever he has set in there (eg, "h2").
*
* The problem is he has to commit to h2 before he can find out if the
* server has the SETTINGS for ws-over-h2 enabled; if not then ws is
* not possible on that connection. So we only try it if he
* assertively said to use h2 alpn.
*/
if (!i->method && !i->alpn) {
wsi->stash->alpn = lws_strdup("http/1.1");
if (!wsi->stash->alpn)
goto bail1;
} else
if (i->alpn) {
wsi->stash->alpn = lws_strdup(i->alpn);
if (!wsi->stash->alpn)
goto bail1;
}
if (i->pwsi)
*i->pwsi = wsi;
@ -1049,14 +1075,13 @@ lws_client_connect_via_info2(struct lws *wsi)
/*
* we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so u.hdr is fine
* 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;
/* these only need u.hdr lifetime as well */
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
goto bail1;
@ -1083,6 +1108,10 @@ lws_client_connect_via_info2(struct lws *wsi)
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;
#if defined(LWS_WITH_SOCKS5)
if (!wsi->vhost->socks_proxy_port)

View file

@ -342,12 +342,7 @@ start_ws_handshake:
* So this is it, we are an h2 master client connection
* now, not an h1 client connection.
*/
lwsl_info("client connection upgraded to h2\n");
lws_h2_configure_if_upgraded(wsi);
lws_role_transition(wsi, LWSIFR_CLIENT,
LRS_H2_CLIENT_SEND_SETTINGS,
&role_ops_h2);
lws_tls_server_conn_alpn(wsi);
/* send the H2 preface to legitimize the connection */
if (lws_h2_issue_preface(wsi)) {

View file

@ -39,6 +39,7 @@ static const char * const paths_global[] = {
"global.timeout-secs",
"global.reject-service-keywords[].*",
"global.reject-service-keywords[]",
"global.default-alpn",
};
enum lejp_global_paths {
@ -51,7 +52,8 @@ enum lejp_global_paths {
LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
LWJPGP_REJECT_SERVICE_KEYWORDS
LWJPGP_REJECT_SERVICE_KEYWORDS,
LWJPGP_DEFAULT_ALPN,
};
static const char * const paths_vhosts[] = {
@ -102,6 +104,7 @@ static const char * const paths_vhosts[] = {
"vhosts[].client-cert-required",
"vhosts[].ignore-missing-cert",
"vhosts[].error-document-404",
"vhosts[].alpn",
};
enum lejp_vhost_paths {
@ -152,6 +155,7 @@ enum lejp_vhost_paths {
LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
LEJPVP_IGNORE_MISSING_CERT,
LEJPVP_ERROR_DOCUMENT_404,
LEJPVP_ALPN,
};
static const char * const parser_errs[] = {
@ -290,6 +294,10 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason)
a->info->timeout_secs = atoi(ctx->buf);
return 0;
case LWJPGP_DEFAULT_ALPN:
a->info->alpn = a->p;
break;
default:
return 0;
}
@ -732,6 +740,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->ssl_options_clear |= atol(ctx->buf);
return 0;
case LEJPVP_ALPN:
a->info->alpn = a->p;
break;
default:
return 0;
}

View file

@ -729,6 +729,7 @@ lws_find_string_in_file(const char *filename, const char *string, int stringlen)
return hit;
}
#endif
static int
lws_unauthorised_basic_auth(struct lws *wsi)
@ -762,8 +763,6 @@ lws_unauthorised_basic_auth(struct lws *wsi)
}
#endif
int lws_clean_url(char *p)
{
if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
@ -1887,13 +1886,14 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
if (type & LWS_ADOPT_SOCKET) { /* socket desc */
lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
(int)(lws_intptr_t)fd.sockfd);
#if !defined(LWS_WITH_ESP32)
if (type & LWS_ADOPT_FLAG_UDP)
/*
* these can be >128 bytes, so just alloc for UDP
*/
new_wsi->udp = lws_malloc(sizeof(*new_wsi->udp),
"udp struct");
#endif
if (type & LWS_ADOPT_HTTP)
/* the transport is accepted...

View file

@ -129,9 +129,11 @@ rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi,
/* already closed cleanly as necessary */
return LWS_HPI_RET_DIE;
if (lws_server_socket_service_ssl(cwsi, accept_fd))
if (lws_server_socket_service_ssl(cwsi, accept_fd)) {
lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
"listen svc fail");
return LWS_HPI_RET_DIE;
}
lwsl_info("%s: new wsi %p: wsistate 0x%x, role_ops %s\n",
__func__, cwsi, cwsi->wsistate, cwsi->role_ops->name);
@ -148,7 +150,8 @@ int rops_handle_POLLOUT_listen(struct lws *wsi)
}
struct lws_role_ops role_ops_listen = {
"listen",
/* role name */ "listen",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -162,6 +165,7 @@ struct lws_role_ops role_ops_listen = {
/* write_role_protocol */ NULL,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,

View file

@ -55,7 +55,8 @@ rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
}
struct lws_role_ops role_ops_pipe = {
"pipe",
/* role name */ "pipe",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -69,6 +70,7 @@ struct lws_role_ops role_ops_pipe = {
/* write_role_protocol */ NULL,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,

View file

@ -151,7 +151,8 @@ rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_role_ops role_ops_raw_skt = {
"raw-skt",
/* role name */ "raw-skt",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -165,6 +166,7 @@ struct lws_role_ops role_ops_raw_skt = {
/* write_role_protocol */ NULL,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
@ -176,7 +178,8 @@ struct lws_role_ops role_ops_raw_skt = {
struct lws_role_ops role_ops_raw_file = {
"raw-file",
/* role name */ "raw-file",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -190,6 +193,7 @@ struct lws_role_ops role_ops_raw_file = {
/* write_role_protocol */ NULL,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,

View file

@ -1847,7 +1847,8 @@ rops_callback_on_writable_ws(struct lws *wsi)
}
struct lws_role_ops role_ops_ws = {
"ws",
/* role name */ "ws",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
@ -1861,6 +1862,7 @@ struct lws_role_ops role_ops_ws = {
/* write_role_protocol */ rops_write_role_protocol_ws,
/* rxflow_cache */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ rops_close_via_role_protocol_ws,
/* close_role */ rops_close_role_ws,
/* close_kill_connection */ rops_close_kill_connection_ws,

View file

@ -27,29 +27,13 @@ OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
return 0;
}
struct alpn_ctx {
unsigned char *data;
unsigned short len;
};
static struct alpn_ctx protos = { (unsigned char *)
#if defined(LWS_WITH_HTTP2)
"\x02h2"
#endif
"\x08http/1.1", 3 +
#if defined(LWS_WITH_HTTP2)
3 +
#endif
9 };
static struct alpn_ctx protos_h1 = { (unsigned char *)"\x08http/1.1", 3 + 9 };
int
lws_ssl_client_bio_create(struct lws *wsi)
{
struct alpn_ctx *apro = &protos;
X509_VERIFY_PARAM *param;
char hostname[128], *p;
const char *alpn_comma = wsi->context->alpn_default;
struct alpn_ctx protos;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_HOST) <= 0) {
@ -75,11 +59,6 @@ lws_ssl_client_bio_create(struct lws *wsi)
if (!wsi->ssl)
return -1;
if (wsi->use_ssl & LCCSCF_NOT_H2)
apro = &protos_h1;
SSL_set_alpn_select_cb(wsi->ssl, apro);
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
@ -91,6 +70,22 @@ lws_ssl_client_bio_create(struct lws *wsi)
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
}
if (wsi->vhost->alpn)
alpn_comma = wsi->vhost->alpn;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_ALPN) > 0)
alpn_comma = hostname;
lwsl_info("%s: %p: client conn sending ALPN list '%s'\n",
__func__, wsi, alpn_comma);
protos.len = lws_alpn_comma_to_openssl(alpn_comma, protos.data,
sizeof(protos.data) - 1);
/* with mbedtls, protos is not pointed to after exit from this call */
SSL_set_alpn_select_cb(wsi->ssl, &protos);
/*
* use server name indication (SNI), if supported,
* when establishing connection
@ -117,20 +112,8 @@ lws_tls_client_connect(struct lws *wsi)
if (n == 1) {
SSL_get0_alpn_selected(wsi->ssl, &prot, &len);
#if !defined(LWS_NO_CLIENT)
if (prot && !strcmp((char *)prot, "h2"))
wsi->client_h2_alpn = 1;
#endif
if (prot && !strcmp((char *)prot, "http/1.1"))
/*
* If alpn asserts it is http/1.1, KA is mandatory.
*
* Knowing this lets us proceed with sending
* pipelined headers before we received the first
* response headers.
*/
wsi->keepalive_active = 1;
lws_role_call_alpn_negotiated(wsi, (const char *)prot);
lwsl_info("client connect OK\n");
return LWS_SSL_CAPABLE_DONE;
}

View file

@ -1648,8 +1648,8 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
*/
struct alpn_ctx {
unsigned char *data;
unsigned short len;
unsigned char data[23];
unsigned char len;
};
static void
@ -1674,6 +1674,9 @@ _openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
break;
}
if (!len)
count++;
if (!count)
return;
@ -1705,6 +1708,12 @@ _openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
if (!len)
break;
}
if (!len) {
*q++ = '\0';
count++;
len = *p++;
alpn_protos[count] = (char *)q;
}
alpn_protos[count] = NULL; /* last pointer ends list with NULL */
}

View file

@ -86,14 +86,6 @@ OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
}
#endif
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
static const unsigned char client_alpn_protocols[] = {
#if defined(LWS_WITH_HTTP2)
2, 'h', '2',
#endif
8, 'h', 't', 't', 'p', '/', '1', '.', '1'
};
#endif
int
lws_ssl_client_bio_create(struct lws *wsi)
@ -102,9 +94,11 @@ lws_ssl_client_bio_create(struct lws *wsi)
X509_VERIFY_PARAM *param;
#endif
char hostname[128], *p;
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
const unsigned char *plist = client_alpn_protocols;
int n = sizeof(client_alpn_protocols);
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
defined(LWS_HAVE_SSL_get0_alpn_selected)
uint8_t openssl_alpn[40];
const char *alpn_comma = wsi->context->alpn_default;
int n;
#endif
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
@ -135,17 +129,6 @@ lws_ssl_client_bio_create(struct lws *wsi)
return -1;
}
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
defined(LWS_HAVE_SSL_get0_alpn_selected)
#if defined(LWS_WITH_HTTP2)
if (wsi->use_ssl & LCCSCF_NOT_H2) {
plist += 3;
n -= 3;
}
#endif
SSL_set_alpn_protos(wsi->ssl, plist, n);
#endif
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
@ -221,6 +204,23 @@ lws_ssl_client_bio_create(struct lws *wsi)
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
defined(LWS_HAVE_SSL_get0_alpn_selected)
if (wsi->vhost->alpn)
alpn_comma = wsi->vhost->alpn;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_ALPN) > 0)
alpn_comma = hostname;
lwsl_info("client conn using alpn list '%s'\n", alpn_comma);
n = lws_alpn_comma_to_openssl(alpn_comma, openssl_alpn,
sizeof(openssl_alpn) - 1);
SSL_set_alpn_protos(wsi->ssl, openssl_alpn, n);
#endif
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, wsi);
return 0;
@ -244,24 +244,10 @@ lws_tls_client_connect(struct lws *wsi)
len = sizeof(a) - 1;
memcpy(a, (const char *)prot, len);
a[len] = '\0';
#if !defined(LWS_NO_CLIENT)
if (prot && !strcmp(a, "h2")) {
lwsl_info("%s: upgraded to H2\n", __func__);
wsi->client_h2_alpn = 1;
}
#endif
if (prot && !strcmp(a, "http/1.1"))
/*
* If alpn asserts it is http/1.1, KA is mandatory.
*
* Knowing this lets us proceed with sending
* pipelined headers before we received the first
* response headers.
*/
wsi->keepalive_active = 1;
lwsl_info("client connect OK\n");
lws_role_call_alpn_negotiated(wsi, (const char *)a);
#endif
lwsl_info("client connect OK\n");
return LWS_SSL_CAPABLE_DONE;
}

View file

@ -21,6 +21,79 @@
#include "private-libwebsockets.h"
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
static int
alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
#if !defined(LWS_WITH_MBEDTLS)
struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg;
if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
alpn_ctx->len, in, inlen) !=
OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_NOACK;
#endif
return SSL_TLSEXT_ERR_OK;
}
#endif
void
lws_context_init_alpn(struct lws_vhost *vhost)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
const char *alpn_comma = vhost->context->alpn_default;
if (vhost->alpn)
alpn_comma = vhost->alpn;
lwsl_info(" Server '%s' advertising ALPN: %s\n",
vhost->name, alpn_comma);
vhost->alpn_ctx.len = lws_alpn_comma_to_openssl(alpn_comma,
vhost->alpn_ctx.data,
sizeof(vhost->alpn_ctx.data) - 1);
SSL_CTX_set_alpn_select_cb(vhost->ssl_ctx, alpn_cb, &vhost->alpn_ctx);
#else
lwsl_err(
" HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n",
OPENSSL_VERSION_NUMBER);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
int
lws_tls_server_conn_alpn(struct lws *wsi)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
const unsigned char *name = NULL;
char cstr[10];
unsigned len;
SSL_get0_alpn_selected(wsi->ssl, &name, &len);
if (!len) {
lwsl_info("no ALPN upgrade\n");
return 0;
}
if (len > sizeof(cstr) - 1)
len = sizeof(cstr) - 1;
memcpy(cstr, name, len);
cstr[len] = '\0';
lwsl_info("negotiated '%s' using ALPN\n", cstr);
wsi->use_ssl |= LCCSCF_USE_SSL;
return lws_role_call_alpn_negotiated(wsi, (const char *)cstr);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
return 0;
}
LWS_VISIBLE int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
@ -95,11 +168,7 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
}
if (vhost->use_ssl)
/*
* SSL is happy and has a cert it's content with
* If we're supporting HTTP2, initialize that
*/
lws_context_init_http2_ssl(vhost);
lws_context_init_alpn(vhost);
return 0;
}
@ -296,11 +365,8 @@ accepted:
context->timeout_secs);
lwsi_set_state(wsi, LRS_ESTABLISHED);
#if defined(LWS_WITH_HTTP2)
if (lws_h2_configure_if_upgraded(wsi))
if (lws_tls_server_conn_alpn(wsi))
goto fail;
#endif
lwsl_debug("accepted new SSL conn\n");
break;

View file

@ -478,3 +478,37 @@ lws_gate_accepts(struct lws_context *context, int on)
return 0;
}
/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */
int
lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
{
uint8_t *oos = os, *plen = NULL;
while (*comma && len > 1) {
if (!plen && *comma == ' ') {
comma++;
continue;
}
if (!plen) {
plen = os++;
len--;
}
if (*comma == ',') {
*plen = lws_ptr_diff(os, plen + 1);
plen = NULL;
comma++;
} else {
*os++ = *comma++;
len--;
}
}
if (plen)
*plen = lws_ptr_diff(os, plen + 1);
return lws_ptr_diff(os, oos);
}

View file

@ -153,24 +153,13 @@ lws_try_client_connection(struct lws_client_connect_info *i, 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)
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_client_connect_info i;
struct lws_context *context;
unsigned long long start, next;
const char *p;
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,
@ -184,14 +173,13 @@ int main(int argc, char **argv)
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]);
staggered = !!lws_cmdline_option(argc, argv, "-s");
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
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");
lwsl_user(" [--h1 (http/1 only)] [-l (localhost)] [-d <logs>]\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
@ -218,14 +206,14 @@ int main(int argc, char **argv)
i.ssl_connection = LCCSCF_USE_SSL;
/* enables h1 or h2 connection sharing */
if (commandline_option(argc, argv, "-p"))
if (lws_cmdline_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 (lws_cmdline_option(argc, argv, "--h1"))
i.alpn = "http/1.1";
if (commandline_option(argc, argv, "-l")) {
if (lws_cmdline_option(argc, argv, "-l")) {
i.port = 7681;
i.address = "localhost";
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;

View file

@ -69,24 +69,26 @@ sigint_handler(int sig)
interrupted = 1;
}
int main(int argc, char **argv)
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_client_connect_info i;
struct lws_context *context;
int n = 0;
const char *p;
int n = 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 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);
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
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 ws client rx\n");
lws_set_log_level(logs, NULL);
lwsl_user("LWS minimal ws client rx [-d <logs>] [--h2]\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
@ -108,22 +110,25 @@ int main(int argc, char **argv)
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.port = 443;
i.address = "libwebsockets.org";
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.ssl_connection = LCCSCF_USE_SSL;
i.protocol = protocols[0].name; /* "dumb-increment-protocol" */
i.pwsi = &client_wsi;
if (lws_cmdline_option(argc, argv, "--h2"))
i.alpn = "h2";
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi && !interrupted)
n = lws_service(context, 1000);
lws_context_destroy(context);
lwsl_user("Completed\n");
return 0;