From be8d791b5e5e601207011c0454371dcb97e28b53 Mon Sep 17 00:00:00 2001
From: Andy Green <andy@warmcat.com>
Date: Mon, 27 Feb 2017 12:55:56 +0800
Subject: [PATCH] adoption: make union for socket and file fds

This lets lws support adopting raw file FDs and raw socket fds.

A test plugin creates a FIFO and prints data sent on it, using
the lws event loop.
---
 CMakeLists.txt                  |   4 +
 lib/client-handshake.c          |  32 ++---
 lib/client.c                    |   4 +-
 lib/libev.c                     |  14 +-
 lib/libuv.c                     |  14 +-
 lib/libwebsockets.c             |  50 ++++---
 lib/libwebsockets.h             |  46 +++++--
 lib/lws-plat-esp32.c            |   4 +-
 lib/lws-plat-esp8266.c          |  18 +--
 lib/lws-plat-optee.c            |   2 +-
 lib/lws-plat-unix.c             |   2 +-
 lib/lws-plat-win.c              |  12 +-
 lib/output.c                    |   8 +-
 lib/pollfd.c                    |  28 ++--
 lib/private-libwebsockets.h     |  13 +-
 lib/server.c                    | 125 ++++++++++++------
 lib/service.c                   |  40 +++++-
 lib/ssl-client.c                |   2 +-
 lib/ssl.c                       |   4 +-
 plugins/protocol_lws_raw_test.c | 227 ++++++++++++++++++++++++++++++++
 20 files changed, 499 insertions(+), 150 deletions(-)
 create mode 100644 plugins/protocol_lws_raw_test.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 579a29c4..95f473ef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1340,6 +1340,10 @@ if (NOT LWS_WITHOUT_TESTAPPS)
 			      "plugins/protocol_post_demo.c" "" "")
 		create_plugin(protocol_lws_table_dirlisting
 			      "plugins/generic-table/protocol_table_dirlisting.c" "" "")
+		if (NOT WIN32)
+      		      create_plugin(protocol_lws_raw_test
+			      "plugins/protocol_lws_raw_test.c" "" "")
+		endif()
 
 if (LWS_WITH_SERVER_STATUS)
 		create_plugin(protocol_lws_server_status
diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index 3339a1de..acb8044b 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -169,34 +169,34 @@ lws_client_connect_2(struct lws *wsi)
 			freeaddrinfo(result);
 	}
 
-	if (!lws_socket_is_valid(wsi->sock)) {
+	if (!lws_socket_is_valid(wsi->desc.sockfd)) {
 
 #ifdef LWS_USE_IPV6
 		if (LWS_IPV6_ENABLED(wsi->vhost))
-			wsi->sock = socket(AF_INET6, SOCK_STREAM, 0);
+			wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0);
 		else
 #endif
-			wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
+			wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
-		if (!lws_socket_is_valid(wsi->sock)) {
+		if (!lws_socket_is_valid(wsi->desc.sockfd)) {
 			lwsl_warn("Unable to open socket\n");
 			cce = "unable to open socket";
 			goto oom4;
 		}
 
-		if (lws_plat_set_socket_options(wsi->vhost, wsi->sock)) {
+		if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd)) {
 			lwsl_err("Failed to set wsi socket options\n");
-			compatible_close(wsi->sock);
+			compatible_close(wsi->desc.sockfd);
 			cce = "set socket opts failed";
 			goto oom4;
 		}
 
 		wsi->mode = LWSCM_WSCL_WAITING_CONNECT;
 
-		lws_libev_accept(wsi, wsi->sock);
-		lws_libuv_accept(wsi, wsi->sock);
+		lws_libev_accept(wsi, wsi->desc);
+		lws_libuv_accept(wsi, wsi->desc);
 		if (insert_wsi_socket_into_fds(context, wsi)) {
-			compatible_close(wsi->sock);
+			compatible_close(wsi->desc.sockfd);
 			cce = "insert wsi failed";
 			goto oom4;
 		}
@@ -217,7 +217,7 @@ lws_client_connect_2(struct lws *wsi)
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
 				AWAITING_TIMEOUT);
 
-		n = lws_socket_bind(wsi->vhost, wsi->sock, 0, wsi->vhost->iface);
+		n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0, wsi->vhost->iface);
 		if (n < 0) {
 			cce = "unable to bind socket";
 			goto failed;
@@ -235,7 +235,7 @@ lws_client_connect_2(struct lws *wsi)
 		n = sizeof(struct sockaddr);
 	}
 
-	if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {
+	if (connect(wsi->desc.sockfd, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {
 		if (LWS_ERRNO == LWS_EALREADY ||
 		    LWS_ERRNO == LWS_EINPROGRESS ||
 		    LWS_ERRNO == LWS_EWOULDBLOCK
@@ -287,7 +287,7 @@ lws_client_connect_2(struct lws *wsi)
 			goto failed;
 		wsi->c_port = wsi->vhost->http_proxy_port;
 
-		n = send(wsi->sock, (char *)pt->serv_buf, plen,
+		n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen,
 			 MSG_NOSIGNAL);
 		if (n < 0) {
 			lwsl_debug("ERROR writing to proxy socket\n");
@@ -317,7 +317,7 @@ lws_client_connect_2(struct lws *wsi)
 			AWAITING_TIMEOUT);
 
 	wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE;
-	pfd.fd = wsi->sock;
+	pfd.fd = wsi->desc.sockfd;
 	pfd.events = LWS_POLLIN;
 	pfd.revents = LWS_POLLIN;
 
@@ -411,10 +411,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 
 	/* close the connection by hand */
 
-	compatible_close(wsi->sock);
+	compatible_close(wsi->desc.sockfd);
 	remove_wsi_socket_from_fds(wsi);
 
-	wsi->sock = LWS_SOCK_INVALID;
+	wsi->desc.sockfd = LWS_SOCK_INVALID;
 	wsi->state = LWSS_CLIENT_UNCONNECTED;
 	wsi->protocol = NULL;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
@@ -581,7 +581,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	wsi->context = i->context;
 	/* assert the mode and union status (hdr) clearly */
 	lws_union_transition(wsi, LWSCM_HTTP_CLIENT);
-	wsi->sock = LWS_SOCK_INVALID;
+	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
diff --git a/lib/client.c b/lib/client.c
index d42a47e2..57b79f40 100755
--- a/lib/client.c
+++ b/lib/client.c
@@ -114,7 +114,7 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
 			return 0;
 		}
 
-		n = recv(wsi->sock, sb, context->pt_serv_buf_size, 0);
+		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
 		if (n < 0) {
 			if (LWS_ERRNO == LWS_EAGAIN) {
 				lwsl_debug("Proxy read returned EAGAIN... retrying\n");
@@ -952,7 +952,7 @@ check_accept:
        wsi->u.ws.rx_ubuf_alloc = n;
 	lwsl_info("Allocating client RX buffer %d\n", n);
 
-	if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n,
+	if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n,
 		       sizeof n)) {
 		lwsl_warn("Failed to set SNDBUF to %d", n);
 		cce = "HS: SO_SNDBUF failed";
diff --git a/lib/libev.c b/lib/libev.c
index 39242bb2..3f882b20 100644
--- a/lib/libev.c
+++ b/lib/libev.c
@@ -97,7 +97,7 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
 	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_io_init(w_accept, lws_accept_cb, vh->lserv_wsi->desc.sockfd,
 				  EV_READ);
 		}
 		vh = vh->vhost_next;
@@ -160,19 +160,25 @@ lws_libev_destroyloop(struct lws_context *context, int tsi)
 }
 
 LWS_VISIBLE void
-lws_libev_accept(struct lws *new_wsi, int accept_fd)
+lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
 {
 	struct lws_context *context = lws_get_context(new_wsi);
 	struct ev_io *r = &new_wsi->w_read.ev_watcher;
 	struct ev_io *w = &new_wsi->w_write.ev_watcher;
+	int fd;
 
 	if (!LWS_LIBEV_ENABLED(context))
 		return;
 
+	if (wsi->mode == LWSCM_RAW_FILEDESC)
+		fd = desc.filefd;
+	else
+		fd = desc.sockfd;
+
 	new_wsi->w_read.context = context;
 	new_wsi->w_write.context = context;
-	ev_io_init(r, lws_accept_cb, accept_fd, EV_READ);
-	ev_io_init(w, lws_accept_cb, accept_fd, EV_WRITE);
+	ev_io_init(r, lws_accept_cb, fd, EV_READ);
+	ev_io_init(w, lws_accept_cb, fd, EV_WRITE);
 }
 
 LWS_VISIBLE void
diff --git a/lib/libuv.c b/lib/libuv.c
index 5572e0a8..24f24cee 100644
--- a/lib/libuv.c
+++ b/lib/libuv.c
@@ -164,10 +164,10 @@ lws_uv_initvhost(struct lws_vhost* vh, struct lws* wsi)
 
 	wsi->w_read.context = vh->context;
 	n = uv_poll_init_socket(pt->io_loop_uv,
-				&wsi->w_read.uv_watcher, wsi->sock);
+				&wsi->w_read.uv_watcher, wsi->desc.sockfd);
 	if (n) {
 		lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
-				 n, (void *)(long)wsi->sock);
+				 n, (void *)(long)wsi->desc.sockfd);
 
 		return -1;
 	}
@@ -318,7 +318,7 @@ lws_libuv_destroyloop(struct lws_context *context, int tsi)
 }
 
 void
-lws_libuv_accept(struct lws *wsi, lws_sockfd_type accept_fd)
+lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc)
 {
 	struct lws_context *context = lws_get_context(wsi);
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
@@ -329,8 +329,12 @@ lws_libuv_accept(struct lws *wsi, lws_sockfd_type accept_fd)
 	lwsl_debug("%s: new wsi %p\n", __func__, wsi);
 
 	wsi->w_read.context = context;
-
-	uv_poll_init_socket(pt->io_loop_uv, &wsi->w_read.uv_watcher, accept_fd);
+	if (wsi->mode == LWSCM_RAW_FILEDESC)
+		uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
+			     desc.filefd);
+	else
+		uv_poll_init_socket(pt->io_loop_uv, &wsi->w_read.uv_watcher,
+				    desc.sockfd);
 }
 
 void
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 9c990866..cdc6c282 100755
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -154,6 +154,15 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 	if (!wsi)
 		return;
 
+	if (wsi->mode == LWSCM_RAW_FILEDESC) {
+		remove_wsi_socket_from_fds(wsi);
+		wsi->protocol->callback(wsi,
+			LWS_CALLBACK_RAW_CLOSE_FILE, wsi->user_space, NULL, 0);
+		lws_free_wsi(wsi);
+
+		return;
+	}
+
 	lws_access_log(wsi);
 #if defined(LWS_WITH_ESP8266)
 	if (wsi->premature_rx)
@@ -222,7 +231,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 #endif
 
 	if (wsi->mode == LWSCM_RAW) {
-		wsi->vhost->protocols->callback(wsi,
+		wsi->protocol->callback(wsi,
 			LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0);
 		wsi->socket_is_permanently_unusable = 1;
 		goto just_kill_connection;
@@ -438,17 +447,17 @@ just_kill_connection:
 #ifdef LWS_OPENSSL_SUPPORT
 		if (lws_is_ssl(wsi) && wsi->ssl)
                {
-                       lwsl_info("%s: shutting down SSL connection: %p (ssl %p, sock %d, state %d)\n", __func__, wsi, wsi->ssl, (int)(long)wsi->sock, wsi->state);
+                       lwsl_info("%s: shutting down SSL connection: %p (ssl %p, sock %d, state %d)\n", __func__, wsi, wsi->ssl, (int)(long)wsi->desc.sockfd, wsi->state);
 			n = SSL_shutdown(wsi->ssl);
                        if (n == 0) /* Complete bidirectional SSL shutdown */
                                n = SSL_shutdown(wsi->ssl);
-                       n = shutdown(wsi->sock, SHUT_WR);
+                       n = shutdown(wsi->desc.sockfd, SHUT_WR);
                }
 		else
 #endif
 		{
-			lwsl_info("%s: shutting down connection: %p (sock %d, state %d)\n", __func__, wsi, (int)(long)wsi->sock, wsi->state);
-			n = shutdown(wsi->sock, SHUT_WR);
+			lwsl_info("%s: shutting down connection: %p (sock %d, state %d)\n", __func__, wsi, (int)(long)wsi->desc.sockfd, wsi->state);
+			n = shutdown(wsi->desc.sockfd, SHUT_WR);
 		}
 		if (n)
 			lwsl_debug("closing: shutdown (state %d) ret %d\n", wsi->state, LWS_ERRNO);
@@ -470,7 +479,7 @@ just_kill_connection:
 #endif
 
 	lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
-		  wsi, wsi->sock);
+		  wsi, wsi->desc.sockfd);
 	
 #ifdef LWS_WITH_HTTP_PROXY
 	if (wsi->rw) {
@@ -487,11 +496,11 @@ just_kill_connection:
 	lws_remove_from_timeout_list(wsi);
 
 	/* checking return redundant since we anyway close */
-	if (wsi->sock != LWS_SOCK_INVALID)
+	if (wsi->desc.sockfd != LWS_SOCK_INVALID)
 		remove_wsi_socket_from_fds(wsi);
 
 #if defined(LWS_WITH_ESP8266)
-	espconn_disconnect(wsi->sock);
+	espconn_disconnect(wsi->desc.sockfd);
 #endif
 
 	wsi->state = LWSS_DEAD_SOCKET;
@@ -606,18 +615,18 @@ lws_close_free_wsi_final(struct lws *wsi)
 {
 	int n;
 
-	if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) {
+	if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->desc.sockfd)) {
 #if LWS_POSIX
-		//lwsl_err("*** closing sockfd %d\n", wsi->sock);
-		n = compatible_close(wsi->sock);
+		//lwsl_err("*** closing sockfd %d\n", wsi->desc.sockfd);
+		n = compatible_close(wsi->desc.sockfd);
 		if (n)
 			lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
 
 #else
-		compatible_close(wsi->sock);
+		compatible_close(wsi->desc.sockfd);
 		(void)n;
 #endif
-		wsi->sock = LWS_SOCK_INVALID;
+		wsi->desc.sockfd = LWS_SOCK_INVALID;
 	}
 
 	/* outermost destroy notification for wsi (user_space still intact) */
@@ -786,7 +795,7 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
 	}
 
 	olen = len;
-	if (getpeername(wsi->sock, p, &len) < 0 || len > olen) {
+	if (getpeername(wsi->desc.sockfd, p, &len) < 0 || len > olen) {
 		lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
 		return NULL;
 	}
@@ -974,7 +983,7 @@ lws_now_secs(void)
 LWS_VISIBLE int
 lws_get_socket_fd(struct lws *wsi)
 {
-	return wsi->sock;
+	return wsi->desc.sockfd;
 }
 
 #endif
@@ -1976,7 +1985,7 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
 	new_wsi->protocol = context->vhost_list->protocols;
 	new_wsi->user_space = NULL;
 	new_wsi->ietf_spec_revision = 0;
-	new_wsi->sock = LWS_SOCK_INVALID;
+	new_wsi->desc.sockfd = LWS_SOCK_INVALID;
 	context->count_wsi_allocated++;
 
 	return new_wsi;
@@ -2024,7 +2033,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 //			 cgi->pipe_fds[n][!!(n == 0)], cgi->pipe_fds[n][!(n == 0)]);
 
 		/* read side is 0, stdin we want the write side, others read */
-		cgi->stdwsi[n]->sock = cgi->pipe_fds[n][!!(n == 0)];
+		cgi->stdwsi[n]->desc.sockfd = cgi->pipe_fds[n][!!(n == 0)];
 		if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK) < 0) {
 			lwsl_err("%s: setting NONBLOCK failed\n", __func__);
 			goto bail2;
@@ -2032,7 +2041,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 	}
 
 	for (n = 0; n < 3; n++) {
-		lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->sock);
+		lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->desc);
 		if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n]))
 			goto bail3;
 		cgi->stdwsi[n]->parent = wsi;
@@ -2045,8 +2054,9 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 	lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN);
 
 	lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__,
-		   cgi->stdwsi[LWS_STDIN]->sock, cgi->stdwsi[LWS_STDOUT]->sock,
-		   cgi->stdwsi[LWS_STDERR]->sock);
+		   cgi->stdwsi[LWS_STDIN]->desc.sockfd,
+		   cgi->stdwsi[LWS_STDOUT]->desc.sockfd,
+		   cgi->stdwsi[LWS_STDERR]->desc.sockfd);
 
 	lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
 
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index a9ee5677..9d69dd7d 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1059,12 +1059,20 @@ enum lws_callback_reasons {
 	LWS_CALLBACK_RAW_WRITEABLE				= 61,
 	/**< RAW mode connection may be written */
 	LWS_CALLBACK_RAW_ADOPT					= 62,
-	/**< RAW mode connection was adopted (equivalent to 'created') */
+	/**< RAW mode connection was adopted (equivalent to 'wsi created') */
+	LWS_CALLBACK_RAW_ADOPT_FILE				= 63,
+	/**< RAW mode file was adopted (equivalent to 'wsi created') */
+	LWS_CALLBACK_RAW_RX_FILE				= 64,
+	/**< RAW mode file has something to read */
+	LWS_CALLBACK_RAW_WRITEABLE_FILE				= 65,
+	/**< RAW mode file is writeable */
+	LWS_CALLBACK_RAW_CLOSE_FILE				= 66,
+	/**< RAW mode wsi that adopted a file is closing */
 
 	/****** add new things just above ---^ ******/
 
 	LWS_CALLBACK_USER = 1000,
-	/**<  user code can use any including / above without fear of clashes */
+	/**<  user code can use any including above without fear of clashes */
 };
 
 
@@ -3703,23 +3711,37 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd);
  */
 LWS_VISIBLE LWS_EXTERN struct lws *
 lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
+
+typedef enum {
+	LWS_ADOPT_HTTP = 1,		/* absent implies RAW */
+	LWS_ADOPT_SOCKET = 2,		/* absent implies file descriptor */
+	LWS_ADOPT_ALLOW_SSL = 4		/* if set requires LWS_ADOPT_SOCKET */
+} lws_adoption_type;
+
+typedef union {
+	lws_sockfd_type sockfd;
+	lws_filefd_type filefd;
+} lws_sock_file_fd_type;
+
 /*
-* lws_adopt_socket_vhost2() - adopt foreign socket as if listen socket accepted it
-* for vhost, allow control over defeat SSL and raw transport mode
+* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
+* if socket descriptor, should already have been accepted from listen socket
+*
 * \param vhost: lws vhost
-* \param accept_fd: fd of already-accepted socket to adopt
-* \param allow_ssl: 0 = no SSL even if vhost supports, 1 = SSL if vhost supports
-* \param raw: 0 = http[s]/wss[s], 1 = raw mode semantics
+* \param type: OR-ed combinations of lws_adoption_type flags
+* \param fd: union with either .sockfd or .filefd set
+* \param vh_prot_name: NULL or vh protocol name to bind raw connection to
 *
 * Either returns new wsi bound to accept_fd, or closes accept_fd and
 * returns NULL, having cleaned up any new wsi pieces.
 *
-* LWS adopts the socket in http serving mode, it's ready to accept an upgrade
-* to ws or just serve http.
+* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's
+* ready to accept an upgrade to ws or just serve http.
 */
 LWS_VISIBLE struct lws *
-lws_adopt_socket_vhost2(struct lws_vhost *vh, lws_sockfd_type accept_fd,
-			int allow_ssl, int raw);
+lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
+			   lws_sock_file_fd_type fd, const char *vh_prot_name);
+
 
 /**
  * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it
@@ -4254,7 +4276,9 @@ struct lws_fop_fd {
 };
 #if defined(WIN32) || defined(_WIN32)
 /* ... */
+#if !defined(ssize_t)
 typedef SSIZE_T ssize_t;
+#endif
 /* !!! >:-[  */
 typedef unsigned __int32 uint32_t;
 typedef unsigned __int8 uint8_t;
diff --git a/lib/lws-plat-esp32.c b/lib/lws-plat-esp32.c
index 1c7bb953..bf550be5 100644
--- a/lib/lws-plat-esp32.c
+++ b/lib/lws-plat-esp32.c
@@ -30,9 +30,9 @@ lws_send_pipe_choked(struct lws *wsi)
 		return 1;
 
 	FD_ZERO(&writefds);
-	FD_SET(wsi->sock, &writefds);
+	FD_SET(wsi->desc.sockfd, &writefds);
 
-	if (select(wsi->sock + 1, NULL, &writefds, NULL, &tv) < 1)
+	if (select(wsi->desc.sockfd + 1, NULL, &writefds, NULL, &tv) < 1)
 		return 1;
 
 	return 0;
diff --git a/lib/lws-plat-esp8266.c b/lib/lws-plat-esp8266.c
index 497fba10..f868fbc7 100644
--- a/lib/lws-plat-esp8266.c
+++ b/lib/lws-plat-esp8266.c
@@ -72,7 +72,7 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
 	//lwsl_notice("%s: wsi %p: len %d\n", __func__, wsi, len);	
 	
 	wsi->pending_send_completion++;
-	espconn_send(wsi->sock, buf, len);
+	espconn_send(wsi->desc.sockfd, buf, len);
 	
 	return len;
 }
@@ -247,7 +247,7 @@ esp8266_create_tcp_listen_socket(struct lws_vhost *vh)
 const char *
 lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen)
 {
-	unsigned char *p = wsi->sock->proto.tcp->remote_ip;
+	unsigned char *p = wsi->desc.sockfd->proto.tcp->remote_ip;
 
 	lws_snprintf(name, namelen, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
 
@@ -493,7 +493,7 @@ esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi)
 	fd->reverse = wsi;
 
 	for (n = 0; n < wsi->context->max_fds ; n++)
-		if (wsi->context->connpool[n] == wsi->sock)
+		if (wsi->context->connpool[n] == wsi->desc.sockfd)
 			wsi->position_in_fds_table = n;
 }
 
@@ -555,7 +555,7 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
 	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
 
 	context->connpool[wsi->position_in_fds_table + context->max_fds] = (lws_sockfd_type)wsi;
-	wsi->sock->reverse = wsi;
+	wsi->desc.sockfd->reverse = wsi;
 	pt->fds_count++;
 }
 
@@ -567,13 +567,13 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
 	int n;
 	
 	for (n = 0; n < wsi->context->max_fds; n++)
-		if (wsi->context->connpool[n] == wsi->sock) {
+		if (wsi->context->connpool[n] == wsi->desc.sockfd) {
 			wsi->context->connpool[n] = NULL;
 			wsi->context->connpool[n + wsi->context->max_fds] = NULL;
 			lwsl_notice(" freed connpool %d\n", n);
 		}
 	
-	wsi->sock->reverse = NULL;
+	wsi->desc.sockfd->reverse = NULL;
 	pt->fds_count--;
 }
 
@@ -596,17 +596,17 @@ lws_plat_change_pollfd(struct lws_context *context,
 			lwsl_notice("replaying buffered rx: wsi %p\n", wsi);
 			p = wsi->premature_rx;
 			wsi->premature_rx = NULL;
-			esp8266_cb_rx(wsi->sock,
+			esp8266_cb_rx(wsi->desc.sockfd,
 				      (char *)p + wsi->prem_rx_pos,
 				      wsi->prem_rx_size - wsi->prem_rx_pos);
 			wsi->prem_rx_size = 0;
 			wsi->prem_rx_pos = 0;
 			lws_free(p);
 		}
-		if (espconn_recv_unhold(wsi->sock) < 0)
+		if (espconn_recv_unhold(wsi->desc.sockfd) < 0)
 			return -1;
 	} else
-		if (espconn_recv_hold(wsi->sock) < 0)
+		if (espconn_recv_hold(wsi->desc.sockfd) < 0)
 			return -1;
 	
 	if (!(pfd->events & LWS_POLLOUT))
diff --git a/lib/lws-plat-optee.c b/lib/lws-plat-optee.c
index 20cab5c9..c6d42b53 100644
--- a/lib/lws-plat-optee.c
+++ b/lib/lws-plat-optee.c
@@ -29,7 +29,7 @@ lws_send_pipe_choked(struct lws *wsi)
 	if (wsi->trunc_len)
 		return 1;
 
-	fds.fd = wsi->sock;
+	fds.fd = wsi->desc.sockfd;
 	fds.events = POLLOUT;
 	fds.revents = 0;
 
diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c
index da9940d0..ae829414 100644
--- a/lib/lws-plat-unix.c
+++ b/lib/lws-plat-unix.c
@@ -35,7 +35,7 @@ lws_send_pipe_choked(struct lws *wsi)
 	if (wsi->trunc_len)
 		return 1;
 
-	fds.fd = wsi->sock;
+	fds.fd = wsi->desc.sockfd;
 	fds.events = POLLOUT;
 	fds.revents = 0;
 
diff --git a/lib/lws-plat-win.c b/lib/lws-plat-win.c
index d7f413da..98f420a4 100644
--- a/lib/lws-plat-win.c
+++ b/lib/lws-plat-win.c
@@ -50,7 +50,7 @@ wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd)
 	int n = 0;
 
 	for (n = 0; n < context->fd_hashtable[h].length; n++)
-		if (context->fd_hashtable[h].wsi[n]->sock == fd)
+		if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd)
 			return context->fd_hashtable[h].wsi[n];
 
 	return NULL;
@@ -59,7 +59,7 @@ wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd)
 int
 insert_wsi(struct lws_context *context, struct lws *wsi)
 {
-	int h = LWS_FD_HASH(wsi->sock);
+	int h = LWS_FD_HASH(wsi->desc.sockfd);
 
 	if (context->fd_hashtable[h].length == (getdtablesize() - 1)) {
 		lwsl_err("hash table overflow\n");
@@ -78,7 +78,7 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
 	int n = 0;
 
 	for (n = 0; n < context->fd_hashtable[h].length; n++)
-		if (context->fd_hashtable[h].wsi[n]->sock == fd) {
+		if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
 			while (n < context->fd_hashtable[h].length) {
 				context->fd_hashtable[h].wsi[n] =
 						context->fd_hashtable[h].wsi[n + 1];
@@ -417,7 +417,7 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
 
 	pt->fds[pt->fds_count++].revents = 0;
 	pt->events[pt->fds_count] = pt->events[0];
-	WSAEventSelect(wsi->sock, pt->events[0],
+	WSAEventSelect(wsi->desc.sockfd, pt->events[0],
 			   LWS_POLLIN | LWS_POLLHUP | FD_CONNECT);
 }
 
@@ -441,7 +441,7 @@ lws_plat_check_connection_error(struct lws *wsi)
 	int optVal;
 	int optLen = sizeof(int);
 
-	if (getsockopt(wsi->sock, SOL_SOCKET, SO_ERROR,
+	if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
 			   (char*)&optVal, &optLen) != SOCKET_ERROR && optVal &&
 		optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS &&
 		optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) {
@@ -465,7 +465,7 @@ lws_plat_change_pollfd(struct lws_context *context,
 	if ((pfd->events & LWS_POLLOUT))
 		networkevents |= LWS_POLLOUT;
 
-	if (WSAEventSelect(wsi->sock,
+	if (WSAEventSelect(wsi->desc.sockfd,
 			pt->events[0],
 						   networkevents) != SOCKET_ERROR)
 		return 0;
diff --git a/lib/output.c b/lib/output.c
index 1e475490..09ceecc3 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -133,7 +133,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
 		goto handle_truncated_send;
 	}
 
-	if (!lws_socket_is_valid(wsi->sock))
+	if (!lws_socket_is_valid(wsi->desc.sockfd))
 		lwsl_warn("** error invalid sock but expected to send\n");
 
 	/* limit sending */
@@ -744,7 +744,7 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
 {
 	int n;
 
-	n = recv(wsi->sock, (char *)buf, len, 0);
+	n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
 	if (n >= 0) {
 		if (wsi->vhost)
 			wsi->vhost->conn_stats.rx += n;
@@ -767,7 +767,7 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
 	int n = 0;
 
 #if LWS_POSIX
-	n = send(wsi->sock, (char *)buf, len, MSG_NOSIGNAL);
+	n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
 //	lwsl_info("%s: sent len %d result %d", __func__, len, n);
 	if (n >= 0)
 		return n;
@@ -789,7 +789,7 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
 	// !!!
 #endif
 
-	lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", len, wsi->sock, n, LWS_ERRNO);
+	lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", len, wsi->desc.sockfd, n, LWS_ERRNO);
 	return LWS_SSL_CAPABLE_ERROR;
 }
 #endif
diff --git a/lib/pollfd.c b/lib/pollfd.c
index c6b658dd..028d96ac 100644
--- a/lib/pollfd.c
+++ b/lib/pollfd.c
@@ -39,7 +39,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 	       wsi->position_in_fds_table < pt->fds_count);
 
 	pfd = &pt->fds[wsi->position_in_fds_table];
-	pa->fd = wsi->sock;
+	pa->fd = wsi->desc.sockfd;
 	pa->prev_events = pfd->events;
 	pa->events = pfd->events = (pfd->events & ~_and) | _or;
 
@@ -132,13 +132,13 @@ lws_accept_modulation(struct lws_context_per_thread *pt, int allow)
 int
 insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 {
-	struct lws_pollargs pa = { wsi->sock, LWS_POLLIN, 0 };
+	struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	int ret = 0;
 
 
 	lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n",
-		  __func__, wsi, wsi->tsi, wsi->sock, pt->fds_count);
+		  __func__, wsi, wsi->tsi, wsi->desc.sockfd, pt->fds_count);
 
 	if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) {
 		lwsl_err("Too many fds (%d vs %d)\n", context->max_fds,
@@ -147,16 +147,16 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 	}
 
 #if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
-	if (wsi->sock >= context->max_fds) {
+	if (wsi->desc.sockfd >= context->max_fds) {
 		lwsl_err("Socket fd %d is too high (%d)\n",
-			 wsi->sock, context->max_fds);
+			 wsi->desc.sockfd, context->max_fds);
 		return 1;
 	}
 #endif
 
 	assert(wsi);
 	assert(wsi->vhost);
-	assert(lws_socket_is_valid(wsi->sock));
+	assert(lws_socket_is_valid(wsi->desc.sockfd));
 
 	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
 					   wsi->user_space, (void *) &pa, 1))
@@ -172,7 +172,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 
 	// lwsl_notice("%s: %p: setting posinfds %d\n", __func__, wsi, wsi->position_in_fds_table);
 
-	pt->fds[wsi->position_in_fds_table].fd = wsi->sock;
+	pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
 #if LWS_POSIX
 	pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
 #else
@@ -204,7 +204,7 @@ int
 remove_wsi_socket_from_fds(struct lws *wsi)
 {
 	struct lws_context *context = wsi->context;
-	struct lws_pollargs pa = { wsi->sock, 0, 0 };
+	struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
 #if !defined(LWS_WITH_ESP8266)
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws *end_wsi;
@@ -213,8 +213,8 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 	int m, ret = 0;
 
 #if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
-	if (wsi->sock > context->max_fds) {
-		lwsl_err("fd %d too high (%d)\n", wsi->sock, context->max_fds);
+	if (wsi->desc.sockfd > context->max_fds) {
+		lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, context->max_fds);
 		return 1;
 	}
 #endif
@@ -259,7 +259,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 	lws_pt_lock(pt);
 
 	lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
-		  __func__, wsi, wsi->sock, wsi->position_in_fds_table,
+		  __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
 		  pt->fds_count, pt->fds[pt->fds_count].fd);
 
 	/* have the last guy take up the now vacant slot */
@@ -278,12 +278,12 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 		end_wsi->position_in_fds_table = m;
 
 	/* deletion guy's lws_lookup entry needs nuking */
-	delete_from_fd(context, wsi->sock);
+	delete_from_fd(context, wsi->desc.sockfd);
 	/* removed wsi has no position any more */
 	wsi->position_in_fds_table = -1;
 
 	/* remove also from external POLL support via protocol 0 */
-	if (lws_socket_is_valid(wsi->sock))
+	if (lws_socket_is_valid(wsi->desc.sockfd))
 		if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
 						   wsi->user_space, (void *) &pa, 0))
 			ret = -1;
@@ -396,7 +396,7 @@ network_sock:
 		return 1;
 
 	if (wsi->position_in_fds_table < 0) {
-		lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock);
+		lwsl_err("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
 		return -1;
 	}
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 1f72f6ac..8d94c0db 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -543,7 +543,8 @@ enum connection_mode {
 	/* special internal types */
 	LWSCM_SERVER_LISTENER,
 	LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */
-	LWSCM_RAW, /* raw */
+	LWSCM_RAW, /* raw with bulk handling */
+	LWSCM_RAW_FILEDESC, /* raw without bulk handling */
 
 	/* HTTP Client related */
 	LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP,
@@ -928,8 +929,7 @@ enum {
 
 #if defined(LWS_USE_LIBEV)
 LWS_EXTERN void
-lws_libev_accept(struct lws *new_wsi, lws_sockfd_type accept_fd);
-LWS_EXTERN void
+lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
 lws_libev_io(struct lws *wsi, int flags);
 LWS_EXTERN int
 lws_libev_init_fd_table(struct lws_context *context);
@@ -956,7 +956,7 @@ LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info)
 
 #if defined(LWS_USE_LIBUV)
 LWS_EXTERN void
-lws_libuv_accept(struct lws *new_wsi, lws_sockfd_type accept_fd);
+lws_libuv_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
 LWS_EXTERN void
 lws_libuv_io(struct lws *wsi, int flags);
 LWS_EXTERN int
@@ -1408,8 +1408,7 @@ struct lws {
 	unsigned long action_start;
 	unsigned long latency_start;
 #endif
-	/* pointer / int */
-	lws_sockfd_type sock;
+	lws_sock_file_fd_type desc; /* .filefd / .sockfd */
 
 	/* ints */
 	int position_in_fds_table;
@@ -1562,7 +1561,7 @@ LWS_EXTERN int
 delete_from_fd(struct lws_context *context, lws_sockfd_type fd);
 #else
 #define wsi_from_fd(A,B)  A->lws_lookup[B]
-#define insert_wsi(A,B)   assert(A->lws_lookup[B->sock] == 0); A->lws_lookup[B->sock]=B
+#define insert_wsi(A,B)   assert(A->lws_lookup[B->desc.sockfd] == 0); A->lws_lookup[B->desc.sockfd]=B
 #define delete_from_fd(A,B) A->lws_lookup[B]=0
 #endif
 
diff --git a/lib/server.c b/lib/server.c
index 24162202..ddf72280 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -142,7 +142,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
 		goto bail;
 	}
 	wsi->context = vhost->context;
-	wsi->sock = sockfd;
+	wsi->desc.sockfd = sockfd;
 	wsi->mode = LWSCM_SERVER_LISTENER;
 	wsi->protocol = vhost->protocols;
 	wsi->tsi = m;
@@ -161,7 +161,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
 	vhost->lserv_wsi = wsi;
 
 #if LWS_POSIX
-	n = listen(wsi->sock, LWS_SOMAXCONN);
+	n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
 	if (n < 0) {
 		lwsl_err("listen failed with error %d\n", LWS_ERRNO);
 		vhost->lserv_wsi = NULL;
@@ -172,7 +172,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
 	} /* for each thread able to independently listen */
 #else
 #if defined(LWS_WITH_ESP8266)
-	esp8266_tcp_stream_bind(wsi->sock, info->port, wsi);
+	esp8266_tcp_stream_bind(wsi->desc.sockfd, info->port, wsi);
 #endif
 #endif
 	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
@@ -1502,7 +1502,7 @@ upgrade_ws:
 		wsi->u.ws.rx_ubuf_alloc = n;
 		lwsl_debug("Allocating RX buffer %d\n", n);
 #if LWS_POSIX && !defined(LWS_WITH_ESP32)
-		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
+		if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
 			       (const char *)&n, sizeof n)) {
 			lwsl_warn("Failed to set SNDBUF to %d", n);
 			return 1;
@@ -1611,7 +1611,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
 	new_wsi->protocol = vhost->protocols;
 	new_wsi->user_space = NULL;
 	new_wsi->ietf_spec_revision = 0;
-	new_wsi->sock = LWS_SOCK_INVALID;
+	new_wsi->desc.sockfd = LWS_SOCK_INVALID;
 	vhost->context->count_wsi_allocated++;
 
 	/*
@@ -1686,33 +1686,56 @@ 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_socket_vhost2(struct lws_vhost *vh, lws_sockfd_type accept_fd,
-			int allow_ssl, int raw)
+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_context *context = vh->context;
 	struct lws *new_wsi = lws_create_new_server_wsi(vh);
-	int n;
+	int n, ssl = 0;
 
 	if (!new_wsi) {
-		compatible_close(accept_fd);
+		if (type & LWS_ADOPT_SOCKET)
+			compatible_close(fd.sockfd);
 		return NULL;
 	}
 
-	lwsl_debug("%s: new wsi %p, sockfd %d, cb %p\n", __func__, new_wsi,
-		   accept_fd, context->vhost_list->protocols[0].callback);
+	new_wsi->desc = fd;
+	new_wsi->protocol = &context->vhost_list->
+			protocols[vh->default_protocol_index];
+
+	if (!(type & LWS_ADOPT_SOCKET)) {
+		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);
+			lws_free(new_wsi);
+
+			return NULL;
+		}
+	}
+	if (type & LWS_ADOPT_SOCKET) {
+
+		lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
+			   (int)(size_t)fd.sockfd);
 
-	new_wsi->sock = accept_fd;
 
-	/* the transport is accepted... give him time to negotiate */
-	lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
-			context->timeout_secs);
+		/* the transport is accepted... give him time to negotiate */
+		lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
+				context->timeout_secs);
 
 #if LWS_POSIX == 0
 #if defined(LWS_WITH_ESP8266)
-	esp8266_tcp_stream_accept(accept_fd, new_wsi);
+		esp8266_tcp_stream_accept(accept_fd, new_wsi);
 #endif
 #endif
+	} else  //* file desc */
+		lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi,
+			   (int)(size_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
@@ -1720,45 +1743,65 @@ lws_adopt_socket_vhost2(struct lws_vhost *vh, lws_sockfd_type accept_fd,
 	 * itself by default protocols[0]
 	 */
 	n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
-	if (raw)
-		n = LWS_CALLBACK_RAW_ADOPT;
-	if ((context->vhost_list->protocols[vh->default_protocol_index].callback)(
+	if (!(type & LWS_ADOPT_HTTP)) {
+		if (!(type & LWS_ADOPT_SOCKET))
+			n = LWS_CALLBACK_RAW_ADOPT_FILE;
+		else
+			n = LWS_CALLBACK_RAW_ADOPT;
+	}
+	if ((new_wsi->protocol->callback)(
 			new_wsi, n, NULL, NULL, 0)) {
-		/* force us off the timeout list by hand */
-		lws_set_timeout(new_wsi, NO_PENDING_TIMEOUT, 0);
-		compatible_close(new_wsi->sock);
+		if (type & LWS_ADOPT_SOCKET) {
+			/* force us off the timeout list by hand */
+			lws_set_timeout(new_wsi, NO_PENDING_TIMEOUT, 0);
+			compatible_close(new_wsi->desc.sockfd);
+		}
 		lws_free(new_wsi);
 		return NULL;
 	}
 
-	lws_libev_accept(new_wsi, new_wsi->sock);
-	lws_libuv_accept(new_wsi, new_wsi->sock);
-
-	if (!LWS_SSL_ENABLED(new_wsi->vhost) || allow_ssl == 0) {
-		if (raw)
-			new_wsi->mode = LWSCM_RAW;
-		if (insert_wsi_socket_into_fds(context, new_wsi)) {
-			lwsl_err("%s: fail inserting socket\n", __func__);
-			goto fail;
+	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))
+				new_wsi->mode = LWSCM_RAW_FILEDESC;
+			else
+				new_wsi->mode = LWSCM_RAW;
 		}
 	} else {
-		if (raw)
+		/* SSL */
+		if (!(type & LWS_ADOPT_HTTP))
 			new_wsi->mode = LWSCM_SSL_INIT_RAW;
 		else
 			new_wsi->mode = LWSCM_SSL_INIT;
-		if (lws_server_socket_service_ssl(new_wsi, accept_fd)) {
+
+		ssl = 1;
+	}
+
+	lws_libev_accept(new_wsi, new_wsi->desc);
+	lws_libuv_accept(new_wsi, new_wsi->desc);
+
+	if (!ssl) {
+		if (insert_wsi_socket_into_fds(context, new_wsi)) {
+			lwsl_err("%s: fail inserting socket\n", __func__);
+			goto fail;
+		}
+	} else
+		if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
 			lwsl_err("%s: fail ssl negotiation\n", __func__);
 			goto fail;
 		}
-	}
 
-	if (!lws_header_table_attach(new_wsi, 0))
-		lwsl_debug("Attached ah immediately\n");
+	if (type & LWS_ADOPT_HTTP)
+		if (!lws_header_table_attach(new_wsi, 0))
+			lwsl_debug("Attached ah immediately\n");
 
 	return new_wsi;
 
 fail:
-	lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS);
+	if (type & LWS_ADOPT_SOCKET)
+		lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS);
 
 	return NULL;
 }
@@ -1766,13 +1809,17 @@ fail:
 LWS_VISIBLE struct lws *
 lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
 {
-	return lws_adopt_socket_vhost2(vh, accept_fd, 1, 0);
+	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);
 }
 
 LWS_VISIBLE struct lws *
 lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
 {
-	return lws_adopt_socket_vhost2(context->vhost_list, accept_fd, 1, 0);
+	return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
 }
 
 /* Common read-buffer adoption for lws_adopt_*_readbuf */
diff --git a/lib/service.c b/lib/service.c
index 931e8d3a..48726c19 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -30,6 +30,9 @@ lws_calllback_as_writeable(struct lws *wsi)
 	case LWSCM_RAW:
 		n = LWS_CALLBACK_RAW_WRITEABLE;
 		break;
+	case LWSCM_RAW_FILEDESC:
+		n = LWS_CALLBACK_RAW_WRITEABLE_FILE;
+		break;
 	case LWSCM_WS_CLIENT:
 		n = LWS_CALLBACK_CLIENT_WRITEABLE;
 		break;
@@ -198,7 +201,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	 */
 
 	ret = 1;
-	if (wsi->mode == LWSCM_RAW)
+	if (wsi->mode == LWSCM_RAW || wsi->mode == LWSCM_RAW_FILEDESC)
 		ret = 0;
 	while (ret == 1) {
 
@@ -366,7 +369,7 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
 	 */
 	if ((time_t)sec > wsi->pending_timeout_limit) {
 //#if LWS_POSIX
-		if (wsi->sock != LWS_SOCK_INVALID && wsi->position_in_fds_table >= 0)
+		if (wsi->desc.sockfd != LWS_SOCK_INVALID && wsi->position_in_fds_table >= 0)
 			n = pt->fds[wsi->position_in_fds_table].events;
 
 		/* no need to log normal idle keepalive timeout */
@@ -734,7 +737,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 		while (wsi) {
 			/* we have to take copies, because he may be deleted */
 			wsi1 = wsi->timeout_list;
-			tmp_fd = wsi->sock;
+			tmp_fd = wsi->desc.sockfd;
 			if (lws_service_timeout_check(wsi, (unsigned int)now)) {
 				/* he did time out... */
 				if (tmp_fd == our_fd)
@@ -863,17 +866,42 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 			return 1;
 		goto handled;
 
+	case LWSCM_RAW_FILEDESC:
+	case LWSCM_RAW:
+		if (pollfd->revents & LWS_POLLOUT) {
+			n = lws_calllback_as_writeable(wsi);
+			if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+				lwsl_info("failed at set pollfd\n");
+				return 1;
+			}
+			if (n)
+				goto close_and_handled;
+		}
+		n = LWS_CALLBACK_RAW_RX;
+		if (wsi->mode == LWSCM_RAW_FILEDESC)
+			n = LWS_CALLBACK_RAW_RX_FILE;
+
+		if (pollfd->revents & LWS_POLLIN) {
+			if (user_callback_handle_rxflow(
+					wsi->protocol->callback,
+					wsi, n,
+					wsi->user_space, NULL, 0)) {
+				lwsl_debug("raw rx callback closed it\n");
+				goto close_and_handled;
+			}
+		}
+		n = 0;
+		goto handled;
+
 	case LWSCM_WS_SERVING:
 	case LWSCM_WS_CLIENT:
 	case LWSCM_HTTP2_SERVING:
 	case LWSCM_HTTP_CLIENT_ACCEPTED:
-	case LWSCM_RAW:
 
 		/* 1: something requested a callback when it was OK to write */
 
 		if ((pollfd->revents & LWS_POLLOUT) &&
-		    ((wsi->mode == LWSCM_RAW) ||
-		    (wsi->state == LWSS_ESTABLISHED ||
+		    ((wsi->state == LWSS_ESTABLISHED ||
 		     wsi->state == LWSS_HTTP2_ESTABLISHED ||
 		     wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS ||
 		     wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
diff --git a/lib/ssl-client.c b/lib/ssl-client.c
index b204495b..6cbfd78e 100644
--- a/lib/ssl-client.c
+++ b/lib/ssl-client.c
@@ -184,7 +184,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
 #endif
 #endif /* USE_WOLFSSL */
 
-	wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
+	wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
 	SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
 
 #ifdef USE_WOLFSSL
diff --git a/lib/ssl.c b/lib/ssl.c
index 9ae6b663..3b6c4a03 100644
--- a/lib/ssl.c
+++ b/lib/ssl.c
@@ -470,7 +470,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 
 		lws_latency_pre(context, wsi);
 
-		n = recv(wsi->sock, (char *)pt->serv_buf, context->pt_serv_buf_size,
+		n = recv(wsi->desc.sockfd, (char *)pt->serv_buf, context->pt_serv_buf_size,
 			 MSG_PEEK);
 
 		/*
@@ -550,7 +550,7 @@ go_again:
 			break;
 		}
 
-                lwsl_err("SSL_accept failed socket %u: %s\n", wsi->sock,
+                lwsl_err("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
                          lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
 		lws_ssl_elaborate_error();
 		goto fail;
diff --git a/plugins/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test.c
new file mode 100644
index 00000000..16557509
--- /dev/null
+++ b/plugins/protocol_lws_raw_test.c
@@ -0,0 +1,227 @@
+/*
+ * ws protocol handler plugin for testing raw file and raw socket
+ *
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
+ *
+ * These test plugins are intended to be adapted for use in your code, which
+ * may be proprietary.  So unlike the library itself, they are licensed
+ * Public Domain.
+ *
+ * Enable on a vhost like this
+ *
+ *        "protocol-lws-raw-test": {
+ *                 "status": "ok",
+ *                 "fifo-path": "/tmp/lws-test-raw"
+ *        },
+ *
+ * Then you can feed it data through the FIFO like this
+ *
+ *  $ sudo sh -c "echo hello > /tmp/lws-test-raw"
+ *
+ * This plugin simply prints the data.  But it does it through the lws event loop /
+ * service poll.
+ */
+
+#if !defined (LWS_PLUGIN_STATIC)
+#define LWS_DLL
+#define LWS_INTERNAL
+#include "../lib/libwebsockets.h"
+#endif
+
+#include <string.h>
+
+struct per_vhost_data__raw_test {
+	struct lws_context *context;
+	struct lws_vhost *vhost;
+	const struct lws_protocols *protocol;
+	char fifo_path[100];
+	int fifo;
+	char zero_length_read;
+};
+
+struct per_session_data__raw_test {
+	int number;
+};
+
+static int
+callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
+			void *user, void *in, size_t len)
+{
+	struct per_session_data__raw_test *pss =
+			(struct per_session_data__raw_test *)user;
+	struct per_vhost_data__raw_test *vhd =
+			(struct per_vhost_data__raw_test *)
+			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
+					lws_get_protocol(wsi));
+	lws_sock_file_fd_type u;
+
+	(void)pss;
+
+	switch (reason) {
+	case LWS_CALLBACK_PROTOCOL_INIT:
+		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
+				lws_get_protocol(wsi),
+				sizeof(struct per_vhost_data__raw_test));
+		vhd->context = lws_get_context(wsi);
+		vhd->protocol = lws_get_protocol(wsi);
+		vhd->vhost = lws_get_vhost(wsi);
+		{
+			const struct lws_protocol_vhost_options *pvo =
+					(const struct lws_protocol_vhost_options *)in;
+			while (pvo) {
+				if (!strcmp(pvo->name, "fifo-path"))
+					strncpy(vhd->fifo_path, pvo->value, sizeof(vhd->fifo_path) - 1);
+				pvo = pvo->next;
+			}
+			if (vhd->fifo_path[0] == '\0') {
+				lwsl_err("Missing pvo \"fifo-path\"\n");
+				return 1;
+			}
+		}
+		unlink(vhd->fifo_path);
+		if (mkfifo(vhd->fifo_path, 0666)) {
+			lwsl_err("mkfifo failed\n");
+			return 1;
+		}
+		vhd->fifo = open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
+		if (vhd->fifo == -1) {
+			lwsl_err("opening fifo failed\n");
+			unlink(vhd->fifo_path);
+			return 1;
+		}
+		lwsl_notice("FIFO %s created\n", vhd->fifo_path);
+		u.filefd = vhd->fifo;
+		if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u, "protocol-lws-raw-test")) {
+			lwsl_err("Failed to adopt fifo descriptor\n");
+			close(vhd->fifo);
+			unlink(vhd->fifo_path);
+			return 1;
+		}
+		break;
+
+	case LWS_CALLBACK_PROTOCOL_DESTROY:
+		if (!vhd)
+			break;
+		if (vhd->fifo >- 0) {
+			close(vhd->fifo);
+			unlink(vhd->fifo_path);
+		}
+		break;
+
+	case LWS_CALLBACK_RAW_ADOPT_FILE:
+		lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n");
+		break;
+
+
+	case LWS_CALLBACK_RAW_RX_FILE:
+		lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n");
+		{
+			char buf[256];
+			int n;
+			
+			n = read(vhd->fifo, buf, sizeof(buf) - 1);
+			if (n < 0) {
+				lwsl_err("FIFO read failed\n");
+				return 1;
+			}
+			/*
+			 * When nobody opened the other side of the FIFO, the FIFO fd acts well and
+			 * only signals POLLIN when somebody opened and wrote to it.
+			 *
+			 * But if the other side of the FIFO closed it, we will see an endless
+			 * POLLIN and 0 available to read.
+			 *
+			 * The only way to handle it is to reopen the FIFO our side and wait for a
+			 * new peer.  This is a quirk of FIFOs not of LWS.
+			 */
+			if (n == 0) { /* peer closed - do reopen in close processing */
+				vhd->zero_length_read = 1;
+				return 1;
+			}
+			buf[n] = '\0';
+			lwsl_info("read %d\n", n);
+			puts(buf);
+		}
+		break;
+
+	case LWS_CALLBACK_RAW_CLOSE_FILE:
+		lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n");
+		if (vhd->zero_length_read) {
+			vhd->zero_length_read = 0;
+			close(vhd->fifo);
+			/* the wsi that adopted the fifo file is closing... reopen the fifo and readopt */
+			vhd->fifo = open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
+			if (vhd->fifo == -1) {
+				lwsl_err("opening fifo failed\n");
+				return 1;
+			}
+			lwsl_notice("FIFO %s reopened\n", vhd->fifo_path);
+			u.filefd = vhd->fifo;
+			if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u, "protocol-lws-raw-test")) {
+				lwsl_err("Failed to adopt fifo descriptor\n");
+				close(vhd->fifo);
+				return 1;
+			}
+		}
+		break;
+
+	case LWS_CALLBACK_RAW_WRITEABLE_FILE:
+		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+#define LWS_PLUGIN_PROTOCOL_RAW_TEST \
+	{ \
+		"protocol-lws-raw-test", \
+		callback_raw_test, \
+		sizeof(struct per_session_data__raw_test), \
+		1024, /* rx buf size must be >= permessage-deflate rx size */ \
+	}
+
+#if !defined (LWS_PLUGIN_STATIC)
+		
+static const struct lws_protocols protocols[] = {
+	LWS_PLUGIN_PROTOCOL_RAW_TEST
+};
+
+LWS_EXTERN LWS_VISIBLE int
+init_protocol_lws_raw_test(struct lws_context *context,
+			     struct lws_plugin_capability *c)
+{
+	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
+		lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
+			 c->api_magic);
+		return 1;
+	}
+
+	c->protocols = protocols;
+	c->count_protocols = ARRAY_SIZE(protocols);
+	c->extensions = NULL;
+	c->count_extensions = 0;
+
+	return 0;
+}
+
+LWS_EXTERN LWS_VISIBLE int
+destroy_protocol_lws_raw_test(struct lws_context *context)
+{
+	return 0;
+}
+
+#endif
-- 
GitLab