diff --git a/README.coding.md b/README.coding.md
index 15d4c4a99c3a946f903643dae7649c9562b6eb43..33a13f6e3a85d22de880636fde1eb0832ad0d8d0 100644
--- a/README.coding.md
+++ b/README.coding.md
@@ -440,6 +440,122 @@ on the flags during open.
 7) There is an optional `mod_time` uint32_t member in the generic fop_fd.  If you are able to set it during open, you
 should indicate it by setting `LWS_FOP_FLAG_MOD_TIME_VALID` on the flags.
 
+@section rawfd RAW file descriptor polling
+
+LWS allows you to include generic platform file descriptors in the lws service / poll / event loop.
+
+Open your fd normally and then
+
+```
+	lws_sock_file_fd_type u;
+
+	u.filefd = your_open_file_fd;
+
+	if (!lws_adopt_descriptor_vhost(vhost, 0, u,
+					"protocol-name-to-bind-to",
+					optional_wsi_parent_or_NULL)) {
+		// failed
+	}
+
+	// OK
+```
+
+A wsi is created for the file fd that acts like other wsi, you will get these
+callbacks on the named protocol
+
+```
+	LWS_CALLBACK_RAW_ADOPT_FILE
+	LWS_CALLBACK_RAW_RX_FILE
+	LWS_CALLBACK_RAW_WRITEABLE_FILE
+	LWS_CALLBACK_RAW_CLOSE_FILE
+```
+
+starting with LWS_CALLBACK_RAW_ADOPT_FILE.
+
+`protocol-lws-raw-test` plugin provides a method for testing this with
+`libwebsockets-test-server-v2.0`:
+
+The plugin creates a FIFO on your system called "/tmp/lws-test-raw"
+
+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.
+
+@section rawsrvsocket RAW server socket descriptor polling
+
+You can also enable your vhost to accept RAW socket connections, in addition to
+HTTP[s] and WS[s].  If the first bytes written on the connection are not a
+valid HTTP method, then the connection switches to RAW mode.
+
+This is disabled by default, you enable it by setting the `.options` flag
+LWS_SERVER_OPTION_FALLBACK_TO_RAW when creating the vhost.
+
+RAW mode socket connections receive the following callbacks
+
+```
+	LWS_CALLBACK_RAW_ADOPT
+	LWS_CALLBACK_RAW_RX
+	LWS_CALLBACK_RAW_WRITEABLE
+	LWS_CALLBACK_RAW_CLOSE
+```
+
+You can control which protocol on your vhost handles these RAW mode
+incoming connections by marking the selected protocol with a pvo `raw`, eg
+
+```
+        "protocol-lws-raw-test": {
+                 "status": "ok",
+                 "raw": "1"
+        },
+```
+
+The "raw" pvo marks this protocol as being used for RAW connections.
+
+`protocol-lws-raw-test` plugin provides a method for testing this with
+`libwebsockets-test-server-v2.0`:
+
+Run libwebsockets-test-server-v2.0 and connect to it by telnet, eg
+
+```
+    $ telnet 127.0.0.1 7681
+```
+
+type something that isn't a valid HTTP method and enter, before the
+connection times out.  The connection will switch to RAW mode using this
+protocol, and pass the unused rx as a raw RX callback.
+    
+The test protocol echos back what was typed on telnet to telnet.
+
+@section rawclientsocket RAW client socket descriptor polling
+
+You can now also open RAW socket connections in client mode.
+
+Follow the usual method for creating a client connection, but set the
+`info.method` to "RAW".  When the connection is made, the wsi will be
+converted to RAW mode and operate using the same callbacks as the
+server RAW sockets described above.
+
+The libwebsockets-test-client supports this using raw:// URLS.  To
+test, open a netcat listener in one window
+
+```
+ $ nc -l 9999
+```
+
+and in another window, connect to it using the test client
+
+```
+ $ libwebsockets-test-client raw://127.0.0.1:9999
+```
+
+The connection should succeed, and text typed in the netcat window (including a CRLF)
+will be received in the client.
+
 @section ecdh ECDH Support
 
 ECDH Certs are now supported.  Enable the CMake option
diff --git a/lib/client.c b/lib/client.c
index 57b79f40464e3e6cc1718af3fd6b449420374bc4..5a29aa6b6c8a0ff53572146285dec76bb9b3f5a2 100755
--- a/lib/client.c
+++ b/lib/client.c
@@ -196,6 +196,9 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
 	case LWSCM_WSCL_ISSUE_HANDSHAKE2:
 		p = lws_generate_client_handshake(wsi, p);
 		if (p == NULL) {
+			if (wsi->mode == LWSCM_RAW)
+				return 0;
+
 			lwsl_err("Failed to generate handshake for client\n");
 			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
 			return 0;
@@ -1027,6 +1030,36 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
 	} else
 		wsi->do_ws = 0;
 
+	if (!strcmp(meth, "RAW")) {
+		const char *pp = lws_hdr_simple_ptr(wsi,
+					_WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
+		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+		lwsl_notice("client transition to raw\n");
+		if (pp) {
+			const struct lws_protocols *pr;
+
+			pr = lws_vhost_name_to_protocol(wsi->vhost, pp);
+
+			if (!pr) {
+				lwsl_err("protocol %s not enabled on vhost\n",
+					 pp);
+				return NULL;
+			}
+
+			lws_bind_protocol(wsi, pr);
+		}
+		if ((wsi->protocol->callback)(wsi,
+				LWS_CALLBACK_RAW_ADOPT,
+				wsi->user_space, NULL, 0))
+			return NULL;
+
+		wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
+		lws_union_transition(wsi, LWSCM_RAW);
+		lws_header_table_detach(wsi, 1);
+
+		return NULL;
+	}
+
 	if (wsi->do_ws) {
 		/*
 		 * create the random key
diff --git a/lib/context.c b/lib/context.c
index 024cc815f2fb7e8a106c6077fe84a287ab4cb41e..7f4237a46ca9096e59a28f586b5ad3fa88c43932 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -175,6 +175,13 @@ lws_protocol_init(struct lws_context *context)
 						   vh->protocols[n].name);
 						vh->default_protocol_index = n;
 					}
+					if (!strcmp(pvo->name, "raw")) {
+						lwsl_notice("Setting raw "
+						   "protocol for vh %s to %s\n",
+						   vh->name,
+						   vh->protocols[n].name);
+						vh->raw_protocol_index = n;
+					}
 					pvo = pvo->next;
 				}
 
diff --git a/lib/handshake.c b/lib/handshake.c
index 68215cb7584550a1fdc3b82ff7df8701c58a3653..5f34b0e2d71c45d15e406ca1163573c5eb625744 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -121,6 +121,11 @@ lws_read(struct lws *wsi, unsigned char *buf, size_t len)
 			/* Handshake indicates this session is done. */
 			goto bail;
 
+		/* we might have transitioned to RAW */
+		if (wsi->mode == LWSCM_RAW)
+			 /* we gave the read buffer to RAW handler already */
+			goto read_ok;
+
 		/*
 		 * It's possible that we've exhausted our data already, or
 		 * rx flow control has stopped us dealing with this early,
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 9443589bce4aca2d2517a98ce58f9e545c486322..d03e740c2f21c5513020757b97807f62319a8e19 100755
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -165,6 +165,32 @@ lws_remove_child_from_any_parent(struct lws *wsi)
 	}
 }
 
+int
+lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
+{
+//	if (wsi->protocol == p)
+//		return 0;
+
+	if (wsi->protocol)
+		wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
+					wsi->user_space, NULL, 0);
+	if (!wsi->user_space_externally_allocated)
+		lws_free_set_NULL(wsi->user_space);
+
+	wsi->protocol = p;
+	if (!p)
+		return 0;
+
+	if (lws_ensure_user_space(wsi))
+		return 1;
+
+	if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BIND_PROTOCOL,
+				    wsi->user_space, NULL, 0))
+		return 1;
+
+	return 0;
+}
+
 void
 lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 {
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 14682a9b7e3820f783510965bf85220987abc913..d76a5580cac4c3fd66580b4761acb0af0ccc4bbe 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1612,6 +1612,8 @@ enum lws_context_options {
 	 * the context, only the string you give in the client connect
 	 * info for .origin (if any) will be used directly.
 	 */
+	LWS_SERVER_OPTION_FALLBACK_TO_RAW			= (1 << 20),
+	/**< (VH) if invalid http is coming in the first line,  */
 
 	/****** add new things just above ---^ ******/
 };
diff --git a/lib/parsers.c b/lib/parsers.c
index 26f53f4b26d31daecdc641e996d2b2c55ff909c0..4eb0459d2b163fd5f2aeda561123e767a1b7c207 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -813,9 +813,16 @@ swallow:
 				}
 			/*
 			 * hm it's an unknown http method from a client in fact,
-			 * treat as dangerous
+			 * it cannot be valid http
 			 */
 			if (m == ARRAY_SIZE(methods)) {
+				/*
+				 * are we set up to accept raw in these cases?
+				 */
+				if (lws_check_opt(wsi->vhost->options,
+					   LWS_SERVER_OPTION_FALLBACK_TO_RAW))
+					return 2; /* transition to raw */
+
 				lwsl_info("Unknown method - dropping\n");
 				goto forbid;
 			}
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 80d0636826798c5e77b25ef13f8d3bc61016db84..b0b59470d9aa1d277a1a7ce241d6b05dfd76b526 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -795,6 +795,7 @@ struct lws_vhost {
 	unsigned int created_vhost_protocols:1;
 
 	unsigned char default_protocol_index;
+	unsigned char raw_protocol_index;
 };
 
 /*
diff --git a/lib/server.c b/lib/server.c
index 32d972e49a7ec7a6e8964604548e4a4be28e8f72..94eb0d0a7aec8b747ed00ae8f27a0b697677d20f 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -210,7 +210,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
 	if (p)
 		colon = p - servername;
 
-	/* first try exact matches */
+	/* Priotity 1: first try exact matches */
 
 	while (vhost) {
 		if (port == vhost->listen_port &&
@@ -222,7 +222,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
 	}
 
 	/*
-	 * if no exact matches, try matching *.vhost-name
+	 * Priority 2: if no exact matches, try matching *.vhost-name
 	 * unintentional matches are possible but resolve to x.com for *.x.com
 	 * which is reasonable.  If exact match exists we already chose it and
 	 * never reach here.  SSL will still fail it if the cert doesn't allow
@@ -243,6 +243,20 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
 		vhost = vhost->vhost_next;
 	}
 
+	/* Priority 3: match the first vhost on our port */
+
+	vhost = context->vhost_list;
+	while (vhost) {
+		if (port == vhost->listen_port) {
+			lwsl_info("vhost match to %s based on port %d\n",
+					vhost->name, port);
+			return vhost;
+		}
+		vhost = vhost->vhost_next;
+	}
+
+	/* no match */
+
 	return NULL;
 }
 
@@ -1137,43 +1151,19 @@ transaction_result_n:
 #endif
 }
 
-int
-lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
-{
-//	if (wsi->protocol == p)
-//		return 0;
-
-	if (wsi->protocol)
-		wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
-					wsi->user_space, NULL, 0);
-	if (!wsi->user_space_externally_allocated)
-		lws_free_set_NULL(wsi->user_space);
-
-	wsi->protocol = p;
-	if (!p)
-		return 0;
-
-	if (lws_ensure_user_space(wsi))
-		return 1;
-
-	if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BIND_PROTOCOL,
-				    wsi->user_space, NULL, 0))
-		return 1;
-
-	return 0;
-}
-
 
 int
 lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 {
+	int protocol_len, n = 0, hit, non_space_char_found = 0, m;
 	struct lws_context *context = lws_get_context(wsi);
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct _lws_header_related hdr;
 	struct allocated_headers *ah;
-	int protocol_len, n = 0, hit, non_space_char_found = 0;
+	unsigned char *obuf = *buf;
 	char protocol_list[128];
 	char protocol_name[64];
+	size_t olen = len;
 	char *p;
 
 	if (len >= 10000000) {
@@ -1195,7 +1185,38 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 			goto bail_nuke_ah;
 		}
 
-		if (lws_parse(wsi, *(*buf)++)) {
+		m = lws_parse(wsi, *(*buf)++);
+		if (m) {
+			if (m == 2) {
+				/*
+				 * we are transitioning from http with
+				 * an AH, to raw.  Drop the ah and set
+				 * the mode.
+				 */
+raw_transition:
+				lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+				lws_bind_protocol(wsi, &wsi->vhost->protocols[
+				                        wsi->vhost->
+				                        raw_protocol_index]);
+				lwsl_info("transition to raw vh %s prot %d\n",
+					  wsi->vhost->name,
+					  wsi->vhost->raw_protocol_index);
+				if ((wsi->protocol->callback)(wsi,
+						LWS_CALLBACK_RAW_ADOPT,
+						wsi->user_space, NULL, 0))
+					goto bail_nuke_ah;
+
+				wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
+				lws_union_transition(wsi, LWSCM_RAW);
+				lws_header_table_detach(wsi, 1);
+
+				if (m == 2 && (wsi->protocol->callback)(wsi,
+						LWS_CALLBACK_RAW_RX,
+						wsi->user_space, obuf, olen))
+					return 1;
+
+				return 0;
+			}
 			lwsl_info("lws_parse failed\n");
 			goto bail_nuke_ah;
 		}
@@ -1253,13 +1274,8 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
 			lwsl_info("Changing to RAW mode\n");
-			lws_union_transition(wsi, LWSCM_RAW);
-			if (!wsi->more_rx_waiting) {
-				wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
-
-				//lwsl_notice("%p: dropping ah EST\n", wsi);
-				lws_header_table_detach(wsi, 1);
-			}
+			m = 0;
+			goto raw_transition;
 		}
 
 		wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
@@ -1991,6 +2007,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 	case LWSCM_HTTP_SERVING:
 	case LWSCM_HTTP_SERVING_ACCEPTED:
 	case LWSCM_HTTP2_SERVING:
+	case LWSCM_RAW:
 
 		/* handle http headers coming in */
 
@@ -2033,9 +2050,9 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 
 		/* these states imply we MUST have an ah attached */
 
-		if (wsi->state == LWSS_HTTP ||
+		if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_HTTP ||
 		    wsi->state == LWSS_HTTP_ISSUING_FILE ||
-		    wsi->state == LWSS_HTTP_HEADERS) {
+		    wsi->state == LWSS_HTTP_HEADERS)) {
 			if (!wsi->u.hdr.ah) {
 				
 				//lwsl_err("wsi %p: missing ah\n", wsi);
@@ -2105,7 +2122,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 
 		len = lws_ssl_capable_read(wsi, pt->serv_buf,
 					   context->pt_serv_buf_size);
-//		lwsl_notice("%s: wsi %p read %d\r\n", __func__, wsi, len);
+		lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
 		switch (len) {
 		case 0:
 			lwsl_info("%s: read 0 len\n", __func__);
@@ -2124,10 +2141,10 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 					wsi, LWS_CALLBACK_RAW_RX,
 					wsi->user_space, pt->serv_buf, len);
 			if (n < 0) {
-				lwsl_info("raw writeable_fail\n");
+				lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
 				goto fail;
 			}
-			break;
+			goto try_pollout;
 		}
 
 		/* just ignore incoming if waiting for close */
@@ -2162,6 +2179,17 @@ try_pollout:
 			goto fail;
 		}
 
+		if (wsi->mode == LWSCM_RAW) {
+			n = user_callback_handle_rxflow(wsi->protocol->callback,
+					wsi, LWS_CALLBACK_RAW_WRITEABLE,
+					wsi->user_space, NULL, 0);
+			if (n < 0) {
+				lwsl_info("writeable_fail\n");
+				goto fail;
+			}
+			break;
+		}
+
 		if (!wsi->hdr_parsing_completed)
 			break;
 
diff --git a/lib/service.c b/lib/service.c
index 84e489dd65d134591fb64747b018e2a3fdc8ac95..4b153195c0a89635c5c8428d9ed4d5a3d7eddee4 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -863,13 +863,15 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 			goto handled;
 		}
 #endif
+		/* fallthru */
+	case LWSCM_RAW:
 		n = lws_server_socket_service(context, wsi, pollfd);
 		if (n) /* closed by above */
 			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)) {
@@ -892,6 +894,9 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 				goto close_and_handled;
 			}
 		}
+
+		if (pollfd->revents & LWS_POLLHUP)
+			goto close_and_handled;
 		n = 0;
 		goto handled;
 
diff --git a/plugins/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test.c
index 047f136ef274aa0ad6ba29e17a0ea8e2f447a17d..35867e64e017d1140f6167228d919eda2ace87ee 100644
--- a/plugins/protocol_lws_raw_test.c
+++ b/plugins/protocol_lws_raw_test.c
@@ -17,6 +17,13 @@
  * may be proprietary.  So unlike the library itself, they are licensed
  * Public Domain.
  *
+ * This plugin test both raw file descriptors and raw socket descriptors.  It
+ * can test both or just one depending on how you configure it.  libwebsockets-
+ * test-server-v2.0 is set up to test both.
+ *
+ * RAW File Descriptor Testing
+ * ===========================
+ *
  * Enable on a vhost like this
  *
  *        "protocol-lws-raw-test": {
@@ -28,8 +35,33 @@
  *
  *  $ 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.
+ * This plugin simply prints the data.  But it does it through the lws event
+ * loop / service poll.
+ *
+ *
+ * RAW Socket Descriptor Testing
+ * =============================
+ *
+ * 1) You must give the vhost the option flag LWS_SERVER_OPTION_FALLBACK_TO_RAW
+ *
+ * 2) Enable on a vhost like this
+ *
+ *        "protocol-lws-raw-test": {
+ *                 "status": "ok",
+ *                 "raw": "1"
+ *        },
+ *
+ *    The "raw" pvo marks this protocol as being used for RAW connections.
+ *
+ * 3) Run libwebsockets-test-server-v2.0 and connect to it by telnet, eg
+ *
+ *    telnet 127.0.0.1 7681
+ *
+ *    type something that isn't a valid HTTP method and enter, before the
+ *    connection times out.  The connection will switch to RAW mode using this
+ *    protocol, and pass the unused rx as a raw RX callback.
+ *
+ *    The test protocol echos back what was typed on telnet to telnet.
  */
 
 #if !defined (LWS_PLUGIN_STATIC)
@@ -51,6 +83,8 @@ struct per_vhost_data__raw_test {
 
 struct per_session_data__raw_test {
 	int number;
+	unsigned char buf[128];
+	int len;
 };
 
 static int
@@ -84,8 +118,8 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 				pvo = pvo->next;
 			}
 			if (vhd->fifo_path[0] == '\0') {
-				lwsl_err("Missing pvo \"fifo-path\"\n");
-				return 1;
+				lwsl_err("%s: Missing pvo \"fifo-path\", raw file fd testing disabled\n", __func__);
+				break;
 			}
 		}
 		unlink(vhd->fifo_path);
@@ -101,7 +135,9 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 		}
 		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", NULL)) {
+		if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u,
+						"protocol-lws-raw-test",
+						NULL)) {
 			lwsl_err("Failed to adopt fifo descriptor\n");
 			close(vhd->fifo);
 			unlink(vhd->fifo_path);
@@ -118,6 +154,11 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 		}
 		break;
 
+
+	/*
+	 * Callbacks related to Raw file descriptor testing
+	 */
+
 	case LWS_CALLBACK_RAW_ADOPT_FILE:
 		lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n");
 		break;
@@ -179,6 +220,32 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
 		break;
 
+	/*
+	 * Callbacks related to Raw socket descriptor testing
+	 */
+
+	case LWS_CALLBACK_RAW_ADOPT:
+		lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
+		break;
+
+	case LWS_CALLBACK_RAW_RX:
+		lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
+		if (len > sizeof(pss->buf))
+			len = sizeof(pss->buf);
+		memcpy(pss->buf, in, len);
+		pss->len = len;
+		lws_callback_on_writable(wsi);
+		break;
+
+	case LWS_CALLBACK_RAW_CLOSE:
+		lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
+		break;
+
+	case LWS_CALLBACK_RAW_WRITEABLE:
+		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
+		lws_write(wsi, pss->buf, pss->len, LWS_WRITE_HTTP);
+		break;
+
 	default:
 		break;
 	}
diff --git a/test-server/test-client.c b/test-server/test-client.c
index 63d0d6d082031796b207b0ad2f1fe65a7d368a32..4575ee041943480a8fcdbc70d782f17cc5bbf5d1 100644
--- a/test-server/test-client.c
+++ b/test-server/test-client.c
@@ -334,10 +334,38 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
 	return 0;
 }
 
+static int
+callback_test_raw_client(struct lws *wsi, enum lws_callback_reasons reason,
+			 void *user, void *in, size_t len)
+{
+	switch (reason) {
+	case LWS_CALLBACK_RAW_ADOPT:
+		lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
+		break;
+
+	case LWS_CALLBACK_RAW_RX:
+		lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
+		puts(in);
+		break;
+
+	case LWS_CALLBACK_RAW_CLOSE:
+		lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
+		break;
+
+	case LWS_CALLBACK_RAW_WRITEABLE:
+		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
 
 /* list of supported protocols and callbacks */
 
-static struct lws_protocols protocols[] = {
+static const struct lws_protocols protocols[] = {
 	{
 		"dumb-increment-protocol",
 		callback_dumb_increment,
@@ -349,6 +377,11 @@ static struct lws_protocols protocols[] = {
 		callback_lws_mirror,
 		0,
 		128,
+	}, {
+		"lws-test-raw-client",
+		callback_test_raw_client,
+		0,
+		128
 	},
 	{ NULL, NULL, 0, 0 } /* end */
 };
@@ -597,7 +630,13 @@ int main(int argc, char **argv)
 			i.method = "GET";
 		do_ws = 0;
 	} else
-		lwsl_notice("using %s mode (ws)\n", prot);
+		if (!strcmp(prot, "raw")) {
+			i.method = "RAW";
+			i.protocol = "lws-test-raw-client";
+			lwsl_notice("using RAW mode connection\n");
+			do_ws = 0;
+		} else
+			lwsl_notice("using %s mode (ws)\n", prot);
 
 	/*
 	 * sit there servicing the websocket context to handle incoming
diff --git a/test-server/test-server-v2.0.c b/test-server/test-server-v2.0.c
index 96e00a50e6458272fd86e059811def79adec1b29..61149f8036eae8c2f64df9af6fd0af4689ab5dc3 100644
--- a/test-server/test-server-v2.0.c
+++ b/test-server/test-server-v2.0.c
@@ -155,13 +155,34 @@ static const struct lws_protocol_vhost_options pvo_opt = {
 	"1"
 };
 
+static const struct lws_protocol_vhost_options pvo_opt4a = {
+	NULL,
+	NULL,
+	"raw", /* indicate we are the protocol that gets raw connections */
+	"1"
+};
+
+static const struct lws_protocol_vhost_options pvo_opt4 = {
+	&pvo_opt4a,
+	NULL,
+	"fifo-path", /* tell the raw test plugin to open a raw file here */
+	"/tmp/lws-test-raw"
+};
+
 /*
  * We must enable the plugin protocols we want into our vhost with a
  * linked-list.  We can also give the plugin per-vhost options here.
  */
 
-static const struct lws_protocol_vhost_options pvo_3 = {
+static const struct lws_protocol_vhost_options pvo_4 = {
 	NULL,
+	&pvo_opt4, /* set us as the protocol who gets raw connections */
+	"protocol-lws-raw-test",
+	"" /* ignored, just matches the protocol name above */
+};
+
+static const struct lws_protocol_vhost_options pvo_3 = {
+	&pvo_4,
 	NULL,
 	"protocol-post-demo",
 	"" /* ignored, just matches the protocol name above */
@@ -367,7 +388,7 @@ int main(int argc, char **argv)
 	info.gid = gid;
 	info.uid = uid;
 	info.max_http_header_pool = 16;
-	info.options = opts |
+	info.options = opts | LWS_SERVER_OPTION_FALLBACK_TO_RAW |
 			LWS_SERVER_OPTION_VALIDATE_UTF8 |
 			LWS_SERVER_OPTION_LIBUV; /* plugins require this */