diff --git a/CMakeLists.txt b/CMakeLists.txt
index 47eed556145a4536ecc50c57261c2206673f436c..93d7210d148d1c2aebff8df18354952678b123f1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1608,7 +1608,10 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS)
 		endif()
 	endif()
 
-
+	if (LWS_WITH_LIBEV)
+		# libev generates a big mess of warnings with gcc, maintainer claims gcc to blame
+		set_source_files_properties( lib/event-libs/libev/libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" )
+	endif()
 
 
 	if (NOT LWS_WITHOUT_SERVER)
@@ -1651,9 +1654,8 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS)
 					""
 					""
 					"")
-				# libev generates a big mess of warnings with gcc, maintainers blame gcc
+				# libev generates a big mess of warnings with gcc, maintainer claims gcc to blame
 				set_source_files_properties( test-apps/test-server-libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" )
-				set_source_files_properties( lib/event-libs/libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" )
 
 			endif()
 			if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
@@ -1671,7 +1673,7 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS)
 		#
 		# test-server-extpoll
 		#
-		if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL)
+		if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL AND NOT WIN32)
 			create_test_app(test-server-extpoll
 				"test-apps/test-server.c"
 				""
@@ -1889,7 +1891,7 @@ endif(LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS)
 			endforeach()
 		endif()
 	endif()
-endif(NOT LWS_WITHOUT_TESTAPPS)
+endif((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS)
 
 if (LWS_WITH_LWSWS)
 		list(APPEND LWSWS_SRCS
diff --git a/lib/context.c b/lib/context.c
index 75bf45aacc913708f654aa936b199ac330fc6236..d42f07c61a9c7d653b3978b93eda0c10115df4d3 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -1794,10 +1794,8 @@ lws_context_destroy(struct lws_context *context)
 	struct lws wsi;
 	int n, m;
 
-	if (!context) {
-		lwsl_notice("%s: ctx %p\n", __func__, context);
+	if (!context)
 		return;
-	}
 
 	if (context->finalize_destroy_after_internal_loops_stopped) {
 		if (context->event_loop_ops->destroy_context2)
@@ -1809,8 +1807,11 @@ lws_context_destroy(struct lws_context *context)
 	}
 
 	if (context->being_destroyed1) {
-		if (!context->being_destroyed2)
-			return lws_context_destroy2(context);
+		if (!context->being_destroyed2) {
+			lws_context_destroy2(context);
+
+			return;
+		}
 		lwsl_info("%s: ctx %p: already being destroyed\n",
 			    __func__, context);
 		return;
diff --git a/lib/event-libs/README.md b/lib/event-libs/README.md
index 3a7c377e1033a441da1ca05646b898f2bed9a0d3..c4c78a7e091d555249f08d3b20e86cab3bca88a0 100644
--- a/lib/event-libs/README.md
+++ b/lib/event-libs/README.md
@@ -43,8 +43,6 @@ event libs, eg,
 ```
 #if defined(LWS_WITH_LIBUV)
  #include "event-libs/libuv/private.h"
-#else
- #define LWS_LIBUV_ENABLED(context) (0)
 #endif
 ```
 
diff --git a/lib/event-libs/libev/libev.c b/lib/event-libs/libev/libev.c
index 06e812b0e804cb8b08110bd86998c894d2d2189a..746d57126c2ed0ee799596ac9b56b238d4dae697 100644
--- a/lib/event-libs/libev/libev.c
+++ b/lib/event-libs/libev/libev.c
@@ -177,9 +177,6 @@ elops_init_context_ev(struct lws_context *context,
 static void
 elops_accept_ev(struct lws *wsi)
 {
-	struct lws_context *context = lws_get_context(wsi);
-	struct ev_io *r = &wsi->w_read.ev.watcher;
-	struct ev_io *w = &wsi->w_write.ev.watcher;
 	int fd;
 
 	if (wsi->role_ops->file_handle)
@@ -187,10 +184,11 @@ elops_accept_ev(struct lws *wsi)
 	else
 		fd = wsi->desc.sockfd;
 
-	wsi->w_read.context = context;
-	wsi->w_write.context = context;
-	ev_io_init(r, lws_accept_cb, fd, EV_READ);
-	ev_io_init(w, lws_accept_cb, fd, EV_WRITE);
+	wsi->w_read.context = wsi->context;
+	wsi->w_write.context = wsi->context;
+
+	ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ);
+	ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE);
 }
 
 static void
diff --git a/lib/event-libs/libev/private.h b/lib/event-libs/libev/private.h
index 6ff8ba1720158bf2b392f00a7db525e415f8de65..54e1b124e240afb6a7d7eb8fa5ce501fce7179f3 100644
--- a/lib/event-libs/libev/private.h
+++ b/lib/event-libs/libev/private.h
@@ -25,6 +25,7 @@
 
 struct lws_pt_eventlibs_libev {
 	struct ev_loop *io_loop;
+	struct ev_timer hrtimer;
 };
 
 struct lws_io_watcher_libev {
diff --git a/lib/event-libs/libevent/libevent.c b/lib/event-libs/libevent/libevent.c
index bd56ea97b269d78a04c1953b99d33769abe1f8aa..43dda05c5ee7d2b8a81820c9c355fe66b60ca669 100644
--- a/lib/event-libs/libevent/libevent.c
+++ b/lib/event-libs/libevent/libevent.c
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
diff --git a/lib/event-libs/libevent/private.h b/lib/event-libs/libevent/private.h
index 2c4de3ea746af07f0e5bfe777f3b8eaf48a9c585..347c7fe5459b8d4090583693d425bc03a6699298 100644
--- a/lib/event-libs/libevent/private.h
+++ b/lib/event-libs/libevent/private.h
@@ -25,6 +25,7 @@
 
 struct lws_pt_eventlibs_libevent {
 	struct event_base *io_loop;
+	struct event *hrtimer;
 };
 
 struct lws_io_watcher_libevent {
diff --git a/lib/event-libs/libuv/private.h b/lib/event-libs/libuv/private.h
index c7b46bd8f53e12689c0f647e8b6818407962be35..173340e354542ea95579ee075fcc3c14265096a2 100644
--- a/lib/event-libs/libuv/private.h
+++ b/lib/event-libs/libuv/private.h
@@ -62,3 +62,5 @@ struct lws_signal_watcher_libuv {
 
 extern struct lws_event_loop_ops event_loop_ops_uv;
 
+LWS_VISIBLE uv_loop_t *
+lws_uv_getloop(struct lws_context *context, int tsi);
diff --git a/lib/event-libs/private.h b/lib/event-libs/private.h
new file mode 100644
index 0000000000000000000000000000000000000000..59705c9ece28baeb2792c88c67e7e94e39c586cd
--- /dev/null
+++ b/lib/event-libs/private.h
@@ -0,0 +1,74 @@
+/*
+ * 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
+ *
+ *  This is included from private-libwebsockets.h
+ */
+
+struct lws_event_loop_ops {
+	const char *name;
+	/* event loop-specific context init during context creation */
+	int (*init_context)(struct lws_context *context,
+			    const struct lws_context_creation_info *info);
+	/* called during lws_destroy_context */
+	int (*destroy_context1)(struct lws_context *context);
+	/* called during lws_destroy_context2 */
+	int (*destroy_context2)(struct lws_context *context);
+	/* init vhost listening wsi */
+	int (*init_vhost_listen_wsi)(struct lws *wsi);
+	/* init the event loop for a pt */
+	int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
+	/* called at end of first phase of close_free_wsi()  */
+	int (*wsi_logical_close)(struct lws *wsi);
+	/* return nonzero if client connect not allowed  */
+	int (*check_client_connect_ok)(struct lws *wsi);
+	/* close handle manually  */
+	void (*close_handle_manually)(struct lws *wsi);
+	/* event loop accept processing  */
+	void (*accept)(struct lws *wsi);
+	/* control wsi active events  */
+	void (*io)(struct lws *wsi, int flags);
+	/* run the event loop for a pt */
+	void (*run_pt)(struct lws_context *context, int tsi);
+	/* called before pt is destroyed */
+	void (*destroy_pt)(struct lws_context *context, int tsi);
+	/* called just before wsi is freed  */
+	void (*destroy_wsi)(struct lws *wsi);
+
+	unsigned int periodic_events_available:1;
+};
+
+/* bring in event libs private declarations */
+
+#if defined(LWS_WITH_POLL)
+#include "event-libs/poll/private.h"
+#endif
+
+#if defined(LWS_WITH_LIBUV)
+#include "event-libs/libuv/private.h"
+#endif
+
+#if defined(LWS_WITH_LIBEVENT)
+#include "event-libs/libevent/private.h"
+#endif
+
+#if defined(LWS_WITH_LIBEV)
+#include "event-libs/libev/private.h"
+#endif
+
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 9f7ce8e2f7377adbba15b23be0fa227728e92909..af4ca2f600ee7cf8e803d69ff7150bf079b07115 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -574,13 +574,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 	 * must go through and close all those first
 	 */
 	if (wsi->vhost) {
-		lws_vhost_lock(wsi->vhost);
+		if ((int)reason != -1)
+			lws_vhost_lock(wsi->vhost);
 		lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
 					wsi->dll_client_transaction_queue_head.next) {
 			struct lws *w = lws_container_of(d, struct lws,
 							 dll_client_transaction_queue);
 
-			__lws_close_free_wsi(w, reason, "trans q leader closing");
+			__lws_close_free_wsi(w, -1, "trans q leader closing");
 		} lws_end_foreach_dll_safe(d, d1);
 
 		/*
@@ -592,7 +593,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 		 * queue leader is closing.
 		 */
 		lws_dll_lws_remove(&wsi->dll_client_transaction_queue);
-		lws_vhost_unlock(wsi->vhost);
+		if ((int)reason !=-1)
+			lws_vhost_unlock(wsi->vhost);
 	}
 #endif
 
@@ -1030,6 +1032,7 @@ lws_buflist_use_segment(struct lws_buflist **head, size_t len)
 void
 lws_buflist_describe(struct lws_buflist **head, void *id)
 {
+	struct lws_buflist *old;
 	int n = 0;
 
 	if (*head == NULL)
@@ -1040,7 +1043,12 @@ lws_buflist_describe(struct lws_buflist **head, void *id)
 			    (unsigned long long)(*head)->pos,
 			    (unsigned long long)(*head)->len,
 			    (unsigned long long)(*head)->len - (*head)->pos);
+		old = *head;
 		head = &((*head)->next);
+		if (*head == old) {
+			lwsl_err("%s: next points to self\n", __func__);
+			break;
+		}
 		n++;
 	}
 }
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index d97ca4a96449fabd649a0b5180eb79311240af62..3ccb4a7255f717e590b2895cdd2c913c727788fb 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -4606,7 +4606,7 @@ lws_plat_recommended_rsa_bits(void);
  * cleanly and at the moment all of its libuv objects have completed close.
  */
 
-LWS_VISIBLE uv_loop_t *
+LWS_VISIBLE LWS_EXTERN uv_loop_t *
 lws_uv_getloop(struct lws_context *context, int tsi);
 
 LWS_VISIBLE LWS_EXTERN void
diff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c
index 80b43b1b0818c4ee0e7cc08a0387b6cc5ecf7c83..8ace47586cf840ba3510a64fedf5e4c3f0196feb 100644
--- a/lib/plat/lws-plat-unix.c
+++ b/lib/plat/lws-plat-unix.c
@@ -57,8 +57,6 @@ lws_plat_pipe_signal(struct lws *wsi)
 
 	n = write(pt->dummy_pipe_fds[1], &buf, 1);
 
-	lwsl_debug("%s: fd %d %d\n", __func__, pt->dummy_pipe_fds[1], n);
-
 	return n != 1;
 }
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 2aafdeb1227f71a3a29b97501fc8ce8f5f191f44..d731c183b57487d4659dd380c0c3dae3d354f158 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -374,210 +374,11 @@ enum lws_ssl_capable_status {
 
 /*
  *
- *  ------ role ------
+ *  ------ roles ------
  *
  */
 
-typedef uint32_t lws_wsi_state_t;
-
-/*
- * The wsi->role_ops pointer decides almost everything about what role the wsi
- * will play, h2, raw, ws, etc.
- *
- * However there are a few additional flags needed that vary, such as if the
- * role is a client or server side, if it has that concept.  And the connection
- * fulfilling the role, has a separate dynamic state.
- *
- *   31           16 15      0
- *   [  role flags ] [ state ]
- *
- * The role flags part is generally invariant for the lifetime of the wsi,
- * although it can change if the connection role itself does, eg, if the
- * connection upgrades from H1 -> WS1 the role flags may be changed at that
- * point.
- *
- * The state part reflects the dynamic connection state, and the states are
- * reused between roles.
- *
- * None of the internal role or state representations are made available outside
- * of lws internals.  Even for lws internals, if you add stuff here, please keep
- * the constants inside this header only by adding necessary helpers here and
- * use the helpers in the actual code.  This is to ease any future refactors.
- *
- * Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our
- * data as a stream inside a different protocol.
- */
-
-#define _RS 16
-
-#define LWSIFR_CLIENT		(0x1000 << _RS) /* client side */
-#define LWSIFR_SERVER		(0x2000 << _RS) /* server side */
-
-#define LWSIFR_P_ENCAP_H2	(0x0100 << _RS) /* we are encapsulated by h2 */
-
-enum lwsi_role {
-	LWSI_ROLE_MASK		=			     (0xffff << _RS),
-	LWSI_ROLE_ENCAP_MASK	=			     (0x0f00 << _RS),
-};
-
-#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK)
-#if !defined (_DEBUG)
-#define lwsi_set_role(wsi, role) wsi->wsistate = \
-				(wsi->wsistate & (~LWSI_ROLE_MASK)) | role
-#else
-void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
-#endif
-
-#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
-#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER))
-#define lwsi_role_h2_ENCAPSULATION(wsi) \
-		((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2)
-
-/* Pollout wants a callback in this state */
-#define LWSIFS_POCB		(0x100)
-/* Before any protocol connection was established */
-#define LWSIFS_NOT_EST		(0x200)
-
-enum lwsi_state {
-
-	/* Phase 1: pre-transport */
-
-	LRS_UNCONNECTED				= LWSIFS_NOT_EST | 0,
-	LRS_WAITING_CONNECT			= LWSIFS_NOT_EST | 1,
-
-	/* Phase 2: establishing intermediaries on top of transport */
-
-	LRS_WAITING_PROXY_REPLY			= LWSIFS_NOT_EST | 2,
-	LRS_WAITING_SSL				= LWSIFS_NOT_EST | 3,
-	LRS_WAITING_SOCKS_GREETING_REPLY	= LWSIFS_NOT_EST | 4,
-	LRS_WAITING_SOCKS_CONNECT_REPLY		= LWSIFS_NOT_EST | 5,
-	LRS_WAITING_SOCKS_AUTH_REPLY		= LWSIFS_NOT_EST | 6,
-
-	/* Phase 3: establishing tls tunnel */
-
-	LRS_SSL_INIT				= LWSIFS_NOT_EST | 7,
-	LRS_SSL_ACK_PENDING			= LWSIFS_NOT_EST | 8,
-	LRS_PRE_WS_SERVING_ACCEPT		= LWSIFS_NOT_EST | 9,
-
-	/* Phase 4: connected */
-
-	LRS_WAITING_SERVER_REPLY		= LWSIFS_NOT_EST | 10,
-	LRS_H2_AWAIT_PREFACE			= LWSIFS_NOT_EST | 11,
-	LRS_H2_AWAIT_SETTINGS			= LWSIFS_NOT_EST |
-						  LWSIFS_POCB | 12,
-
-	/* Phase 5: protocol logically established */
-
-	LRS_H2_CLIENT_SEND_SETTINGS		= LWSIFS_POCB | 13,
-	LRS_H2_WAITING_TO_SEND_HEADERS		= LWSIFS_POCB | 14,
-	LRS_DEFERRING_ACTION			= LWSIFS_POCB | 15,
-	LRS_IDLING				= 16,
-	LRS_H1C_ISSUE_HANDSHAKE			= 17,
-	LRS_H1C_ISSUE_HANDSHAKE2		= 18,
-	LRS_ISSUE_HTTP_BODY			= 19,
-	LRS_ISSUING_FILE			= 20,
-	LRS_HEADERS				= 21,
-	LRS_BODY				= 22,
-	LRS_ESTABLISHED				= LWSIFS_POCB | 23,
-
-	/* Phase 6: finishing */
-
-	LRS_WAITING_TO_SEND_CLOSE		= LWSIFS_POCB | 24,
-	LRS_RETURNED_CLOSE			= LWSIFS_POCB | 25,
-	LRS_AWAITING_CLOSE_ACK			= LWSIFS_POCB | 26,
-	LRS_FLUSHING_BEFORE_CLOSE		= LWSIFS_POCB | 27,
-	LRS_SHUTDOWN				= 28,
-
-	/* Phase 7: dead */
-
-	LRS_DEAD_SOCKET				= 29,
-
-	LRS_MASK				= 0xffff
-};
-
-#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_est(wsi) (!(wsi->wsistate & 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 = \
-			  (wsi->wsistate & (~LRS_MASK)) | lrs
-#else
-void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
-#endif
-
-/*
- * internal role-specific ops
- */
-struct lws_context_per_thread;
-struct lws_role_ops {
-	const char *name;
-	const char *alpn;
-	/*
-	 * After http headers have parsed, this is the last chance for a role
-	 * to upgrade the connection to something else using the headers.
-	 * ws-over-h2 is upgraded from h2 like this.
-	 */
-	int (*check_upgrades)(struct lws *wsi);
-	/* role-specific context init during context creation */
-	int (*init_context)(struct lws_context *context,
-			    const struct lws_context_creation_info *info);
-	/* role-specific per-vhost init during vhost creation */
-	int (*init_vhost)(struct lws_vhost *vh,
-			  const struct lws_context_creation_info *info);
-	/* role-specific per-vhost destructor during vhost destroy */
-	int (*destroy_vhost)(struct lws_vhost *vh);
-	/* generic 1Hz callback for the role itself */
-	int (*periodic_checks)(struct lws_context *context, int tsi,
-			       time_t now);
-	/* chance for the role to force POLLIN without network activity */
-	int (*service_flag_pending)(struct lws_context *context, int tsi);
-	/* an fd using this role has POLLIN signalled */
-	int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi,
-			     struct lws_pollfd *pollfd);
-	/* an fd using the role wanted a POLLOUT callback and now has it */
-	int (*handle_POLLOUT)(struct lws *wsi);
-	/* perform user pollout */
-	int (*perform_user_POLLOUT)(struct lws *wsi);
-	/* do effective callback on writeable */
-	int (*callback_on_writable)(struct lws *wsi);
-	/* connection-specific tx credit in bytes */
-	lws_fileofs_t (*tx_credit)(struct lws *wsi);
-	/* role-specific write formatting */
-	int (*write_role_protocol)(struct lws *wsi, unsigned char *buf,
-				   size_t len, enum lws_write_protocol *wp);
-
-	/* get encapsulation parent */
-	struct lws * (*encapsulation_parent)(struct lws *wsi);
-
-	/* role-specific destructor */
-	int (*alpn_negotiated)(struct lws *wsi, const char *alpn);
-
-	/* chance for the role to handle close in the protocol */
-	int (*close_via_role_protocol)(struct lws *wsi,
-				       enum lws_close_status reason);
-	/* role-specific close processing */
-	int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi);
-	/* role-specific connection close processing */
-	int (*close_kill_connection)(struct lws *wsi,
-				     enum lws_close_status reason);
-	/* role-specific destructor */
-	int (*destroy_role)(struct lws *wsi);
-
-	/*
-	 * the callback reasons for WRITEABLE for client, server
-	 * (just client applies if no concept of client or server)
-	 */
-	uint16_t writeable_cb[2];
-	/*
-	 * the callback reasons for CLOSE for client, server
-	 * (just client applies if no concept of client or server)
-	 */
-	uint16_t close_cb[2];
-
-	unsigned int file_handle:1; /* role operates on files not sockets */
-};
+#include "roles/private.h"
 
 /* null-terminated array of pointers to roles lws built with */
 extern const struct lws_role_ops *available_roles[];
@@ -589,113 +390,13 @@ extern const struct lws_role_ops *available_roles[];
 
 #define LWS_FOR_EVERY_AVAILABLE_ROLE_END }}
 
-/* core roles */
-extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
-			   role_ops_pipe;
-
-/* bring in role private declarations */
-
-#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
- #include "roles/http/private.h"
-#else
- #define lwsi_role_http(wsi) (0)
-#endif
-
-#if defined(LWS_ROLE_H1)
- #include "roles/h1/private.h"
-#else
- #define lwsi_role_h1(wsi) (0)
-#endif
-
-#if defined(LWS_ROLE_H2)
- #include "roles/h2/private.h"
-#else
- #define lwsi_role_h2(wsi) (0)
-#endif
-
-#if defined(LWS_ROLE_WS)
- #include "roles/ws/private.h"
-#else
- #define lwsi_role_ws(wsi) (0)
-#endif
-
-#if defined(LWS_ROLE_CGI)
- #include "roles/cgi/private.h"
-#else
- #define lwsi_role_cgi(wsi) (0)
-#endif
-
-enum {
-	LWS_HP_RET_BAIL_OK,
-	LWS_HP_RET_BAIL_DIE,
-	LWS_HP_RET_USER_SERVICE,
-
-	LWS_HPI_RET_WSI_ALREADY_DIED,	/* we closed it */
-	LWS_HPI_RET_HANDLED,		/* no probs */
-	LWS_HPI_RET_PLEASE_CLOSE_ME,	/* close it for us */
-
-	LWS_UPG_RET_DONE,
-	LWS_UPG_RET_CONTINUE,
-	LWS_UPG_RET_BAIL
-};
-
 /*
  *
  *  ------ event_loop ops ------
  *
  */
 
-struct lws_event_loop_ops {
-	const char *name;
-	/* event loop-specific context init during context creation */
-	int (*init_context)(struct lws_context *context,
-			    const struct lws_context_creation_info *info);
-	/* called during lws_destroy_context */
-	int (*destroy_context1)(struct lws_context *context);
-	/* called during lws_destroy_context2 */
-	int (*destroy_context2)(struct lws_context *context);
-	/* init vhost listening wsi */
-	int (*init_vhost_listen_wsi)(struct lws *wsi);
-	/* init the event loop for a pt */
-	int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
-	/* called at end of first phase of close_free_wsi()  */
-	int (*wsi_logical_close)(struct lws *wsi);
-	/* return nonzero if client connect not allowed  */
-	int (*check_client_connect_ok)(struct lws *wsi);
-	/* close handle manually  */
-	void (*close_handle_manually)(struct lws *wsi);
-	/* event loop accept processing  */
-	void (*accept)(struct lws *wsi);
-	/* control wsi active events  */
-	void (*io)(struct lws *wsi, int flags);
-	/* run the event loop for a pt */
-	void (*run_pt)(struct lws_context *context, int tsi);
-	/* called before pt is destroyed */
-	void (*destroy_pt)(struct lws_context *context, int tsi);
-	/* called just before wsi is freed  */
-	void (*destroy_wsi)(struct lws *wsi);
-
-	unsigned int periodic_events_available:1;
-};
-
-/* bring in event libs private declarations */
-
-#if defined(LWS_WITH_POLL)
-#include "event-libs/poll/private.h"
-#endif
-
-#if defined(LWS_WITH_LIBUV)
-#include "event-libs/libuv/private.h"
-#endif
-
-#if defined(LWS_WITH_LIBEVENT)
-#include "event-libs/libevent/private.h"
-#endif
-
-#if defined(LWS_WITH_LIBEV)
-#include "event-libs/libev/private.h"
-#endif
-
+#include "event-libs/private.h"
 
 /* enums of socks version */
 enum socks_version {
@@ -774,8 +475,6 @@ struct lws_ring {
 struct lws_protocols;
 struct lws;
 
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-
 struct lws_io_watcher {
 #ifdef LWS_WITH_LIBEV
 	struct lws_io_watcher_libev ev;
@@ -803,7 +502,6 @@ struct lws_signal_watcher {
 #endif
 	struct lws_context *context;
 };
-#endif
 
 #ifdef _WIN32
 #define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS)
diff --git a/lib/roles/README.md b/lib/roles/README.md
index 0e3a01c49857eaf24691f7736b7f5840a6698c91..7e7126879fa3ed802d6052d8862257b74a700a34 100644
--- a/lib/roles/README.md
+++ b/lib/roles/README.md
@@ -66,7 +66,7 @@ However when the declarations must be accessible to other things in lws build, e
 the role adds members to `struct lws` when enabled, they should be in the role
 directory in a file `private.h`.
 
-Search for "bring in role private declarations" in `./lib/private-libwebsockets.h
+Search for "bring in role private declarations" in `./lib/roles/private.h
 and add your private role file there following the style used for the other roles,
 eg,
 
diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index 0cde99d5f1d47a5d3ed97105913f4f1612072963..0f14264b25b6a4caff963d68cbd189fce060ee18 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -314,11 +314,17 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
 			goto try_pollout;
 		}
 
+		/*
+		 * We got here because there was specifically POLLIN...
+		 * regardless of our buflist state, we need to get it,
+		 * and either use it, or append to the buflist and use
+		 * buflist head material.
+		 */
+
 		buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
 		switch (ebuf.len) {
 		case 0:
-			lwsl_info("%s: read 0 len a\n",
-				   __func__);
+			lwsl_info("%s: read 0 len a\n", __func__);
 			wsi->seen_zero_length_recv = 1;
 			lws_change_pollfd(wsi, LWS_POLLIN, 0);
 			goto try_pollout;
diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c
index f10f7c93e7e924184c4c0515862134f41dd9d120..208342f4ccb3d4fc2af6e932b43eee7f1a37bade 100644
--- a/lib/roles/h2/ops-h2.c
+++ b/lib/roles/h2/ops-h2.c
@@ -279,11 +279,19 @@ drain:
 				lws_dll_lws_remove(&wsi->dll_buflist);
 			}
 		} else
-			if (n != ebuf.len &&
-			    lws_buflist_append_segment(&wsi->buflist,
+			if (n != ebuf.len) {
+				m = lws_buflist_append_segment(&wsi->buflist,
 						(uint8_t *)ebuf.token + n,
-						ebuf.len - n) < 0)
-				return LWS_HPI_RET_PLEASE_CLOSE_ME;
+						ebuf.len - n);
+				if (m < 0)
+					return LWS_HPI_RET_PLEASE_CLOSE_ME;
+				if (m) {
+					lwsl_debug("%s: added %p to rxflow list\n",
+							__func__, wsi);
+					lws_dll_lws_add_front(&wsi->dll_buflist,
+							&pt->dll_head_buflist);
+				}
+			}
 	}
 
 	// lws_buflist_describe(&wsi->buflist, wsi);
diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c
index 6455a1c04a3a3e109a1e60447e4a329b2583b5d4..c4b61bcb902ced680cd96daba372610a2d2db4a7 100644
--- a/lib/roles/http/client/client-handshake.c
+++ b/lib/roles/http/client/client-handshake.c
@@ -123,10 +123,11 @@ lws_client_connect_2(struct lws *wsi)
 			}
 #endif
 
-			lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n", wsi, w,
-				w->wsistate);
+			lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n",
+				wsi, w, w->wsistate);
 			/*
 			 * ...let's add ourselves to his transaction queue...
+			 * we are adding ourselves at the HEAD
 			 */
 			lws_dll_lws_add_front(&wsi->dll_client_transaction_queue,
 				&w->dll_client_transaction_queue_head);
@@ -522,12 +523,15 @@ send_hs:
 		 * LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
 		 *
 		 * If we are trying to do this too early, before the master
-		 * connection has written his own headers,
+		 * connection has written his own headers, then it will just
+		 * wait in the queue until it's possible to send them.
 		 */
 		lws_callback_on_writable(wsi_piggyback);
-		lwsl_info("wsi %p: waiting to send headers\n", wsi);
+		lwsl_info("%s: wsi %p: waiting to send headers (parent state %x)\n",
+			    __func__, wsi, lwsi_state(wsi_piggyback));
 	} else {
-		lwsl_info("wsi %p: client creating own connection\n", wsi);
+		lwsl_info("%s: wsi %p: client creating own connection\n",
+			    __func__, wsi);
 
 		/* we are making our own connection */
 		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c
index 1a1a27c29000b98b64de2cd32914af00c02e0bec..1a61e62c12d92055793f960c5148cdd1dafecddf 100644
--- a/lib/roles/http/client/client.c
+++ b/lib/roles/http/client/client.c
@@ -29,24 +29,26 @@ lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
 
 /*
  * return self, or queued client wsi we are acting on behalf of
+ *
+ * That is the TAIL of the queue (new queue elements are added at the HEAD)
  */
 
 struct lws *
 lws_client_wsi_effective(struct lws *wsi)
 {
-	struct lws *wsi_eff = wsi;
-	struct lws_dll_lws *d;
+	struct lws_dll_lws *tail = NULL;
 
 	if (!wsi->transaction_from_pipeline_queue ||
 	    !wsi->dll_client_transaction_queue_head.next)
 		return wsi;
 
-	d = wsi->dll_client_transaction_queue_head.next;
-	if (d)
-		wsi_eff = lws_container_of(d, struct lws,
-					dll_client_transaction_queue);
+	lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+				   wsi->dll_client_transaction_queue_head.next) {
+		tail = d;
+	} lws_end_foreach_dll_safe(d, d1);
 
-	return wsi_eff;
+	return lws_container_of(tail, struct lws,
+				  dll_client_transaction_queue);
 }
 
 /*
@@ -97,34 +99,40 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
 	if ((pollfd->revents & LWS_POLLOUT) &&
 	     wsi->keepalive_active &&
 	     wsi->dll_client_transaction_queue_head.next) {
-		int found = 0;
+		struct lws *wfound = NULL;
 
 		lwsl_debug("%s: pollout HANDSHAKE2\n", __func__);
 
-		/* we have a transaction queue that wants to pipeline */
+		/*
+		 * We have a transaction queued that wants to pipeline.
+		 *
+		 * We have to allow it to send headers strictly in the order
+		 * that it was queued, ie, tail-first.
+		 */
 		lws_vhost_lock(wsi->vhost);
 		lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
 					   wsi->dll_client_transaction_queue_head.next) {
 			struct lws *w = lws_container_of(d, struct lws,
 						  dll_client_transaction_queue);
 
-			lwsl_notice("%s: %p states 0x%x\n", __func__, w, w->wsistate);
-			if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) {
-				/*
-				 * pollfd has the master sockfd in it... we
-				 * need to use that in HANDSHAKE2 to understand
-				 * which wsi to actually write on
-				 */
-				lws_client_socket_service(w, pollfd, wsi);
-				lws_callback_on_writable(wsi);
-				found = 1;
-				break;
-			}
+			lwsl_debug("%s: %p states 0x%x\n", __func__, w, w->wsistate);
+			if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2)
+				wfound = w;
 		} lws_end_foreach_dll_safe(d, d1);
-		lws_vhost_unlock(wsi->vhost);
 
-		if (!found)
-			lwsl_err("%s: didn't find anything in HS2\n", __func__);
+		if (wfound) {
+			/*
+			 * pollfd has the master sockfd in it... we
+			 * need to use that in HANDSHAKE2 to understand
+			 * which wsi to actually write on
+			 */
+			lws_client_socket_service(wfound, pollfd, wsi);
+			lws_callback_on_writable(wsi);
+		} else
+			lwsl_debug("%s: didn't find anything in txn q in HS2\n",
+							   __func__);
+
+		lws_vhost_unlock(wsi->vhost);
 
 		return 0;
 	}
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index 2f2224f5138f198f954f7e1aa09826636823ce82..8616b23761f37df5554c7b11cb2724ba0bc20fc2 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -1342,7 +1342,7 @@ deal_body:
 			 */
 
 			while (1) {
-				ebuf.len = lws_buflist_next_segment_len(
+				ebuf.len = (int)lws_buflist_next_segment_len(
 						&wsi->buflist, (uint8_t **)&ebuf.token);
 				if (!ebuf.len)
 					break;
@@ -1789,7 +1789,8 @@ lws_http_transaction_completed(struct lws *wsi)
 			}
 #endif
 		} else {
-			lwsl_debug("%s: resetting and keeping ah as more pipeline stuff available\n", __func__);
+			lwsl_debug("%s: resetting and keeping ah as pipeline\n",
+				   __func__);
 			lws_header_table_reset(wsi, 0);
 			/*
 			 * If we kept the ah, we should restrict the amount
@@ -1804,13 +1805,12 @@ lws_http_transaction_completed(struct lws *wsi)
 		if (wsi->http.ah)
 			wsi->http.ah->ues = URIES_IDLE;
 
-		lwsi_set_state(wsi, LRS_ESTABLISHED);
+		//lwsi_set_state(wsi, LRS_ESTABLISHED);
 	} else
 		if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
 			if (lws_header_table_attach(wsi, 0))
 				lwsl_debug("acquired ah\n");
 
-
 	lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
 	lws_callback_on_writable(wsi);
 
@@ -2063,6 +2063,7 @@ 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;
@@ -2070,8 +2071,13 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
 	if (!readbuf || len == 0)
 		return wsi;
 
-	if (lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, len) < 0)
+	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.
@@ -2086,7 +2092,6 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
 	if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
 
 		lwsl_notice("%s: calling service on readbuf ah\n", __func__);
-		pt = &wsi->context->pt[(int)wsi->tsi];
 
 		/* unlike a normal connect, we have the headers already
 		 * (or the first part of them anyway).
diff --git a/lib/roles/private.h b/lib/roles/private.h
new file mode 100644
index 0000000000000000000000000000000000000000..f13e14aeb36bfe6f50841ca7e43678e5ab73139d
--- /dev/null
+++ b/lib/roles/private.h
@@ -0,0 +1,273 @@
+/*
+ * 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
+ *
+ *  This is included from private-libwebsockets.h
+ */
+
+typedef uint32_t lws_wsi_state_t;
+
+/*
+ * The wsi->role_ops pointer decides almost everything about what role the wsi
+ * will play, h2, raw, ws, etc.
+ *
+ * However there are a few additional flags needed that vary, such as if the
+ * role is a client or server side, if it has that concept.  And the connection
+ * fulfilling the role, has a separate dynamic state.
+ *
+ *   31           16 15      0
+ *   [  role flags ] [ state ]
+ *
+ * The role flags part is generally invariant for the lifetime of the wsi,
+ * although it can change if the connection role itself does, eg, if the
+ * connection upgrades from H1 -> WS1 the role flags may be changed at that
+ * point.
+ *
+ * The state part reflects the dynamic connection state, and the states are
+ * reused between roles.
+ *
+ * None of the internal role or state representations are made available outside
+ * of lws internals.  Even for lws internals, if you add stuff here, please keep
+ * the constants inside this header only by adding necessary helpers here and
+ * use the helpers in the actual code.  This is to ease any future refactors.
+ *
+ * Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our
+ * data as a stream inside a different protocol.
+ */
+
+#define _RS 16
+
+#define LWSIFR_CLIENT		(0x1000 << _RS) /* client side */
+#define LWSIFR_SERVER		(0x2000 << _RS) /* server side */
+
+#define LWSIFR_P_ENCAP_H2	(0x0100 << _RS) /* we are encapsulated by h2 */
+
+enum lwsi_role {
+	LWSI_ROLE_MASK		=			     (0xffff << _RS),
+	LWSI_ROLE_ENCAP_MASK	=			     (0x0f00 << _RS),
+};
+
+#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK)
+#if !defined (_DEBUG)
+#define lwsi_set_role(wsi, role) wsi->wsistate = \
+				(wsi->wsistate & (~LWSI_ROLE_MASK)) | role
+#else
+void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
+#endif
+
+#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
+#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER))
+#define lwsi_role_h2_ENCAPSULATION(wsi) \
+		((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2)
+
+/* Pollout wants a callback in this state */
+#define LWSIFS_POCB		(0x100)
+/* Before any protocol connection was established */
+#define LWSIFS_NOT_EST		(0x200)
+
+enum lwsi_state {
+
+	/* Phase 1: pre-transport */
+
+	LRS_UNCONNECTED				= LWSIFS_NOT_EST | 0,
+	LRS_WAITING_CONNECT			= LWSIFS_NOT_EST | 1,
+
+	/* Phase 2: establishing intermediaries on top of transport */
+
+	LRS_WAITING_PROXY_REPLY			= LWSIFS_NOT_EST | 2,
+	LRS_WAITING_SSL				= LWSIFS_NOT_EST | 3,
+	LRS_WAITING_SOCKS_GREETING_REPLY	= LWSIFS_NOT_EST | 4,
+	LRS_WAITING_SOCKS_CONNECT_REPLY		= LWSIFS_NOT_EST | 5,
+	LRS_WAITING_SOCKS_AUTH_REPLY		= LWSIFS_NOT_EST | 6,
+
+	/* Phase 3: establishing tls tunnel */
+
+	LRS_SSL_INIT				= LWSIFS_NOT_EST | 7,
+	LRS_SSL_ACK_PENDING			= LWSIFS_NOT_EST | 8,
+	LRS_PRE_WS_SERVING_ACCEPT		= LWSIFS_NOT_EST | 9,
+
+	/* Phase 4: connected */
+
+	LRS_WAITING_SERVER_REPLY		= LWSIFS_NOT_EST | 10,
+	LRS_H2_AWAIT_PREFACE			= LWSIFS_NOT_EST | 11,
+	LRS_H2_AWAIT_SETTINGS			= LWSIFS_NOT_EST |
+						  LWSIFS_POCB | 12,
+
+	/* Phase 5: protocol logically established */
+
+	LRS_H2_CLIENT_SEND_SETTINGS		= LWSIFS_POCB | 13,
+	LRS_H2_WAITING_TO_SEND_HEADERS		= LWSIFS_POCB | 14,
+	LRS_DEFERRING_ACTION			= LWSIFS_POCB | 15,
+	LRS_IDLING				= 16,
+	LRS_H1C_ISSUE_HANDSHAKE			= 17,
+	LRS_H1C_ISSUE_HANDSHAKE2		= 18,
+	LRS_ISSUE_HTTP_BODY			= 19,
+	LRS_ISSUING_FILE			= 20,
+	LRS_HEADERS				= 21,
+	LRS_BODY				= 22,
+	LRS_ESTABLISHED				= LWSIFS_POCB | 23,
+
+	/* Phase 6: finishing */
+
+	LRS_WAITING_TO_SEND_CLOSE		= LWSIFS_POCB | 24,
+	LRS_RETURNED_CLOSE			= LWSIFS_POCB | 25,
+	LRS_AWAITING_CLOSE_ACK			= LWSIFS_POCB | 26,
+	LRS_FLUSHING_BEFORE_CLOSE		= LWSIFS_POCB | 27,
+	LRS_SHUTDOWN				= 28,
+
+	/* Phase 7: dead */
+
+	LRS_DEAD_SOCKET				= 29,
+
+	LRS_MASK				= 0xffff
+};
+
+#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_est(wsi) (!(wsi->wsistate & 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 = \
+			  (wsi->wsistate & (~LRS_MASK)) | lrs
+#else
+void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
+#endif
+
+/*
+ * internal role-specific ops
+ */
+struct lws_context_per_thread;
+struct lws_role_ops {
+	const char *name;
+	const char *alpn;
+	/*
+	 * After http headers have parsed, this is the last chance for a role
+	 * to upgrade the connection to something else using the headers.
+	 * ws-over-h2 is upgraded from h2 like this.
+	 */
+	int (*check_upgrades)(struct lws *wsi);
+	/* role-specific context init during context creation */
+	int (*init_context)(struct lws_context *context,
+			    const struct lws_context_creation_info *info);
+	/* role-specific per-vhost init during vhost creation */
+	int (*init_vhost)(struct lws_vhost *vh,
+			  const struct lws_context_creation_info *info);
+	/* role-specific per-vhost destructor during vhost destroy */
+	int (*destroy_vhost)(struct lws_vhost *vh);
+	/* generic 1Hz callback for the role itself */
+	int (*periodic_checks)(struct lws_context *context, int tsi,
+			       time_t now);
+	/* chance for the role to force POLLIN without network activity */
+	int (*service_flag_pending)(struct lws_context *context, int tsi);
+	/* an fd using this role has POLLIN signalled */
+	int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi,
+			     struct lws_pollfd *pollfd);
+	/* an fd using the role wanted a POLLOUT callback and now has it */
+	int (*handle_POLLOUT)(struct lws *wsi);
+	/* perform user pollout */
+	int (*perform_user_POLLOUT)(struct lws *wsi);
+	/* do effective callback on writeable */
+	int (*callback_on_writable)(struct lws *wsi);
+	/* connection-specific tx credit in bytes */
+	lws_fileofs_t (*tx_credit)(struct lws *wsi);
+	/* role-specific write formatting */
+	int (*write_role_protocol)(struct lws *wsi, unsigned char *buf,
+				   size_t len, enum lws_write_protocol *wp);
+
+	/* get encapsulation parent */
+	struct lws * (*encapsulation_parent)(struct lws *wsi);
+
+	/* role-specific destructor */
+	int (*alpn_negotiated)(struct lws *wsi, const char *alpn);
+
+	/* chance for the role to handle close in the protocol */
+	int (*close_via_role_protocol)(struct lws *wsi,
+				       enum lws_close_status reason);
+	/* role-specific close processing */
+	int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi);
+	/* role-specific connection close processing */
+	int (*close_kill_connection)(struct lws *wsi,
+				     enum lws_close_status reason);
+	/* role-specific destructor */
+	int (*destroy_role)(struct lws *wsi);
+
+	/*
+	 * the callback reasons for WRITEABLE for client, server
+	 * (just client applies if no concept of client or server)
+	 */
+	uint16_t writeable_cb[2];
+	/*
+	 * the callback reasons for CLOSE for client, server
+	 * (just client applies if no concept of client or server)
+	 */
+	uint16_t close_cb[2];
+
+	unsigned int file_handle:1; /* role operates on files not sockets */
+};
+
+/* core roles */
+extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
+			   role_ops_pipe;
+
+/* bring in role private declarations */
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ #include "roles/http/private.h"
+#else
+ #define lwsi_role_http(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_H1)
+ #include "roles/h1/private.h"
+#else
+ #define lwsi_role_h1(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_H2)
+ #include "roles/h2/private.h"
+#else
+ #define lwsi_role_h2(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_WS)
+ #include "roles/ws/private.h"
+#else
+ #define lwsi_role_ws(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_CGI)
+ #include "roles/cgi/private.h"
+#else
+ #define lwsi_role_cgi(wsi) (0)
+#endif
+
+enum {
+	LWS_HP_RET_BAIL_OK,
+	LWS_HP_RET_BAIL_DIE,
+	LWS_HP_RET_USER_SERVICE,
+
+	LWS_HPI_RET_WSI_ALREADY_DIED,	/* we closed it */
+	LWS_HPI_RET_HANDLED,		/* no probs */
+	LWS_HPI_RET_PLEASE_CLOSE_ME,	/* close it for us */
+
+	LWS_UPG_RET_DONE,
+	LWS_UPG_RET_CONTINUE,
+	LWS_UPG_RET_BAIL
+};
diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c
index 8327918629c52f70e905dad1ba761ac41ec25e21..0d8a0b98c303763c6f813a662daf9dce149e164f 100644
--- a/lib/roles/ws/server-ws.c
+++ b/lib/roles/ws/server-ws.c
@@ -568,7 +568,7 @@ lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
 {
 	uint8_t *buffer = *buf, mask[4];
 	struct lws_tokens ebuf;
-	unsigned int avail = len;
+	unsigned int avail = (unsigned int)len;
 #if !defined(LWS_WITHOUT_EXTENSIONS)
 	unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
 #endif
diff --git a/lib/service.c b/lib/service.c
index 291995cf2f4208c81fad1979cd024db6e931253f..0dd4b35891393aaba6e2acf0f6e9aa70baae0c15 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -351,33 +351,64 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
 	return timeout_ms;
 }
 
-
+/*
+ * POLLIN said there is something... we must read it, and either use it; or
+ * if other material already in the buflist append it and return the buflist
+ * head material.
+ */
 int
 lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
 		       struct lws_tokens *ebuf)
 {
-	ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
-						 (uint8_t **)&ebuf->token);
-	if (!ebuf->len) {
-		ebuf->token = (char *)pt->serv_buf;
-		ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf,
-					wsi->context->pt_serv_buf_size);
+	int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL);
+
+	ebuf->token = (char *)pt->serv_buf;
+	ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf,
+					 wsi->context->pt_serv_buf_size);
+
+	if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior)
+		goto get_from_buflist;
 
-		// if (ebuf->len > 0)
-		//	lwsl_hexdump_notice(ebuf->token, ebuf->len);
+	if (ebuf->len <= 0)
+		return 0;
+
+	/* nothing in buflist already?  Then just use what we read */
+
+	if (!prior)
+		return 0;
+
+	/* stash what we read */
 
-		return 0; /* fresh */
+	n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token,
+				       ebuf->len);
+	if (n < 0)
+		return -1;
+	if (n) {
+		lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
+		lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
 	}
 
-	return 1; /* buffered */
+	/* get the first buflist guy in line */
+
+get_from_buflist:
+
+	ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
+						      (uint8_t **)&ebuf->token);
+
+	return 1; /* came from buflist */
 }
 
 int
 lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
 			  int buffered)
 {
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
 	int m;
 
+	/* it's in the buflist; we didn't use any */
+
+	if (!used && buffered)
+		return 0;
 
 	if (used && buffered) {
 		m = lws_buflist_use_segment(&wsi->buflist, used);
@@ -394,10 +425,17 @@ lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
 
 	/* any remainder goes on the buflist */
 
-	if (used != ebuf->len &&
-	    lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token +
-					    used, ebuf->len - used) < 0)
-		return 1; /* OOM */
+	if (used != ebuf->len) {
+		m = lws_buflist_append_segment(&wsi->buflist,
+					       (uint8_t *)ebuf->token + used,
+					       ebuf->len - used);
+		if (m < 0)
+			return 1; /* OOM */
+		if (m) {
+			lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
+			lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
+		}
+	}
 
 	return 0;
 }
diff --git a/lwsws/main.c b/lwsws/main.c
index 1a89f852ac65600e06124f93e7a52a4c591cfaf5..fba948ef61aaf499d8cbb7d2ec5f7eb5dfcf2431 100644
--- a/lwsws/main.c
+++ b/lwsws/main.c
@@ -316,9 +316,11 @@ int main(int argc, char **argv)
 	}
 
 	lws_context_destroy(context);
-
+	(void)budget;
+#if (UV_VERSION_MAJOR > 0) // Travis...
 	while ((n = uv_loop_close(&loop)) && --budget)
 		uv_run(&loop, UV_RUN_ONCE);
+#endif
 
 	fprintf(stderr, "lwsws exited cleanly: %d\n", n);
 
diff --git a/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh b/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh
index af8a4bcb181d151669e9e930d99a888cbd443f15..2da54b66931295b4bc1728a043c0b5203b44f4a6 100755
--- a/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh
+++ b/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh
@@ -33,9 +33,9 @@ wait $SPID 2>/dev/null
 
 if [ -z "$TRAVIS_OS_NAME" ] ; then
 	SPID=""
-	spawn "" $5/http-server/minimal-http-server-libuv $1/lws-minimal-http-server-libuv -s
+	spawn "" $5/http-server/minimal-http-server-eventlib $1/lws-minimal-http-server-eventlib --uv -s
 	dotest $1 $2 localhost-suv -l
-	spawn $SPID $5/http-server/minimal-http-server-libuv $1/lws-minimal-http-server-libuv -s
+	spawn $SPID $5/http-server/minimal-http-server-eventlib $1/lws-minimal-http-server-eventlib --uv -s
 	dotest $1 $2 localhost-suv-h1 -l --h1
 
 	kill $SPID 2>/dev/null
diff --git a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c
index e7c8d076b58303e65c40df93314f6d294381c927..19940ffeb273b4d9aa59feafea7331f3c02d51be 100644
--- a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c
+++ b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c
@@ -94,7 +94,8 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
 		return 0; /* don't passthru */
 
 	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
-		lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %d\n", u->index);
+		lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %p: idx %d\n",
+			  wsi, u->index);
 		client_wsi[u->index] = NULL;
 		if (++completed == COUNT) {
 			if (!failed)
@@ -166,7 +167,7 @@ lws_try_client_connection(struct lws_client_connect_info *i, int m)
 			interrupted = 1;
 		}
 	} else
-		lwsl_user("started connection %d\n", m);
+		lwsl_user("started connection %p: idx %d\n", client_wsi[m], m);
 }
 
 int main(int argc, const char **argv)
@@ -201,7 +202,6 @@ int main(int argc, const char **argv)
 	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
 	info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
 	info.protocols = protocols;
-	info.max_http_header_pool = 20;
 
 #if defined(LWS_WITH_MBEDTLS)
 	/*
@@ -237,6 +237,9 @@ int main(int argc, const char **argv)
 		i.address = "warmcat.com";
 	}
 
+	if ((p = lws_cmdline_option(argc, argv, "--port")))
+		i.port = atoi(p);
+
 	i.host = i.address;
 	i.origin = i.address;
 	i.method = "GET";
@@ -267,7 +270,7 @@ int main(int argc, const char **argv)
 				if (m == (int)LWS_ARRAY_SIZE(client_wsi) - 1)
 					next = us() + 1000000;
 				else
-					next = us() + 100000;
+					next = us() + 300000;
 			}
 		}
 
diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt
index 6ad94936b75272ff0861d9f7995ac6c454539a6d..2d804a70cf9ed5c08639ccb58de866d49ab1589e 100644
--- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt
+++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt
@@ -1,8 +1,8 @@
 cmake_minimum_required(VERSION 2.8)
 include(CheckCSourceCompiles)
 
-set(SAMP lws-minimal-http-server-libuv-foreign)
-set(SRCS minimal-http-server-libuv-foreign.c)
+set(SAMP lws-minimal-http-server-eventlib-foreign)
+set(SRCS minimal-http-server-eventlib-foreign.c)
 
 # If we are being built as part of lws, confirm current build config supports
 # reqconfig, else skip building ourselves.
@@ -63,16 +63,25 @@ ENDMACRO()
 
 set(requirements 1)
 require_lws_config(LWS_ROLE_H1 1 requirements)
-require_lws_config(LWS_WITH_LIBUV 1 requirements)
 require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
 
-CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\n#include <uv.h>\nint main(void) {\n#if (UV_VERSION_MAJOR > 0)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_RECENT_LIBUV)
-if (NOT HAS_RECENT_LIBUV)
-	if (LWS_WITH_MINIMAL_EXAMPLES)
-		message("libuv is too old (pre- 1.0)")
-	else()
-		message(FATAL_ERROR "libuv is too old (pre- 1.0)")
-	endif()
+CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(LWS_WITH_LIBUV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBUV)
+CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(LWS_WITH_LIBEVENT)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEVENT)
+CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(LWS_WITH_LIBEV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEV)
+
+if (LWS_WITH_LIBUV)
+	set(extralibs ${extralibs} uv)
+endif()
+if (LWS_WITH_LIBEVENT)
+	set(extralibs ${extralibs} event)
+endif()
+if (LWS_WITH_LIBEV)
+	set(extralibs ${extralibs} ev)
+endif()
+
+message("Extra libs: ${extralibs}")
+
+if (NOT LWS_WITH_LIBUV AND NOT LWS_WITH_LIBEVENT AND NOT LWS_WITH_LIBEV)
 	set(requirements 0)
 endif()
 
@@ -80,9 +89,9 @@ if (requirements)
 	add_executable(${SAMP} ${SRCS})
 
 	if (websockets_shared)
-		target_link_libraries(${SAMP} websockets_shared uv)
+		target_link_libraries(${SAMP} websockets_shared ${extralibs})
 		add_dependencies(${SAMP} websockets_shared)
 	else()
-		target_link_libraries(${SAMP} websockets uv)
+		target_link_libraries(${SAMP} websockets ${extralibs})
 	endif()
 endif()
diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md
index 41cc9d93a0fa3cd9644b4d59b135721a5549b7c8..a663d6e81eae02d4090bdfd133c1822d762d513d 100644
--- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md
+++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md
@@ -1,4 +1,19 @@
-# lws minimal http server libuv foreign
+# lws minimal http server eventlib foreign
+
+Commandline option|Meaning
+---|---
+-d <loglevel>|Debug verbosity in decimal, eg, -d15
+--uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`)
+--event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`)
+--ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`)
+
+Notice libevent and libev cannot coexist in the one library.  But all the other combinations are OK.
+
+x|libuv|libevent|libev
+---|---|---|---
+libuv|-|OK|OK
+libevent|OK|-|no
+libev|OK|no|-
 
 This demonstrates having lws take part in a libuv loop owned by
 something else, with its own objects running in the loop.
diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c
new file mode 100644
index 0000000000000000000000000000000000000000..3e807d9c894668489556160a59290a639523e039
--- /dev/null
+++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c
@@ -0,0 +1,424 @@
+/*
+ * lws-minimal-http-server-eventlib-foreign
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * This demonstrates the most minimal http server you can make with lws that
+ * uses a libuv event loop created outside lws.  It shows how lws can
+ * participate in someone else's event loop and clean up after itself.
+ *
+ * You choose the event loop to work with at runtime, by giving the
+ * --uv, --event or --ev switch.  Lws has to have been configured to build the
+ * selected event lib support.
+ *
+ * To keep it simple, it serves stuff from the subdirectory 
+ * "./mount-origin" of the directory it was started in.
+ * You can change that by changing mount.origin below.
+ */
+
+#include <libwebsockets.h>
+#include <string.h>
+#include <signal.h>
+
+struct lws_context_creation_info info;
+static struct lws_context *context;
+static int lifetime = 5, reported;
+
+static void foreign_timer_service(void *foreign_loop);
+
+enum {
+	TEST_STATE_CREATE_LWS_CONTEXT,
+	TEST_STATE_DESTROY_LWS_CONTEXT,
+	TEST_STATE_EXIT
+};
+
+static int sequence = TEST_STATE_CREATE_LWS_CONTEXT;
+
+static const struct lws_http_mount mount = {
+	/* .mount_next */		NULL,		/* linked-list "next" */
+	/* .mountpoint */		"/",		/* mountpoint URL */
+	/* .origin */			"./mount-origin", /* serve from dir */
+	/* .def */			"index.html",	/* default filename */
+	/* .protocol */			NULL,
+	/* .cgienv */			NULL,
+	/* .extra_mimetypes */		NULL,
+	/* .interpret */		NULL,
+	/* .cgi_timeout */		0,
+	/* .cache_max_age */		0,
+	/* .auth_mask */		0,
+	/* .cache_reusable */		0,
+	/* .cache_revalidate */		0,
+	/* .cache_intermediaries */	0,
+	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
+	/* .mountpoint_len */		1,		/* char count */
+	/* .basic_auth_login_file */	NULL,
+};
+
+static void
+signal_cb(int signum)
+{
+	lwsl_notice("Signal %d caught, exiting...\n", signum);
+
+	switch (signum) {
+	case SIGTERM:
+	case SIGINT:
+		break;
+	default:
+		break;
+	}
+
+	lws_context_destroy(context);
+}
+
+/*
+ * The event-loop specific foreign loop code, one set for each event loop lib
+ *
+ * Only the code in this section is specific to the event library used.
+ */
+
+#if defined(LWS_WITH_LIBUV)
+
+static uv_loop_t loop_uv;
+static uv_timer_t timer_outer_uv;
+static uv_signal_t sighandler_uv;
+
+static void
+timer_cb_uv(uv_timer_t *t)
+{
+	foreign_timer_service(&loop_uv);
+}
+
+static void
+signal_cb_uv(uv_signal_t *watcher, int signum)
+{
+	signal_cb(signum);
+}
+
+static void
+foreign_event_loop_init_and_run_libuv(void)
+{
+	/* we create and start our "foreign loop" */
+
+#if (UV_VERSION_MAJOR > 0) // Travis...
+	uv_loop_init(&loop_uv);
+#endif
+	uv_signal_init(&loop_uv, &sighandler_uv);
+	uv_signal_start(&sighandler_uv, signal_cb_uv, SIGINT);
+
+	uv_timer_init(&loop_uv, &timer_outer_uv);
+#if (UV_VERSION_MAJOR > 0) // Travis...
+	uv_timer_start(&timer_outer_uv, timer_cb_uv, 0, 1000);
+#else
+	(void)timer_cb_uv;
+#endif
+
+	uv_run(&loop_uv, UV_RUN_DEFAULT);
+}
+
+static void
+foreign_event_loop_stop_libuv(void)
+{
+	uv_stop(&loop_uv);
+}
+
+static void
+foreign_event_loop_cleanup_libuv(void)
+{
+	/* cleanup the foreign loop assets */
+
+	uv_timer_stop(&timer_outer_uv);
+	uv_close((uv_handle_t*)&timer_outer_uv, NULL);
+	uv_signal_stop(&sighandler_uv);
+	uv_close((uv_handle_t *)&sighandler_uv, NULL);
+
+	uv_run(&loop_uv, UV_RUN_DEFAULT);
+#if (UV_VERSION_MAJOR > 0) // Travis...
+	uv_loop_close(&loop_uv);
+#endif
+}
+
+#endif
+
+#if defined(LWS_WITH_LIBEVENT)
+
+static struct event_base *loop_event;
+static struct event *timer_outer_event;
+static struct event *sighandler_event;
+
+static void
+timer_cb_event(int fd, short event, void *arg)
+{
+	foreign_timer_service(loop_event);
+}
+
+static void
+signal_cb_event(int fd, short event, void *arg)
+{
+	signal_cb((int)(lws_intptr_t)arg);
+}
+
+static void
+foreign_event_loop_init_and_run_libevent(void)
+{
+	struct timeval tv;
+
+	/* we create and start our "foreign loop" */
+
+	tv.tv_sec = 1;
+	tv.tv_usec = 0;
+
+	loop_event = event_base_new();
+
+	sighandler_event = evsignal_new(loop_event, SIGINT, signal_cb_event,
+					(void*)SIGINT);
+
+	timer_outer_event = event_new(loop_event, -1, EV_PERSIST,
+				      timer_cb_event, NULL);
+	//evtimer_new(loop_event, timer_cb_event, NULL);
+	evtimer_add(timer_outer_event, &tv);
+
+	event_base_loop(loop_event, 0);
+}
+
+static void
+foreign_event_loop_stop_libevent(void)
+{
+	event_base_loopexit(loop_event, NULL);
+}
+
+static void
+foreign_event_loop_cleanup_libevent(void)
+{
+	/* cleanup the foreign loop assets */
+
+	evtimer_del(timer_outer_event);
+	event_free(timer_outer_event);
+	evsignal_del(sighandler_event);
+	event_free(sighandler_event);
+
+	event_base_loop(loop_event, 0);
+	event_base_free(loop_event);
+}
+
+#endif
+
+#if defined(LWS_WITH_LIBEV)
+
+static struct ev_loop *loop_ev;
+static struct ev_timer timer_outer_ev;
+static struct ev_signal sighandler_ev;
+
+static void
+timer_cb_ev(struct ev_loop *loop, struct ev_timer *watcher, int revents)
+{
+	foreign_timer_service(loop_ev);
+}
+
+static void
+signal_cb_ev(struct ev_loop *loop, struct ev_signal *watcher, int revents)
+{
+	signal_cb(watcher->signum);
+}
+
+static void
+foreign_event_loop_init_and_run_libev(void)
+{
+	/* we create and start our "foreign loop" */
+
+	loop_ev = ev_loop_new(0);
+
+	ev_signal_init(&sighandler_ev, signal_cb_ev, SIGINT);
+	ev_signal_start(loop_ev, &sighandler_ev);
+
+	ev_timer_init(&timer_outer_ev, timer_cb_ev, 0, 1);
+	ev_timer_start(loop_ev, &timer_outer_ev);
+
+	ev_run(loop_ev, 0);
+}
+
+static void
+foreign_event_loop_stop_libev(void)
+{
+	ev_break(loop_ev, EVBREAK_ALL);
+}
+
+static void
+foreign_event_loop_cleanup_libev(void)
+{
+	/* cleanup the foreign loop assets */
+
+	ev_timer_stop(loop_ev, &timer_outer_ev);
+	ev_signal_stop(loop_ev, &sighandler_ev);
+
+	ev_run(loop_ev, UV_RUN_DEFAULT);
+	ev_loop_destroy(loop_ev);
+}
+
+#endif
+
+/* this is called at 1Hz using a foreign loop timer */
+
+static void
+foreign_timer_service(void *foreign_loop)
+{
+	void *foreign_loops[1];
+
+	lwsl_user("Foreign 1Hz timer\n");
+
+	if (sequence == TEST_STATE_EXIT && !context && !reported) {
+		/*
+		 * at this point the lws_context_destroy() we did earlier
+		 * has completed and the entire context is wholly destroyed
+		 */
+		lwsl_user("lws_destroy_context() done, continuing for 5s\n");
+		reported = 1;
+	}
+
+	if (--lifetime)
+		return;
+
+	switch (sequence++) {
+	case TEST_STATE_CREATE_LWS_CONTEXT:
+		/* this only has to exist for the duration of create context */
+		foreign_loops[0] = foreign_loop;
+		info.foreign_loops = foreign_loops;
+
+		context = lws_create_context(&info);
+		if (!context) {
+			lwsl_err("lws init failed\n");
+			return;
+		}
+		lwsl_user("LWS Context created and will be active for 10s\n");
+		lifetime = 11;
+		break;
+
+	case TEST_STATE_DESTROY_LWS_CONTEXT:
+		/* cleanup the lws part */
+		lwsl_user("Destroying lws context and continuing loop for 5s\n");
+		lws_context_destroy(context);
+		lifetime = 6;
+		break;
+
+	case TEST_STATE_EXIT:
+		lwsl_user("Deciding to exit foreign loop too\n");
+#if defined(LWS_WITH_LIBUV)
+		if (info.options & LWS_SERVER_OPTION_LIBUV)
+			foreign_event_loop_stop_libuv();
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+		if (info.options & LWS_SERVER_OPTION_LIBEVENT)
+			foreign_event_loop_stop_libevent();
+#endif
+#if defined(LWS_WITH_LIBEV)
+		if (info.options & LWS_SERVER_OPTION_LIBEV)
+			foreign_event_loop_stop_libev();
+#endif
+		break;
+	default:
+		break;
+	}
+}
+
+int main(int argc, const char **argv)
+{
+	const char *p;
+	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
+			/* for LLL_ verbosity above NOTICE to be built into lws,
+			 * lws must have been configured and built with
+			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
+			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
+			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
+			/* | LLL_DEBUG */;
+
+	if ((p = lws_cmdline_option(argc, argv, "-d")))
+		logs = atoi(p);
+
+	lws_set_log_level(logs, NULL);
+	lwsl_user("LWS minimal http server libuv + foreign loop |"
+		  " visit http://localhost:7681\n");
+
+	/*
+	 * We prepare the info here, but don't use it until later in the
+	 * timer callback, to demonstrate the independence of the foreign loop
+	 * and lws.
+	 */
+
+	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+	info.port = 7681;
+	info.mounts = &mount;
+	info.error_document_404 = "/404.html";
+	info.pcontext = &context;
+
+	if (lws_cmdline_option(argc, argv, "-s")) {
+		info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+		info.ssl_cert_filepath = "localhost-100y.cert";
+		info.ssl_private_key_filepath = "localhost-100y.key";
+	}
+
+	if (lws_cmdline_option(argc, argv, "--uv"))
+		info.options |= LWS_SERVER_OPTION_LIBUV;
+	else
+		if (lws_cmdline_option(argc, argv, "--event"))
+			info.options |= LWS_SERVER_OPTION_LIBEVENT;
+		else
+			if (lws_cmdline_option(argc, argv, "--ev"))
+				info.options |= LWS_SERVER_OPTION_LIBEV;
+			else {
+				lwsl_err("This app only makes sense when used\n");
+				lwsl_err(" with a foreign loop, --uv, --event, or --ev\n");
+
+				return 1;
+			}
+
+	lwsl_user("  This app creates a foreign event loop with a timer +\n");
+	lwsl_user("  signalhandler, and performs a test in three phases:\n");
+	lwsl_user("\n");
+	lwsl_user("  1) 5s: Runs the loop with just the timer\n");
+	lwsl_user("  2) 10s: create an lws context serving on localhost:7681\n");
+	lwsl_user("     using the same foreign loop.  Destroy it after 10s.\n");
+	lwsl_user("  3) 5s: Run the loop again with just the timer\n");
+	lwsl_user("\n");
+	lwsl_user("  Finally close only the timer and signalhandler and\n");
+	lwsl_user("   exit the loop cleanly\n");
+
+	lwsl_notice("%s\n", info.ssl_cert_filepath);
+
+	/* foreign loop specific startup and run */
+
+#if defined(LWS_WITH_LIBUV)
+	if (info.options & LWS_SERVER_OPTION_LIBUV)
+		foreign_event_loop_init_and_run_libuv();
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+	if (info.options & LWS_SERVER_OPTION_LIBEVENT)
+		foreign_event_loop_init_and_run_libevent();
+#endif
+#if defined(LWS_WITH_LIBEV)
+	if (info.options & LWS_SERVER_OPTION_LIBEV)
+		foreign_event_loop_init_and_run_libev();
+#endif
+
+	lws_context_destroy(context);
+
+	/* foreign loop specific cleanup and exit */
+
+#if defined(LWS_WITH_LIBUV)
+	if (info.options & LWS_SERVER_OPTION_LIBUV)
+		foreign_event_loop_cleanup_libuv();
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+	if (info.options & LWS_SERVER_OPTION_LIBEVENT)
+		foreign_event_loop_cleanup_libevent();
+#endif
+#if defined(LWS_WITH_LIBEV)
+	if (info.options & LWS_SERVER_OPTION_LIBEV)
+		foreign_event_loop_cleanup_libev();
+#endif
+
+	lwsl_user("%s: exiting...\n", __func__);
+
+	return 0;
+}
diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-libuv-foreign.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-libuv-foreign.c
deleted file mode 100644
index 7108133eed09982e6efce411442f7d2823d409b1..0000000000000000000000000000000000000000
--- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-libuv-foreign.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * lws-minimal-http-server-libuv-foreign
- *
- * Copyright (C) 2018 Andy Green <andy@warmcat.com>
- *
- * This file is made available under the Creative Commons CC0 1.0
- * Universal Public Domain Dedication.
- *
- * This demonstrates the most minimal http server you can make with lws that
- * uses a libuv event loop created outside lws.  It shows how lws can
- * participate in someone else's event loop and clean up after itself.
- *
- * To keep it simple, it serves stuff from the subdirectory 
- * "./mount-origin" of the directory it was started in.
- * You can change that by changing mount.origin below.
- */
-
-#include <libwebsockets.h>
-#include <string.h>
-#include <signal.h>
-
-static struct lws_context *context;
-static uv_loop_t loop;
-static int lifetime = 5, reported;
-struct lws_context_creation_info info;
-
-enum {
-	TEST_STATE_CREATE_LWS_CONTEXT,
-	TEST_STATE_DESTROY_LWS_CONTEXT,
-	TEST_STATE_EXIT
-};
-
-static int sequence = TEST_STATE_CREATE_LWS_CONTEXT;
-
-static const struct lws_http_mount mount = {
-	/* .mount_next */		NULL,		/* linked-list "next" */
-	/* .mountpoint */		"/",		/* mountpoint URL */
-	/* .origin */			"./mount-origin", /* serve from dir */
-	/* .def */			"index.html",	/* default filename */
-	/* .protocol */			NULL,
-	/* .cgienv */			NULL,
-	/* .extra_mimetypes */		NULL,
-	/* .interpret */		NULL,
-	/* .cgi_timeout */		0,
-	/* .cache_max_age */		0,
-	/* .auth_mask */		0,
-	/* .cache_reusable */		0,
-	/* .cache_revalidate */		0,
-	/* .cache_intermediaries */	0,
-	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
-	/* .mountpoint_len */		1,		/* char count */
-	/* .basic_auth_login_file */	NULL,
-};
-
-void signal_cb(uv_signal_t *watcher, int signum)
-{
-	lwsl_notice("Signal %d caught, exiting...\n", watcher->signum);
-
-	switch (watcher->signum) {
-	case SIGTERM:
-	case SIGINT:
-		break;
-	default:
-		signal(SIGABRT, SIG_DFL);
-		abort();
-		break;
-	}
-
-	if (context)
-		lws_context_destroy(context);
-}
-
-/* this logs once a second to show that the foreign loop assets are working */
-
-static void
-timer_cb(uv_timer_t *t)
-{
-	void *foreign_loops[1];
-
-	foreign_loops[0] = &loop;
-	info.foreign_loops = foreign_loops;
-
-	lwsl_user("Foreign 1Hz timer\n");
-
-	if (sequence == TEST_STATE_EXIT && !context && !reported) {
-		/*
-		 * at this point the lws_context_destroy() we did earlier
-		 * has completed and the entire context is wholly destroyed
-		 */
-		lwsl_user("lws_destroy_context() completed, continuing for 5s\n");
-		reported = 1;
-	}
-
-	if (--lifetime)
-		return;
-
-	switch (sequence++) {
-	case TEST_STATE_CREATE_LWS_CONTEXT:
-		context = lws_create_context(&info);
-		if (!context) {
-			lwsl_err("lws init failed\n");
-			return;
-		}
-		lwsl_user("LWS Context created and active for 10s\n");
-		lifetime = 11;
-		break;
-	case TEST_STATE_DESTROY_LWS_CONTEXT:
-		/* cleanup the lws part */
-		lwsl_user("Destroying lws context and continuing loop for 5s\n");
-		lws_context_destroy(context);
-		lifetime = 6;
-		break;
-
-	case TEST_STATE_EXIT:
-		lwsl_user("Deciding to exit foreign loop too\n");
-		uv_stop(&loop);
-		break;
-	default:
-		break;
-	}
-}
-
-
-int main(int argc, const char **argv)
-{
-	uv_timer_t timer_outer;
-	uv_signal_t sighandler;
-	const char *p;
-	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
-			/* for LLL_ verbosity above NOTICE to be built into lws,
-			 * lws must have been configured and built with
-			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
-			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
-			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
-			/* | LLL_DEBUG */;
-
-	if ((p = lws_cmdline_option(argc, argv, "-d")))
-		logs = atoi(p);
-
-	lws_set_log_level(logs, NULL);
-	lwsl_user("LWS minimal http server libuv + foreign loop |"
-		  " visit http://localhost:7681\n");
-
-	/*
-	 * We prepare the info here, but don't use it until later in the
-	 * timer callback, to demonstrate the independence of the foreign loop
-	 * and lws.
-	 */
-
-	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
-	info.port = 7681;
-	info.mounts = &mount;
-	info.error_document_404 = "/404.html";
-	info.options = LWS_SERVER_OPTION_LIBUV;
-	info.pcontext = &context;
-	if (lws_cmdline_option(argc, argv, "-s")) {
-		info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
-		info.ssl_cert_filepath = "localhost-100y.cert";
-		info.ssl_private_key_filepath = "localhost-100y.key";
-	}
-	info.pcontext = &context;
-
-	lwsl_user("  This app creates a uv loop with a timer + signalhandler, and\n");
-	lwsl_user("  performs a test in three phases:\n");
-	lwsl_user("\n");
-	lwsl_user("  1) 5s: Runs the loop with just the timer\n");
-	lwsl_user("  2) 10s: create an lws context serving on localhost:7681\n");
-	lwsl_user("     using the same uv loop.  Destroy it after 10s.\n");
-	lwsl_user("  3) 5s: Run the loop again with just the timer\n");
-	lwsl_user("\n");
-	lwsl_user("  Finally close only the timer and signalhandler and\n");
-	lwsl_user("   exit the loop cleanly\n");
-
-	/* we create and start our "foreign loop" */
-
-	uv_loop_init(&loop);
-	uv_signal_init(&loop, &sighandler);
-	uv_signal_start(&sighandler, signal_cb, SIGINT);
-
-	uv_timer_init(&loop, &timer_outer);
-	uv_timer_start(&timer_outer, timer_cb, 0, 1000);
-
-	uv_run(&loop, UV_RUN_DEFAULT);
-
-	/* in the case we hit ^C while lws still exists */
-	lws_context_destroy(context);
-
-	/* cleanup the foreign loop assets */
-
-	uv_timer_stop(&timer_outer);
-	uv_close((uv_handle_t*)&timer_outer, NULL);
-	uv_signal_stop(&sighandler);
-	uv_close((uv_handle_t *)&sighandler, NULL);
-
-	uv_run(&loop, UV_RUN_DEFAULT);
-	uv_loop_close(&loop);
-
-	lwsl_user("%s: exiting...\n", __func__);
-
-	return 0;
-}
diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html
index 31a6dfaf2c6f9242b22e6c0128d74f60255f8b3a..3ec21dffbbd26c7c59138278bbe7b2ccfd42bc3c 100644
--- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html
+++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html
@@ -3,10 +3,10 @@
 	<body>
 		<img src="libwebsockets.org-logo.png"><br>
 
-		Hello from the <b>minimal http server libuv foreign loop example</b>.
+		Hello from the <b>minimal http server eventlib foreign loop example</b>.
 		<br>
 		The timer messages in the console are coming from<br>
-		a timer on the libuv loop set up before the lws context<br>
+		a timer on the event library lib loop set up before the lws context<br>
 		started using it.
 	</body>
 </html>
diff --git a/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-libuv.c b/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c
similarity index 72%
rename from minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-libuv.c
rename to minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c
index 9d73be132e3a1956d560418fa6d15e949157b549..4b9d3c996e1875b32c9255b608118d8cae4374c1 100644
--- a/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-libuv.c
+++ b/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c
@@ -1,13 +1,13 @@
 /*
- * lws-minimal-http-server-libuv
+ * lws-minimal-http-server-eventlib
  *
  * Copyright (C) 2018 Andy Green <andy@warmcat.com>
  *
  * This file is made available under the Creative Commons CC0 1.0
  * Universal Public Domain Dedication.
  *
- * This demonstrates the most minimal http server you can make with lws using
- * the libuv event loop.
+ * This demonstrates a minimal http[s] server that can work with any of the
+ * supported event loop backends, or the default poll() one.
  *
  * To keep it simple, it serves stuff from the subdirectory 
  * "./mount-origin" of the directory it was started in.
@@ -42,22 +42,22 @@ static const struct lws_http_mount mount = {
 
 void signal_cb(void *handle, int signum)
 {
-	uv_signal_t *watcher = (uv_signal_t *)handle;
-
-	lwsl_notice("Signal %d caught, exiting...\n", watcher->signum);
-
-	switch (watcher->signum) {
+	switch (signum) {
 	case SIGTERM:
 	case SIGINT:
 		break;
 	default:
-		signal(SIGABRT, SIG_DFL);
-		abort();
+		lwsl_err("%s: signal %d\n", __func__, signum);
 		break;
 	}
 	lws_context_destroy(context);
 }
 
+void sigint_handler(int sig)
+{
+	signal_cb(NULL, sig);
+}
+
 int main(int argc, const char **argv)
 {
 	struct lws_context_creation_info info;
@@ -74,19 +74,32 @@ int main(int argc, const char **argv)
 		logs = atoi(p);
 
 	lws_set_log_level(logs, NULL);
-	lwsl_user("LWS minimal http server libuv [-s (ssl)] | visit http://localhost:7681\n");
+	lwsl_user("LWS minimal http server eventlib | visit http://localhost:7681\n");
+	lwsl_user(" [-s (ssl)] [--uv (libuv)] [--ev (libev)] [--event (libevent)]\n");
 
 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
 	info.port = 7681;
 	info.mounts = &mount;
 	info.error_document_404 = "/404.html";
+	info.pcontext = &context;
+	info.signal_cb = signal_cb;
+
 	if (lws_cmdline_option(argc, argv, "-s")) {
 		info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
 		info.ssl_cert_filepath = "localhost-100y.cert";
 		info.ssl_private_key_filepath = "localhost-100y.key";
 	}
-	info.options |= LWS_SERVER_OPTION_LIBUV;
-	info.signal_cb = signal_cb;
+
+	if (lws_cmdline_option(argc, argv, "--uv"))
+		info.options |= LWS_SERVER_OPTION_LIBUV;
+	else
+		if (lws_cmdline_option(argc, argv, "--event"))
+			info.options |= LWS_SERVER_OPTION_LIBEVENT;
+		else
+			if (lws_cmdline_option(argc, argv, "--ev"))
+				info.options |= LWS_SERVER_OPTION_LIBEV;
+			else
+				signal(SIGINT, sigint_handler);
 
 	context = lws_create_context(&info);
 	if (!context) {
@@ -94,8 +107,10 @@ int main(int argc, const char **argv)
 		return 1;
 	}
 
-	lws_service(context, 0);
+	while (!lws_service(context, 0))
+		;
 
+	lwsl_info("calling external context destroy\n");
 	lws_context_destroy(context);
 
 	return 0;
diff --git a/minimal-examples/selftests-library.sh b/minimal-examples/selftests-library.sh
index 13f34ddeb246a08a03da4d108b4e675b6874a284..feef8f89b298bb9f4697844ca6f4a9cbee7bf671 100755
--- a/minimal-examples/selftests-library.sh
+++ b/minimal-examples/selftests-library.sh
@@ -79,6 +79,11 @@ dotest() {
 	if [ -e $2/$MYTEST/$T.result ] ; then
 		R=`cat $2/$MYTEST/$T.result`
 		cat $2/$MYTEST/$T.log | tail -n 3 > $2/$MYTEST/$T.time
+		if [ $R -ne 0 ] ; then
+			echo
+			cat $2/$MYTEST/$T.log
+			echo
+		fi
 	fi
 
 	feedback $MYTEST $R $T
diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c
index 37cb9745a8364317f67aa63487ff69a32f4f2ba3..5c7678146a555d563f533cc86286fe1c5bca0890 100644
--- a/plugins/protocol_post_demo.c
+++ b/plugins/protocol_post_demo.c
@@ -68,6 +68,8 @@ file_upload_cb(void *data, const char *name, const char *filename,
 			(struct per_session_data__post_demo *)data;
 #if !defined(LWS_WITH_ESP32)
 	int n;
+
+	(void)n;
 #endif
 
 	switch (state) {
@@ -92,7 +94,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 
 #if !defined(LWS_WITH_ESP32)
 			n = write((int)(long long)pss->fd, buf, len);
-			lwsl_notice("%s: write %d says %d\n", __func__, len, n);
+			lwsl_info("%s: write %d says %d\n", __func__, len, n);
 #else
 			lwsl_notice("%s: Received chunk size %d\n", __func__, len);
 #endif
diff --git a/test-apps/attack.sh b/scripts/attack.sh
similarity index 74%
rename from test-apps/attack.sh
rename to scripts/attack.sh
index 38c6cd5f87e1a960ec031aec61c670bc8259b054..66691fcfad9272644c8105f4711cb50e5725ca4b 100755
--- a/test-apps/attack.sh
+++ b/scripts/attack.sh
@@ -16,6 +16,10 @@ LOG=/tmp/lwslog
 A=`which libwebsockets-test-server`
 INSTALLED=`dirname $A`
 
+SHAREDIR=$INSTALLED/../share/libwebsockets-test-server
+CORPUS=$SHAREDIR/test.html
+
+
 CPID=
 LEN=0
 
@@ -28,24 +32,26 @@ function check {
 	#dd if=$LOG bs=1 skip=$LEN 2>/dev/null
 
 	if [ "$1" = "default" ] ; then
-		diff /tmp/lwscap $INSTALLED/../share/libwebsockets-test-server/test.html > /dev/null
+		diff /tmp/lwscap $CORPUS > /dev/null
 		if [ $? -ne 0 ] ; then
-			echo "FAIL: got something other than test.html back"
+			echo "FAIL: got something other than $CORPUS back"
 			exit 1
 		fi
 	fi
 	if [ "$1" = "defaultplusforbidden" ] ; then
-	cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb
+	cat $CORPUS > /tmp/plusforb
 	echo -e -n "HTTP/1.0 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
 		diff /tmp/lwscap /tmp/plusforb > /dev/null
 		if [ $? -ne 0 ] ; then
-			cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb
+			cat $CORPUS > /tmp/plusforb
 
 			echo -e -n "HTTP/1.1 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
 			diff /tmp/lwscap /tmp/plusforb > /dev/null
 			if [ $? -ne 0 ] ; then
 
-				echo "FAIL: got something other than test.html + forbidden back"
+				echo "FAIL: got something other than $CORPUS + forbidden back"
+				tail -n 10 /tmp/lwscap
+				tail -n 100 $LOG
 				exit 1
 			fi
 		fi
@@ -58,8 +64,16 @@ function check {
 		fi
 	fi
 
+	if [ "$1" = "notfound" ] ; then
+		if [ -z "`grep '<h1>404</h1>' /tmp/lwscap`" ] ; then
+			echo "FAIL: should have told not found"
+			exit 1
+		fi
+	fi
+
+
 	if [ "$1" = "rejected" ] ; then
-		if [ -z "`grep '<h1>406</h1>' /tmp/lwscap`" ] ; then
+		if [ -z "`grep '<h1>404</h1>' /tmp/lwscap`" ] ; then
 			echo "FAIL: should have told forbidden (test server has no dirs)"
 			exit 1
 		fi
@@ -67,7 +81,7 @@ function check {
 
 
 	if [ "$1" = "media" ] ; then
-		if [ -z "`grep '<h1>415</h1>' /tmp/lwscap`" ] ; then
+		if [ -z "`grep '<h1>404</h1>' /tmp/lwscap`" ] ; then
 			echo "FAIL: should have told unknown media type"
 			exit 1
 		fi
@@ -126,6 +140,7 @@ echo
 echo "---- /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F"
 rm -f /tmp/lwscap
 echo -n -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+cat /tmp/lwscap
 check 1 "UPDATE_SETTINGS=1"
 check 2 "Root_Channels_1_Channel_name_http_post=?"
 check 3 "Root_Channels_1_Channel_location_http_post=?"
@@ -147,16 +162,16 @@ check 1 "key1_2=value1"
 check
 
 echo
-echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)"
+echo "---- ? processing (%2f%2e%2e%2f%2e./xxtest.html?arg=1)"
 rm -f /tmp/lwscap
-echo  -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo  -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 1 "arg=1"
 check
 
 echo
-echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)"
+echo "---- ? processing (%2f%2e%2e%2f%2e./xxtest.html?arg=/../.)"
 rm -f /tmp/lwscap
-echo -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 1 "arg=/../."
 check
 
@@ -167,23 +182,23 @@ check
 
 echo
 echo "---- spam more than the name buffer of crap"
-dd if=/dev/urandom bs=1 count=80 2>/dev/null | nc -i1s $SERVER $PORT
+dd if=/dev/urandom bs=1 count=80 2>/dev/null | nc -i1 $SERVER $PORT
 check
 
 echo
 echo "---- spam 10MB of crap"
-dd if=/dev/urandom bs=1 count=655360 | nc -i1s $SERVER $PORT
+dd if=/dev/urandom bs=1 count=655360 | nc -i1 $SERVER $PORT
 check
 
 echo
 echo "---- malformed URI"
 echo "GET nonsense................................................................................................................" \
-	| nc -i1s $SERVER $PORT
+	| nc -i1 $SERVER $PORT
 check
 
 echo
 echo "---- missing URI"
-echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1s $SERVER $PORT >/tmp/lwscap
+echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1 $SERVER $PORT >/tmp/lwscap
 check
 
 echo
@@ -210,7 +225,7 @@ echo -n -e "GET blah HTTP/1.0\x0d\x0a...........................................
  	"......................................................................................................................." \
  	"......................................................................................................................." \
  	"......................................................................................................................." \
- | nc -i1s $SERVER $PORT
+ | nc -i1 $SERVER $PORT
 check
 
 echo
@@ -232,28 +247,13 @@ echo -n -e "GET ................................................................
  	"......................................................................................................................." \
  	"......................................................................................................................." \
  	"......................................................................................................................." \
- | nc -i1s $SERVER $PORT
+ | nc -i1 $SERVER $PORT
 check
 
 echo
 echo "---- good request but http payload coming too (test.html served then forbidden)"
 echo -n -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
-	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
-	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
- 	"......................................................................................................................." \
-	 | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+	| cat - /dev/zero | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check defaultplusforbidden
 check
 
@@ -261,7 +261,8 @@ echo
 echo "---- nonexistent file"
 rm -f /tmp/lwscap
 echo -n -e "GET /nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check media
+cat /tmp/lwscap
+check notfound
 check
 
 echo
@@ -275,7 +276,7 @@ echo
 echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
 echo -n -e "GET /../../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check rejected
+check notfound
 check
 
 echo
@@ -317,21 +318,21 @@ echo
 echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
 echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check rejected
+check notfound
 check
 
 echo
 echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
 echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check rejected
+check notfound
 check
 
 echo
 echo "---- http/1.1 pipelining"
 rm -f /tmp/lwscap
 wget -O/tmp/lwsdump http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html 2>&1 | grep "Downloaded: 8 files" > /tmp/lwscap
-good=`cat $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html | md5sum | cut -d' ' -f1`
+good=`cat $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS | md5sum | cut -d' ' -f1`
 if [ "$good" != "`md5sum /tmp/lwsdump | cut -d' ' -f 1`" ] ; then
 	echo "FAIL: mismatched content good=$good received=`md5sum /tmp/lwsdump`"
 	exit 1
@@ -551,13 +552,11 @@ for i in \
 
 R=`rm -f /tmp/lwscap ; echo -n -e "GET $i HTTP/1.0\r\n\r\n" | nc localhost 7681 2>/dev/null >/tmp/lwscap; head -n1 /tmp/lwscap| cut -d' ' -f2`
 
-#cat /tmp/lwscap | head -n1
 #echo ==== $R
 
 
 if [ "$R" != "403" ]; then
-	U=`cat $LOG | grep lws_http_serve | tail -n 1 | cut -d':' -f6 | cut -d' ' -f2`
-#	echo $U
+	U=`cat $LOG | grep Method: | tail -n 1 | cut -d"'" -f4 | sed "s|\\'||g"`
 	echo "- \"$i\" -> $R \"$U\"" >>/tmp/results
 else
 	echo "- \"$i\" -> $R" >>/tmp/results
@@ -565,36 +564,36 @@ fi
 done
 
 cat <<EOF >/tmp/lwsresult1
-- "/..../" -> 406 "/..../"
-- "/.../." -> 406 "/.../"
-- "/...//" -> 406 "/.../"
-- "/.../a" -> 406 "/.../a"
-- "/.../w" -> 406 "/.../w"
-- "/.../?" -> 406 "/.../"
+- "/..../" -> 404 "/..../"
+- "/.../." -> 404 "/.../"
+- "/...//" -> 404 "/.../"
+- "/.../a" -> 404 "/.../a"
+- "/.../w" -> 404 "/.../w"
+- "/.../?" -> 404 "/.../"
 - "/.../%" -> 403
 - "/../.." -> 200 "/"
 - "/.././" -> 200 "/"
-- "/../.a" -> 415 "/.a"
-- "/../.w" -> 415 "/.w"
+- "/../.a" -> 404 "/.a"
+- "/../.w" -> 404 "/.w"
 - "/../.." -> 200 "/"
 - "/../.%" -> 403
 - "/..//." -> 200 "/"
 - "/..///" -> 200 "/"
-- "/..//a" -> 415 "/a"
-- "/..//w" -> 415 "/w"
+- "/..//a" -> 404 "/a"
+- "/..//w" -> 404 "/w"
 - "/..//?" -> 200 "/"
 - "/..//%" -> 403
-- "/../a." -> 415 "/a."
-- "/../a/" -> 406 "/a/"
-- "/../aa" -> 415 "/aa"
-- "/../aw" -> 415 "/aw"
-- "/../a?" -> 415 "/a"
+- "/../a." -> 404 "/a."
+- "/../a/" -> 404 "/a/"
+- "/../aa" -> 404 "/aa"
+- "/../aw" -> 404 "/aw"
+- "/../a?" -> 404 "/a"
 - "/../a%" -> 403
-- "/../w." -> 415 "/w."
-- "/../w/" -> 406 "/w/"
-- "/../wa" -> 415 "/wa"
-- "/../ww" -> 415 "/ww"
-- "/../w?" -> 415 "/w"
+- "/../w." -> 404 "/w."
+- "/../w/" -> 404 "/w/"
+- "/../wa" -> 404 "/wa"
+- "/../ww" -> 404 "/ww"
+- "/../w?" -> 404 "/w"
 - "/../w%" -> 403
 - "/../?." -> 200 "/"
 - "/../?/" -> 200 "/"
@@ -608,49 +607,49 @@ cat <<EOF >/tmp/lwsresult1
 - "/../%w" -> 403
 - "/../%?" -> 403
 - "/../%%" -> 403
-- "/./..." -> 415 "/..."
+- "/./..." -> 404 "/..."
 - "/./../" -> 200 "/"
-- "/./..a" -> 415 "/..a"
-- "/./..w" -> 415 "/..w"
+- "/./..a" -> 404 "/..a"
+- "/./..w" -> 404 "/..w"
 - "/./..?" -> 200 "/"
 - "/./..%" -> 403
 - "/.//.." -> 200 "/"
-- "/.a../" -> 406 "/.a../"
+- "/.a../" -> 404 "/.a../"
 - "/.a/.." -> 200 "/"
-- "/.w../" -> 406 "/.w../"
+- "/.w../" -> 404 "/.w../"
 - "/.w/.." -> 200 "/"
-- "/.?../" -> 415 "/."
+- "/.?../" -> 404 "/."
 - "/../.." -> 200 "/"
 - "/.%../" -> 403
 - "/.%/.." -> 403
-- "//...." -> 415 "/...."
-- "//.../" -> 406 "/.../"
-- "//...a" -> 415 "/...a"
-- "//...w" -> 415 "/...w"
-- "//...?" -> 415 "/..."
+- "//...." -> 404 "/...."
+- "//.../" -> 404 "/.../"
+- "//...a" -> 404 "/...a"
+- "//...w" -> 404 "/...w"
+- "//...?" -> 404 "/..."
 - "//...%" -> 403
 - "//../." -> 200 "/"
 - "//..//" -> 200 "/"
-- "//../a" -> 415 "/a"
-- "//../w" -> 415 "/w"
+- "//../a" -> 404 "/a"
+- "//../w" -> 404 "/w"
 - "//../?" -> 200 "/"
 - "//../%" -> 403
-- "//..a." -> 415 "/..a."
-- "//..a/" -> 406 "/..a/"
-- "//..aa" -> 415 "/..aa"
-- "//..aw" -> 415 "/..aw"
-- "//..a?" -> 415 "/..a"
+- "//..a." -> 404 "/..a."
+- "//..a/" -> 404 "/..a/"
+- "//..aa" -> 404 "/..aa"
+- "//..aw" -> 404 "/..aw"
+- "//..a?" -> 404 "/..a"
 - "//..a%" -> 403
-- "//..w." -> 415 "/..w."
-- "//..w/" -> 406 "/..w/"
-- "//..wa" -> 415 "/..wa"
-- "//..ww" -> 415 "/..ww"
-- "//..w?" -> 415 "/..w"
+- "//..w." -> 404 "/..w."
+- "//..w/" -> 404 "/..w/"
+- "//..wa" -> 404 "/..wa"
+- "//..ww" -> 404 "/..ww"
+- "//..w?" -> 404 "/..w"
 - "//..w%" -> 403
 - "//..?." -> 200 "/"
 - "//..?/" -> 200 "/"
-- "//..?a" -> 415 "/a"
-- "//..?w" -> 415 "/w"
+- "//..?a" -> 404 "/a"
+- "//..?w" -> 404 "/w"
 - "//..??" -> 200 "/"
 - "//..?%" -> 403
 - "//..%." -> 403
@@ -660,65 +659,65 @@ cat <<EOF >/tmp/lwsresult1
 - "//..%?" -> 403
 - "//..%%" -> 403
 - "//./.." -> 200 "/"
-- "///..." -> 415 "/..."
+- "///..." -> 404 "/..."
 - "///../" -> 200 "/"
-- "///..a" -> 415 "/..a"
-- "///..w" -> 415 "/..w"
+- "///..a" -> 404 "/..a"
+- "///..w" -> 404 "/..w"
 - "///..?" -> 200 "/"
 - "///..%" -> 403
 - "////.." -> 200 "/"
-- "//a../" -> 406 "/a../"
+- "//a../" -> 404 "/a../"
 - "//a/.." -> 200 "/"
-- "//w../" -> 406 "/w../"
+- "//w../" -> 404 "/w../"
 - "//w/.." -> 200 "/"
 - "//?../" -> 200 "/"
 - "//?/.." -> 200 "/"
 - "//%../" -> 403
 - "//%/.." -> 403
-- "/a.../" -> 406 "/a.../"
-- "/a../." -> 406 "/a../"
-- "/a..//" -> 406 "/a../"
-- "/a../a" -> 406 "/a../a"
-- "/a../w" -> 406 "/a../w"
-- "/a../?" -> 406 "/a../"
+- "/a.../" -> 404 "/a.../"
+- "/a../." -> 404 "/a../"
+- "/a..//" -> 404 "/a../"
+- "/a../a" -> 404 "/a../a"
+- "/a../w" -> 404 "/a../w"
+- "/a../?" -> 404 "/a../"
 - "/a../%" -> 403
 - "/a./.." -> 200 "/"
-- "/a/..." -> 406 "/a/..."
+- "/a/..." -> 404 "/a/..."
 - "/a/../" -> 200 "/"
-- "/a/..a" -> 406 "/a/..a"
-- "/a/..w" -> 406 "/a/..w"
+- "/a/..a" -> 404 "/a/..a"
+- "/a/..w" -> 404 "/a/..w"
 - "/a/..?" -> 200 "/"
 - "/a/..%" -> 403
 - "/a//.." -> 200 "/"
-- "/aa../" -> 406 "/aa../"
+- "/aa../" -> 404 "/aa../"
 - "/aa/.." -> 200 "/"
-- "/aw../" -> 406 "/aw../"
+- "/aw../" -> 404 "/aw../"
 - "/aw/.." -> 200 "/"
-- "/a?../" -> 415 "/a"
-- "/a?/.." -> 415 "/a"
+- "/a?../" -> 404 "/a"
+- "/a?/.." -> 404 "/a"
 - "/a%../" -> 403
 - "/a%/.." -> 403
-- "/w.../" -> 406 "/w.../"
-- "/w../." -> 406 "/w../"
-- "/w..//" -> 406 "/w../"
-- "/w../a" -> 406 "/w../a"
-- "/w../w" -> 406 "/w../w"
-- "/w../?" -> 406 "/w../"
+- "/w.../" -> 404 "/w.../"
+- "/w../." -> 404 "/w../"
+- "/w..//" -> 404 "/w../"
+- "/w../a" -> 404 "/w../a"
+- "/w../w" -> 404 "/w../w"
+- "/w../?" -> 404 "/w../"
 - "/w../%" -> 403
 - "/w./.." -> 200 "/"
-- "/w/..." -> 406 "/w/..."
+- "/w/..." -> 404 "/w/..."
 - "/w/../" -> 200 "/"
-- "/w/..a" -> 406 "/w/..a"
-- "/w/..w" -> 406 "/w/..w"
+- "/w/..a" -> 404 "/w/..a"
+- "/w/..w" -> 404 "/w/..w"
 - "/w/..?" -> 200 "/"
 - "/w/..%" -> 403
 - "/w//.." -> 200 "/"
-- "/wa../" -> 406 "/wa../"
+- "/wa../" -> 404 "/wa../"
 - "/wa/.." -> 200 "/"
-- "/ww../" -> 406 "/ww../"
+- "/ww../" -> 404 "/ww../"
 - "/ww/.." -> 200 "/"
-- "/w?../" -> 415 "/w"
-- "/w?/.." -> 415 "/w"
+- "/w?../" -> 404 "/w"
+- "/w?/.." -> 404 "/w"
 - "/w%../" -> 403
 - "/w%/.." -> 403
 - "/?.../" -> 200 "/"
@@ -767,13 +766,13 @@ cat <<EOF >/tmp/lwsresult1
 - "/%?/.." -> 403
 - "/%%../" -> 403
 - "/%%/.." -> 403
-- "/a/w/../a" -> 406 "/a/a"
-- "/path/to/dir/../other/dir" -> 406 "/path/to/other/dir"
+- "/a/w/../a" -> 404 "/a/a"
+- "/path/to/dir/../other/dir" -> 404 "/path/to/other/dir"
 EOF
 
 if [ "`md5sum /tmp/results | cut -d' ' -f 1`" != "`md5sum /tmp/lwsresult1 | cut -d' ' -f1`" ] ; then
 	echo "Differences..."
-	diff -urN /tmp/results /tmp/lwsresult1
+	diff -urN /tmp/lwsresult1 /tmp/results
 	exit 1
 else
 	echo "OK"
diff --git a/scripts/travis_control.sh b/scripts/travis_control.sh
index 2eef3cf4da0d9e7955ab6ee7eef5f17dcfe337b0..a5fe7e67705a716078b5c2a3877ec4c93e129778 100755
--- a/scripts/travis_control.sh
+++ b/scripts/travis_control.sh
@@ -14,7 +14,7 @@ else
 			sudo make install &&
 			../minimal-examples/selftests.sh &&
 			../scripts/h2spec.sh &&
-			../test-apps/attack.sh &&
+			../scripts/attack.sh &&
 			../scripts/h2load.sh &&
 			../scripts/autobahn-test.sh
 		else
diff --git a/test-apps/test-server.c b/test-apps/test-server.c
index f2cfc0d13d05f23fc4deda7ee81530c05d20ed0f..3ad1c634464e5780af50529beb2804df3a3480e1 100644
--- a/test-apps/test-server.c
+++ b/test-apps/test-server.c
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include <signal.h>
 
 int close_testing;
 int max_poll_elements;
@@ -77,12 +78,69 @@ char crl_path[1024] = "";
 #include "../plugins/protocol_post_demo.c"
 #endif
 
+static int
+lws_callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
+		  void *in, size_t len)
+{
+	const unsigned char *c;
+	char buf[1024];
+	int n = 0, hlen;
+
+	switch (reason) {
+	case LWS_CALLBACK_HTTP:
+
+		/* non-mount-handled accesses will turn up here */
+
+		/* dump the headers */
+
+		do {
+			c = lws_token_to_string(n);
+			if (!c) {
+				n++;
+				continue;
+			}
+
+			hlen = lws_hdr_total_length(wsi, n);
+			if (!hlen || hlen > (int)sizeof(buf) - 1) {
+				n++;
+				continue;
+			}
+
+			lws_hdr_copy(wsi, buf, sizeof buf, n);
+			buf[sizeof(buf) - 1] = '\0';
+
+			fprintf(stderr, "    %s = %s\n", (char *)c, buf);
+			n++;
+		} while (c);
+
+		/* dump the individual URI Arg parameters */
+
+		n = 0;
+		while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf),
+					     WSI_TOKEN_HTTP_URI_ARGS, n) > 0) {
+			lwsl_notice("URI Arg %d: %s\n", ++n, buf);
+		}
+
+		if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
+			return -1;
+
+		if (lws_http_transaction_completed(wsi))
+			return -1;
+
+		return 0;
+	default:
+		break;
+	}
+
+	return lws_callback_http_dummy(wsi, reason, user, in, len);
+}
+
 /* list of supported protocols and callbacks */
 
 static struct lws_protocols protocols[] = {
 	/* first protocol must always be HTTP handler */
 
-	{ "http-only", lws_callback_http_dummy, 0, 0, },
+	{ "http-only", lws_callback_http, 0, 0, },
 #if defined(LWS_ROLE_WS)
 	LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT,
 	LWS_PLUGIN_PROTOCOL_MIRROR,