|
@@ -2121,6 +2121,7 @@ enum {
|
|
KEEP_ALIVE_TIMEOUT,
|
|
KEEP_ALIVE_TIMEOUT,
|
|
#if defined(USE_WEBSOCKET)
|
|
#if defined(USE_WEBSOCKET)
|
|
WEBSOCKET_TIMEOUT,
|
|
WEBSOCKET_TIMEOUT,
|
|
|
|
+ ENABLE_WEBSOCKET_PING_PONG,
|
|
#endif
|
|
#endif
|
|
DECODE_URL,
|
|
DECODE_URL,
|
|
#if defined(USE_LUA)
|
|
#if defined(USE_LUA)
|
|
@@ -2214,7 +2215,8 @@ static struct mg_option config_options[] = {
|
|
{"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
|
|
{"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
|
|
{"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
|
|
{"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
|
|
#if defined(USE_WEBSOCKET)
|
|
#if defined(USE_WEBSOCKET)
|
|
- {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
|
|
|
|
|
|
+ {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
|
|
|
|
+ {"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"},
|
|
#endif
|
|
#endif
|
|
{"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
|
|
{"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
|
|
#if defined(USE_LUA)
|
|
#if defined(USE_LUA)
|
|
@@ -11507,7 +11509,12 @@ read_websocket(struct mg_connection *conn,
|
|
* dynamically allocated buffer if it is too large. */
|
|
* dynamically allocated buffer if it is too large. */
|
|
unsigned char mem[4096];
|
|
unsigned char mem[4096];
|
|
unsigned char mop; /* mask flag and opcode */
|
|
unsigned char mop; /* mask flag and opcode */
|
|
|
|
+
|
|
|
|
+
|
|
double timeout = -1.0;
|
|
double timeout = -1.0;
|
|
|
|
+ int enable_ping_pong =
|
|
|
|
+ !mg_strcasecmp(conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG],
|
|
|
|
+ "yes");
|
|
|
|
|
|
if (conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) {
|
|
if (conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) {
|
|
timeout = atoi(conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
|
|
timeout = atoi(conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
|
|
@@ -11646,18 +11653,26 @@ read_websocket(struct mg_connection *conn,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /* Exit the loop if callback signals to exit (server side),
|
|
|
|
- * or "connection close" opcode received (client side). */
|
|
|
|
exit_by_callback = 0;
|
|
exit_by_callback = 0;
|
|
- if ((ws_data_handler != NULL)
|
|
|
|
- && !ws_data_handler(conn,
|
|
|
|
- mop,
|
|
|
|
- (char *)data,
|
|
|
|
- (size_t)data_len,
|
|
|
|
- callback_data)) {
|
|
|
|
- exit_by_callback = 1;
|
|
|
|
|
|
+ if (enable_ping_pong && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PONG)) {
|
|
|
|
+ /* filter PONG messages */
|
|
|
|
+ DEBUG_TRACE("PONG from %s:%u",
|
|
|
|
+ conn->request_info.remote_addr,
|
|
|
|
+ conn->request_info.remote_port);
|
|
|
|
+ } else {
|
|
|
|
+ /* Exit the loop if callback signals to exit (server side),
|
|
|
|
+ * or "connection close" opcode received (client side). */
|
|
|
|
+ if ((ws_data_handler != NULL)
|
|
|
|
+ && !ws_data_handler(conn,
|
|
|
|
+ mop,
|
|
|
|
+ (char *)data,
|
|
|
|
+ (size_t)data_len,
|
|
|
|
+ callback_data)) {
|
|
|
|
+ exit_by_callback = 1;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* It a buffer has been allocated, free it again */
|
|
if (data != mem) {
|
|
if (data != mem) {
|
|
mg_free(data);
|
|
mg_free(data);
|
|
}
|
|
}
|
|
@@ -11684,6 +11699,25 @@ read_websocket(struct mg_connection *conn,
|
|
if (n > 0) {
|
|
if (n > 0) {
|
|
conn->data_len += n;
|
|
conn->data_len += n;
|
|
} else {
|
|
} else {
|
|
|
|
+ if (!conn->phys_ctx->stop_flag && !conn->must_close) {
|
|
|
|
+ if (enable_ping_pong) {
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* Send Websocket PING message */
|
|
|
|
+ DEBUG_TRACE("PING to %s:%u",
|
|
|
|
+ conn->request_info.remote_addr,
|
|
|
|
+ conn->request_info.remote_port);
|
|
|
|
+ ret = mg_websocket_write(conn,
|
|
|
|
+ MG_WEBSOCKET_OPCODE_PING,
|
|
|
|
+ NULL,
|
|
|
|
+ 0);
|
|
|
|
+
|
|
|
|
+ if (ret <= 0) {
|
|
|
|
+ /* Error: send failed */
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
/* Timeout: should retry */
|
|
/* Timeout: should retry */
|
|
/* TODO: get timeout def */
|
|
/* TODO: get timeout def */
|
|
}
|
|
}
|
|
@@ -11703,9 +11737,8 @@ mg_websocket_write_exec(struct mg_connection *conn,
|
|
uint32_t masking_key)
|
|
uint32_t masking_key)
|
|
{
|
|
{
|
|
unsigned char header[14];
|
|
unsigned char header[14];
|
|
- size_t headerLen = 1;
|
|
|
|
-
|
|
|
|
- int retval = -1;
|
|
|
|
|
|
+ size_t headerLen;
|
|
|
|
+ int retval;
|
|
|
|
|
|
#if defined(__GNUC__) || defined(__MINGW32__)
|
|
#if defined(__GNUC__) || defined(__MINGW32__)
|
|
/* Disable spurious conversion warning for GCC */
|
|
/* Disable spurious conversion warning for GCC */
|
|
@@ -11761,11 +11794,17 @@ mg_websocket_write_exec(struct mg_connection *conn,
|
|
(void)mg_lock_connection(conn);
|
|
(void)mg_lock_connection(conn);
|
|
|
|
|
|
retval = mg_write(conn, header, headerLen);
|
|
retval = mg_write(conn, header, headerLen);
|
|
- if (dataLen > 0) {
|
|
|
|
- retval = mg_write(conn, data, dataLen);
|
|
|
|
|
|
+ if (retval != (int)headerLen) {
|
|
|
|
+ /* Did not send complete header */
|
|
|
|
+ retval = -1;
|
|
|
|
+ } else {
|
|
|
|
+ if (dataLen > 0) {
|
|
|
|
+ retval = mg_write(conn, data, dataLen);
|
|
|
|
+ }
|
|
|
|
+ /* if dataLen == 0, the header length (2) is returned */
|
|
}
|
|
}
|
|
|
|
|
|
- /* TODO: Remove this unlock as well, when lock is moved. */
|
|
|
|
|
|
+ /* TODO: Remove this unlock as well, when lock is removed. */
|
|
mg_unlock_connection(conn);
|
|
mg_unlock_connection(conn);
|
|
|
|
|
|
return retval;
|
|
return retval;
|
|
@@ -11816,7 +11855,7 @@ mg_websocket_client_write(struct mg_connection *conn,
|
|
int retval = -1;
|
|
int retval = -1;
|
|
char *masked_data =
|
|
char *masked_data =
|
|
(char *)mg_malloc_ctx(((dataLen + 7) / 4) * 4, conn->phys_ctx);
|
|
(char *)mg_malloc_ctx(((dataLen + 7) / 4) * 4, conn->phys_ctx);
|
|
- uint32_t masking_key = (uint32_t)get_random();
|
|
|
|
|
|
+ uint32_t masking_key = 0;
|
|
|
|
|
|
if (masked_data == NULL) {
|
|
if (masked_data == NULL) {
|
|
/* Return -1 in an error case */
|
|
/* Return -1 in an error case */
|
|
@@ -11827,6 +11866,11 @@ mg_websocket_client_write(struct mg_connection *conn,
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ do {
|
|
|
|
+ /* Get a masking key - but not 0 */
|
|
|
|
+ masking_key = (uint32_t)get_random();
|
|
|
|
+ } while (masking_key == 0);
|
|
|
|
+
|
|
mask_data(data, dataLen, masking_key, masked_data);
|
|
mask_data(data, dataLen, masking_key, masked_data);
|
|
|
|
|
|
retval = mg_websocket_write_exec(
|
|
retval = mg_websocket_write_exec(
|