diff --git a/lib/extension-permessage-deflate.c b/lib/extension-permessage-deflate.c
index 65f28caaecdf36dd3a11dcd7ce7a18018b668617..82d957380c1b75d5ae3a59c67710b03dbccfaaf3 100644
--- a/lib/extension-permessage-deflate.c
+++ b/lib/extension-permessage-deflate.c
@@ -202,7 +202,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
 		 * rx buffer by the caller, so this assumption is safe while
 		 * we block new rx while draining the existing rx
 		 */
-		if (eff_buf->token && eff_buf->token_len) {
+		if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) {
 			priv->rx.next_in = (unsigned char *)eff_buf->token;
 			priv->rx.avail_in = eff_buf->token_len;
 		}
diff --git a/lib/libuv.c b/lib/libuv.c
index da1405c10e45325305bf0df216a2241f9ca24182..2103d2f921a783c402cdbd4583ba4074eab7f579 100644
--- a/lib/libuv.c
+++ b/lib/libuv.c
@@ -40,7 +40,7 @@ lws_uv_idle(uv_idle_t *handle
 	struct lws_context_per_thread *pt = lws_container_of(handle,
 					struct lws_context_per_thread, uv_idle);
 
-	lwsl_debug("%s\n", __func__);
+//	lwsl_debug("%s\n", __func__);
 
 	/*
 	 * is there anybody with pending stuff that needs service forcing?
@@ -51,7 +51,7 @@ lws_uv_idle(uv_idle_t *handle
 		/* still somebody left who wants forced service? */
 		if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
 			/* yes... come back again later */
-			lwsl_debug("%s: done again\n", __func__);
+//			lwsl_debug("%s: done again\n", __func__);
 		return;
 	}
 
diff --git a/lib/parsers.c b/lib/parsers.c
index 61f1295e2ff17a2884a9e941457f62cd955b6866..c44a3571c0b7e1f8698c8135a0e924883775d460 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -986,6 +986,24 @@ LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
 {
 	return wsi->u.ws.frame_is_binary;
 }
+static void
+lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	struct lws **w = &pt->rx_draining_ext_list;
+
+	wsi->u.ws.rx_draining_ext = 0;
+	/* remove us from context draining ext list */
+	while (*w) {
+		if (*w == wsi) {
+			*w = wsi->u.ws.rx_draining_ext_list;
+			break;
+		}
+		w = &((*w)->u.ws.rx_draining_ext_list);
+	}
+	wsi->u.ws.rx_draining_ext_list = NULL;
+}
+
 
 int
 lws_rx_sm(struct lws *wsi, unsigned char c)
@@ -995,26 +1013,19 @@ lws_rx_sm(struct lws *wsi, unsigned char c)
 	int ret = 0, n, rx_draining_ext = 0;
 	struct lws_tokens eff_buf;
 
+	eff_buf.token = NULL;
+	eff_buf.token_len = 0;
+
 	if (wsi->socket_is_permanently_unusable)
 		return -1;
 
+
 	switch (wsi->lws_rx_parse_state) {
 	case LWS_RXPS_NEW:
 		if (wsi->u.ws.rx_draining_ext) {
-			struct lws **w = &pt->rx_draining_ext_list;
-
 			eff_buf.token = NULL;
 			eff_buf.token_len = 0;
-			wsi->u.ws.rx_draining_ext = 0;
-			/* remove us from context draining ext list */
-			while (*w) {
-				if (*w == wsi) {
-					*w = wsi->u.ws.rx_draining_ext_list;
-					break;
-				}
-				w = &((*w)->u.ws.rx_draining_ext_list);
-			}
-			wsi->u.ws.rx_draining_ext_list = NULL;
+			lws_remove_wsi_from_draining_ext_list(wsi);
 			rx_draining_ext = 1;
 			lwsl_err("%s: doing draining flow\n", __func__);
 
@@ -1270,6 +1281,9 @@ handle_first:
 	case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
 		assert(wsi->u.ws.rx_ubuf);
 
+		if (wsi->u.ws.rx_draining_ext)
+			goto drain_extension;
+
 		if (wsi->u.ws.rx_ubuf_head + LWS_PRE >=
 		    wsi->u.ws.rx_ubuf_alloc) {
 			lwsl_err("Attempted overflow \n");
@@ -1430,6 +1444,9 @@ drain_extension:
 			goto already_done;
 
 		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
+		/* eff_buf may be pointing somewhere completely different now,
+		 * it's the output
+		 */
 		if (n < 0) {
 			/*
 			 * we may rely on this to get RX, just drop connection
@@ -1443,9 +1460,12 @@ drain_extension:
 
 		if (n && eff_buf.token_len) {
 			/* extension had more... main loop will come back */
+			// lwsl_notice("ext has stuff to drain\n");
 			wsi->u.ws.rx_draining_ext = 1;
 			wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list;
 			pt->rx_draining_ext_list = wsi;
+		} else {
+			lws_remove_wsi_from_draining_ext_list(wsi);
 		}
 
 		if (eff_buf.token_len > 0 ||
diff --git a/lib/server.c b/lib/server.c
index 933914a519ce94014e3bbff7e6aa26cba0d31cba..8f7e4d887c9beb3d0068da2d0b379d03cf23f7f3 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -1690,7 +1690,7 @@ try_pollout:
 			if (accept_fd < 0) {
 				if (LWS_ERRNO == LWS_EAGAIN ||
 				    LWS_ERRNO == LWS_EWOULDBLOCK) {
-					lwsl_err("accept asks to try again\n");
+//					lwsl_err("accept asks to try again\n");
 					break;
 				}
 				lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO));
@@ -1854,6 +1854,7 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
 		}
 
 		if (wsi->u.ws.rx_draining_ext) {
+			// lwsl_notice("draining with 0\n");
 			m = lws_rx_sm(wsi, 0);
 			if (m < 0)
 				return -1;
@@ -1865,7 +1866,8 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
 			wsi->rxflow_pos++;
 
 		/* consume payload bytes efficiently */
-		if (wsi->lws_rx_parse_state ==
+		if (
+		    wsi->lws_rx_parse_state ==
 		    LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
 			m = lws_payload_until_length_exhausted(wsi, buf, &len);
 			if (wsi->rxflow_buffer)
diff --git a/lib/service.c b/lib/service.c
index 72d78c97528d7df1b75d470aa78a593711f86039..f8f1bdaafa83a4d5f1250e6024aa989e43a1640e 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -630,6 +630,17 @@ completed:
 }
 #endif
 
+static int
+lws_is_ws_with_ext(struct lws *wsi)
+{
+#if defined(LWS_NO_EXTENSIONS)
+	return 0;
+#else
+	return wsi->state == LWSS_ESTABLISHED &&
+	       !!wsi->count_act_ext;
+#endif
+}
+
 /**
  * lws_service_fd() - Service polled socket with something waiting
  * @context:	Websocket context
@@ -894,9 +905,21 @@ read:
 					wsi->u.hdr.ah->rxpos;
 		} else {
 			if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) {
+				/*
+				 * extension may not consume everything (eg, pmd may be constrained
+				 * as to what it can output...) has to go in per-wsi rx buf area.
+				 * Otherwise in large temp serv_buf area.
+				 */
+				eff_buf.token = (char *)pt->serv_buf;
+				if (lws_is_ws_with_ext(wsi)) {
+					eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
+				} else {
+					eff_buf.token_len = LWS_MAX_SOCKET_IO_BUF;
+				}
+
 				eff_buf.token_len = lws_ssl_capable_read(wsi,
-					pt->serv_buf, pending ? pending :
-							LWS_MAX_SOCKET_IO_BUF);
+					(unsigned char *)eff_buf.token, pending ? pending :
+					eff_buf.token_len);
 				switch (eff_buf.token_len) {
 				case 0:
 					lwsl_info("%s: zero length read\n", __func__);
@@ -909,8 +932,7 @@ read:
 					lwsl_info("Closing when error\n");
 					goto close_and_handled;
 				}
-
-				eff_buf.token = (char *)pt->serv_buf;
+				// lwsl_notice("Actual RX %d\n", eff_buf.token_len);
 			}
 		}
 
@@ -966,6 +988,8 @@ drain:
 				 * around again it will pick up from where it
 				 * left off.
 				 */
+				// lwsl_notice("doing lws_read from pt->serv_buf %p %p for len %d\n", pt->serv_buf, eff_buf.token, (int)eff_buf.token_len);
+
 				n = lws_read(wsi, (unsigned char *)eff_buf.token,
 					     eff_buf.token_len);
 				if (n < 0) {
@@ -992,7 +1016,11 @@ drain:
 
 		pending = lws_ssl_pending(wsi);
 		if (pending) {
-			pending = pending > LWS_MAX_SOCKET_IO_BUF ?
+			if (lws_is_ws_with_ext(wsi))
+				pending = pending > wsi->u.ws.rx_ubuf_alloc ?
+					wsi->u.ws.rx_ubuf_alloc : pending;
+			else
+				pending = pending > LWS_MAX_SOCKET_IO_BUF ?
 					LWS_MAX_SOCKET_IO_BUF : pending;
 			goto read;
 		}
diff --git a/plugins/protocol_lws_status.c b/plugins/protocol_lws_status.c
index c9d828fcd7fd076ac499dde4058b12b19745d9fe..e26f41ea1272e4cb5044560f7e4d0fda81021748 100644
--- a/plugins/protocol_lws_status.c
+++ b/plugins/protocol_lws_status.c
@@ -151,6 +151,11 @@ callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
 		}
 		break;
 
+	case LWS_CALLBACK_RECEIVE:
+		lwsl_notice("pmd test: RX len %d\n", (int)len);
+		puts(in);
+		break;
+
 	case LWS_CALLBACK_CLOSED:
 		pp = &list;
 		while (*pp) {
diff --git a/test-server/test-server-status.c b/test-server/test-server-status.c
index 50fe5bf98552191615b24d4983ca4b9b1fe5d936..29d77b3293344ac6197c6986405b09def872a8f8 100644
--- a/test-server/test-server-status.c
+++ b/test-server/test-server-status.c
@@ -131,6 +131,11 @@ callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
 		update_status(wsi, pss);
 		break;
 
+	case LWS_CALLBACK_RECEIVE:
+		lwsl_notice("pmd test: RX len %d\n", (int)len);
+		puts(in);
+		break;
+
 	case LWS_CALLBACK_SERVER_WRITEABLE:
 		m = lws_write(wsi, (unsigned char *)cache + LWS_PRE, cache_len,
 			      LWS_WRITE_TEXT);
diff --git a/test-server/test.html b/test-server/test.html
index ebf01de75994e048bf1049f8605f55182c6c6edf..a0bf13d18f25525b3e6dd2705c32e34b0145b699 100644
--- a/test-server/test.html
+++ b/test-server/test.html
@@ -234,7 +234,8 @@ initiate the close, which it does with code 1001 and reason "Seeya".
 	 <div id=s_status>Websocket connection not initialized</div>
 	</td>
 	        <td colspan=1>
-<span class="title">Server Info</span>
+<span class="title">Server Info</span>	     <input type=button id=pmd value="Test pmd" onclick=on_pmd>
+
 	</td>
 	</tr><tr>
 <td class="explain" colspan=2>
@@ -550,6 +551,10 @@ function junk() {
 	socket_di.send(word);
 }
 
+function on_pmd() {
+   socket_status.send("{ \"RequestType\":\"DDoS\", \"blob\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAJbElEQVR4Xu2af4xUVxXHv+e9O0OhIEibgrRlF8rOG6CWKiumrUlTo6byj6mmNFta2DdQsRJJrP5j+0cnjSVp1BoSU1yzO28LEeL6h7GJNhpJNa2CBQ1gd9k3QPlRBU2LLV0W2p173zFvZvbtzOybmbd7J9DEd/+befece97nfe+95/4gxEWLAGlZx8aIAWqKIAYYA9QkoGkeKzAGqElA0zxWYAxQk4CmeazAGKAmAU3zWIExQE0CmuaxAmOAmgQ0zWMFxgA1CWiaxwqMAWoS0DRvrQJ3ujeb17Hrx6SuzLwV32x7VzO+1pj3HJprJue8BcBQCSONRzr+2RrHaO2OtHDc1wDcUwqOj0s7nWpVoDp+hOMeA5Au+zggbesuHX+Vti1VoOh3j4BxRxEf8xmVSbe3KlAdP2bOPUmEpaXviiGZsVbq+IsBxgCnpp9YgVPjNan2RwagmXMfUpnUAEAc9k6Rx8AHB0zzy3euU5nUXk02RXMzl+9Ssw8PYN06FeYvOkAmM5dfpzLWL6LGFXkSMR13jIAEmKXMpBM6AIXjeih+BVLqTEcSWfJ/T71ks4bZ9vAYgU1/epC2ZegAFLnhAogEM0tV5x1r/UcGKBw3UJ1sTyVwH8laZ5EU2DO4WCTFmXFbOTbnemxZdHnq9AAMvDVTjF4ObKVS7di8IvA97jOSArNsiLZ8oGBpW5HYRKrkB1IFcGwkiS2dhWkB7B1qE6Z5uiUAew7NEsk5o4EveEtgLw98TwngwIApRlcFoogBVnzdSArUAWg6+T4QS3U69Xi98eiqK7A4vnX1+YsldWbPJmSzk8fJa6FAP67FXTv9sVLZqU1k9rvHibGsmKQzBlTGeih0gqgcA69CFxaO+zKA+4uxEF6W3dbaSXFdA4Bmzt1DhK5SLHycTMd9n4A5pT9oSNqp0GXO1VagcFx/cphZhnZF2tasjwJAkRs+CqJPFvEBIzHA8a8ScQyMAdbbTGgJwAY7FS3LA3edXCyUbJoHitzwKIhK3Zb5ssykr5/Uha9BHigc9whQ3nHyu7DIDe8A0TY/OI/xtJexngnP5ocLRCRAkLLbas1KhOGps6lE2MxvOO6TBvBsMS7Pe8rbtHz7pLhavRLpdwtgCDCkzIS/o9Gff8pg/n55EtlRSqT7h+8B0xhs62AYvHJlf524QZ3duzs0pfCT7aj7gQNsmqPHH1V2qr9+ewBePHYXIICNHfsb1TNzw7Y6e343svdNWh35dpHyQL9iKUV5VGVSu+qt94tx7DrZCa8wA93pP0deiTR80fLDyACjOGthncgAp9FmqwG+CsbnynHkpW1Z04ip5Sai3x0CY3kpU+P9sjt9d6saaSlA9JybJWaMXARDyRFjLrZ1fNiqQLX8DAwmxai4CCAhzblzsWFhsH7W8lvM8eOiRSAGqIUvVqAmvhhgDFCbgKaDqY+B2ayBBV2fQJJnwzQlSL6HjcsvhMaRHUwG/2dXjjWMtXdwPjBjPryCARKXIcQ7sJd8EOn9fFuTbkCBCDOMUZw69596SXWVv4HB2bicXAClBARdwqm95+stEurFER1gz6GESM45DGBFmLNJW+C5E7cKUmfH68pkoh3rl046r8CL7s3CQ+hdFQY2KNvaXS94o8/\" }");
+}
+
 var socket_ot;
 
 function ot_open() {