diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index f4018b18d86fd6b58c54894495c2888521bc10ae..eed9b5ef49f31d4acf043184c4bbd2256766f72e 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -19,28 +19,28 @@ lws_client_connect_2(struct lws *wsi)
 
 	/* proxy? */
 
-	if (context->http_proxy_port) {
+	if (wsi->vhost->http_proxy_port) {
 		plen = sprintf((char *)pt->serv_buf,
 			"CONNECT %s:%u HTTP/1.0\x0d\x0a"
 			"User-agent: libwebsockets\x0d\x0a",
 			lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
 			wsi->u.hdr.c_port);
 
-		if (context->proxy_basic_auth_token[0])
+		if (wsi->vhost->proxy_basic_auth_token[0])
 			plen += sprintf((char *)pt->serv_buf + plen,
 					"Proxy-authorization: basic %s\x0d\x0a",
-					context->proxy_basic_auth_token);
+					wsi->vhost->proxy_basic_auth_token);
 
 		plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
-		ads = context->http_proxy_address;
+		ads = wsi->vhost->http_proxy_address;
 
 #ifdef LWS_USE_IPV6
 		if (LWS_IPV6_ENABLED(context)) {
 			memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
-			server_addr6.sin6_port = htons(context->http_proxy_port);
+			server_addr6.sin6_port = htons(wsi->vhost->http_proxy_port);
 		} else
 #endif
-			server_addr4.sin_port = htons(context->http_proxy_port);
+			server_addr4.sin_port = htons(wsi->vhost->http_proxy_port);
 
 	} else {
 		ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
@@ -151,7 +151,7 @@ lws_client_connect_2(struct lws *wsi)
 			goto oom4;
 		}
 
-		if (lws_plat_set_socket_options(context, wsi->sock)) {
+		if (lws_plat_set_socket_options(wsi->vhost, wsi->sock)) {
 			lwsl_err("Failed to set wsi socket options\n");
 			compatible_close(wsi->sock);
 			goto oom4;
@@ -176,7 +176,7 @@ lws_client_connect_2(struct lws *wsi)
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
 				AWAITING_TIMEOUT);
 
-		n = lws_socket_bind(context, wsi->sock, 0, context->iface);
+		n = lws_socket_bind(context, wsi->sock, 0, wsi->vhost->iface);
 		if (n < 0)
 			goto failed;
 	}
@@ -222,7 +222,7 @@ lws_client_connect_2(struct lws *wsi)
 
 	/* we are connected to server, or proxy */
 
-	if (context->http_proxy_port) {
+	if (wsi->vhost->http_proxy_port) {
 
 		/*
 		 * OK from now on we talk via the proxy, so connect to that
@@ -231,9 +231,9 @@ lws_client_connect_2(struct lws *wsi)
 		 * leaving old string/frag there but unreferenced)
 		 */
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
-					  context->http_proxy_address))
+					  wsi->vhost->http_proxy_address))
 			goto failed;
-		wsi->u.hdr.c_port = context->http_proxy_port;
+		wsi->u.hdr.c_port = wsi->vhost->http_proxy_port;
 
 		n = send(wsi->sock, (char *)pt->serv_buf, plen,
 			 MSG_NOSIGNAL);
@@ -486,8 +486,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->position_in_fds_table = -1;
 	wsi->u.hdr.c_port = i->port;
+	wsi->vhost = i->vhost;
+	if (!wsi->vhost)
+		wsi->vhost = i->context->vhost_list;
 
-	wsi->protocol = &i->context->protocols[0];
+	wsi->protocol = &wsi->vhost->protocols[0];
 	if (wsi && !wsi->user_space && i->userdata) {
 		wsi->user_space_externally_allocated = 1;
 		wsi->user_space = i->userdata;
diff --git a/lib/client.c b/lib/client.c
index b8262f810f02fdbbe5ae3622bcfa09d2f963fbf1..d55d7f512e7ccf222fb3bd69928a20f5352860fa 100644
--- a/lib/client.c
+++ b/lib/client.c
@@ -154,7 +154,7 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
 						_WSI_TOKEN_CLIENT_HOST);
 #endif
 
-			wsi->ssl = SSL_new(context->ssl_client_ctx);
+			wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
 #ifndef USE_WOLFSSL
 			SSL_set_mode(wsi->ssl,
 					SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@@ -541,12 +541,12 @@ int
 lws_client_interpret_server_handshake(struct lws *wsi)
 {
 	int n, len, okay = 0, isErrorCodeReceived = 0, port = 0, ssl = 0;
-	struct lws_context *context = wsi->context;
 	int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
 	const char *pc, *prot, *ads = NULL, *path;
 	struct allocated_headers *ah;
 	char *p;
 #ifndef LWS_NO_EXTENSIONS
+	struct lws_context *context = wsi->context;
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	char *sb = (char *)&pt->serv_buf[0];
 	const struct lws_ext_options *opts;
@@ -739,7 +739,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 		 * no protocol name to work from,
 		 * default to first protocol
 		 */
-		wsi->protocol = &context->protocols[0];
+		wsi->protocol = &wsi->vhost->protocols[0];
 		goto check_extensions;
 	}
 
@@ -768,9 +768,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 	 */
 	n = 0;
 	wsi->protocol = NULL;
-	while (context->protocols[n].callback && !wsi->protocol) {
-		if (strcmp(p, context->protocols[n].name) == 0) {
-			wsi->protocol = &context->protocols[n];
+	while (wsi->vhost->protocols[n].callback && !wsi->protocol) {
+		if (strcmp(p, wsi->vhost->protocols[n].name) == 0) {
+			wsi->protocol = &wsi->vhost->protocols[n];
 			break;
 		}
 		n++;
@@ -838,7 +838,7 @@ check_extensions:
 		lwsl_notice("checking client ext %s\n", ext_name);
 
 		n = 0;
-		ext = lws_get_context(wsi)->extensions;
+		ext = wsi->vhost->extensions;
 		while (ext && ext->callback) {
 			if (strcmp(ext_name, ext->name)) {
 				ext++;
@@ -987,7 +987,7 @@ check_accept:
 	 * inform all extensions, not just active ones since they
 	 * already know
 	 */
-	ext = context->extensions;
+	ext = wsi->vhost->extensions;
 
 	while (ext && ext->callback) {
 		v = NULL;
@@ -1103,7 +1103,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
 		/* tell the server what extensions we could support */
 
 #ifndef LWS_NO_EXTENSIONS
-		ext = context->extensions;
+		ext = wsi->vhost->extensions;
 		while (ext && ext->callback) {
 			n = lws_ext_cb_all_exts(context, wsi,
 				   LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
@@ -1113,7 +1113,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
 				ext++;
 				continue;
 			}
-			n = context->protocols[0].callback(wsi,
+			n = wsi->vhost->protocols[0].callback(wsi,
 				LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
 					wsi->user_space, (char *)ext->name, 0);
 
@@ -1162,7 +1162,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
 
 	/* give userland a chance to append, eg, cookies */
 
-	context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+	wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
 				       NULL, &p, (pkt + LWS_MAX_SOCKET_IO_BUF) - p - 12);
 
 	p += sprintf(p, "\x0d\x0a");
diff --git a/lib/context.c b/lib/context.c
index 884e123b142625fd950b643e43149d52240001f3..b8d031b08ca4e8daf89d32046efbb97daeb75fb3 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -40,6 +40,160 @@ lws_get_library_version(void)
 	return library_version;
 }
 
+static const char * const mount_protocols[] = {
+	"http://",
+	"https://",
+	"file://",
+	"cgi://"
+};
+
+LWS_VISIBLE LWS_EXTERN int
+lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
+		     void *store, const char *mountpoint, const char *origin,
+		     const char *def)
+{
+	struct lws_http_mount *m;
+	void *orig = store;
+	unsigned long l = (unsigned long)store;
+	int n;
+
+	if (l & 15)
+		l += 16 - (l & 15);
+
+	store = (void *)l;
+	m = (struct lws_http_mount *)store;
+	*res = m;
+
+	m->def = def;
+	m->mountpoint = mountpoint;
+	m->mountpoint_len = (unsigned char)strlen(mountpoint);
+	m->mount_next = NULL;
+	if (next)
+		next->mount_next = m;
+	for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
+		if (!strncmp(origin, mount_protocols[n],
+		     strlen(mount_protocols[n]))) {
+			m->origin_protocol = n;
+			m->origin = origin + strlen(mount_protocols[n]);
+			break;
+		}
+
+	if (n == ARRAY_SIZE(mount_protocols)) {
+		lwsl_err("unsupported protocol://\n");
+		return 0; /* ie, fail */
+	}
+
+	return ((char *)store + sizeof(*m)) - (char *)orig;
+}
+
+LWS_VISIBLE struct lws_vhost *
+lws_create_vhost(struct lws_context *context,
+		 struct lws_context_creation_info *info,
+		 struct lws_http_mount *mounts)
+{
+	struct lws_vhost *vh = lws_zalloc(sizeof(*vh)),
+			 **vh1 = &context->vhost_list;
+	struct lws wsi;
+	char *p;
+	int n;
+
+	if (!vh)
+		return NULL;
+
+	vh->context = context;
+	if (!info->vhost_name)
+		vh->name = "default";
+	else
+		vh->name = info->vhost_name;
+
+	vh->iface = info->iface;
+	vh->protocols = info->protocols;
+	for (vh->count_protocols = 0;
+	     info->protocols[vh->count_protocols].callback;
+	     vh->count_protocols++)
+		;
+
+	vh->mount_list = mounts;
+
+	lwsl_notice("Creating Vhost '%s' port %d, %d protocols\n",
+			vh->name, info->port, vh->count_protocols);
+
+	while (mounts) {
+		lwsl_notice("   mounting %s%s to %s\n",
+				mount_protocols[mounts->origin_protocol],
+				mounts->origin, mounts->mountpoint);
+		mounts = mounts->mount_next;
+	}
+
+#ifndef LWS_NO_EXTENSIONS
+	vh->extensions = info->extensions;
+#endif
+
+	vh->listen_port = info->port;
+	vh->http_proxy_port = 0;
+	vh->http_proxy_address[0] = '\0';
+
+	/* either use proxy from info, or try get it from env var */
+
+	if (info->http_proxy_address) {
+		/* override for backwards compatibility */
+		if (info->http_proxy_port)
+			vh->http_proxy_port = info->http_proxy_port;
+		lws_set_proxy(vh, info->http_proxy_address);
+	} else {
+#ifdef LWS_HAVE_GETENV
+		p = getenv("http_proxy");
+		if (p)
+			lws_set_proxy(vh, p);
+#endif
+	}
+
+	memset(&wsi, 0, sizeof(wsi));
+	wsi.context = context;
+	wsi.vhost = vh;
+
+	/* initialize supported protocols */
+
+	for (n = 0; n < vh->count_protocols; n++)
+		/*
+		 * inform all the protocols that they are doing their one-time
+		 * initialization if they want to.
+		 *
+		 * NOTE the wsi is all zeros except for the context & vh ptrs
+		 * so lws_get_context(wsi) can work in the callback.
+		 */
+		info->protocols[n].callback(&wsi,
+				LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
+
+	vh->ka_time = info->ka_time;
+	vh->ka_interval = info->ka_interval;
+	vh->ka_probes = info->ka_probes;
+
+	if (lws_context_init_server_ssl(info, vh))
+		goto bail;
+
+	if (lws_context_init_client_ssl(info, vh))
+		goto bail;
+
+	if (lws_context_init_server(info, vh))
+		goto bail;
+
+	while (1) {
+		if (!(*vh1)) {
+			*vh1 = vh;
+			break;
+		}
+		vh1 = &(*vh1)->vhost_next;
+	};
+
+	return vh;
+
+bail:
+	lws_free(vh);
+
+	return NULL;
+}
+
 /**
  * lws_create_context() - Create the websocket handler
  * @info:	pointer to struct with parameters
@@ -77,7 +231,6 @@ lws_create_context(struct lws_context_creation_info *info)
 #ifndef LWS_NO_DAEMONIZE
 	int pid_daemon = get_daemonize_pid();
 #endif
-	char *p;
 	int n, m;
 
 	lwsl_notice("Initial logging level %d\n", log_level);
@@ -92,6 +245,7 @@ lws_create_context(struct lws_context_creation_info *info)
 	lwsl_notice("IPV6 not compiled in\n");
 #endif
 	lws_feature_status_libev(info);
+	lws_feature_status_libuv(info);
 #endif
 	lwsl_info(" LWS_DEF_HEADER_LEN    : %u\n", LWS_DEF_HEADER_LEN);
 	lwsl_info(" LWS_MAX_PROTOCOLS     : %u\n", LWS_MAX_PROTOCOLS);
@@ -125,16 +279,10 @@ lws_create_context(struct lws_context_creation_info *info)
 	if (context->count_threads > LWS_MAX_SMP)
 		context->count_threads = LWS_MAX_SMP;
 
-	context->protocols = info->protocols;
 	context->token_limits = info->token_limits;
-	context->listen_port = info->port;
-	context->http_proxy_port = 0;
-	context->http_proxy_address[0] = '\0';
+
 	context->options = info->options;
-	context->iface = info->iface;
-	context->ka_time = info->ka_time;
-	context->ka_interval = info->ka_interval;
-	context->ka_probes = info->ka_probes;
+
 	if (info->timeout_secs)
 		context->timeout_secs = info->timeout_secs;
 	else
@@ -253,6 +401,18 @@ lws_create_context(struct lws_context_creation_info *info)
 	if (lws_plat_init(context, info))
 		goto bail;
 
+	lws_context_init_ssl_library(info);
+
+	/*
+	 * if he's not saying he'll make his own vhosts later then act
+	 * compatibly and make a default vhost using the data in the info
+	 */
+	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
+		if (!lws_create_vhost(context, info, NULL)) {
+			lwsl_err("Failed to create default vhost\n");
+			return NULL;
+		}
+
 	lws_context_init_extensions(info, context);
 
 	context->user_space = info->user;
@@ -263,51 +423,16 @@ lws_create_context(struct lws_context_creation_info *info)
 	strcpy(context->canonical_hostname, "unknown");
 	lws_server_get_canonical_hostname(context, info);
 
-	/* either use proxy from info, or try get it from env var */
-
-	if (info->http_proxy_address) {
-		/* override for backwards compatibility */
-		if (info->http_proxy_port)
-			context->http_proxy_port = info->http_proxy_port;
-		lws_set_proxy(context, info->http_proxy_address);
-	} else {
-#ifdef LWS_HAVE_GETENV
-		p = getenv("http_proxy");
-		if (p)
-			lws_set_proxy(context, p);
-#endif
-	}
-
-	if (lws_context_init_server_ssl(info, context))
-		goto bail;
-
-	if (lws_context_init_client_ssl(info, context))
-		goto bail;
-
-	if (lws_context_init_server(info, context))
-		goto bail;
+	context->uid = info->uid;
+	context->gid = info->gid;
 
 	/*
 	 * drop any root privs for this process
 	 * to listen on port < 1023 we would have needed root, but now we are
 	 * listening, we don't want the power for anything else
 	 */
-	lws_plat_drop_app_privileges(info);
-
-	/* initialize supported protocols */
-
-	for (context->count_protocols = 0;
-	     info->protocols[context->count_protocols].callback;
-	     context->count_protocols++)
-		/*
-		 * inform all the protocols that they are doing their one-time
-		 * initialization if they want to.
-		 *
-		 * NOTE the wsi is all zeros except for the context pointer
-		 * so lws_get_context(wsi) can work in the callback.
-		 */
-		info->protocols[context->count_protocols].callback(&wsi,
-				LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
+	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
+		lws_plat_drop_app_privileges(info);
 
 	/*
 	 * give all extensions a chance to create any per-context
@@ -342,6 +467,7 @@ lws_context_destroy(struct lws_context *context)
 {
 	const struct lws_protocols *protocol = NULL;
 	struct lws_context_per_thread *pt;
+	struct lws_vhost *vh;
 	struct lws wsi;
 	int n, m;
 
@@ -390,13 +516,18 @@ lws_context_destroy(struct lws_context *context)
 	 * inform all the protocols that they are done and will have no more
 	 * callbacks
 	 */
-	protocol = context->protocols;
-	if (protocol)
-		while (protocol->callback) {
-			protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
-					   NULL, NULL, 0);
-			protocol++;
-		}
+	vh = context->vhost_list;
+	while (vh) {
+		protocol = vh->protocols;
+		if (protocol)
+			while (protocol->callback) {
+				protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
+						   NULL, NULL, 0);
+				protocol++;
+			}
+		lws_ssl_SSL_CTX_destroy(vh);
+		vh = vh->vhost_next;
+	}
 
 	for (n = 0; n < context->count_threads; n++) {
 		pt = &context->pt[n];
@@ -412,6 +543,7 @@ lws_context_destroy(struct lws_context *context)
 	}
 	lws_plat_context_early_destroy(context);
 	lws_ssl_context_destroy(context);
+
 	if (context->pt[0].fds)
 		lws_free_set_NULL(context->pt[0].fds);
 
diff --git a/lib/extension-permessage-deflate.c b/lib/extension-permessage-deflate.c
index 6ae348a6e3ee2cdf2e9cb8025277536bca35d1aa..322f4688e730004ac3d1e4b8bc4dab32741a9816 100644
--- a/lib/extension-permessage-deflate.c
+++ b/lib/extension-permessage-deflate.c
@@ -275,7 +275,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
 			if (deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
 					 Z_DEFLATED,
 					 -priv->args[PMD_CLIENT_MAX_WINDOW_BITS +
-						     !wsi->context->listen_port],
+						     !wsi->vhost->listen_port],
 					 priv->args[PMD_MEM_LEVEL],
 					 Z_DEFAULT_STRATEGY) != Z_OK) {
 				lwsl_ext("inflateInit2 failed\n");
diff --git a/lib/extension.c b/lib/extension.c
index f093850a8aa065690365226a092369b7058f0cc5..df24dd5514796e8f1b9d8ca25e42b1880525c926 100644
--- a/lib/extension.c
+++ b/lib/extension.c
@@ -6,7 +6,6 @@ LWS_VISIBLE void
 lws_context_init_extensions(struct lws_context_creation_info *info,
 			    struct lws_context *context)
 {
-	context->extensions = info->extensions;
 	lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
 }
 
@@ -185,7 +184,12 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
 			int reason, void *arg, int len)
 {
 	int n = 0, m, handled = 0;
-	const struct lws_extension *ext = context->extensions;
+	const struct lws_extension *ext;
+
+	if (!wsi || !wsi->vhost)
+		return 0;
+
+	ext = wsi->vhost->extensions;
 
 	while (ext && ext->callback && !handled) {
 		m = ext->callback(context, ext, wsi, reason,
diff --git a/lib/http2.c b/lib/http2.c
index a2aa3f920311d456d5fa3ecb8def44796449881b..224668bda42a209c410766082f5cf20d79b79234 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -52,10 +52,10 @@ lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid)
 }
 
 struct lws *
-lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi,
+lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
 			    unsigned int sid)
 {
-	struct lws *wsi = lws_create_new_server_wsi(context);
+	struct lws *wsi = lws_create_new_server_wsi(vhost);
 
 	if (!wsi)
 		return NULL;
@@ -82,7 +82,7 @@ lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi,
 	wsi->state = LWSS_HTTP2_ESTABLISHED;
 	wsi->mode = parent_wsi->mode;
 
-	wsi->protocol = &context->protocols[0];
+	wsi->protocol = &vhost->protocols[0];
 	lws_ensure_user_space(wsi);
 
 	lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__,
@@ -198,7 +198,6 @@ static const char * https_client_preface =
 int
 lws_http2_parser(struct lws *wsi, unsigned char c)
 {
-	struct lws_context *context = wsi->context;
 	struct lws *swsi;
 	int n;
 
@@ -378,7 +377,8 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
 				if (!wsi->u.http2.stream_id)
 					return 1;
 				if (!wsi->u.http2.stream_wsi)
-					wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, wsi->u.http2.stream_id);
+					wsi->u.http2.stream_wsi =
+						lws_create_server_child_wsi(wsi->vhost, wsi, wsi->u.http2.stream_id);
 
 				/* END_STREAM means after servicing this, close the stream */
 				wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM);
@@ -474,7 +474,8 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
 			 */
 			lwsl_info("%s: setting up sid 1\n", __func__);
 
-			swsi = wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, 1);
+			swsi = wsi->u.http2.stream_wsi =
+					lws_create_server_child_wsi(wsi->vhost, wsi, 1);
 			/* pass on the initial headers to SID 1 */
 			swsi->u.http.ah = wsi->u.http.ah;
 			wsi->u.http.ah = NULL;
diff --git a/lib/libev.c b/lib/libev.c
index 17430936b97407de8ba77fe8f27127ac00d63bbe..cb8c3c514a54a27760d45d8fec6531e5cacb44b5 100644
--- a/lib/libev.c
+++ b/lib/libev.c
@@ -78,6 +78,7 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
 {
 	struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher;
 	struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher;
+	struct lws_vhost *vh = context->vhost_list;
 	const char * backend_name;
 	int status = 0;
 	int backend;
@@ -90,10 +91,17 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
 	context->pt[tsi].io_loop_ev = loop;
 
 	/*
-	 * Initialize the accept w_accept with the listening socket
+	 * Initialize the accept w_accept with all the listening sockets
 	 * and register a callback for read operations
 	 */
-	ev_io_init(w_accept, lws_accept_cb, context->pt[tsi].lserv_fd, EV_READ);
+	while (vh) {
+		if (vh->lserv_wsi) {
+			vh->lserv_wsi->w_read.context = context;
+			ev_io_init(w_accept, lws_accept_cb, vh->lserv_wsi->sock,
+				  EV_READ);
+		}
+		vh = vh->vhost_next;
+	}
 	ev_io_start(context->pt[tsi].io_loop_ev, w_accept);
 
 	/* Register the signal watcher unless the user says not to */
diff --git a/lib/libuv.c b/lib/libuv.c
index 92d44eea4e97f9858d2ccc84e9f461e7fac219b8..e74e81db7b07b7ef209d22a57528ef9be47167f7 100644
--- a/lib/libuv.c
+++ b/lib/libuv.c
@@ -102,7 +102,7 @@ LWS_VISIBLE int
 lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
 {
 	struct lws_context_per_thread *pt = &context->pt[tsi];
-	struct lws *wsi = wsi_from_fd(context, pt->lserv_fd);
+	struct lws_vhost *vh = context->vhost_list;
 	int status = 0, n;
 
 	if (!loop) {
@@ -119,23 +119,28 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
 		for (n = 0; n < ARRAY_SIZE(sigs); n++) {
 			uv_signal_init(loop, &pt->signals[n]);
 			pt->signals[n].data = pt->context;
-			uv_signal_start(&pt->signals[n], context->lws_uv_sigint_cb, sigs[n]);
+			uv_signal_start(&pt->signals[n],
+					context->lws_uv_sigint_cb, sigs[n]);
 		}
 	}
 
 	/*
-	 * Initialize the accept wsi read watcher with the listening socket
+	 * Initialize the accept wsi read watcher with all the listening sockets
 	 * and register a callback for read operations
 	 *
 	 * We have to do it here because the uv loop(s) are not
 	 * initialized until after context creation.
 	 */
-	if (wsi) {
-		wsi->w_read.context = context;
-		uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
-			     pt->lserv_fd);
-		uv_poll_start(&wsi->w_read.uv_watcher, UV_READABLE,
-			      lws_io_cb);
+	while (vh) {
+		if (vh->lserv_wsi) {
+			vh->lserv_wsi->w_read.context = context;
+			uv_poll_init(pt->io_loop_uv,
+				     &vh->lserv_wsi->w_read.uv_watcher,
+				     vh->lserv_wsi->sock);
+			uv_poll_start(&vh->lserv_wsi->w_read.uv_watcher,
+				      UV_READABLE, lws_io_cb);
+		}
+		vh = vh->vhost_next;
 	}
 
 	uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 5e08b9988ba810654f6c62a086de89e6e1ecc2db..b83b31437f9e885e46939a7dda889e68a6a97510 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -194,8 +194,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 	    wsi->u.http.fd != LWS_INVALID_FILE) {
 		lws_plat_file_close(wsi, wsi->u.http.fd);
 		wsi->u.http.fd = LWS_INVALID_FILE;
-		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
-					       wsi->user_space, NULL, 0);
+		wsi->vhost->protocols[0].callback(wsi,
+			LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
 	}
 	if (wsi->socket_is_permanently_unusable ||
 	    reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
@@ -234,10 +234,10 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 		goto just_kill_connection;
 
 	if (wsi->mode == LWSCM_HTTP_SERVING)
-		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+		wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
 					       wsi->user_space, NULL, 0);
 	if (wsi->mode == LWSCM_HTTP_CLIENT)
-		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
+		wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
 					       wsi->user_space, NULL, 0);
 
 	/*
@@ -468,12 +468,12 @@ just_kill_connection:
 					wsi->user_space, NULL, 0);
 	} else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
 		lwsl_debug("calling back CLOSED_HTTP\n");
-		context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+		wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
 					       wsi->user_space, NULL, 0 );
 	} else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
 		   wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
 		lwsl_debug("Connection closed before server reply\n");
-		context->protocols[0].callback(wsi,
+		wsi->vhost->protocols[0].callback(wsi,
 					LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 					wsi->user_space, NULL, 0);
 	} else
@@ -525,7 +525,7 @@ lws_close_free_wsi_final(struct lws *wsi)
 	}
 
 	/* outermost destroy notification for wsi (user_space still intact) */
-	wsi->context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
+	wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
 				       wsi->user_space, NULL, 0);
 
 	lws_free_wsi(wsi);
@@ -903,7 +903,7 @@ int user_callback_handle_rxflow(lws_callback_function callback_function,
  */
 
 LWS_VISIBLE int
-lws_set_proxy(struct lws_context *context, const char *proxy)
+lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
 {
 	char *p;
 	char authstring[96];
@@ -920,35 +920,35 @@ lws_set_proxy(struct lws_context *context, const char *proxy)
 		strncpy(authstring, proxy, p - proxy);
 		// null termination not needed on input
 		if (lws_b64_encode_string(authstring, (p - proxy),
-		    context->proxy_basic_auth_token,
-		    sizeof context->proxy_basic_auth_token) < 0)
+				vhost->proxy_basic_auth_token,
+		    sizeof vhost->proxy_basic_auth_token) < 0)
 			goto auth_too_long;
 
 		lwsl_notice(" Proxy auth in use\n");
 
 		proxy = p + 1;
 	} else
-		context->proxy_basic_auth_token[0] = '\0';
+		vhost->proxy_basic_auth_token[0] = '\0';
 
-	strncpy(context->http_proxy_address, proxy,
-				sizeof(context->http_proxy_address) - 1);
-	context->http_proxy_address[
-				sizeof(context->http_proxy_address) - 1] = '\0';
+	strncpy(vhost->http_proxy_address, proxy,
+				sizeof(vhost->http_proxy_address) - 1);
+	vhost->http_proxy_address[
+				sizeof(vhost->http_proxy_address) - 1] = '\0';
 
-	p = strchr(context->http_proxy_address, ':');
-	if (!p && !context->http_proxy_port) {
+	p = strchr(vhost->http_proxy_address, ':');
+	if (!p && !vhost->http_proxy_port) {
 		lwsl_err("http_proxy needs to be ads:port\n");
 
 		return -1;
 	} else {
 		if (p) {
 			*p = '\0';
-			context->http_proxy_port = atoi(p + 1);
+			vhost->http_proxy_port = atoi(p + 1);
 		}
 	}
 
-	lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address,
-						context->http_proxy_port);
+	lwsl_notice(" Proxy %s:%u\n", vhost->http_proxy_address,
+			vhost->http_proxy_port);
 
 	return 0;
 
@@ -1469,8 +1469,8 @@ lws_socket_bind(struct lws_context *context, int sockfd, int port,
 
 	n = bind(sockfd, v, n);
 	if (n < 0) {
-		lwsl_err("ERROR on binding to port %d (%d %d)\n",
-					      port, n, LWS_ERRNO);
+		lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n",
+				sockfd, port, n, LWS_ERRNO);
 		return -1;
 	}
 
@@ -1512,6 +1512,21 @@ lws_urlencode(const char *in, int inlen, char *out, int outlen)
 	return out - start;
 }
 
+LWS_VISIBLE LWS_EXTERN int
+lws_finalize_startup(struct lws_context *context)
+{
+	struct lws_context_creation_info info;
+
+	info.uid = context->uid;
+	info.gid = context->gid;
+
+	if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
+		lws_plat_drop_app_privileges(&info);
+
+	return 0;
+}
+
+
 LWS_VISIBLE LWS_EXTERN int
 lws_is_cgi(struct lws *wsi) {
 #ifdef LWS_WITH_CGI
@@ -1555,10 +1570,10 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
 	/*
 	 * these can only be set once the protocol is known
 	 * we set an unestablished connection's protocol pointer
-	 * to the start of the supported list, so it can look
+	 * to the start of the defauly vhost supported list, so it can look
 	 * for matching ones during the handshake
 	 */
-	new_wsi->protocol = context->protocols;
+	new_wsi->protocol = context->vhost_list->protocols;
 	new_wsi->user_space = NULL;
 	new_wsi->ietf_spec_revision = 0;
 	new_wsi->sock = LWS_SOCK_INVALID;
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index c6359ed2cfbd5d4d2b640971afc2ab71f67371e2..939843e2c1890f9bc156f989314eb2d343183e1b 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -307,6 +307,7 @@ enum lws_context_options {
 								  (1 << 3) |
 								  (1 << 12),
 	LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT			= (1 << 12),
+	LWS_SERVER_OPTION_EXPLICIT_VHOSTS			= (1 << 13),
 
 	/****** add new things just above ---^ ******/
 };
@@ -1292,6 +1293,13 @@ extern int lws_extension_callback_pm_deflate(
 /**
  * struct lws_context_creation_info - parameters to create context with
  *
+ * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS
+ * is not given, then for backwards compatibility one vhost is created at
+ * context-creation time using the info from this struct.
+ *
+ * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created
+ * at the same time as the context, they are expected to be created afterwards.
+ *
  * @port:	Port to listen on... you can use CONTEXT_PORT_NO_LISTEN to
  *		suppress listening on any port, that's what you want if you are
  *		not running a websocket server at all but just using it as a
@@ -1359,22 +1367,22 @@ extern int lws_extension_callback_pm_deflate(
  */
 
 struct lws_context_creation_info {
-	int port;
-	const char *iface;
-	const struct lws_protocols *protocols;
-	const struct lws_extension *extensions;
+	int port;					/* VH */
+	const char *iface;				/* VH */
+	const struct lws_protocols *protocols;		/* VH */
+	const struct lws_extension *extensions;		/* VH */
 	const struct lws_token_limits *token_limits;
-	const char *ssl_private_key_password;
-	const char *ssl_cert_filepath;
-	const char *ssl_private_key_filepath;
-	const char *ssl_ca_filepath;
-	const char *ssl_cipher_list;
-	const char *http_proxy_address;
-	unsigned int http_proxy_port;
-	int gid;
-	int uid;
-	unsigned int options;
-	void *user;
+	const char *ssl_private_key_password;		/* VH */
+	const char *ssl_cert_filepath;			/* VH */
+	const char *ssl_private_key_filepath;		/* VH */
+	const char *ssl_ca_filepath;			/* VH */
+	const char *ssl_cipher_list;			/* VH */
+	const char *http_proxy_address;			/* VH */
+	unsigned int http_proxy_port;			/* VH */
+	int gid;					/* context */
+	int uid;					/* context */
+	unsigned int options;				/* context */
+	void *user;					/* context */
 	int ka_time;
 	int ka_probes;
 	int ka_interval;
@@ -1387,10 +1395,11 @@ struct lws_context_creation_info {
 	short max_http_header_data;
 	short max_http_header_pool;
 
-	unsigned int count_threads;
-	unsigned int fd_limit_per_thread;
-	unsigned int timeout_secs;
-	const char *ecdh_curve;
+	unsigned int count_threads;			/* context */
+	unsigned int fd_limit_per_thread;		/* context */
+	unsigned int timeout_secs;			/* VH */
+	const char *ecdh_curve;				/* VH */
+	const char *vhost_name;				/* VH */
 
 	/* Add new things just above here ---^
 	 * This is part of the ABI, don't needlessly break compatibility
@@ -1426,6 +1435,7 @@ struct lws_context_creation_info {
  * @uri_replace_from: if non-NULL, when this string is found in URIs in
  *		text/html content-encoding, it's replaced with @uri_replace_to
  * @uri_replace_to: see above
+ * @vhost:	vhost to bind to (used to determine related SSL_CTX)
  */
 
 struct lws_client_connect_info {
@@ -1444,6 +1454,7 @@ struct lws_client_connect_info {
 	struct lws *parent_wsi;
 	const char *uri_replace_from;
 	const char *uri_replace_to;
+	struct lws_vhost *vhost;
 
 	/* Add new things just above here ---^
 	 * This is part of the ABI, don't needlessly break compatibility
@@ -1456,6 +1467,13 @@ struct lws_client_connect_info {
 	void *_unused[4];
 };
 
+struct lws_http_mount;
+
+LWS_VISIBLE LWS_EXTERN int
+lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
+		     void *store, const char *mountpoint, const char *origin,
+		     const char *def);
+
 LWS_VISIBLE LWS_EXTERN void
 lws_set_log_level(int level,
 		  void (*log_emit_function)(int level, const char *line));
@@ -1466,8 +1484,18 @@ lwsl_emit_syslog(int level, const char *line);
 LWS_VISIBLE LWS_EXTERN struct lws_context *
 lws_create_context(struct lws_context_creation_info *info);
 
+struct lws_vhost;
+
+LWS_VISIBLE struct lws_vhost *
+lws_create_vhost(struct lws_context *context,
+		 struct lws_context_creation_info *info,
+		 struct lws_http_mount *mounts);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_finalize_startup(struct lws_context *context);
+
 LWS_VISIBLE LWS_EXTERN int
-lws_set_proxy(struct lws_context *context, const char *proxy);
+lws_set_proxy(struct lws_vhost *vhost, const char *proxy);
 
 LWS_VISIBLE LWS_EXTERN void
 lws_context_destroy(struct lws_context *context);
diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c
index 48a89703d082a8545012c3d136fd5c4fcdf2c887..a9e8c88e39e4b8e80f7414ce88c940fc46d9d4c4 100644
--- a/lib/lws-plat-unix.c
+++ b/lib/lws-plat-unix.c
@@ -127,7 +127,7 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 
 	/* stay dead once we are dead */
 
-	if (!context)
+	if (!context || !context->vhost_list)
 		return 1;
 
 	lws_libev_run(context, tsi);
@@ -139,7 +139,7 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 		memset(&_lws, 0, sizeof(_lws));
 		_lws.context = context;
 
-		context->service_tid_detected = context->protocols[0].callback(
+		context->service_tid_detected = context->vhost_list->protocols[0].callback(
 			&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
 	}
 	context->service_tid = context->service_tid_detected;
@@ -200,7 +200,7 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
 }
 
 LWS_VISIBLE int
-lws_plat_set_socket_options(struct lws_context *context, int fd)
+lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
 {
 	int optval = 1;
 	socklen_t optlen = sizeof(optval);
@@ -212,7 +212,7 @@ lws_plat_set_socket_options(struct lws_context *context, int fd)
 	struct protoent *tcp_proto;
 #endif
 
-	if (context->ka_time) {
+	if (vhost->ka_time) {
 		/* enable keepalive on this socket */
 		optval = 1;
 		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
@@ -230,17 +230,17 @@ lws_plat_set_socket_options(struct lws_context *context, int fd)
 		 */
 #else
 		/* set the keepalive conditions we want on it too */
-		optval = context->ka_time;
+		optval = vhost->ka_time;
 		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
 			       (const void *)&optval, optlen) < 0)
 			return 1;
 
-		optval = context->ka_interval;
+		optval = vhost->ka_interval;
 		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
 			       (const void *)&optval, optlen) < 0)
 			return 1;
 
-		optval = context->ka_probes;
+		optval = vhost->ka_probes;
 		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
 			       (const void *)&optval, optlen) < 0)
 			return 1;
@@ -283,7 +283,7 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
 			if (setuid(info->uid))
 				lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
 			else
-				lwsl_notice(" Set privs to user '%s'\n", p->pw_name);
+				lwsl_notice("Set privs to user '%s'\n", p->pw_name);
 		} else
 			lwsl_warn("getpwuid: unable to find uid %d", info->uid);
 	}
diff --git a/lib/lws-plat-win.c b/lib/lws-plat-win.c
index 428ae7cd104280c26949012022e645f09090f9e9..751771c47b77f2c96c4769ef29e682edef6c8c3b 100644
--- a/lib/lws-plat-win.c
+++ b/lib/lws-plat-win.c
@@ -171,28 +171,30 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 		memset(&_lws, 0, sizeof(_lws));
 		_lws.context = context;
 
-		context->service_tid_detected = context->protocols[0].callback(
-			&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
+		context->service_tid_detected = context->vhost_list->
+			protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID,
+					      NULL, NULL, 0);
 	}
 	context->service_tid = context->service_tid_detected;
 
 	for (i = 0; i < pt->fds_count; ++i) {
 		pfd = &pt->fds[i];
-		if (pfd->fd == pt->lserv_fd)
+
+		if (!(pfd->events & LWS_POLLOUT))
 			continue;
 
-		if (pfd->events & LWS_POLLOUT) {
-			wsi = wsi_from_fd(context, pfd->fd);
-			if (!wsi || wsi->sock_send_blocking)
-				continue;
-			pfd->revents = LWS_POLLOUT;
-			n = lws_service_fd(context, pfd);
-			if (n < 0)
-				return -1;
-			/* if something closed, retry this slot */
-			if (n)
-				i--;
-		}
+		wsi = wsi_from_fd(context, pfd->fd);
+		if (wsi->listener)
+			continue;
+		if (!wsi || wsi->sock_send_blocking)
+			continue;
+		pfd->revents = LWS_POLLOUT;
+		n = lws_service_fd(context, pfd);
+		if (n < 0)
+			return -1;
+		/* if something closed, retry this slot */
+		if (n)
+			i--;
 	}
 
 	/* if we know something needs service already, don't wait in poll */
@@ -264,7 +266,7 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
 }
 
 LWS_VISIBLE int
-lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
+lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
 {
 	int optval = 1;
 	int optlen = sizeof(optval);
@@ -276,7 +278,7 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
 	struct protoent *tcp_proto;
 #endif
 
-	if (context->ka_time) {
+	if (vhost->ka_time) {
 		/* enable keepalive on this socket */
 		optval = 1;
 		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
@@ -284,8 +286,8 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
 			return 1;
 
 		alive.onoff = TRUE;
-		alive.keepalivetime = context->ka_time;
-		alive.keepaliveinterval = context->ka_interval;
+		alive.keepalivetime = vhost->ka_time;
+		alive.keepaliveinterval = vhost->ka_interval;
 
 		if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
 					      NULL, 0, &dwBytesRet, NULL, NULL))
diff --git a/lib/pollfd.c b/lib/pollfd.c
index 65a28e4155e30b28e614b717730ad9538f7e1c33..1d359bda8c54cc9b1ab50986b74046e16367887f 100644
--- a/lib/pollfd.c
+++ b/lib/pollfd.c
@@ -43,7 +43,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 	pa->prev_events = pfd->events;
 	pa->events = pfd->events = (pfd->events & ~_and) | _or;
 
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
 					   wsi->user_space, (void *)pa, 0)) {
 		ret = -1;
 		goto bail;
@@ -86,7 +86,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 
 		sampled_tid = context->service_tid;
 		if (sampled_tid) {
-			tid = context->protocols[0].callback(wsi,
+			tid = wsi->vhost->protocols[0].callback(wsi,
 				     LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
 			if (tid == -1) {
 				ret = -1;
@@ -114,7 +114,8 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 		  __func__, wsi, wsi->tsi, wsi->sock, pt->fds_count);
 
 	if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) {
-		lwsl_err("Too many fds (%d)\n", context->max_fds);
+		lwsl_err("Too many fds (%d vs %d)\n", context->max_fds,
+				context->fd_limit_per_thread	);
 		return 1;
 	}
 
@@ -127,9 +128,10 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 #endif
 
 	assert(wsi);
+	assert(wsi->vhost);
 	assert(lws_socket_is_valid(wsi->sock));
 
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
 					   wsi->user_space, (void *) &pa, 1))
 		return -1;
 
@@ -144,7 +146,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 	lws_plat_insert_socket_into_fds(context, wsi);
 
 	/* external POLL support via protocol 0 */
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
 					   wsi->user_space, (void *) &pa, 0))
 		ret =  -1;
 #ifndef LWS_NO_SERVER
@@ -154,7 +156,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 #endif
 	lws_pt_unlock(pt);
 
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
 					   wsi->user_space, (void *)&pa, 1))
 		ret = -1;
 
@@ -180,7 +182,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 	}
 #endif
 
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
 					   wsi->user_space, (void *)&pa, 1))
 		return -1;
 
@@ -213,7 +215,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 
 	/* remove also from external POLL support via protocol 0 */
 	if (lws_socket_is_valid(wsi->sock))
-		if (context->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
+		if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
 						   wsi->user_space, (void *) &pa, 0))
 			ret = -1;
 #ifndef LWS_NO_SERVER
@@ -224,7 +226,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 #endif
 	lws_pt_unlock(pt);
 
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
 					   wsi->user_space, (void *) &pa, 1))
 		ret = -1;
 
@@ -246,7 +248,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or)
 	if (!context)
 		return 1;
 
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
 					   wsi->user_space,  (void *) &pa, 0))
 		return -1;
 
@@ -255,7 +257,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or)
 	lws_pt_lock(pt);
 	ret = _lws_change_pollfd(wsi, _and, _or, &pa);
 	lws_pt_unlock(pt);
-	if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
 					   wsi->user_space, (void *) &pa, 0))
 		ret = -1;
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 58e8b707afe3d8dda54681a30e78f9982ba74015..125f89270be0ea7651abe1d1d9d80ae129ea2c32 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -581,7 +581,6 @@ struct lws_context_per_thread {
 	struct lws_signal_watcher w_sigint;
 	unsigned char ev_loop_foreign:1;
 #endif
-	lws_sockfd_type lserv_fd;
 
 	unsigned long count_conns;
 	/*
@@ -601,13 +600,71 @@ struct lws_context_per_thread {
 	unsigned char tid;
 };
 
+struct lws_http_mount {
+	struct lws_http_mount *mount_next;
+	const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
+	const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
+	const char *def; /* default target, eg, "index.html" */
+
+	unsigned char origin_protocol;
+	unsigned char mountpoint_len;
+};
+
+/*
+ * virtual host -related context information
+ *   vhostwide SSL context
+ *   vhostwide proxy
+ *
+ * heirarchy:
+ *
+ * context -> vhost -> wsi
+ *
+ * incoming connection non-SSL vhost binding:
+ *
+ *    listen socket -> wsi -> select vhost after first headers
+ *
+ * incoming connection SSL vhost binding:
+ *
+ *    SSL SNI -> wsi -> bind after SSL negotiation
+ */
+
+struct lws_vhost {
+	char http_proxy_address[128];
+	char proxy_basic_auth_token[128];
+	struct lws_context *context;
+	struct lws_vhost *vhost_next;
+	struct lws_http_mount *mount_list;
+	struct lws *lserv_wsi;
+	const char *name;
+	const char *iface;
+	const struct lws_protocols *protocols;
+#ifdef LWS_OPENSSL_SUPPORT
+	SSL_CTX *ssl_ctx;
+	SSL_CTX *ssl_client_ctx;
+#endif
+#ifndef LWS_NO_EXTENSIONS
+	const struct lws_extension *extensions;
+#endif
+
+	int listen_port;
+	unsigned int http_proxy_port;
+	int count_protocols;
+	int ka_time;
+	int ka_probes;
+	int ka_interval;
+
+#ifdef LWS_OPENSSL_SUPPORT
+	int use_ssl;
+	int allow_non_ssl_on_ssl_port;
+	unsigned int user_supplied_ssl_ctx:1;
+#endif
+};
+
 /*
  * the rest is managed per-context, that includes
  *
  *  - processwide single fd -> wsi lookup
  *  - contextwide headers pool
- *  - contextwide ssl context
- *  - contextwide proxy
  */
 
 struct lws_context {
@@ -620,27 +677,16 @@ struct lws_context {
 #else
 	struct lws **lws_lookup;  /* fd to wsi */
 #endif
-	const char *iface;
+	struct lws_vhost *vhost_list;
 	const struct lws_token_limits *token_limits;
 	void *user_space;
 
-	const struct lws_protocols *protocols;
-
-#ifdef LWS_OPENSSL_SUPPORT
-	SSL_CTX *ssl_ctx;
-	SSL_CTX *ssl_client_ctx;
-#endif
-#ifndef LWS_NO_EXTENSIONS
-	const struct lws_extension *extensions;
-#endif
 #if defined(LWS_USE_LIBEV)
 	lws_ev_signal_cb_t * lws_ev_sigint_cb;
 #endif
 #if defined(LWS_USE_LIBUV)
 	uv_signal_cb lws_uv_sigint_cb;
 #endif
-	char http_proxy_address[128];
-	char proxy_basic_auth_token[128];
 	char canonical_hostname[128];
 #ifdef LWS_LATENCY
 	unsigned long worst_latency;
@@ -648,16 +694,25 @@ struct lws_context {
 #endif
 
 	int max_fds;
-	int listen_port;
 #if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV)
 	int use_ev_sigint;
 #endif
 	int started_with_parent;
+	int uid, gid;
 
 	int fd_random;
-	int lserv_mod;
+#ifdef LWS_OPENSSL_SUPPORT
+#define lws_ssl_anybody_has_buffered_read(w) \
+		(w->vhost->use_ssl && \
+		 w->context->pt[(int)w->tsi].pending_read_list)
+#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \
+		(/*c->use_ssl && */ \
+		 c->pt[(int)t].pending_read_list)
+#else
+#define lws_ssl_anybody_has_buffered_read(ctx) (0)
+#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0)
+#endif
 	int count_wsi_allocated;
-	unsigned int http_proxy_port;
 	unsigned int options;
 	unsigned int fd_limit_per_thread;
 	unsigned int timeout_secs;
@@ -671,26 +726,6 @@ struct lws_context {
 	volatile int service_tid;
 	int service_tid_detected;
 
-	int count_protocols;
-	int ka_time;
-	int ka_probes;
-	int ka_interval;
-
-#ifdef LWS_OPENSSL_SUPPORT
-	int use_ssl;
-	int allow_non_ssl_on_ssl_port;
-	unsigned int user_supplied_ssl_ctx:1;
-#define lws_ssl_anybody_has_buffered_read(w) \
-		(w->context->use_ssl && \
-		 w->context->pt[(int)w->tsi].pending_read_list)
-#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \
-		(c->use_ssl && \
-		 c->pt[(int)t].pending_read_list)
-#else
-#define lws_ssl_anybody_has_buffered_read(ctx) (0)
-#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0)
-#endif
-
 	short max_http_header_data;
 	short max_http_header_pool;
 	short count_threads;
@@ -699,6 +734,9 @@ struct lws_context {
 	unsigned int requested_kill:1;
 };
 
+#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
+#define lws_get_vh_protocol(vh, x) vh->protocols[x]
+
 LWS_EXTERN void
 lws_close_free_wsi_final(struct lws *wsi);
 LWS_EXTERN void
@@ -1101,6 +1139,7 @@ struct lws {
 	/* pointers */
 
 	struct lws_context *context;
+	struct lws_vhost *vhost;
 	struct lws *parent; /* points to parent, if any */
 	struct lws *child_list; /* points to first child */
 	struct lws *sibling_list; /* subsequent children at same level */
@@ -1146,6 +1185,7 @@ struct lws {
 #endif
 
 	unsigned int hdr_parsing_completed:1;
+	unsigned int listener:1;
 	unsigned int user_space_externally_allocated:1;
 	unsigned int socket_is_permanently_unusable:1;
 	unsigned int rxflow_change_to:2;
@@ -1282,7 +1322,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port,
 		 const char *path, const char *host);
 
 LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_create_new_server_wsi(struct lws_context *context);
+lws_create_new_server_wsi(struct lws_vhost *vhost);
 
 LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
 lws_generate_client_handshake(struct lws *wsi, char *pkt);
@@ -1377,7 +1417,7 @@ void lws_http2_configure_if_upgraded(struct lws *wsi);
 #endif
 
 LWS_EXTERN int
-lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd);
+lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
 
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_header_table_attach(struct lws *wsi, int autoservice);
@@ -1402,7 +1442,9 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or);
 
 #ifndef LWS_NO_SERVER
 int lws_context_init_server(struct lws_context_creation_info *info,
-			    struct lws_context *context);
+			    struct lws_vhost *vhost);
+LWS_EXTERN struct lws_vhost *
+lws_select_vhost(struct lws_context *context, int port, const char *servername);
 LWS_EXTERN int
 handshake_0405(struct lws_context *context, struct lws *wsi);
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
@@ -1445,7 +1487,9 @@ enum lws_ssl_capable_status {
 #define lws_server_socket_service_ssl(_b, _c) (0)
 #define lws_ssl_close(_a) (0)
 #define lws_ssl_context_destroy(_a)
+#define lws_ssl_SSL_CTX_destroy(_a)
 #define lws_ssl_remove_wsi_from_buffered_list(_a)
+#define lws_context_init_ssl_library(_a)
 #else
 #define LWS_SSL_ENABLED(context) (context->use_ssl)
 LWS_EXTERN int openssl_websocket_private_data_index;
@@ -1455,23 +1499,27 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len);
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_ssl_pending(struct lws *wsi);
+LWS_EXTERN int
+lws_context_init_ssl_library(struct lws_context_creation_info *info);
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd);
 LWS_EXTERN int
 lws_ssl_close(struct lws *wsi);
 LWS_EXTERN void
+lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost);
+LWS_EXTERN void
 lws_ssl_context_destroy(struct lws_context *context);
 LWS_VISIBLE void
 lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
 #ifndef LWS_NO_SERVER
 LWS_EXTERN int
 lws_context_init_server_ssl(struct lws_context_creation_info *info,
-		 	    struct lws_context *context);
+			    struct lws_vhost *vhost);
 #else
 #define lws_context_init_server_ssl(_a, _b) (0)
 #endif
 LWS_EXTERN void
-lws_ssl_destroy(struct lws_context *context);
+lws_ssl_destroy(struct lws_vhost *vhost);
 
 /* HTTP2-related */
 
@@ -1549,7 +1597,7 @@ lws_http_transaction_completed_client(struct lws *wsi);
 #ifdef LWS_OPENSSL_SUPPORT
 LWS_EXTERN int
 lws_context_init_client_ssl(struct lws_context_creation_info *info,
-			    struct lws_context *context);
+			    struct lws_vhost *vhost);
 #else
 	#define lws_context_init_client_ssl(_a, _b) (0)
 #endif
diff --git a/lib/server-handshake.c b/lib/server-handshake.c
index 5f52585256928208d3e5566e4aefec1280494b6b..bab9c35947011d0b1f3d40f0a4a19691003a8974 100644
--- a/lib/server-handshake.c
+++ b/lib/server-handshake.c
@@ -84,7 +84,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
 
 		/* check a client's extension against our support */
 
-		ext = lws_get_context(wsi)->extensions;
+		ext = wsi->vhost->extensions;
 
 		while (ext && ext->callback) {
 
@@ -106,7 +106,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
 			 * ask user code if it's OK to apply it on this
 			 * particular connection + protocol
 			 */
-			m = lws_get_context(wsi)->protocols[0].callback(wsi,
+			m = wsi->vhost->protocols[0].callback(wsi,
 				LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
 				wsi->user_space, ext_name, 0);
 
diff --git a/lib/server.c b/lib/server.c
index 48cbc31455f7ed3fff8395b9155c6245bde780ff..4b7078a2a93ec0acf4addc1da4cad0bbe5c92cea 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -24,12 +24,13 @@
 
 int
 lws_context_init_server(struct lws_context_creation_info *info,
-			struct lws_context *context)
+			struct lws_vhost *vhost)
 {
 #ifdef LWS_POSIX
 	int n, opt = 1, limit = 1;
 #endif
 	lws_sockfd_type sockfd;
+	struct lws_vhost *vh;
 	struct lws *wsi;
 	int m = 0;
 
@@ -38,9 +39,25 @@ lws_context_init_server(struct lws_context_creation_info *info,
 	if (info->port == CONTEXT_PORT_NO_LISTEN)
 		return 0;
 
+	vh = vhost->context->vhost_list;
+	while (vh) {
+		if (vh->listen_port == info->port) {
+			if ((!info->iface && !vh->iface) ||
+			    (info->iface && vh->iface &&
+			    !strcmp(info->iface, vh->iface))) {
+				vhost->listen_port = info->port;
+				vhost->iface = info->iface;
+				lwsl_notice(" using listen skt from vhost %s\n",
+					    vh->name);
+				return 0;
+			}
+		}
+		vh = vh->vhost_next;
+	}
+
 #if LWS_POSIX
 #if defined(__linux__)
-	limit = context->count_threads;
+	limit = vhost->context->count_threads;
 #endif
 
 	for (m = 0; m < limit; m++) {
@@ -70,7 +87,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
 		return 1;
 	}
 #if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1
-	if (context->count_threads > 1)
+	if (vhost->context->count_threads > 1)
 		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
 				(const void *)&opt, sizeof(opt)) < 0) {
 			compatible_close(sockfd);
@@ -78,33 +95,36 @@ lws_context_init_server(struct lws_context_creation_info *info,
 		}
 #endif
 #endif
-	lws_plat_set_socket_options(context, sockfd);
+	lws_plat_set_socket_options(vhost, sockfd);
 
 #if LWS_POSIX
-	n = lws_socket_bind(context, sockfd, info->port, info->iface);
+	n = lws_socket_bind(vhost->context, sockfd, info->port, info->iface);
 	if (n < 0)
 		goto bail;
 	info->port = n;
 #endif
-	context->listen_port = info->port;
+	vhost->listen_port = info->port;
+	vhost->iface = info->iface;
 
 	wsi = lws_zalloc(sizeof(struct lws));
 	if (wsi == NULL) {
 		lwsl_err("Out of mem\n");
 		goto bail;
 	}
-	wsi->context = context;
+	wsi->context = vhost->context;
 	wsi->sock = sockfd;
 	wsi->mode = LWSCM_SERVER_LISTENER;
-	wsi->protocol = context->protocols;
+	wsi->protocol = vhost->protocols;
 	wsi->tsi = m;
+	wsi->vhost = vhost;
+	wsi->listener = 1;
 
-	context->pt[m].wsi_listening = wsi;
-	if (insert_wsi_socket_into_fds(context, wsi))
+	vhost->context->pt[m].wsi_listening = wsi;
+	if (insert_wsi_socket_into_fds(vhost->context, wsi))
 		goto bail;
 
-	context->count_wsi_allocated++;
-	context->pt[m].lserv_fd = sockfd;
+	vhost->context->count_wsi_allocated++;
+	vhost->lserv_wsi = wsi;
 
 #if LWS_POSIX
 	listen(wsi->sock, LWS_SOMAXCONN);
@@ -112,7 +132,8 @@ lws_context_init_server(struct lws_context_creation_info *info,
 #else
 	mbed3_tcp_stream_bind(wsi->sock, info->port, wsi);
 #endif
-	lwsl_notice(" Listening on port %d\n", info->port);
+	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
+		lwsl_notice(" Listening on port %d\n", info->port);
 
 	return 0;
 
@@ -143,6 +164,69 @@ _lws_server_listen_accept_flow_control(struct lws *twsi, int on)
 	return n;
 }
 
+struct lws_vhost *
+lws_select_vhost(struct lws_context *context, int port, const char *servername)
+{
+	struct lws_vhost *vhost = context->vhost_list;
+
+	while (vhost) {
+		if (port == vhost->listen_port &&
+		    !strcmp(vhost->name, servername)) {
+			lwsl_info("SNI: Found: %s\n", servername);
+			return vhost;
+		}
+		vhost = vhost->vhost_next;
+	}
+
+	return NULL;
+}
+
+static const char * get_mimetype(const char *file)
+{
+	int n = strlen(file);
+
+	if (n < 5)
+		return NULL;
+
+	if (!strcmp(&file[n - 4], ".ico"))
+		return "image/x-icon";
+
+	if (!strcmp(&file[n - 4], ".png"))
+		return "image/png";
+
+	if (!strcmp(&file[n - 5], ".html"))
+		return "text/html";
+
+	if (!strcmp(&file[n - 4], ".css"))
+		return "text/css";
+
+	return NULL;
+}
+
+int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
+{
+	const char *mimetype;
+	char path[256];
+	int n;
+
+	lwsl_notice("%s: %s %s\n", __func__, uri, origin);
+	snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
+
+	mimetype = get_mimetype(path);
+	if (!mimetype) {
+		lwsl_err("unknown mimetype for %s", path);
+		lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
+		return -1;
+	}
+
+	n = lws_serve_http_file(wsi, path, mimetype, NULL, 0);
+	if (n < 0)
+	if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
+		return -1; /* error or can't reuse connection: close the socket */
+
+	return 0;
+}
+
 int
 lws_http_action(struct lws *wsi)
 {
@@ -152,6 +236,7 @@ lws_http_action(struct lws *wsi)
 	enum http_connection_type connection_type;
 	enum http_version request_version;
 	char content_length_str[32];
+	struct lws_http_mount *hm;
 	unsigned int n, count = 0;
 	char http_version_str[10];
 	char http_conn_str[20];
@@ -280,7 +365,7 @@ lws_http_action(struct lws *wsi)
 			goto bail_nuke_ah;
 		if (lws_add_http_header_status(wsi, 301, &p, end))
 			goto bail_nuke_ah;
-		n = sprintf((char *)end, "https://%s/",
+		n = sprintf((char *)end, "htt	struct lws_http_mount *hm;ps://%s/",
 			    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
 		if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
 				end, n, &p, end))
@@ -295,7 +380,28 @@ lws_http_action(struct lws *wsi)
 	}
 #endif
 
-	n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
+	/* can we serve it from the mount list? */
+
+	hm = wsi->vhost->mount_list;
+	while (hm) {
+		char *s = uri_ptr + hm->mountpoint_len;
+
+		if (s[0] == '\0')
+			s = (char *)hm->def;
+
+		if (!s)
+			s = "index.html";
+
+		if (uri_len >= hm->mountpoint_len &&
+		    !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len)) {
+			n = lws_http_serve(wsi, s, hm->origin);
+			break;
+		}
+		hm = hm->mount_next;
+	}
+
+	if (!hm)
+		n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
 				    wsi->user_space, uri_ptr, uri_len);
 	if (n) {
 		lwsl_info("LWS_CALLBACK_HTTP closing\n");
@@ -387,13 +493,25 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 		lwsl_info("No upgrade\n");
 		ah = wsi->u.hdr.ah;
 
+		/* select vhost */
+
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
+			struct lws_vhost *vhost = lws_select_vhost(
+				context, wsi->vhost->listen_port,
+				lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
+
+			if (vhost)
+				wsi->vhost = vhost;
+		}
+
 		lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
 		wsi->state = LWSS_HTTP;
 		wsi->u.http.fd = LWS_INVALID_FILE;
 
 		/* expose it at the same offset as u.hdr */
 		wsi->u.http.ah = ah;
-		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, (void *)wsi->u.hdr.ah);
+		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
+			   (void *)wsi->u.hdr.ah);
 
 		n = lws_http_action(wsi);
 
@@ -485,12 +603,12 @@ upgrade_ws:
 			lwsl_info("checking %s\n", protocol_name);
 
 			n = 0;
-			while (context->protocols[n].callback) {
-				if (context->protocols[n].name &&
-				    !strcmp(context->protocols[n].name,
+			while (wsi->vhost->protocols[n].callback) {
+				if (wsi->vhost->protocols[n].name &&
+				    !strcmp(wsi->vhost->protocols[n].name,
 					    protocol_name)) {
 					lwsl_info("prot match %d\n", n);
-					wsi->protocol = &context->protocols[n];
+					wsi->protocol = &wsi->vhost->protocols[n];
 					hit = 1;
 					break;
 				}
@@ -513,7 +631,7 @@ upgrade_ws:
 			 * allow it and match to protocol 0
 			 */
 			lwsl_info("defaulting to prot 0 handler\n");
-			wsi->protocol = &context->protocols[0];
+			wsi->protocol = &wsi->vhost->protocols[0];
 		}
 
 		/* allocate wsi->user storage */
@@ -643,10 +761,10 @@ lws_get_idlest_tsi(struct lws_context *context)
 }
 
 struct lws *
-lws_create_new_server_wsi(struct lws_context *context)
+lws_create_new_server_wsi(struct lws_vhost *vhost)
 {
 	struct lws *new_wsi;
-	int n = lws_get_idlest_tsi(context);
+	int n = lws_get_idlest_tsi(vhost->context);
 
 	if (n < 0) {
 		lwsl_err("no space for new conn\n");
@@ -662,7 +780,8 @@ lws_create_new_server_wsi(struct lws_context *context)
 	new_wsi->tsi = n;
 	lwsl_info("Accepted %p to tsi %d\n", new_wsi, new_wsi->tsi);
 
-	new_wsi->context = context;
+	new_wsi->vhost = vhost;
+	new_wsi->context = vhost->context;
 	new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
 
@@ -673,7 +792,7 @@ lws_create_new_server_wsi(struct lws_context *context)
 	new_wsi->hdr_parsing_completed = 0;
 
 #ifdef LWS_OPENSSL_SUPPORT
-	new_wsi->use_ssl = LWS_SSL_ENABLED(context);
+	new_wsi->use_ssl = LWS_SSL_ENABLED(vhost);
 #endif
 
 	/*
@@ -682,17 +801,17 @@ lws_create_new_server_wsi(struct lws_context *context)
 	 * to the start of the supported list, so it can look
 	 * for matching ones during the handshake
 	 */
-	new_wsi->protocol = context->protocols;
+	new_wsi->protocol = vhost->protocols;
 	new_wsi->user_space = NULL;
 	new_wsi->ietf_spec_revision = 0;
 	new_wsi->sock = LWS_SOCK_INVALID;
-	context->count_wsi_allocated++;
+	vhost->context->count_wsi_allocated++;
 
 	/*
 	 * outermost create notification for wsi
 	 * no user_space because no protocol selection
 	 */
-	context->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
+	vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
 				       NULL, NULL, 0);
 
 	return new_wsi;
@@ -772,7 +891,7 @@ lws_http_transaction_completed(struct lws *wsi)
 LWS_VISIBLE struct lws *
 lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
 {
-	struct lws *new_wsi = lws_create_new_server_wsi(context);
+	struct lws *new_wsi = lws_create_new_server_wsi(context->vhost_list);
 
 	if (!new_wsi) {
 		compatible_close(accept_fd);
@@ -796,7 +915,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
 	 * set properties of the newly created wsi. There's no protocol
 	 * selected yet so we issue this to protocols[0]
 	 */
-	if ((context->protocols[0].callback)(new_wsi,
+	if ((context->vhost_list->protocols[0].callback)(new_wsi,
 	     LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0)) {
 		compatible_close(new_wsi->sock);
 		lws_free(new_wsi);
@@ -806,7 +925,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
 	lws_libev_accept(new_wsi, new_wsi->sock);
 	lws_libuv_accept(new_wsi, new_wsi->sock);
 
-	if (!LWS_SSL_ENABLED(context)) {
+	if (!LWS_SSL_ENABLED(new_wsi->vhost)) {
 		if (insert_wsi_socket_into_fds(context, new_wsi))
 			goto fail;
 	} else {
@@ -1113,7 +1232,7 @@ try_pollout:
 				break;
 			}
 
-			lws_plat_set_socket_options(context, accept_fd);
+			lws_plat_set_socket_options(wsi->vhost, accept_fd);
 
 			lwsl_debug("accepted new conn  port %u on fd=%d\n",
 					  ntohs(cli_addr.sin_port), accept_fd);
@@ -1127,7 +1246,7 @@ try_pollout:
 			 * to reject based on client IP.  There's no protocol selected
 			 * yet so we issue this to protocols[0]
 			 */
-			if ((context->protocols[0].callback)(wsi,
+			if ((wsi->vhost->protocols[0].callback)(wsi,
 					LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
 					NULL, (void *)(long)accept_fd, 0)) {
 				lwsl_debug("Callback denied network connection\n");
diff --git a/lib/ssl.c b/lib/ssl.c
index 7c7ad6e7d78d200f710d5e76a6a6f3ff2c45d7e9..f00524c3873cabb1b6684cc477674bd70e62a017 100644
--- a/lib/ssl.c
+++ b/lib/ssl.c
@@ -28,7 +28,8 @@
 #include <openssl/ecdh.h>
 #endif
 
-int openssl_websocket_private_data_index;
+int openssl_websocket_private_data_index,
+    openssl_SSL_CTX_private_data_index;
 
 static int
 lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
@@ -55,13 +56,47 @@ static void lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creatio
 	SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
 }
 
+int
+lws_context_init_ssl_library(struct lws_context_creation_info *info)
+{
+#ifdef USE_WOLFSSL
+#ifdef USE_OLD_CYASSL
+	lwsl_notice(" Compiled with CyaSSL support\n");
+#else
+	lwsl_notice(" Compiled with wolfSSL support\n");
+#endif
+#else
+	lwsl_notice(" Compiled with OpenSSL support\n");
+#endif
+
+	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
+		lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
+		return 0;
+	}
+
+	/* basic openssl init */
+
+	SSL_library_init();
+
+	OpenSSL_add_all_algorithms();
+	SSL_load_error_strings();
+
+	openssl_websocket_private_data_index =
+		SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
+
+	openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
+			NULL, NULL, NULL, NULL);
+
+	return 0;
+}
+
 #ifndef LWS_NO_SERVER
 static int
 OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
 	SSL *ssl;
 	int n;
-	struct lws_context *context;
+	struct lws_vhost *vh;
 	struct lws wsi;
 
 	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
@@ -71,16 +106,17 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
 	 * !!! nasty openssl requires the index to come as a library-scope
 	 * static
 	 */
-	context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
+	vh = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
 
 	/*
 	 * give him a fake wsi with context set, so he can use lws_get_context()
 	 * in the callback
 	 */
 	memset(&wsi, 0, sizeof(wsi));
-	wsi.context = context;
+	wsi.vhost = vh;
+	wsi.context = vh->context;
 
-	n = context->protocols[0].callback(&wsi,
+	n = vh->protocols[0].callback(&wsi,
 			LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
 					   x509_ctx, ssl, preverify_ok);
 
@@ -89,7 +125,7 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
 }
 
 static int
-lws_context_ssl_init_ecdh(struct lws_context *context)
+lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
 {
 #ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
 	EC_KEY *EC_key = NULL;
@@ -97,13 +133,13 @@ lws_context_ssl_init_ecdh(struct lws_context *context)
 	int KeyType;
 	X509 *x;
 
-	if (!lws_check_opt(context->options, LWS_SERVER_OPTION_SSL_ECDH))
+	if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
 		return 0;
 
 	lwsl_notice(" Using ECDH certificate support\n");
 
 	/* Get X509 certificate from ssl context */
-	x = sk_X509_value(context->ssl_ctx->extra_certs, 0);
+	x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
 	if (!x) {
 		lwsl_err("%s: x is NULL\n", __func__);
 		return 1;
@@ -129,7 +165,7 @@ lws_context_ssl_init_ecdh(struct lws_context *context)
 		lwsl_err("%s: ECDH key is NULL \n", __func__);
 		return 1;
 	}
-	SSL_CTX_set_tmp_ecdh(context->ssl_ctx, EC_key);
+	SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
 	EC_KEY_free(EC_key);
 #endif
 	return 0;
@@ -137,7 +173,7 @@ lws_context_ssl_init_ecdh(struct lws_context *context)
 
 static int
 lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
-				struct lws_context *context)
+				struct lws_vhost *vhost)
 {
 #ifdef LWS_HAVE_OPENSSL_ECDH_H
 	EC_KEY *ecdh;
@@ -158,10 +194,10 @@ lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
 		lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
 		return 1;
 	}
-	SSL_CTX_set_tmp_ecdh(context->ssl_ctx, ecdh);
+	SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
 	EC_KEY_free(ecdh);
 
-	SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
+	SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
 
 	lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
 #else
@@ -175,18 +211,43 @@ static int
 lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
 {
 	struct lws_context *context;
+	struct lws_vhost *vhost, *vh;
 	const char *servername;
+	int port;
 
 	if (!ssl)
 		return SSL_TLSEXT_ERR_NOACK;
 
 	context = (struct lws_context *)SSL_CTX_get_ex_data(
-					SSL_get_SSL_CTX(ssl), 0);
+					SSL_get_SSL_CTX(ssl),
+					openssl_SSL_CTX_private_data_index);
+
+	/*
+	 * We can only get ssl accepted connections by using a vhost's ssl_ctx
+	 * find out which listening one took us and only match vhosts on the
+	 * same port.
+	 */
+	vh = context->vhost_list;
+	while (vh) {
+		if (vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
+			break;
+		vh = vh->vhost_next;
+	}
+
+	assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */
+	port = vh->listen_port;
 
 	servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-	lwsl_err("ServerName: %s, context = %p\n", servername, context);
 
-	//SSL_set_SSL_CTX(ssl, sslctx);
+	if (servername) {
+		vhost = lws_select_vhost(context, port, servername);
+		if (vhost) {
+			lwsl_info("SNI: Found: %s\n", servername);
+			SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
+			return SSL_TLSEXT_ERR_OK;
+		}
+		lwsl_err("SNI: Unknown ServerName: %s\n", servername);
+	}
 
 	return SSL_TLSEXT_ERR_OK;
 }
@@ -194,57 +255,39 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
 
 LWS_VISIBLE int
 lws_context_init_server_ssl(struct lws_context_creation_info *info,
-			    struct lws_context *context)
+			    struct lws_vhost *vhost)
 {
 	SSL_METHOD *method;
+	struct lws_context *context = vhost->context;
 	struct lws wsi;
 	int error;
 	int n;
 
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
-	lwsl_notice(" Compiled with CyaSSL support\n");
-#else
-	lwsl_notice(" Compiled with wolfSSL support\n");
-#endif
-#else
-	lwsl_notice(" Compiled with OpenSSL support\n");
-#endif
-
 	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
-		lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
+		vhost->use_ssl = 0;
 		return 0;
 	}
 
 	if (info->port != CONTEXT_PORT_NO_LISTEN) {
 
-		context->use_ssl = info->ssl_cert_filepath != NULL;
+		vhost->use_ssl = info->ssl_cert_filepath != NULL;
 
-		if (info->ssl_cipher_list)
+		if (vhost->use_ssl && info->ssl_cipher_list)
 			lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
 
-		if (context->use_ssl)
+		if (vhost->use_ssl)
 			lwsl_notice(" Using SSL mode\n");
 		else
 			lwsl_notice(" Using non-SSL mode\n");
 	}
 
 	/*
-	 * give him a fake wsi with context set, so he can use
+	 * give him a fake wsi with context + vhost set, so he can use
 	 * lws_get_context() in the callback
 	 */
 	memset(&wsi, 0, sizeof(wsi));
-	wsi.context = context;
-
-	/* basic openssl init */
-
-	SSL_library_init();
-
-	OpenSSL_add_all_algorithms();
-	SSL_load_error_strings();
-
-	openssl_websocket_private_data_index =
-		SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL);
+	wsi.vhost = vhost;
+	wsi.context = vhost->context;
 
 	/*
 	 * Firefox insists on SSLv23 not SSLv3
@@ -263,8 +306,8 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 					      (char *)context->pt[0].serv_buf));
 		return 1;
 	}
-	context->ssl_ctx = SSL_CTX_new(method);	/* create context */
-	if (!context->ssl_ctx) {
+	vhost->ssl_ctx = SSL_CTX_new(method);	/* create context */
+	if (!vhost->ssl_ctx) {
 		error = ERR_get_error();
 		lwsl_err("problem creating ssl context %lu: %s\n",
 			error, ERR_error_string(error,
@@ -273,22 +316,19 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 	}
 
 	/* associate the lws context with the SSL_CTX */
-	n = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
-	if (n) {
-		lwsl_err("cannot register arg0 on SSL_CTX %d\n", n);
-		return 1;
-	}
-	SSL_CTX_set_ex_data(context->ssl_ctx, 0, context);
+
+	SSL_CTX_set_ex_data(vhost->ssl_ctx,
+			openssl_SSL_CTX_private_data_index, vhost->context);
 
 	/* Disable SSLv2 and SSLv3 */
-	SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+	SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 #ifdef SSL_OP_NO_COMPRESSION
-	SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION);
+	SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
 #endif
-	SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_DH_USE);
-	SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+	SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
+	SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
 	if (info->ssl_cipher_list)
-		SSL_CTX_set_cipher_list(context->ssl_ctx,
+		SSL_CTX_set_cipher_list(vhost->ssl_ctx,
 						info->ssl_cipher_list);
 
 	/* as a server, are we requiring clients to identify themselves? */
@@ -299,17 +339,17 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 		if (!lws_check_opt(info->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
 			verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
 
-		SSL_CTX_set_session_id_context(context->ssl_ctx,
+		SSL_CTX_set_session_id_context(vhost->ssl_ctx,
 				(unsigned char *)context, sizeof(void *));
 
 		/* absolutely require the client cert */
 
-		SSL_CTX_set_verify(context->ssl_ctx,
+		SSL_CTX_set_verify(vhost->ssl_ctx,
 		       verify_options, OpenSSL_verify_callback);
 	}
 
 #ifndef OPENSSL_NO_TLSEXT
-	SSL_CTX_set_tlsext_servername_callback(context->ssl_ctx,
+	SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
 					       lws_ssl_server_name_cb);
 #endif
 
@@ -319,27 +359,29 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 	 */
 
 	if (info->ssl_ca_filepath &&
-	    !SSL_CTX_load_verify_locations(context->ssl_ctx,
+	    !SSL_CTX_load_verify_locations(vhost->ssl_ctx,
 					   info->ssl_ca_filepath, NULL)) {
-		lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n");
+		lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
 	}
 
-	if (lws_context_ssl_init_ecdh_curve(info, context))
-		return -1;
+	if (vhost->use_ssl) {
+		if (lws_context_ssl_init_ecdh_curve(info, vhost))
+			return -1;
 
-	context->protocols[0].callback(&wsi,
+		vhost->protocols[0].callback(&wsi,
 			LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
-					       context->ssl_ctx, NULL, 0);
+			vhost->ssl_ctx, NULL, 0);
+	}
 
 	if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
 		/* Normally SSL listener rejects non-ssl, optionally allow */
-		context->allow_non_ssl_on_ssl_port = 1;
+		vhost->allow_non_ssl_on_ssl_port = 1;
 
-	if (context->use_ssl) {
+	if (vhost->use_ssl) {
 		/* openssl init for server sockets */
 
 		/* set the local certificate from CertFile */
-		n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx,
+		n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
 					info->ssl_cert_filepath);
 		if (n != 1) {
 			error = ERR_get_error();
@@ -350,11 +392,11 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 					      (char *)context->pt[0].serv_buf));
 			return 1;
 		}
-		lws_ssl_bind_passphrase(context->ssl_ctx, info);
+		lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
 
 		if (info->ssl_private_key_filepath != NULL) {
 			/* set the private key from KeyFile */
-			if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx,
+			if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
 				     info->ssl_private_key_filepath,
 						       SSL_FILETYPE_PEM) != 1) {
 				error = ERR_get_error();
@@ -365,21 +407,21 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 				return 1;
 			}
 		} else
-			if (context->protocols[0].callback(&wsi,
+			if (vhost->protocols[0].callback(&wsi,
 				LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
-						context->ssl_ctx, NULL, 0)) {
+				vhost->ssl_ctx, NULL, 0)) {
 				lwsl_err("ssl private key not set\n");
 
 				return 1;
 			}
 
 		/* verify private key */
-		if (!SSL_CTX_check_private_key(context->ssl_ctx)) {
+		if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
 			lwsl_err("Private SSL key doesn't match cert\n");
 			return 1;
 		}
 
-		if (lws_context_ssl_init_ecdh(context))
+		if (lws_context_ssl_init_ecdh(vhost))
 			return 1;
 
 		/*
@@ -395,15 +437,15 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 #endif
 
 LWS_VISIBLE void
-lws_ssl_destroy(struct lws_context *context)
+lws_ssl_destroy(struct lws_vhost *vhost)
 {
-	if (!lws_check_opt(context->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+	if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
 		return;
 
-	if (context->ssl_ctx)
-		SSL_CTX_free(context->ssl_ctx);
-	if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx)
-		SSL_CTX_free(context->ssl_client_ctx);
+	if (vhost->ssl_ctx)
+		SSL_CTX_free(vhost->ssl_ctx);
+	if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
+		SSL_CTX_free(vhost->ssl_client_ctx);
 
 #if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL)
 	ERR_remove_state(0);
@@ -430,7 +472,7 @@ lws_decode_ssl_error(void)
 #ifndef LWS_NO_CLIENT
 
 int lws_context_init_client_ssl(struct lws_context_creation_info *info,
-			        struct lws_context *context)
+			        struct lws_vhost *vhost)
 {
 	int error;
 	int n;
@@ -442,9 +484,9 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
 
 	if (info->provided_client_ssl_ctx) {
 		/* use the provided OpenSSL context if given one */
-		context->ssl_client_ctx = info->provided_client_ssl_ctx;
+		vhost->ssl_client_ctx = info->provided_client_ssl_ctx;
 		/* nothing for lib to delete */
-		context->user_supplied_ssl_ctx = 1;
+		vhost->user_supplied_ssl_ctx = 1;
 		return 0;
 	}
 
@@ -463,39 +505,39 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
 		error = ERR_get_error();
 		lwsl_err("problem creating ssl method %lu: %s\n",
 			error, ERR_error_string(error,
-				      (char *)context->pt[0].serv_buf));
+				      (char *)vhost->context->pt[0].serv_buf));
 		return 1;
 	}
 	/* create context */
-	context->ssl_client_ctx = SSL_CTX_new(method);
-	if (!context->ssl_client_ctx) {
+	vhost->ssl_client_ctx = SSL_CTX_new(method);
+	if (!vhost->ssl_client_ctx) {
 		error = ERR_get_error();
 		lwsl_err("problem creating ssl context %lu: %s\n",
 			error, ERR_error_string(error,
-				      (char *)context->pt[0].serv_buf));
+				      (char *)vhost->context->pt[0].serv_buf));
 		return 1;
 	}
 
 #ifdef SSL_OP_NO_COMPRESSION
-	SSL_CTX_set_options(context->ssl_client_ctx,
+	SSL_CTX_set_options(vhost->ssl_client_ctx,
 						 SSL_OP_NO_COMPRESSION);
 #endif
-	SSL_CTX_set_options(context->ssl_client_ctx,
+	SSL_CTX_set_options(vhost->ssl_client_ctx,
 				       SSL_OP_CIPHER_SERVER_PREFERENCE);
 	if (info->ssl_cipher_list)
-		SSL_CTX_set_cipher_list(context->ssl_client_ctx,
+		SSL_CTX_set_cipher_list(vhost->ssl_client_ctx,
 						info->ssl_cipher_list);
 
 #ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
 	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
 		/* loads OS default CA certs */
-		SSL_CTX_set_default_verify_paths(context->ssl_client_ctx);
+		SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
 #endif
 
 	/* openssl init for cert verification (for client sockets) */
 	if (!info->ssl_ca_filepath) {
 		if (!SSL_CTX_load_verify_locations(
-			context->ssl_client_ctx, NULL,
+			vhost->ssl_client_ctx, NULL,
 					     LWS_OPENSSL_CLIENT_CERTS))
 			lwsl_err(
 			    "Unable to load SSL Client certs from %s "
@@ -504,7 +546,7 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
 			    "going to work", LWS_OPENSSL_CLIENT_CERTS);
 	} else
 		if (!SSL_CTX_load_verify_locations(
-			context->ssl_client_ctx, info->ssl_ca_filepath,
+			vhost->ssl_client_ctx, info->ssl_ca_filepath,
 							  NULL))
 			lwsl_err(
 				"Unable to load SSL Client certs "
@@ -520,32 +562,32 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
 
 	/* support for client-side certificate authentication */
 	if (info->ssl_cert_filepath) {
-		n = SSL_CTX_use_certificate_chain_file(context->ssl_client_ctx,
+		n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
 						       info->ssl_cert_filepath);
 		if (n != 1) {
 			lwsl_err("problem getting cert '%s' %lu: %s\n",
 				info->ssl_cert_filepath,
 				ERR_get_error(),
 				ERR_error_string(ERR_get_error(),
-				(char *)context->pt[0].serv_buf));
+				(char *)vhost->context->pt[0].serv_buf));
 			return 1;
 		}
 	}
 	if (info->ssl_private_key_filepath) {
-		lws_ssl_bind_passphrase(context->ssl_client_ctx, info);
+		lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
 		/* set the private key from KeyFile */
-		if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx,
+		if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
 		    info->ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) {
 			lwsl_err("use_PrivateKey_file '%s' %lu: %s\n",
 				info->ssl_private_key_filepath,
 				ERR_get_error(),
 				ERR_error_string(ERR_get_error(),
-				      (char *)context->pt[0].serv_buf));
+				      (char *)vhost->context->pt[0].serv_buf));
 			return 1;
 		}
 
 		/* verify private key */
-		if (!SSL_CTX_check_private_key(context->ssl_client_ctx)) {
+		if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
 			lwsl_err("Private SSL key doesn't match cert\n");
 			return 1;
 		}
@@ -556,11 +598,12 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
 	 * lws_get_context() in the callback
 	 */
 	memset(&wsi, 0, sizeof(wsi));
-	wsi.context = context;
+	wsi.vhost = vhost;
+	wsi.context = vhost->context;
 
-	context->protocols[0].callback(&wsi,
+	vhost->protocols[0].callback(&wsi,
 			LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
-				       context->ssl_client_ctx, NULL, 0);
+				       vhost->ssl_client_ctx, NULL, 0);
 
 	return 0;
 }
@@ -712,13 +755,13 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 	BIO *bio;
 #endif
 
-	if (!LWS_SSL_ENABLED(context))
+	if (!LWS_SSL_ENABLED(wsi->vhost))
 		return 0;
 
 	switch (wsi->mode) {
 	case LWSCM_SSL_INIT:
 
-		wsi->ssl = SSL_new(context->ssl_ctx);
+		wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
 		if (wsi->ssl == NULL) {
 			lwsl_err("SSL_new failed: %s\n",
 				 ERR_error_string(SSL_get_error(wsi->ssl, 0), NULL));
@@ -787,7 +830,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 		 * it disabled unless you know it's not a problem for you
 		 */
 
-		if (context->allow_non_ssl_on_ssl_port) {
+		if (wsi->vhost->allow_non_ssl_on_ssl_port) {
 			if (n >= 1 && pt->serv_buf[0] >= ' ') {
 				/*
 				* TLS content-type for Handshake is 0x16, and
@@ -876,14 +919,18 @@ fail:
 	return 1;
 }
 
-LWS_VISIBLE void
-lws_ssl_context_destroy(struct lws_context *context)
+void
+lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
 {
-	if (context->ssl_ctx)
-		SSL_CTX_free(context->ssl_ctx);
-	if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx)
-		SSL_CTX_free(context->ssl_client_ctx);
+	if (vhost->ssl_ctx)
+		SSL_CTX_free(vhost->ssl_ctx);
+	if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
+		SSL_CTX_free(vhost->ssl_client_ctx);
+}
 
+void
+lws_ssl_context_destroy(struct lws_context *context)
+{
 #if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL)
 	ERR_remove_state(0);
 #else
diff --git a/test-server/test-server.c b/test-server/test-server.c
index 58f3d7cad5f7963eeecbc8e85503aad6be095b29..b285cda51cfff322ef8c2dcf59ce8261eea21788 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -1,5 +1,5 @@
 /*
- * libwebsockets-test-servet - libwebsockets test implementation
+ * libwebsockets-test-server - libwebsockets test implementation
  *
  * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
  *