diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 602acaf2..06c6f58e 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 b14aaebf..20e6fe92 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 fbd22b5c..380af858 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 1eeaf99f..e1b322ca 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)