diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1eb909cc354d7a63a40ead14d2678f211eb374b0..9c6844246067ef662f9475b4699d881bab63c95e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -727,15 +727,15 @@ set(HDR_PUBLIC
 	)
 
 set(SOURCES
-	lib/misc/base64-decode.c
+	lib/core/alloc.c
+	lib/core/context.c
 	lib/core/libwebsockets.c
-	lib/core/service.c
-	lib/core/pollfd.c
 	lib/core/output.c
-	lib/core/context.c
-	lib/core/alloc.c
-	lib/roles/pipe/ops-pipe.c
-	lib/misc/lws-ring.c)
+	lib/core/pollfd.c
+	lib/core/service.c
+	lib/misc/base64-decode.c
+	lib/misc/lws-ring.c
+	lib/roles/pipe/ops-pipe.c)
 
 if (LWS_ROLE_H1 OR LWS_ROLE_H2)
 	list(APPEND SOURCES
@@ -764,7 +764,8 @@ endif()
 
 if (LWS_ROLE_RAW)
 	list(APPEND SOURCES
-		lib/roles/raw/ops-raw.c)
+		lib/roles/raw-skt/ops-raw-skt.c
+		lib/roles/raw-file/ops-raw-file.c)
 endif()
 
 if (LWS_ROLE_CGI)
@@ -785,12 +786,14 @@ endif()
 
 if (NOT LWS_WITHOUT_CLIENT)
 	list(APPEND SOURCES
+		lib/core/connect.c
 		lib/roles/http/client/client.c
 		lib/roles/http/client/client-handshake.c)
 endif()
 
 if (NOT LWS_WITHOUT_SERVER)
 	list(APPEND SOURCES
+		lib/core/adopt.c
 		lib/roles/listen/ops-listen.c)
 endif()
 
diff --git a/changelog b/changelog
index 422813b1ab0fa900a919182433bc7ac43f8a559f..0931c8923505b8020c2a419f9cf8d09bb793cc22 100644
--- a/changelog
+++ b/changelog
@@ -1,6 +1,12 @@
 Changelog
 ---------
 
+ - CHANGE: REMOVED: lws_client_connect() and lws_client_connect_extended()
+   compatibility apis for lws_client_connect_via_info() have been marked as
+   deprecated for several versions and are now removed.  Use
+   lws_client_connect_via_info() instead.
+
+
 v3.0.0
 ======
 
diff --git a/lib/core/adopt.c b/lib/core/adopt.c
new file mode 100644
index 0000000000000000000000000000000000000000..b94faffbd16ee073edb2c6db2fbda56615f76fbb
--- /dev/null
+++ b/lib/core/adopt.c
@@ -0,0 +1,407 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * 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
+ *  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
+ */
+
+#include "core/private.h"
+
+
+static int
+lws_get_idlest_tsi(struct lws_context *context)
+{
+	unsigned int lowest = ~0;
+	int n = 0, hit = -1;
+
+	for (; n < context->count_threads; n++) {
+		if ((unsigned int)context->pt[n].fds_count !=
+		    context->fd_limit_per_thread - 1 &&
+		    (unsigned int)context->pt[n].fds_count < lowest) {
+			lowest = context->pt[n].fds_count;
+			hit = n;
+		}
+	}
+
+	return hit;
+}
+
+struct lws *
+lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
+{
+	struct lws *new_wsi;
+	int n = fixed_tsi;
+
+	if (n < 0)
+		n = lws_get_idlest_tsi(vhost->context);
+
+	if (n < 0) {
+		lwsl_err("no space for new conn\n");
+		return NULL;
+	}
+
+	new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi");
+	if (new_wsi == NULL) {
+		lwsl_err("Out of memory for new connection\n");
+		return NULL;
+	}
+
+	new_wsi->tsi = n;
+	lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi,
+		   vhost->name, new_wsi->tsi);
+
+	new_wsi->vhost = vhost;
+	new_wsi->context = vhost->context;
+	new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
+	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
+
+	/* initialize the instance struct */
+
+	lwsi_set_state(new_wsi, LRS_UNCONNECTED);
+	new_wsi->hdr_parsing_completed = 0;
+
+#ifdef LWS_WITH_TLS
+	new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost);
+#endif
+
+	/*
+	 * these can only be set once the protocol is known
+	 * we set an un-established connection's protocol pointer
+	 * to the start of the supported list, so it can look
+	 * for matching ones during the handshake
+	 */
+	new_wsi->protocol = vhost->protocols;
+	new_wsi->user_space = NULL;
+	new_wsi->desc.sockfd = LWS_SOCK_INVALID;
+	new_wsi->position_in_fds_table = LWS_NO_FDS_POS;
+
+	vhost->context->count_wsi_allocated++;
+
+	/*
+	 * outermost create notification for wsi
+	 * no user_space because no protocol selection
+	 */
+	vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
+				       NULL, NULL, 0);
+
+	return new_wsi;
+}
+
+
+/* if not a socket, it's a raw, non-ssl file descriptor */
+
+LWS_VISIBLE struct lws *
+lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
+			   lws_sock_file_fd_type fd, const char *vh_prot_name,
+			   struct lws *parent)
+{
+	struct lws_context *context = vh->context;
+	struct lws *new_wsi;
+	struct lws_context_per_thread *pt;
+	int n;
+
+#if defined(LWS_WITH_PEER_LIMITS)
+	struct lws_peer *peer = NULL;
+
+	if (type & LWS_ADOPT_SOCKET) {
+		peer = lws_get_or_create_peer(vh, fd.sockfd);
+
+		if (peer && context->ip_limit_wsi &&
+		    peer->count_wsi >= context->ip_limit_wsi) {
+			lwsl_notice("Peer reached wsi limit %d\n",
+					context->ip_limit_wsi);
+			lws_stats_atomic_bump(context, &context->pt[0],
+					      LWSSTATS_C_PEER_LIMIT_WSI_DENIED,
+					      1);
+			return NULL;
+		}
+	}
+#endif
+
+	n = -1;
+	if (parent)
+		n = parent->tsi;
+	new_wsi = lws_create_new_server_wsi(vh, n);
+	if (!new_wsi) {
+		if (type & LWS_ADOPT_SOCKET)
+			compatible_close(fd.sockfd);
+		return NULL;
+	}
+#if defined(LWS_WITH_PEER_LIMITS)
+	if (peer)
+		lws_peer_add_wsi(context, peer, new_wsi);
+#endif
+	pt = &context->pt[(int)new_wsi->tsi];
+	lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1);
+
+	if (parent) {
+		new_wsi->parent = parent;
+		new_wsi->sibling_list = parent->child_list;
+		parent->child_list = new_wsi;
+	}
+
+	new_wsi->desc = fd;
+
+	if (vh_prot_name) {
+		new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost,
+							       vh_prot_name);
+		if (!new_wsi->protocol) {
+			lwsl_err("Protocol %s not enabled on vhost %s\n",
+				 vh_prot_name, new_wsi->vhost->name);
+			goto bail;
+		}
+		if (lws_ensure_user_space(new_wsi)) {
+		       lwsl_notice("OOM trying to get user_space\n");
+			goto bail;
+		}
+	}
+
+	if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_SOCKET))
+		type &= ~LWS_ADOPT_ALLOW_SSL;
+
+	if (lws_role_call_adoption_bind(new_wsi, type, vh_prot_name)) {
+		lwsl_err("Unable to find a role that can adopt descriptor\n");
+		goto bail;
+	}
+
+	/*
+	 * A new connection was accepted. Give the user a chance to
+	 * set properties of the newly created wsi. There's no protocol
+	 * selected yet so we issue this to the vhosts's default protocol,
+	 * itself by default protocols[0]
+	 */
+	n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
+	if (!(type & LWS_ADOPT_HTTP)) {
+		if (!(type & LWS_ADOPT_SOCKET))
+			n = LWS_CALLBACK_RAW_ADOPT_FILE;
+		else
+			n = LWS_CALLBACK_RAW_ADOPT;
+	}
+
+	lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate);
+
+	if (context->event_loop_ops->accept)
+		context->event_loop_ops->accept(new_wsi);
+
+	if (!(type & LWS_ADOPT_ALLOW_SSL)) {
+		lws_pt_lock(pt, __func__);
+		if (__insert_wsi_socket_into_fds(context, new_wsi)) {
+			lws_pt_unlock(pt);
+			lwsl_err("%s: fail inserting socket\n", __func__);
+			goto fail;
+		}
+		lws_pt_unlock(pt);
+	} else
+		if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
+			lwsl_info("%s: fail ssl negotiation\n", __func__);
+			goto fail;
+		}
+
+	/*
+	 *  by deferring callback to this point, after insertion to fds,
+	 * lws_callback_on_writable() can work from the callback
+	 */
+	if ((new_wsi->protocol->callback)(
+			new_wsi, n, new_wsi->user_space, NULL, 0))
+		goto fail;
+
+	/* role may need to do something after all adoption completed */
+
+	lws_role_call_adoption_bind(new_wsi, type | _LWS_ADOPT_FINISH,
+				    vh_prot_name);
+
+	lws_cancel_service_pt(new_wsi);
+
+	return new_wsi;
+
+fail:
+	if (type & LWS_ADOPT_SOCKET)
+		lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS,
+				   "adopt skt fail");
+
+	return NULL;
+
+bail:
+       lwsl_notice("%s: exiting on bail\n", __func__);
+	if (parent)
+		parent->child_list = new_wsi->sibling_list;
+	if (new_wsi->user_space)
+		lws_free(new_wsi->user_space);
+	lws_free(new_wsi);
+       compatible_close(fd.sockfd);
+
+	return NULL;
+}
+
+LWS_VISIBLE struct lws *
+lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
+{
+	lws_sock_file_fd_type fd;
+
+	fd.sockfd = accept_fd;
+	return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET |
+			LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL);
+}
+
+LWS_VISIBLE struct lws *
+lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
+{
+	return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
+}
+
+/* Common read-buffer adoption for lws_adopt_*_readbuf */
+static struct lws*
+adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
+{
+	struct lws_context_per_thread *pt;
+	struct lws_pollfd *pfd;
+	int n;
+
+	if (!wsi)
+		return NULL;
+
+	if (!readbuf || len == 0)
+		return wsi;
+
+	if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
+		return wsi;
+
+	pt = &wsi->context->pt[(int)wsi->tsi];
+
+	n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf,
+				       len);
+	if (n < 0)
+		goto bail;
+	if (n)
+		lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
+
+	/*
+	 * we can't process the initial read data until we can attach an ah.
+	 *
+	 * if one is available, get it and place the data in his ah rxbuf...
+	 * wsi with ah that have pending rxbuf get auto-POLLIN service.
+	 *
+	 * no autoservice because we didn't get a chance to attach the
+	 * readbuf data to wsi or ah yet, and we will do it next if we get
+	 * the ah.
+	 */
+	if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
+
+		lwsl_notice("%s: calling service on readbuf ah\n", __func__);
+
+		/* unlike a normal connect, we have the headers already
+		 * (or the first part of them anyway).
+		 * libuv won't come back and service us without a network
+		 * event, so we need to do the header service right here.
+		 */
+		pfd = &pt->fds[wsi->position_in_fds_table];
+		pfd->revents |= LWS_POLLIN;
+		lwsl_err("%s: calling service\n", __func__);
+		if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi))
+			/* service closed us */
+			return NULL;
+
+		return wsi;
+	}
+	lwsl_err("%s: deferring handling ah\n", __func__);
+
+	return wsi;
+
+bail:
+	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+			   "adopt skt readbuf fail");
+
+	return NULL;
+}
+
+LWS_EXTERN struct lws *
+lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
+		     const char *protocol_name, struct lws *parent_wsi)
+{
+	lws_sock_file_fd_type sock;
+	struct addrinfo h, *r, *rp;
+	struct lws *wsi = NULL;
+	char buf[16];
+	int n;
+
+	memset(&h, 0, sizeof(h));
+	h.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+	h.ai_socktype = SOCK_DGRAM;
+	h.ai_protocol = IPPROTO_UDP;
+	h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+
+	lws_snprintf(buf, sizeof(buf), "%u", port);
+	n = getaddrinfo(NULL, buf, &h, &r);
+	if (n) {
+		lwsl_info("%s: getaddrinfo error: %s\n", __func__,
+			  gai_strerror(n));
+		goto bail;
+	}
+
+	for (rp = r; rp; rp = rp->ai_next) {
+		sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
+				     rp->ai_protocol);
+		if (sock.sockfd >= 0)
+			break;
+	}
+	if (!rp) {
+		lwsl_err("%s: unable to create INET socket\n", __func__);
+		goto bail1;
+	}
+
+	if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr,
+#if defined(_WIN32)
+			    (int)rp->ai_addrlen
+#else
+			    rp->ai_addrlen
+#endif
+	   ) == -1) {
+		lwsl_err("%s: bind failed\n", __func__);
+		goto bail2;
+	}
+
+	wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,
+				        protocol_name, parent_wsi);
+	if (!wsi)
+		lwsl_err("%s: udp adoption failed\n", __func__);
+
+bail2:
+	if (!wsi)
+		close((int)sock.sockfd);
+bail1:
+	freeaddrinfo(r);
+
+bail:
+	return wsi;
+}
+
+LWS_VISIBLE struct lws *
+lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
+			 const char *readbuf, size_t len)
+{
+        return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd),
+				     readbuf, len);
+}
+
+LWS_VISIBLE struct lws *
+lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
+			       lws_sockfd_type accept_fd,
+			       const char *readbuf, size_t len)
+{
+        return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd),
+				     readbuf, len);
+}
diff --git a/lib/core/connect.c b/lib/core/connect.c
new file mode 100644
index 0000000000000000000000000000000000000000..ace3dc18eec4fafbd9f5f898d89dfd3d06cbe361
--- /dev/null
+++ b/lib/core/connect.c
@@ -0,0 +1,229 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * 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
+ *  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
+ */
+
+#include "core/private.h"
+
+void
+lws_client_stash_destroy(struct lws *wsi)
+{
+	if (!wsi || !wsi->stash)
+		return;
+
+	lws_free_set_NULL(wsi->stash->address);
+	lws_free_set_NULL(wsi->stash->path);
+	lws_free_set_NULL(wsi->stash->host);
+	lws_free_set_NULL(wsi->stash->origin);
+	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);
+}
+
+LWS_VISIBLE struct lws *
+lws_client_connect_via_info(const struct lws_client_connect_info *i)
+{
+	struct lws *wsi;
+	const struct lws_protocols *p;
+	const char *local = i->protocol;
+
+	if (i->context->requested_kill)
+		return NULL;
+
+	if (!i->context->protocol_init_done)
+		lws_protocol_init(i->context);
+	/*
+	 * If we have .local_protocol_name, use it to select the local protocol
+	 * handler to bind to.  Otherwise use .protocol if http[s].
+	 */
+	if (i->local_protocol_name)
+		local = i->local_protocol_name;
+
+	/* PHASE 1: create a bare wsi */
+
+	wsi = lws_zalloc(sizeof(struct lws), "client wsi");
+	if (wsi == NULL)
+		goto bail;
+
+	wsi->context = i->context;
+	wsi->desc.sockfd = LWS_SOCK_INVALID;
+
+	/*
+	 * PHASE 2: Choose an initial role for the wsi and do role-specific init
+	 *
+	 * Note the initial role may not reflect the final role, eg,
+	 * we may want ws, but first we have to go through h1 to get that
+	 */
+
+	lws_role_call_client_bind(wsi, i);
+
+	/*
+	 * PHASE 3: fill up the wsi with stuff from the connect_info as far as
+	 * it can go.  It's uncertain because not only is our connection
+	 * going to complete asynchronously, we might have bound to h1 and not
+	 * even be able to get ahold of an ah immediately.
+	 */
+
+	wsi->user_space = NULL;
+	wsi->pending_timeout = NO_PENDING_TIMEOUT;
+	wsi->position_in_fds_table = LWS_NO_FDS_POS;
+	wsi->c_port = i->port;
+	wsi->vhost = i->vhost;
+	if (!wsi->vhost)
+		wsi->vhost = i->context->vhost_list;
+
+	if (!wsi->vhost) {
+		lwsl_err("%s: No vhost in the context\n", __func__);
+
+		goto bail;
+	}
+
+	wsi->protocol = &wsi->vhost->protocols[0];
+	wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
+
+	if (local) {
+		lwsl_info("%s: protocol binding to %s\n", __func__, local);
+		p = lws_vhost_name_to_protocol(wsi->vhost, local);
+		if (p)
+			wsi->protocol = p;
+	}
+
+	/*
+	 * PHASE 4: handle external user_space now, generic alloc is done in
+	 * role finalization
+	 */
+
+	if (wsi && !wsi->user_space && i->userdata) {
+		wsi->user_space_externally_allocated = 1;
+		wsi->user_space = i->userdata;
+	}
+
+#if defined(LWS_WITH_TLS)
+	wsi->tls.use_ssl = i->ssl_connection;
+#else
+	if (i->ssl_connection & LCCSCF_USE_SSL) {
+		lwsl_err("%s: lws not configured for tls\n", __func__);
+		goto bail;
+	}
+#endif
+
+	/*
+	 * PHASE 5: stash the things from connect_info that we can't process
+	 * right now, eg, if http binding, without an ah.  If h1 and no ah, we
+	 * will go on the ah waiting list and process those things later (after
+	 * the connect_info and maybe the things pointed to have gone out of
+	 * scope)
+	 *
+	 * However these things are stashed in a generic way at this point,
+	 * with no relationship to http or ah
+	 */
+
+	wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash");
+	if (!wsi->stash) {
+		lwsl_err("%s: OOM\n", __func__);
+		goto bail1;
+	}
+
+	wsi->stash->address = lws_strdup(i->address);
+	wsi->stash->path = lws_strdup(i->path);
+	wsi->stash->host = lws_strdup(i->host);
+
+	if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host)
+		goto bail1;
+
+	if (i->origin) {
+		wsi->stash->origin = lws_strdup(i->origin);
+		if (!wsi->stash->origin)
+			goto bail1;
+	}
+	if (i->protocol) {
+		wsi->stash->protocol = lws_strdup(i->protocol);
+		if (!wsi->stash->protocol)
+			goto bail1;
+	}
+	if (i->method) {
+		wsi->stash->method = lws_strdup(i->method);
+		if (!wsi->stash->method)
+			goto bail1;
+	}
+	if (i->iface) {
+		wsi->stash->iface = lws_strdup(i->iface);
+		if (!wsi->stash->iface)
+			goto bail1;
+	}
+	if (i->alpn) {
+		wsi->stash->alpn = lws_strdup(i->alpn);
+		if (!wsi->stash->alpn)
+			goto bail1;
+	}
+
+	/*
+	 * PHASE 6: Do any role-specific finalization processing.  We can still
+	 * see important info things via wsi->stash
+	 */
+
+	if (wsi->role_ops->client_bind) {
+		int n = wsi->role_ops->client_bind(wsi, NULL);
+
+		if (n < 0)
+			/* we didn't survive, wsi is freed */
+			goto bail2;
+
+		if (n)
+			/* something else failed, wsi needs freeing */
+			goto bail;
+	}
+
+	/* let the caller's optional wsi storage have the wsi we created */
+
+	if (i->pwsi)
+		*i->pwsi = wsi;
+
+	if (i->parent_wsi) {
+		lwsl_info("%s: created child %p of parent %p\n", __func__,
+			  wsi, i->parent_wsi);
+		wsi->parent = i->parent_wsi;
+		wsi->sibling_list = i->parent_wsi->child_list;
+		i->parent_wsi->child_list = wsi;
+	}
+#ifdef LWS_WITH_HTTP_PROXY
+	if (i->uri_replace_to)
+		wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
+					     i->uri_replace_from,
+					     i->uri_replace_to);
+#endif
+
+	return wsi;
+
+bail1:
+	lws_client_stash_destroy(wsi);
+
+bail:
+	lws_free(wsi);
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+bail2:
+#endif
+	if (i->pwsi)
+		*i->pwsi = NULL;
+
+	return NULL;
+}
diff --git a/lib/core/context.c b/lib/core/context.c
index db9151b95f2ba76cddf9944a074bc60ef58142b6..dd4bfd20ea23289d22786ace45fb610b4dc021e0 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -86,6 +86,57 @@ lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
 	return 0;
 }
 
+#if !defined(LWS_WITHOUT_SERVER)
+int
+lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot)
+{
+	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
+		if (ar->adoption_bind)
+			if (ar->adoption_bind(wsi, type, prot))
+				return 0;
+	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
+
+	/* fall back to raw socket role if, eg, h1 not configured */
+
+	if (role_ops_raw_skt.adoption_bind &&
+	    role_ops_raw_skt.adoption_bind(wsi, type, prot))
+		return 0;
+
+	/* fall back to raw file role if, eg, h1 not configured */
+
+	if (role_ops_raw_file.adoption_bind &&
+	    role_ops_raw_file.adoption_bind(wsi, type, prot))
+		return 0;
+
+	return 1;
+}
+#endif
+
+#if !defined(LWS_WITHOUT_CLIENT)
+int
+lws_role_call_client_bind(struct lws *wsi,
+			  const struct lws_client_connect_info *i)
+{
+	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
+		if (ar->client_bind) {
+			int m = ar->client_bind(wsi, i);
+			if (m < 0)
+				return m;
+			if (m)
+				return 0;
+		}
+	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
+
+	/* fall back to raw socket role if, eg, h1 not configured */
+
+	if (role_ops_raw_skt.client_bind &&
+	    role_ops_raw_skt.client_bind(wsi, i))
+		return 0;
+
+	return 1;
+}
+#endif
+
 static const char * const mount_protocols[] = {
 	"http://",
 	"https://",
diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c
index 0da02b17e491ca5143ac65523db1a674d23a09b8..e560b36b06f94b79ac1670ca34127dc23b4468b7 100644
--- a/lib/core/libwebsockets.c
+++ b/lib/core/libwebsockets.c
@@ -1149,9 +1149,6 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
 
 	wsi = lws_get_network_wsi(wsi);
 
-	if (wsi->parent_carries_io)
-		wsi = wsi->parent;
-
 #ifdef LWS_WITH_IPV6
 	if (LWS_IPV6_ENABLED(wsi->vhost)) {
 		len = sizeof(sin6);
@@ -2158,12 +2155,6 @@ lws_get_child(const struct lws *wsi)
 	return wsi->child_list;
 }
 
-LWS_VISIBLE LWS_EXTERN void
-lws_set_parent_carries_io(struct lws *wsi)
-{
-	wsi->parent_carries_io = 1;
-}
-
 LWS_VISIBLE LWS_EXTERN void *
 lws_get_opaque_parent_data(const struct lws *wsi)
 {
@@ -2350,6 +2341,17 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
 	return 0;
 }
 
+char *
+lws_strdup(const char *s)
+{
+	char *d = lws_malloc(strlen(s) + 1, "strdup");
+
+	if (d)
+		strcpy(d, s);
+
+	return d;
+}
+
 #if defined(LWS_WITHOUT_EXTENSIONS)
 
 /* we need to provide dummy callbacks for internal exts
@@ -2619,73 +2621,6 @@ lws_get_addr_scope(const char *ipaddr)
 }
 #endif
 
-#if !defined(LWS_NO_SERVER)
-
-LWS_EXTERN struct lws *
-lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
-		     const char *protocol_name, struct lws *parent_wsi)
-{
-	lws_sock_file_fd_type sock;
-	struct addrinfo h, *r, *rp;
-	struct lws *wsi = NULL;
-	char buf[16];
-	int n;
-
-	memset(&h, 0, sizeof(h));
-	h.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
-	h.ai_socktype = SOCK_DGRAM;
-	h.ai_protocol = IPPROTO_UDP;
-	h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
-
-	lws_snprintf(buf, sizeof(buf), "%u", port);
-	n = getaddrinfo(NULL, buf, &h, &r);
-	if (n) {
-		lwsl_info("%s: getaddrinfo error: %s\n", __func__,
-			  gai_strerror(n));
-		goto bail;
-	}
-
-	for (rp = r; rp; rp = rp->ai_next) {
-		sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
-				     rp->ai_protocol);
-		if (sock.sockfd >= 0)
-			break;
-	}
-	if (!rp) {
-		lwsl_err("%s: unable to create INET socket\n", __func__);
-		goto bail1;
-	}
-
-	if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr,
-#if defined(_WIN32)
-			    (int)rp->ai_addrlen
-#else
-			    rp->ai_addrlen
-#endif
-	   ) == -1) {
-		lwsl_err("%s: bind failed\n", __func__);
-		goto bail2;
-	}
-
-	wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,
-				        protocol_name, parent_wsi);
-	if (!wsi)
-		lwsl_err("%s: udp adoption failed\n", __func__);
-
-bail2:
-	if (!wsi)
-		close((int)sock.sockfd);
-bail1:
-	freeaddrinfo(r);
-
-bail:
-	return wsi;
-}
-
-#endif
-
-
-
 static const char *hex = "0123456789ABCDEF";
 
 LWS_VISIBLE LWS_EXTERN const char *
diff --git a/lib/core/output.c b/lib/core/output.c
index e2ff18ef275a1fa27737109a4a8b2146ea8873c0..6aa8d057b5721c04a333b693393a674bf5ba005b 100644
--- a/lib/core/output.c
+++ b/lib/core/output.c
@@ -187,23 +187,6 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 {
 	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
 
-	if (wsi->parent_carries_io) {
-		struct lws_write_passthru pas;
-
-		pas.buf = buf;
-		pas.len = len;
-		pas.wp = wp;
-		pas.wsi = wsi;
-
-		if (wsi->parent->protocol->callback(wsi->parent,
-				LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
-				wsi->parent->user_space,
-				(void *)&pas, 0))
-			return 1;
-
-		return (int)len;
-	}
-
 	lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
 
 	if ((int)len < 0) {
diff --git a/lib/core/pollfd.c b/lib/core/pollfd.c
index 2a632ce8ec3ea60607ef03dceeab0838cf1d53f6..24f7c7ab765ecb883a37edfb3e49fa8f6d8239b5 100644
--- a/lib/core/pollfd.c
+++ b/lib/core/pollfd.c
@@ -298,11 +298,6 @@ __remove_wsi_socket_from_fds(struct lws *wsi)
 	int v;
 	int m, ret = 0;
 
-	if (wsi->parent_carries_io) {
-		lws_same_vh_protocol_remove(wsi);
-		return 0;
-	}
-
 #if !defined(_WIN32)
 	if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) {
 		lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
@@ -426,7 +421,6 @@ LWS_VISIBLE int
 lws_callback_on_writable(struct lws *wsi)
 {
 	struct lws_context_per_thread *pt;
-	int n;
 
 	if (lwsi_state(wsi) == LRS_SHUTDOWN)
 		return 0;
@@ -436,22 +430,6 @@ lws_callback_on_writable(struct lws *wsi)
 
 	pt = &wsi->context->pt[(int)wsi->tsi];
 
-	if (wsi->parent_carries_io) {
-#if defined(LWS_WITH_STATS)
-		if (!wsi->active_writable_req_us) {
-			wsi->active_writable_req_us = time_in_microseconds();
-			lws_stats_atomic_bump(wsi->context, pt,
-					      LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
-		}
-#endif
-		n = lws_callback_on_writable(wsi->parent);
-		if (n < 0)
-			return n;
-
-		wsi->parent_pending_cb_on_writable = 1;
-		return 1;
-	}
-
 	lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1);
 #if defined(LWS_WITH_STATS)
 	if (!wsi->active_writable_req_us) {
diff --git a/lib/core/private.h b/lib/core/private.h
index 0597df4b6b2082ebfa04107003a73ea46047b3d9..2e64000017c7b8da41058cdb4b5c3ef445c9e088 100644
--- a/lib/core/private.h
+++ b/lib/core/private.h
@@ -1120,7 +1120,6 @@ struct lws {
 	unsigned int waiting_to_send_close_frame:1;
 	unsigned int close_needs_ack:1;
 	unsigned int ipv6:1;
-	unsigned int parent_carries_io:1;
 	unsigned int parent_pending_cb_on_writable:1;
 	unsigned int cgi_stdout_zero_length:1;
 	unsigned int seen_zero_length_recv:1;
@@ -1186,6 +1185,9 @@ struct lws {
 	volatile char leave_pollout_active;
 };
 
+LWS_EXTERN char *
+lws_strdup(const char *s);
+
 #define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap))
 
 void
@@ -1297,7 +1299,7 @@ LWS_EXTERN int
 lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
 
 LWS_EXTERN struct lws *
-lws_client_connect_via_info2(struct lws *wsi);
+lws_http_client_connect_via_info2(struct lws *wsi);
 
 
 
@@ -1731,7 +1733,8 @@ lws_generate_client_ws_handshake(struct lws *wsi, char *p);
 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);
+lws_create_client_ws_object(const struct lws_client_connect_info *i,
+			    struct lws *wsi);
 int
 lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len);
 int
@@ -1745,6 +1748,9 @@ void
 lws_destroy_event_pipe(struct lws *wsi);
 void
 lws_context_destroy2(struct lws_context *context);
+int
+lws_role_call_client_bind(struct lws *wsi,
+			  const struct lws_client_connect_info *i);
 
 #ifdef __cplusplus
 };
diff --git a/lib/core/service.c b/lib/core/service.c
index 6523058814de992529002e92cbf60f8ed834ba51..7ecc769e0c606f0ab468c1ecbb19c967cde9a49d 100644
--- a/lib/core/service.c
+++ b/lib/core/service.c
@@ -126,13 +126,6 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 
 	/* one shot */
 
-	if (wsi->parent_carries_io) {
-		vwsi->handling_pollout = 0;
-		vwsi->leave_pollout_active = 0;
-
-		return lws_callback_as_writeable(wsi);
-	}
-
 	if (pollfd) {
 		int eff = vwsi->leave_pollout_active;
 
diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c
index 57947d4e239c803cc796c32562e73788e1b99d2a..b04cf556fe32db98bf8e949bf05389d3dc60299e 100644
--- a/lib/event-libs/libuv/libuv.c
+++ b/lib/event-libs/libuv/libuv.c
@@ -562,7 +562,7 @@ elops_destroy_context2_uv(struct lws_context *context)
 static int
 elops_wsi_logical_close_uv(struct lws *wsi)
 {
-	if (wsi->parent_carries_io || !lws_socket_is_valid(wsi->desc.sockfd))
+	if (!lws_socket_is_valid(wsi->desc.sockfd))
 		return 0;
 
 	if (wsi->listener || wsi->event_pipe) {
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index b125d1b888aba691a232d6b606695b6443898c41..c0a50bca88330bce8f1b3c47a821841675167dbd 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1565,13 +1565,6 @@ enum lws_callback_reasons {
 	 * destroyed.  in is the child wsi.
 	 */
 
-	LWS_CALLBACK_CHILD_WRITE_VIA_PARENT			= 68,
-	/**< Child has been marked with parent_carries_io attribute, so
-	 * lws_write directs the to this callback at the parent,
-	 * in is a struct lws_write_passthru containing the args
-	 * the lws_write() was called with.
-	 */
-
 	/* ---------------------------------------------------------------------
 	 * ----- Callbacks related to TLS certificate management -----
 	 */
@@ -3490,61 +3483,7 @@ struct lws_client_connect_info {
  *	information provided in ccinfo.
  */
 LWS_VISIBLE LWS_EXTERN struct lws *
-lws_client_connect_via_info(struct lws_client_connect_info * ccinfo);
-
-/**
- * lws_client_connect() - Connect to another websocket server
- * 		\deprecated DEPRECATED use lws_client_connect_via_info
- * \param clients:	Websocket context
- * \param address:	Remote server address, eg, "myserver.com"
- * \param port:	Port to connect to on the remote server, eg, 80
- * \param ssl_connection:	0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
- *			signed certs
- * \param path:	Websocket path on server
- * \param host:	Hostname on server
- * \param origin:	Socket origin name
- * \param protocol:	Comma-separated list of protocols being asked for from
- *		the server, or just one.  The server will pick the one it
- *		likes best.  If you don't want to specify a protocol, which is
- *		legal, use NULL here.
- * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest
- *		protocol supported, or the specific protocol ordinal
- *
- *	This function creates a connection to a remote server
- */
-/* deprecated, use lws_client_connect_via_info() */
-LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_client_connect(struct lws_context *clients, const char *address,
-		   int port, int ssl_connection, const char *path,
-		   const char *host, const char *origin, const char *protocol,
-		   int ietf_version_or_minus_one) LWS_WARN_DEPRECATED;
-/* deprecated, use lws_client_connect_via_info() */
-/**
- * lws_client_connect_extended() - Connect to another websocket server
- * 			\deprecated DEPRECATED use lws_client_connect_via_info
- * \param clients:	Websocket context
- * \param address:	Remote server address, eg, "myserver.com"
- * \param port:	Port to connect to on the remote server, eg, 80
- * \param ssl_connection:	0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
- *			signed certs
- * \param path:	Websocket path on server
- * \param host:	Hostname on server
- * \param origin:	Socket origin name
- * \param protocol:	Comma-separated list of protocols being asked for from
- *		the server, or just one.  The server will pick the one it
- *		likes best.
- * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest
- *		protocol supported, or the specific protocol ordinal
- * \param userdata: Pre-allocated user data
- *
- *	This function creates a connection to a remote server
- */
-LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_client_connect_extended(struct lws_context *clients, const char *address,
-			    int port, int ssl_connection, const char *path,
-			    const char *host, const char *origin,
-			    const char *protocol, int ietf_version_or_minus_one,
-			    void *userdata) LWS_WARN_DEPRECATED;
+lws_client_connect_via_info(const struct lws_client_connect_info *ccinfo);
 
 /**
  * lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost
@@ -5283,9 +5222,6 @@ typedef enum {
 	LWS_ADOPT_HTTP = 1,		/* flag: absent implies RAW */
 	LWS_ADOPT_SOCKET = 2,		/* flag: absent implies file descr */
 	LWS_ADOPT_ALLOW_SSL = 4,	/* flag: if set requires LWS_ADOPT_SOCKET */
-	LWS_ADOPT_WS_PARENTIO = 8,	/* flag: ws mode parent handles IO
-					 *   if given must be only flag
-					 *   wsi put directly into ws mode */
 	LWS_ADOPT_FLAG_UDP = 16,	/* flag: socket is UDP */
 
 	LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
@@ -5933,15 +5869,6 @@ lws_get_child(const struct lws *wsi);
 LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT
 lws_get_udp(const struct lws *wsi);
 
-/**
- * lws_parent_carries_io() - mark wsi as needing to send messages via parent
- *
- * \param wsi: child lws connection
- */
-
-LWS_VISIBLE LWS_EXTERN void
-lws_set_parent_carries_io(struct lws *wsi);
-
 LWS_VISIBLE LWS_EXTERN void *
 lws_get_opaque_parent_data(const struct lws *wsi);
 
diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c
index 8555f7baa5180b8b525b48a2c87cbdc3da87876a..0a3cc5cbb3cc117e078afaa8db6ca6da923c101b 100644
--- a/lib/roles/cgi/ops-cgi.c
+++ b/lib/roles/cgi/ops-cgi.c
@@ -96,6 +96,8 @@ struct lws_role_ops role_ops_cgi = {
 	/* close_role */		NULL,
 	/* close_kill_connection */	NULL,
 	/* destroy_role */		NULL,
+	/* adoption_bind */		NULL,
+	/* client_bind */		NULL,
 	/* writeable cb clnt, srv */	{ 0, 0 },
 	/* close cb clnt, srv */	{ 0, 0 },
 	/* file_handle */		0,
diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index 49dfe4b47f241747c215424a92150ab7c0b4aae0..938642502431f38ddae20241f1e14692596f1995 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -659,6 +659,133 @@ rops_destroy_role_h1(struct lws *wsi)
 	return 0;
 }
 
+#if !defined(LWS_NO_SERVER)
+
+static int
+rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name)
+{
+	if (!(type & LWS_ADOPT_HTTP))
+		return 0; /* no match */
+
+
+	if (type & _LWS_ADOPT_FINISH) {
+		if (!lws_header_table_attach(wsi, 0))
+			lwsl_debug("Attached ah immediately\n");
+		else
+			lwsl_info("%s: waiting for ah\n", __func__);
+
+		return 1;
+	}
+
+	lws_role_transition(wsi, LWSIFR_SERVER, type & LWS_ADOPT_ALLOW_SSL ?
+			    LRS_SSL_INIT : LRS_HEADERS, &role_ops_h1);
+
+	if (!vh_prot_name)
+		wsi->protocol = &wsi->vhost->protocols[
+					wsi->vhost->default_protocol_index];
+
+	/* the transport is accepted... give him time to negotiate */
+	lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
+			wsi->context->timeout_secs);
+
+	return 1; /* bound */
+}
+
+#endif
+
+#if !defined(LWS_NO_CLIENT)
+
+static const char * const http_methods[] = {
+	"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT"
+};
+
+static int
+rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i)
+{
+	int n;
+
+	if (!i) {
+		/* we are finalizing an already-selected role */
+
+		/*
+		 * If we stay in http, assuming there wasn't already-set
+		 * external user_space, since we know our initial protocol
+		 * we can assign the user space now, otherwise do it after the
+		 * ws subprotocol negotiated
+		 */
+		if (!wsi->user_space && wsi->stash->method)
+			if (lws_ensure_user_space(wsi))
+				return 1;
+
+		 /*
+		  * For ws, default to http/1.1 only.  If i->alpn had been set
+		  * though, 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 h2 if he assertively said to use h2 alpn, otherwise
+		  * ws implies alpn restriction to h1.
+		  */
+		if (!wsi->stash->method && !wsi->stash->alpn) {
+			wsi->stash->alpn = lws_strdup("http/1.1");
+			if (!wsi->stash->alpn)
+				return 1;
+		}
+
+		/* if we went on the ah waiting list, it's ok, we can wait.
+		 *
+		 * When we do get the ah, now or later, he will end up at
+		 * lws_http_client_connect_via_info2().
+		 */
+		if (lws_header_table_attach(wsi, 0) < 0)
+			/*
+			 * if we failed here, the connection is already closed
+			 * and freed.
+			 */
+			return -1;
+
+		return 0;
+	}
+
+	/*
+	 * Clients that want to be h1, h2, or ws all start out as h1
+	 * (we don't yet know if the server supports h2 or ws)
+	 */
+
+	if (!i->method) { /* websockets */
+#if defined(LWS_ROLE_WS)
+		if (lws_create_client_ws_object(i, wsi))
+			goto fail_wsi;
+#else
+		lwsl_err("%s: ws role not configured\n", __func__);
+
+		goto fail_wsi;
+#endif
+		goto bind_h1;
+	}
+
+	/* if a recognized http method, bind to it */
+
+	for (n = 0; n < (int)LWS_ARRAY_SIZE(http_methods); n++)
+		if (!strcmp(i->method, http_methods[n]))
+			goto bind_h1;
+
+	/* other roles may bind to it */
+
+	return 0; /* no match */
+
+bind_h1:
+	/* assert the mode and union status (hdr) clearly */
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
+
+	return 1; /* matched */
+
+fail_wsi:
+	return -1;
+}
+#endif
+
 struct lws_role_ops role_ops_h1 = {
 	/* role name */			"h1",
 	/* alpn id */			"http/1.1",
@@ -680,6 +807,16 @@ struct lws_role_ops role_ops_h1 = {
 	/* close_role */		NULL,
 	/* close_kill_connection */	NULL,
 	/* destroy_role */		rops_destroy_role_h1,
+#if !defined(LWS_NO_SERVER)
+	/* adoption_bind */		rops_adoption_bind_h1,
+#else
+					NULL,
+#endif
+#if !defined(LWS_NO_CLIENT)
+	/* client_bind */		rops_client_bind_h1,
+#else
+					NULL,
+#endif
 	/* writeable cb clnt, srv */	{ LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
 					  LWS_CALLBACK_HTTP_WRITEABLE },
 	/* close cb clnt, srv */	{ LWS_CALLBACK_CLOSED_CLIENT_HTTP,
diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c
index 0e0afeeacc19fba6614c84bec2553bec80bb1153..e4eebbb6a275341056ab5dd723dad2daf6219f27 100644
--- a/lib/roles/h2/ops-h2.c
+++ b/lib/roles/h2/ops-h2.c
@@ -1076,6 +1076,8 @@ struct lws_role_ops role_ops_h2 = {
 	/* close_role */		NULL,
 	/* close_kill_connection */	rops_close_kill_connection_h2,
 	/* destroy_role */		rops_destroy_role_h2,
+	/* adoption_bind */		NULL,
+	/* client_bind */		NULL,
 	/* writeable cb clnt, srv */	{ LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
 					  LWS_CALLBACK_HTTP_WRITEABLE },
 	/* close cb clnt, srv */	{ LWS_CALLBACK_CLOSED_CLIENT_HTTP,
diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c
index 4830fc9ecae378374ef11c4ebb4ececd19d747b7..36cf43192d3286dc9e7f91820f2403dba5564ba8 100644
--- a/lib/roles/http/client/client-handshake.c
+++ b/lib/roles/http/client/client-handshake.c
@@ -846,252 +846,8 @@ html_parser_cb(const hubbub_token *token, void *pw)
 
 #endif
 
-static char *
-lws_strdup(const char *s)
-{
-	char *d = lws_malloc(strlen(s) + 1, "strdup");
-
-	if (d)
-		strcpy(d, s);
-
-	return d;
-}
-
-void
-lws_client_stash_destroy(struct lws *wsi)
-{
-	if (!wsi || !wsi->stash)
-		return;
-
-	lws_free_set_NULL(wsi->stash->address);
-	lws_free_set_NULL(wsi->stash->path);
-	lws_free_set_NULL(wsi->stash->host);
-	lws_free_set_NULL(wsi->stash->origin);
-	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);
-}
-
-LWS_VISIBLE struct lws *
-lws_client_connect_via_info(struct lws_client_connect_info *i)
-{
-	struct lws *wsi;
-	const struct lws_protocols *p;
-	const char *local = i->protocol;
-
-	if (i->context->requested_kill)
-		return NULL;
-
-	if (!i->context->protocol_init_done)
-		lws_protocol_init(i->context);
-	/*
-	 * If we have .local_protocol_name, use it to select the
-	 * local protocol handler to bind to.  Otherwise use .protocol if
-	 * http[s].
-	 */
-	if (i->local_protocol_name)
-		local = i->local_protocol_name;
-
-	wsi = lws_zalloc(sizeof(struct lws), "client wsi");
-	if (wsi == NULL)
-		goto bail;
-
-	wsi->context = i->context;
-#if defined(LWS_ROLE_H1)
-	/* assert the mode and union status (hdr) clearly */
-	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
-#else
-	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_raw_skt);
-#endif
-	wsi->desc.sockfd = LWS_SOCK_INVALID;
-
-	/* 1) fill up the wsi with stuff from the connect_info as far as it
-	 * can go.  It's because not only is our connection async, we might
-	 * not even be able to get ahold of an ah at this point.
-	 */
-
-	if (!i->method) /* ie, ws */
-#if defined(LWS_ROLE_WS)
-		if (lws_create_client_ws_object(i, wsi))
-			return NULL;
-#else
-		return NULL;
-#endif
-
-	wsi->user_space = NULL;
-	wsi->pending_timeout = NO_PENDING_TIMEOUT;
-	wsi->position_in_fds_table = LWS_NO_FDS_POS;
-	wsi->c_port = i->port;
-	wsi->vhost = i->vhost;
-	if (!wsi->vhost)
-		wsi->vhost = i->context->vhost_list;
-
-	if (!wsi->vhost) {
-		lwsl_err("At least one vhost in the context is required\n");
-
-		goto bail;
-	}
-
-	wsi->protocol = &wsi->vhost->protocols[0];
-	wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
-
-	/* reasonable place to start */
-	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
-#if defined(LWS_ROLE_H1)
-			&role_ops_h1);
-#else
-	&role_ops_raw_skt);
-#endif
-
-	/*
-	 * 1) for http[s] connection, allow protocol selection by name
-	 * 2) for ws[s], if local_protocol_name given also use it for
-	 *    local protocol binding... this defeats the server
-	 *    protocol negotiation if so
-	 *
-	 * Otherwise leave at protocols[0]... the server will tell us
-	 * which protocol we are associated with since we can give it a
-	 * list.
-	 */
-	if (/*(i->method || i->local_protocol_name) && */local) {
-		lwsl_info("binding to %s\n", local);
-		p = lws_vhost_name_to_protocol(wsi->vhost, local);
-		if (p)
-			wsi->protocol = p;
-	}
-
-	if (wsi && !wsi->user_space && i->userdata) {
-		wsi->user_space_externally_allocated = 1;
-		wsi->user_space = i->userdata;
-	} else
-		/* if we stay in http, we can assign the user space now,
-		 * otherwise do it after the protocol negotiated
-		 */
-		if (i->method)
-			if (lws_ensure_user_space(wsi))
-				goto bail;
-
-#if defined(LWS_WITH_TLS)
-	wsi->tls.use_ssl = i->ssl_connection;
-#else
-	if (i->ssl_connection & LCCSCF_USE_SSL) {
-		lwsl_err("libwebsockets not configured for ssl\n");
-		goto bail;
-	}
-#endif
-
-	/* 2) stash the things from connect_info that we can't process without
-	 * an ah.  Because if no ah, we will go on the ah waiting list and
-	 * process those things later (after the connect_info and maybe the
-	 * things pointed to have gone out of scope.
-	 */
-
-	wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash");
-	if (!wsi->stash) {
-		lwsl_err("%s: OOM\n", __func__);
-		goto bail1;
-	}
-
-	wsi->stash->address = lws_strdup(i->address);
-	wsi->stash->path = lws_strdup(i->path);
-	wsi->stash->host = lws_strdup(i->host);
-
-	if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host)
-		goto bail1;
-
-	if (i->origin) {
-		wsi->stash->origin = lws_strdup(i->origin);
-		if (!wsi->stash->origin)
-			goto bail1;
-	}
-	if (i->protocol) {
-		wsi->stash->protocol = lws_strdup(i->protocol);
-		if (!wsi->stash->protocol)
-			goto bail1;
-	}
-	if (i->method) {
-		wsi->stash->method = lws_strdup(i->method);
-		if (!wsi->stash->method)
-			goto bail1;
-	}
-	if (i->iface) {
-		wsi->stash->iface = lws_strdup(i->iface);
-		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;
-
-#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
-	/* if we went on the waiting list, no probs just return the wsi
-	 * when we get the ah, now or later, he will call
-	 * lws_client_connect_via_info2() below.
-	 */
-	if (lws_header_table_attach(wsi, 0) < 0) {
-		/*
-		 * if we failed here, the connection is already closed
-		 * and freed.
-		 */
-		goto bail2;
-	}
-
-#endif
-
-	if (i->parent_wsi) {
-		lwsl_info("%s: created child %p of parent %p\n", __func__,
-				wsi, i->parent_wsi);
-		wsi->parent = i->parent_wsi;
-		wsi->sibling_list = i->parent_wsi->child_list;
-		i->parent_wsi->child_list = wsi;
-	}
-#ifdef LWS_WITH_HTTP_PROXY
-	if (i->uri_replace_to)
-		wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
-					     i->uri_replace_from,
-					     i->uri_replace_to);
-#endif
-
-	return wsi;
-
-bail1:
-	lws_client_stash_destroy(wsi);
-
-bail:
-	lws_free(wsi);
-#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
-bail2:
-#endif
-	if (i->pwsi)
-		*i->pwsi = NULL;
-
-	return NULL;
-}
-
 struct lws *
-lws_client_connect_via_info2(struct lws *wsi)
+lws_http_client_connect_via_info2(struct lws *wsi)
 {
 	struct client_info_stash *stash = wsi->stash;
 
@@ -1156,55 +912,6 @@ bail1:
 	return NULL;
 }
 
-LWS_VISIBLE struct lws *
-lws_client_connect_extended(struct lws_context *context, const char *address,
-			    int port, int ssl_connection, const char *path,
-			    const char *host, const char *origin,
-			    const char *protocol, int ietf_version_or_minus_one,
-			    void *userdata)
-{
-	struct lws_client_connect_info i;
-
-	memset(&i, 0, sizeof(i));
-
-	i.context = context;
-	i.address = address;
-	i.port = port;
-	i.ssl_connection = ssl_connection;
-	i.path = path;
-	i.host = host;
-	i.origin = origin;
-	i.protocol = protocol;
-	i.ietf_version_or_minus_one = ietf_version_or_minus_one;
-	i.userdata = userdata;
-
-	return lws_client_connect_via_info(&i);
-}
-
-LWS_VISIBLE struct lws *
-lws_client_connect(struct lws_context *context, const char *address,
-			    int port, int ssl_connection, const char *path,
-			    const char *host, const char *origin,
-			    const char *protocol, int ietf_version_or_minus_one)
-{
-	struct lws_client_connect_info i;
-
-	memset(&i, 0, sizeof(i));
-
-	i.context = context;
-	i.address = address;
-	i.port = port;
-	i.ssl_connection = ssl_connection;
-	i.path = path;
-	i.host = host;
-	i.origin = origin;
-	i.protocol = protocol;
-	i.ietf_version_or_minus_one = ietf_version_or_minus_one;
-	i.userdata = NULL;
-
-	return lws_client_connect_via_info(&i);
-}
-
 #if defined(LWS_WITH_SOCKS5)
 void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
 			ssize_t *msg_len)
diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c
index cb022e362bef07aad1247bb455f7b3ff42483692..31851862e240bc96dc38c6eec33db7c204f1efa3 100644
--- a/lib/roles/http/server/parsers.c
+++ b/lib/roles/http/server/parsers.c
@@ -259,7 +259,7 @@ reset:
 
 #ifndef LWS_NO_CLIENT
 	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
-		if (!lws_client_connect_via_info2(wsi))
+		if (!lws_http_client_connect_via_info2(wsi))
 			/* our client connect has failed, the wsi
 			 * has been closed
 			 */
@@ -386,7 +386,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
 		lws_pt_unlock(pt);
 
-		if (!lws_client_connect_via_info2(wsi)) {
+		if (!lws_http_client_connect_via_info2(wsi)) {
 			/* our client connect has failed, the wsi
 			 * has been closed
 			 */
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index 350af3cd7eaf3b979225d4e186aeddab5cb0aafe..c5e0b88a9cddc2a8a98eeb1a39cd77cd5d38b43c 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -1630,85 +1630,6 @@ bail_nuke_ah:
 }
 
 
-static int
-lws_get_idlest_tsi(struct lws_context *context)
-{
-	unsigned int lowest = ~0;
-	int n = 0, hit = -1;
-
-	for (; n < context->count_threads; n++) {
-		if ((unsigned int)context->pt[n].fds_count !=
-		    context->fd_limit_per_thread - 1 &&
-		    (unsigned int)context->pt[n].fds_count < lowest) {
-			lowest = context->pt[n].fds_count;
-			hit = n;
-		}
-	}
-
-	return hit;
-}
-
-struct lws *
-lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
-{
-	struct lws *new_wsi;
-	int n = fixed_tsi;
-
-	if (n < 0)
-		n = lws_get_idlest_tsi(vhost->context);
-
-	if (n < 0) {
-		lwsl_err("no space for new conn\n");
-		return NULL;
-	}
-
-	new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi");
-	if (new_wsi == NULL) {
-		lwsl_err("Out of memory for new connection\n");
-		return NULL;
-	}
-
-	new_wsi->tsi = n;
-	lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi,
-		   vhost->name, new_wsi->tsi);
-
-	new_wsi->vhost = vhost;
-	new_wsi->context = vhost->context;
-	new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
-	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
-	/* initialize the instance struct */
-
-	lwsi_set_state(new_wsi, LRS_UNCONNECTED);
-	new_wsi->hdr_parsing_completed = 0;
-
-#ifdef LWS_WITH_TLS
-	new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost);
-#endif
-
-	/*
-	 * these can only be set once the protocol is known
-	 * we set an un-established connection's protocol pointer
-	 * to the start of the supported list, so it can look
-	 * for matching ones during the handshake
-	 */
-	new_wsi->protocol = vhost->protocols;
-	new_wsi->user_space = NULL;
-	new_wsi->desc.sockfd = LWS_SOCK_INVALID;
-	new_wsi->position_in_fds_table = LWS_NO_FDS_POS;
-
-	vhost->context->count_wsi_allocated++;
-
-	/*
-	 * outermost create notification for wsi
-	 * no user_space because no protocol selection
-	 */
-	vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
-				       NULL, NULL, 0);
-
-	return new_wsi;
-}
-
 LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
 lws_http_transaction_completed(struct lws *wsi)
 {
@@ -1821,325 +1742,6 @@ lws_http_transaction_completed(struct lws *wsi)
 	return 0;
 }
 
-/* if not a socket, it's a raw, non-ssl file descriptor */
-
-LWS_VISIBLE struct lws *
-lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
-			   lws_sock_file_fd_type fd, const char *vh_prot_name,
-			   struct lws *parent)
-{
-	struct lws_context *context = vh->context;
-	struct lws *new_wsi;
-	struct lws_context_per_thread *pt;
-	int n, ssl = 0;
-
-#if defined(LWS_WITH_PEER_LIMITS)
-	struct lws_peer *peer = NULL;
-
-	if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) {
-		peer = lws_get_or_create_peer(vh, fd.sockfd);
-
-		if (peer && context->ip_limit_wsi &&
-		    peer->count_wsi >= context->ip_limit_wsi) {
-			lwsl_notice("Peer reached wsi limit %d\n",
-					context->ip_limit_wsi);
-			lws_stats_atomic_bump(context, &context->pt[0],
-					      LWSSTATS_C_PEER_LIMIT_WSI_DENIED, 1);
-			return NULL;
-		}
-	}
-#endif
-
-	n = -1;
-	if (parent)
-		n = parent->tsi;
-	new_wsi = lws_create_new_server_wsi(vh, n);
-	if (!new_wsi) {
-		if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO))
-			compatible_close(fd.sockfd);
-		return NULL;
-	}
-#if defined(LWS_WITH_PEER_LIMITS)
-	if (peer)
-		lws_peer_add_wsi(context, peer, new_wsi);
-#endif
-	pt = &context->pt[(int)new_wsi->tsi];
-	lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1);
-
-	if (parent) {
-		new_wsi->parent = parent;
-		new_wsi->sibling_list = parent->child_list;
-		parent->child_list = new_wsi;
-
-		if (type & LWS_ADOPT_WS_PARENTIO)
-			new_wsi->parent_carries_io = 1;
-	}
-
-	new_wsi->desc = fd;
-
-	if (vh_prot_name) {
-		new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost,
-							       vh_prot_name);
-		if (!new_wsi->protocol) {
-			lwsl_err("Protocol %s not enabled on vhost %s\n",
-				 vh_prot_name, new_wsi->vhost->name);
-			goto bail;
-		}
-               if (lws_ensure_user_space(new_wsi)) {
-                       lwsl_notice("OOM trying to get user_space\n");
-			goto bail;
-               }
-#if defined(LWS_ROLE_WS)
-               if (type & LWS_ADOPT_WS_PARENTIO) {
-			new_wsi->desc.sockfd = LWS_SOCK_INVALID;
-			lwsl_debug("binding to %s\n", new_wsi->protocol->name);
-			lws_bind_protocol(new_wsi, new_wsi->protocol);
-			lws_role_transition(new_wsi, LWSIFR_SERVER,
-					    LRS_ESTABLISHED, &role_ops_ws);
-			/* allocate the ws struct for the wsi */
-			new_wsi->ws = lws_zalloc(sizeof(*new_wsi->ws), "ws struct");
-			if (!new_wsi->ws) {
-				lwsl_notice("OOM\n");
-				goto bail;
-			}
-			lws_server_init_wsi_for_ws(new_wsi);
-
-			return new_wsi;
-               }
-#endif
-	} else
-#if defined(LWS_ROLE_H1)
-		if (type & LWS_ADOPT_HTTP) {/* he will transition later */
-			new_wsi->protocol =
-				&vh->protocols[vh->default_protocol_index];
-			new_wsi->role_ops = &role_ops_h1;
-		}
-		else
-#endif
-		{ /* this is the only time he will transition */
-			lws_bind_protocol(new_wsi,
-				&vh->protocols[vh->raw_protocol_index]);
-			lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
-					    &role_ops_raw_skt);
-		}
-
-	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...
-			 * give him time to negotiate */
-			lws_set_timeout(new_wsi,
-					PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
-					context->timeout_secs);
-
-	} else /* file desc */
-		lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi,
-			   (int)(lws_intptr_t)fd.filefd);
-
-	/*
-	 * A new connection was accepted. Give the user a chance to
-	 * set properties of the newly created wsi. There's no protocol
-	 * selected yet so we issue this to the vhosts's default protocol,
-	 * itself by default protocols[0]
-	 */
-	n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
-	if (!(type & LWS_ADOPT_HTTP)) {
-		if (!(type & LWS_ADOPT_SOCKET))
-			n = LWS_CALLBACK_RAW_ADOPT_FILE;
-		else
-			n = LWS_CALLBACK_RAW_ADOPT;
-	}
-
-	if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) ||
-	    !(type & LWS_ADOPT_SOCKET)) {
-		/* non-SSL */
-		if (!(type & LWS_ADOPT_HTTP)) {
-			if (!(type & LWS_ADOPT_SOCKET))
-				lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
-						    &role_ops_raw_file);
-			else
-				lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
-						    &role_ops_raw_skt);
-		}
-#if defined(LWS_ROLE_H1)
-		else
-			lws_role_transition(new_wsi, LWSIFR_SERVER,
-					    LRS_HEADERS, &role_ops_h1);
-#endif
-	} else {
-		/* SSL */
-		if (!(type & LWS_ADOPT_HTTP))
-			lws_role_transition(new_wsi, 0, LRS_SSL_INIT,
-					    &role_ops_raw_skt);
-#if defined(LWS_ROLE_H1)
-		else
-			lws_role_transition(new_wsi, LWSIFR_SERVER,
-					    LRS_SSL_INIT, &role_ops_h1);
-#endif
-		ssl = 1;
-	}
-
-	lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate);
-
-	if (context->event_loop_ops->accept)
-		context->event_loop_ops->accept(new_wsi);
-
-	if (!ssl) {
-		lws_pt_lock(pt, __func__);
-		if (__insert_wsi_socket_into_fds(context, new_wsi)) {
-			lws_pt_unlock(pt);
-			lwsl_err("%s: fail inserting socket\n", __func__);
-			goto fail;
-		}
-		lws_pt_unlock(pt);
-	} else
-		if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
-			lwsl_info("%s: fail ssl negotiation\n", __func__);
-			goto fail;
-		}
-
-	/*
-	 *  by deferring callback to this point, after insertion to fds,
-	 * lws_callback_on_writable() can work from the callback
-	 */
-	if ((new_wsi->protocol->callback)(
-			new_wsi, n, new_wsi->user_space, NULL, 0))
-		goto fail;
-
-	if (type & LWS_ADOPT_HTTP) {
-		if (!lws_header_table_attach(new_wsi, 0))
-			lwsl_debug("Attached ah immediately\n");
-		else
-			lwsl_info("%s: waiting for ah\n", __func__);
-	}
-
-	lws_cancel_service_pt(new_wsi);
-
-	return new_wsi;
-
-fail:
-	if (type & LWS_ADOPT_SOCKET)
-		lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt fail");
-
-	return NULL;
-
-bail:
-       lwsl_notice("%s: exiting on bail\n", __func__);
-	if (parent)
-		parent->child_list = new_wsi->sibling_list;
-	if (new_wsi->user_space)
-		lws_free(new_wsi->user_space);
-	lws_free(new_wsi);
-       compatible_close(fd.sockfd);
-
-	return NULL;
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
-{
-	lws_sock_file_fd_type fd;
-
-	fd.sockfd = accept_fd;
-	return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET |
-			LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL);
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
-{
-	return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
-}
-
-/* Common read-buffer adoption for lws_adopt_*_readbuf */
-static struct lws*
-adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
-{
-	struct lws_context_per_thread *pt;
-	struct lws_pollfd *pfd;
-	int n;
-
-	if (!wsi)
-		return NULL;
-
-	if (!readbuf || len == 0)
-		return wsi;
-
-	if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
-		return wsi;
-
-	pt = &wsi->context->pt[(int)wsi->tsi];
-
-	n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, len);
-	if (n < 0)
-		goto bail;
-	if (n)
-		lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
-
-	/*
-	 * we can't process the initial read data until we can attach an ah.
-	 *
-	 * if one is available, get it and place the data in his ah rxbuf...
-	 * wsi with ah that have pending rxbuf get auto-POLLIN service.
-	 *
-	 * no autoservice because we didn't get a chance to attach the
-	 * readbuf data to wsi or ah yet, and we will do it next if we get
-	 * the ah.
-	 */
-	if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
-
-		lwsl_notice("%s: calling service on readbuf ah\n", __func__);
-
-		/* unlike a normal connect, we have the headers already
-		 * (or the first part of them anyway).
-		 * libuv won't come back and service us without a network
-		 * event, so we need to do the header service right here.
-		 */
-		pfd = &pt->fds[wsi->position_in_fds_table];
-		pfd->revents |= LWS_POLLIN;
-		lwsl_err("%s: calling service\n", __func__);
-		if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi))
-			/* service closed us */
-			return NULL;
-
-		return wsi;
-	}
-	lwsl_err("%s: deferring handling ah\n", __func__);
-
-	return wsi;
-
-bail:
-	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt readbuf fail");
-
-	return NULL;
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
-			 const char *readbuf, size_t len)
-{
-        return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd),
-        			    readbuf, len);
-}
-
-LWS_VISIBLE struct lws *
-lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
-			       lws_sockfd_type accept_fd,
-			       const char *readbuf, size_t len)
-{
-        return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd),
-        			    readbuf, len);
-}
 
 LWS_VISIBLE int
 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c
index dbeb9753a29150598a5d90e074655fefb4fd7be3..2aaee329d075b52ac35685006838b4dd886adbcd 100644
--- a/lib/roles/listen/ops-listen.c
+++ b/lib/roles/listen/ops-listen.c
@@ -171,6 +171,8 @@ struct lws_role_ops role_ops_listen = {
 	/* close_role */		NULL,
 	/* close_kill_connection */	NULL,
 	/* destroy_role */		NULL,
+	/* adoption_bind */		NULL,
+	/* client_bind */		NULL,
 	/* writeable cb clnt, srv */	{ 0, 0 },
 	/* close cb clnt, srv */	{ 0, 0 },
 	/* file_handle */		0,
diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c
index b9348d58d7238a41e3962ec08f0a566962d1e87b..96b06e7fd9e0e49d77f4eebbf4abd9e2fc92d373 100644
--- a/lib/roles/pipe/ops-pipe.c
+++ b/lib/roles/pipe/ops-pipe.c
@@ -75,6 +75,8 @@ struct lws_role_ops role_ops_pipe = {
 	/* close_role */		NULL,
 	/* close_kill_connection */	NULL,
 	/* destroy_role */		NULL,
+	/* adoption_bind */		NULL,
+	/* client_bind */		NULL,
 	/* writeable cb clnt, srv */	{ 0, 0 },
 	/* close cb clnt, srv */	{ 0, 0 },
 	/* file_handle */		1,
diff --git a/lib/roles/private.h b/lib/roles/private.h
index ae4278b5d35fa6f45be88ffbde280c94f29fe6cd..7729b0b394839abee6a49e149c898183e3f72e4a 100644
--- a/lib/roles/private.h
+++ b/lib/roles/private.h
@@ -148,9 +148,11 @@ enum lwsi_state {
 };
 
 #define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK))
-#define lwsi_state_PRE_CLOSE(wsi) ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK))
+#define lwsi_state_PRE_CLOSE(wsi) \
+		((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK))
 #define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOT_EST))
-#define lwsi_state_est_PRE_CLOSE(wsi) (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST))
+#define lwsi_state_est_PRE_CLOSE(wsi) \
+		(!(wsi->wsistate_pre_close & LWSIFS_NOT_EST))
 #define lwsi_state_can_handle_POLLOUT(wsi) (wsi->wsistate & LWSIFS_POCB)
 #if !defined (_DEBUG)
 #define lwsi_set_state(wsi, lrs) wsi->wsistate = \
@@ -159,6 +161,8 @@ enum lwsi_state {
 void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
 #endif
 
+#define _LWS_ADOPT_FINISH (1 << 24)
+
 /*
  * internal role-specific ops
  */
@@ -217,6 +221,16 @@ struct lws_role_ops {
 	/* role-specific destructor */
 	int (*destroy_role)(struct lws *wsi);
 
+	/* role-specific socket-adopt */
+	int (*adoption_bind)(struct lws *wsi, int type, const char *prot);
+	/* role-specific client-bind:
+	 * ret 1 = bound, 0 = not bound, -1 = fail out
+	 * i may be NULL, indicating client_bind is being called after
+	 * a successful bind earlier, to finalize the binding.  In that
+	 * case ret 0 = OK, 1 = fail, wsi needs freeing, -1 = fail, wsi freed */
+	int (*client_bind)(struct lws *wsi,
+			   const struct lws_client_connect_info *i);
+
 	/*
 	 * the callback reasons for WRITEABLE for client, server
 	 * (just client applies if no concept of client or server)
@@ -280,3 +294,6 @@ enum {
 	LWS_UPG_RET_CONTINUE,
 	LWS_UPG_RET_BAIL
 };
+
+int
+lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot);
diff --git a/lib/roles/raw-file/ops-raw-file.c b/lib/roles/raw-file/ops-raw-file.c
new file mode 100644
index 0000000000000000000000000000000000000000..bbe80341f09f38286f2c0a794d9ddbc98c19b6ee
--- /dev/null
+++ b/lib/roles/raw-file/ops-raw-file.c
@@ -0,0 +1,104 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * 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
+ *  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
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
+			    struct lws_pollfd *pollfd)
+{
+	int n;
+
+	if (pollfd->revents & LWS_POLLOUT) {
+		n = lws_callback_as_writeable(wsi);
+		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+			lwsl_info("failed at set pollfd\n");
+			return LWS_HPI_RET_WSI_ALREADY_DIED;
+		}
+		if (n)
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	}
+
+	if (pollfd->revents & LWS_POLLIN) {
+		if (user_callback_handle_rxflow(wsi->protocol->callback,
+						wsi, LWS_CALLBACK_RAW_RX_FILE,
+						wsi->user_space, NULL, 0)) {
+			lwsl_debug("raw rx callback closed it\n");
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		}
+	}
+
+	if (pollfd->revents & LWS_POLLHUP)
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+	return LWS_HPI_RET_HANDLED;
+}
+
+#if !defined(LWS_NO_SERVER)
+static int
+rops_adoption_bind_raw_file(struct lws *wsi, int type, const char *vh_prot_name)
+{
+	/* no socket or http: it can only be a raw file */
+	if ((type & LWS_ADOPT_HTTP) || (type & LWS_ADOPT_SOCKET) ||
+	    (type & _LWS_ADOPT_FINISH))
+		return 0; /* no match */
+
+	lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_file);
+
+	if (!vh_prot_name)
+		wsi->protocol = &wsi->vhost->protocols[
+					wsi->vhost->default_protocol_index];
+
+	return 1; /* bound */
+}
+#endif
+
+struct lws_role_ops role_ops_raw_file = {
+	/* role name */			"raw-file",
+	/* alpn id */			NULL,
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		NULL,
+	/* destroy_vhost */		NULL,
+	/* periodic_checks */		NULL,
+	/* service_flag_pending */	NULL,
+	/* handle_POLLIN */		rops_handle_POLLIN_raw_file,
+	/* handle_POLLOUT */		NULL,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	NULL,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	NULL,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		NULL,
+	/* close_via_role_protocol */	NULL,
+	/* close_role */		NULL,
+	/* close_kill_connection */	NULL,
+	/* destroy_role */		NULL,
+#if !defined(LWS_NO_SERVER)
+	/* adoption_bind */		rops_adoption_bind_raw_file,
+#else
+					NULL,
+#endif
+	/* client_bind */		NULL,
+	/* writeable cb clnt, srv */	{ LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
+	/* close cb clnt, srv */	{ LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
+	/* file_handle */		1,
+};
diff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw-skt/ops-raw-skt.c
similarity index 74%
rename from lib/roles/raw/ops-raw.c
rename to lib/roles/raw-skt/ops-raw-skt.c
index 68b52bded64bbb026ed8995c669b881bcaac87e1..2dbd3b2f65916150c2caefc8ad44a94f75485bcb 100644
--- a/lib/roles/raw/ops-raw.c
+++ b/lib/roles/raw-skt/ops-raw-skt.c
@@ -135,38 +135,61 @@ fail:
 	return LWS_HPI_RET_WSI_ALREADY_DIED;
 }
 
+#if !defined(LWS_NO_SERVER)
+static int
+rops_adoption_bind_raw_skt(struct lws *wsi, int type, const char *vh_prot_name)
+{
+	/* no http but socket... must be raw skt */
+	if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
+	    (type & _LWS_ADOPT_FINISH))
+		return 0; /* no match */
+
+#if !defined(LWS_WITH_ESP32)
+	if (type & LWS_ADOPT_FLAG_UDP)
+		/*
+		 * these can be >128 bytes, so just alloc for UDP
+		 */
+		wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
+#endif
 
+	if (!vh_prot_name)
+		lws_bind_protocol(wsi, wsi->protocol);
+	else
+		/* this is the only time he will transition */
+		lws_bind_protocol(wsi,
+			&wsi->vhost->protocols[wsi->vhost->raw_protocol_index]);
+
+	lws_role_transition(wsi, 0, type & LWS_ADOPT_ALLOW_SSL ? LRS_SSL_INIT :
+				LRS_ESTABLISHED, &role_ops_raw_skt);
+
+	return 1; /* bound */
+}
+#endif
+
+#if !defined(LWS_NO_CLIENT)
 static int
-rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
-			    struct lws_pollfd *pollfd)
+rops_client_bind_raw_skt(struct lws *wsi,
+			 const struct lws_client_connect_info *i)
 {
-	int n;
+	if (!i) {
 
-	if (pollfd->revents & LWS_POLLOUT) {
-		n = lws_callback_as_writeable(wsi);
-		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
-			lwsl_info("failed at set pollfd\n");
-			return LWS_HPI_RET_WSI_ALREADY_DIED;
-		}
-		if (n)
-			return LWS_HPI_RET_PLEASE_CLOSE_ME;
-	}
+		/* finalize */
 
-	if (pollfd->revents & LWS_POLLIN) {
-		if (user_callback_handle_rxflow(wsi->protocol->callback,
-						wsi, LWS_CALLBACK_RAW_RX_FILE,
-						wsi->user_space, NULL, 0)) {
-			lwsl_debug("raw rx callback closed it\n");
-			return LWS_HPI_RET_PLEASE_CLOSE_ME;
-		}
+		if (!wsi->user_space && wsi->stash->method)
+			if (lws_ensure_user_space(wsi))
+				return 1;
+
+		return 0;
 	}
 
-	if (pollfd->revents & LWS_POLLHUP)
-		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	/* we are a fallback if nothing else matched */
 
-	return LWS_HPI_RET_HANDLED;
-}
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
+			    &role_ops_raw_skt);
 
+	return 1; /* matched */
+}
+#endif
 
 struct lws_role_ops role_ops_raw_skt = {
 	/* role name */			"raw-skt",
@@ -189,35 +212,17 @@ struct lws_role_ops role_ops_raw_skt = {
 	/* close_role */		NULL,
 	/* close_kill_connection */	NULL,
 	/* destroy_role */		NULL,
+#if !defined(LWS_NO_SERVER)
+	/* adoption_bind */		rops_adoption_bind_raw_skt,
+#else
+					NULL,
+#endif
+#if !defined(LWS_NO_CLIENT)
+	/* client_bind */		rops_client_bind_raw_skt,
+#else
+					NULL,
+#endif
 	/* writeable cb clnt, srv */	{ LWS_CALLBACK_RAW_WRITEABLE, 0 },
 	/* close cb clnt, srv */	{ LWS_CALLBACK_RAW_CLOSE, 0 },
 	/* file_handle */		0,
 };
-
-
-
-struct lws_role_ops role_ops_raw_file = {
-	/* role name */			"raw-file",
-	/* alpn id */			NULL,
-	/* check_upgrades */		NULL,
-	/* init_context */		NULL,
-	/* init_vhost */		NULL,
-	/* destroy_vhost */		NULL,
-	/* periodic_checks */		NULL,
-	/* service_flag_pending */	NULL,
-	/* handle_POLLIN */		rops_handle_POLLIN_raw_file,
-	/* handle_POLLOUT */		NULL,
-	/* perform_user_POLLOUT */	NULL,
-	/* callback_on_writable */	NULL,
-	/* tx_credit */			NULL,
-	/* write_role_protocol */	NULL,
-	/* encapsulation_parent */	NULL,
-	/* alpn_negotiated */		NULL,
-	/* close_via_role_protocol */	NULL,
-	/* close_role */		NULL,
-	/* close_kill_connection */	NULL,
-	/* destroy_role */		NULL,
-	/* writeable cb clnt, srv */	{ LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
-	/* close cb clnt, srv */	{ LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
-	/* file_handle */		1,
-};
diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c
index fd6cf4255129c24e9d20617349b983121332ae07..ad2d6f5dc92d62a3e6a7881379b7f6d40f0b91e0 100644
--- a/lib/roles/ws/client-ws.c
+++ b/lib/roles/ws/client-ws.c
@@ -40,7 +40,8 @@ strtolower(char *s)
 }
 
 int
-lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)
+lws_create_client_ws_object(const struct lws_client_connect_info *i,
+			    struct lws *wsi)
 {
 	int v = SPEC_LATEST_SUPPORTED;
 
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index 5ddaba9e18c1d9fb4407c527bc3c87732704593a..c9a353e82bee0973dfe30b7a69cb0ec35f2eb1d6 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -781,8 +781,7 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
 	lwsl_debug("Allocating RX buffer %d\n", n);
 
 #if !defined(LWS_WITH_ESP32)
-	if (!wsi->parent_carries_io &&
-	    !wsi->h2_stream_carries_ws)
+	if (!wsi->h2_stream_carries_ws)
 		if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
 		       (const char *)&n, sizeof n)) {
 			lwsl_warn("Failed to set SNDBUF to %d", n);
@@ -1984,6 +1983,8 @@ struct lws_role_ops role_ops_ws = {
 	/* close_role */		rops_close_role_ws,
 	/* close_kill_connection */	rops_close_kill_connection_ws,
 	/* destroy_role */		rops_destroy_role_ws,
+	/* adoption_bind */		NULL,
+	/* client_bind */		NULL,
 	/* writeable cb clnt, srv */	{ LWS_CALLBACK_CLIENT_WRITEABLE,
 					  LWS_CALLBACK_SERVER_WRITEABLE },
 	/* close cb clnt, srv */	{ LWS_CALLBACK_CLIENT_CLOSED,