diff --git a/CMakeLists.txt b/CMakeLists.txt
index 260979cb6b5c98eeda2b6ea83a958f127071844b..3424c225f6b96a5edf135f9b192b43ba2a4543dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/lib/context.c b/lib/context.c
index ed8ff38cd13f91769f123061a52acb92e1e0bf12..8ef057da9be6561ed389585f1f89757151bd84e3 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -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
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 6c430b4d8b029d28af04900efe4402290a07e867..af8ad17228746393ec027c345618b3f9459f6320 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -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
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index d2c254a7f328a4b8a285e3239a4ca5ada3615d5c..13b0984fdf15a66f6bb69f54be42da989c99d8eb 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -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
diff --git a/lib/output.c b/lib/output.c
index c51e7c5af09598d34073e9660660b73e05332ffa..96c1ae4f467d1acc80930904ebaf84cdf98f842b 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -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);
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 2edcf9d6d25e45f2f83dfff8dfb0ec3fe2b1ec65..c1a2c3025d8e4f801fb1a3dea48df60c53c74a8b 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -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,16 +2652,8 @@ 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);
-
-/* 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
+/* HTTP2-related */
 
 #if LWS_MAX_SMP > 1
 
@@ -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
diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c
index 88210160ea3a11677cf2f8b4b61931cc806c432c..5e883aec7bcd94fc464b786e405ec0be96fc4d2b 100644
--- a/lib/roles/cgi/ops-cgi.c
+++ b/lib/roles/cgi/ops-cgi.c
@@ -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,
diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index 6e6d6142d11519e0c4310cf3418918ba2545c469..fd9c0382f9b7b79f0ac250321040ef711b081f96 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -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,
diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c
index ded3ba24e61808f8b160444206f5cbf8555b08b1..148c6cb37a1996d0ce69d991eed9c1f085fab3e8 100644
--- a/lib/roles/h2/ops-h2.c
+++ b/lib/roles/h2/ops-h2.c
@@ -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,
diff --git a/lib/roles/h2/ssl-http2.c b/lib/roles/h2/ssl-http2.c
deleted file mode 100644
index 5174b6f748667ca04a7213ce1220ea6e8c33f19f..0000000000000000000000000000000000000000
--- a/lib/roles/h2/ssl-http2.c
+++ /dev/null
@@ -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
diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c
index cbd8e740fd6f7484001ff17ecdbbe2fc35d6bc16..bf24bc263d47be27b4ae3ed273013977c61168e6 100644
--- a/lib/roles/http/client/client-handshake.c
+++ b/lib/roles/http/client/client-handshake.c
@@ -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)
diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c
index 5ffaf894a28d86659de61c19e4c5ceab989b81a3..4b60bf8a1fb0092b7aaeaa58d1673c9b22667728 100644
--- a/lib/roles/http/client/client.c
+++ b/lib/roles/http/client/client.c
@@ -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)) {
diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c
index 7a5799d2ed409717b58fe0dcc1a62c74cad5d84f..b41779d2b31638353cdd330e43f041b179cdbbd8 100644
--- a/lib/roles/http/server/lejp-conf.c
+++ b/lib/roles/http/server/lejp-conf.c
@@ -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;
 	}
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index 42398222ff114f8af61575bd315d2edc816762a5..ef0af5d7ce05718c56a19fcf82de0c9dbdeaf262 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -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...
diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c
index e9d4d147a16c98b8e6ec2824fb65b329ef95d94c..b61c615a5d188ad27915c43aaf30e08b89ff67bc 100644
--- a/lib/roles/listen/ops-listen.c
+++ b/lib/roles/listen/ops-listen.c
@@ -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,
diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c
index 8a573e7054107fa136127f9ddf182b521b6473af..aaccb5b5a9509e69227b07c5c9f4a2755136c20b 100644
--- a/lib/roles/pipe/ops-pipe.c
+++ b/lib/roles/pipe/ops-pipe.c
@@ -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,
diff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw/ops-raw.c
index 1afb4b9f23144002e6ea51000a7d018de645fa47..7921fd9d9d732ef487df28590b04961836757aa2 100644
--- a/lib/roles/raw/ops-raw.c
+++ b/lib/roles/raw/ops-raw.c
@@ -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,
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index 914b348fceda2bdd72395bc3c40ae70217fa821c..7e997d775aa17f99c39894c0e7126d1483985549 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -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,
diff --git a/lib/tls/mbedtls/mbedtls-client.c b/lib/tls/mbedtls/mbedtls-client.c
index 7f67473638087276db51126d1231c1076721010f..522332f0d2cc545654a2dbc9dcd182d9103119c5 100644
--- a/lib/tls/mbedtls/mbedtls-client.c
+++ b/lib/tls/mbedtls/mbedtls-client.c
@@ -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;
 	}
 
diff --git a/lib/tls/mbedtls/wrapper/library/ssl_lib.c b/lib/tls/mbedtls/wrapper/library/ssl_lib.c
index 0b13cca1b29db4593f9ee8c97ecbc66a699bf3d9..2f688ca9effd4220ec7dfaaba335b669e8df59b0 100644
--- a/lib/tls/mbedtls/wrapper/library/ssl_lib.c
+++ b/lib/tls/mbedtls/wrapper/library/ssl_lib.c
@@ -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 */
 }
 
diff --git a/lib/tls/openssl/openssl-client.c b/lib/tls/openssl/openssl-client.c
index 7317b437b1b11c7838ac9ed933050f4f070b2885..5c5bb489975524b1709abeebff6af1fe62b592d0 100644
--- a/lib/tls/openssl/openssl-client.c
+++ b/lib/tls/openssl/openssl-client.c
@@ -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;
 	}
 
diff --git a/lib/tls/tls-server.c b/lib/tls/tls-server.c
index c2a16f7dfa1ea212b2642fbb5a00941b07b48147..1a0f3edc0ee6df44958fff4bf47520369f77bd11 100644
--- a/lib/tls/tls-server.c
+++ b/lib/tls/tls-server.c
@@ -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;
 
diff --git a/lib/tls/tls.c b/lib/tls/tls.c
index 9333e39982edf9cc6a55722848f9a57cc930a2ec..3839464230dd5dd81e9dcf71015f1ef2411ff894 100644
--- a/lib/tls/tls.c
+++ b/lib/tls/tls.c
@@ -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);
+}
+
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 6e271036d4ce284ce66a08cddc4ba3bd522cad2e..7e87780d1505a785254a35bba1bd62434f6ab539 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
@@ -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;
diff --git a/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c b/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c
index a7ff86ae2902d81836b1766a18dcd0ea9b86d507..a367e19149f8eab776295a04edd43f3617a68fa1 100644
--- a/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c
+++ b/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c
@@ -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;