diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 602acaf200f2304d562a42a35cd217e0053f4ac9..06c6f58e23a408dc327a57a3e2e3c5377e19f5b4 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -224,6 +224,12 @@ just_kill_connection:
 			wsi->truncated_send_malloc = NULL;
 			wsi->truncated_send_len = 0;
 		}
+		if (wsi->u.ws.ping_payload_buf) {
+			free(wsi->u.ws.ping_payload_buf);
+			wsi->u.ws.ping_payload_buf = NULL;
+			wsi->u.ws.ping_payload_alloc = 0;
+			wsi->u.ws.ping_payload_len = 0;
+		}
 	}
 
 	/* tell the user it's all over for this guy */
diff --git a/lib/parsers.c b/lib/parsers.c
index b14aaebf03e8450812d00b72dac97e3e0f4d5318..20e6fe9263f7169ffcfd78d4571ccaf6071f1588 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -898,16 +898,45 @@ spill:
 		case LWS_WS_OPCODE_07__PING:
 			lwsl_info("received %d byte ping, sending pong\n",
 						 wsi->u.ws.rx_user_buffer_head);
-			lwsl_hexdump(&wsi->u.ws.rx_user_buffer[
-					LWS_SEND_BUFFER_PRE_PADDING],
-						 wsi->u.ws.rx_user_buffer_head);
-			/* parrot the ping packet payload back as a pong */
-			n = libwebsocket_write(wsi, (unsigned char *)
-			&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
-				 wsi->u.ws.rx_user_buffer_head, LWS_WRITE_PONG);
-			if (n < 0)
-				return -1;
-			/* ... then just drop it */
+
+			if (wsi->u.ws.ping_payload_len) {
+				/* 
+				 * there is already a pending ping payload
+				 * we should just log and drop
+				 */
+				lwsl_parser("DROP PING since one pending\n");
+				goto ping_drop;
+			}
+			
+			/* control packets can only be < 128 bytes long */
+			if (wsi->u.ws.ping_payload_len > 128 - 4) {
+				lwsl_parser("DROP PING payload too large\n");
+				goto ping_drop;
+			}
+			
+			/* if existing buffer is too small, drop it */
+			if (wsi->u.ws.ping_payload_buf &&
+			    wsi->u.ws.ping_payload_alloc < wsi->u.ws.rx_user_buffer_head) {
+				free(wsi->u.ws.ping_payload_buf);
+				wsi->u.ws.ping_payload_buf = NULL;
+			}
+
+			/* if no buffer, allocate it */
+			if (!wsi->u.ws.ping_payload_buf) {
+				wsi->u.ws.ping_payload_buf = malloc(wsi->u.ws.rx_user_buffer_head + LWS_SEND_BUFFER_PRE_PADDING);
+				wsi->u.ws.ping_payload_alloc = wsi->u.ws.rx_user_buffer_head;
+			}
+			
+			/* stash the pong payload */
+			memcpy(wsi->u.ws.ping_payload_buf + LWS_SEND_BUFFER_PRE_PADDING,
+			       &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
+				wsi->u.ws.rx_user_buffer_head);
+			
+			wsi->u.ws.ping_payload_len = wsi->u.ws.rx_user_buffer_head;
+			
+			/* get it sent as soon as possible */
+			libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi);
+ping_drop:
 			wsi->u.ws.rx_user_buffer_head = 0;
 			return 0;
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index fbd22b5c5cd1da5f5f152be06a1648eed17db956..380af8586cc1a2c9274ac6a5eb1cb5eacfd84e0c 100755
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -565,6 +565,10 @@ struct _lws_websocket_related {
 	unsigned int this_frame_masked:1;
 	unsigned int inside_frame:1; /* next write will be more of frame */
 	unsigned int clean_buffer:1; /* buffer not rewritten by extension */
+
+	unsigned char *ping_payload_buf; /* non-NULL if malloc'd */
+	unsigned int ping_payload_alloc; /* length malloc'd */
+	unsigned int ping_payload_len; /* nonzero if PONG pending */
 };
 
 struct libwebsocket {
diff --git a/lib/service.c b/lib/service.c
index 1eeaf99fed8c638d896579e98c0eb767efa9bc94..e1b322caa9ec873e7ead3f3f28f9da98d531560a 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -48,7 +48,24 @@ lws_handle_POLLOUT_event(struct libwebsocket_context *context,
 			return -1; /* retry closing now */
 		}
 
+	/* pending control packets have next priority */
+	
+	if (wsi->u.ws.ping_payload_len) {
+		n = libwebsocket_write(wsi, 
+				&wsi->u.ws.ping_payload_buf[
+					LWS_SEND_BUFFER_PRE_PADDING],
+					wsi->u.ws.ping_payload_len,
+							       LWS_WRITE_PONG);
+		if (n < 0)
+			return -1;
+		/* well he is sent, mark him done */
+		wsi->u.ws.ping_payload_len = 0;
+		/* leave POLLOUT active either way */
+		return 0;
+	}
 
+	/* if nothing critical, user can get the callback */
+	
 	m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE,
 								       NULL, 0);
 	if (handled == 1)