|
@@ -441,6 +441,11 @@ _civet_safe_clock_gettime(int clk_id, struct timespec *t)
|
|
|
(((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))
|
|
|
#endif
|
|
|
|
|
|
+#if defined(USE_ZLIB)
|
|
|
+#include "zconf.h"
|
|
|
+#include "zlib.h"
|
|
|
+#endif
|
|
|
+
|
|
|
|
|
|
/********************************************************************/
|
|
|
/* CivetWeb configuration defines */
|
|
@@ -2960,23 +2965,6 @@ enum {
|
|
|
PROTOCOL_TYPE_HTTP2 = 2
|
|
|
};
|
|
|
|
|
|
-#define mg_cry_internal(conn, fmt, ...) \
|
|
|
- mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
|
|
|
-
|
|
|
-#define mg_cry_ctx_internal(ctx, fmt, ...) \
|
|
|
- mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)
|
|
|
-
|
|
|
-static void mg_cry_internal_wrap(const struct mg_connection *conn,
|
|
|
- struct mg_context *ctx,
|
|
|
- const char *func,
|
|
|
- unsigned line,
|
|
|
- const char *fmt,
|
|
|
- ...) PRINTF_ARGS(5, 6);
|
|
|
-
|
|
|
-#if defined(USE_ZLIB)
|
|
|
-#include "mod_zlib.inl"
|
|
|
-#endif
|
|
|
-
|
|
|
struct mg_connection {
|
|
|
int connection_type; /* see CONNECTION_TYPE_* above */
|
|
|
int protocol_type; /* see PROTOCOL_TYPE_*: 0=http/1.x, 1=ws, 2=http/2 */
|
|
@@ -3027,17 +3015,18 @@ struct mg_connection {
|
|
|
* pages */
|
|
|
#if defined(USE_WEBSOCKET)
|
|
|
int in_websocket_handling; /* 1 if in read_websocket */
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- /* Parameters for websocket data compression according to https://tools.ietf.org/html/rfc7692 */
|
|
|
- int websocket_deflate_server_max_windows_bits;
|
|
|
- int websocket_deflate_client_max_windows_bits;
|
|
|
- int websocket_deflate_server_no_context_takeover;
|
|
|
- int websocket_deflate_client_no_context_takeover;
|
|
|
- int websocket_deflate_initialized;
|
|
|
- int websocket_deflate_flush;
|
|
|
- z_stream websocket_deflate_state;
|
|
|
- z_stream websocket_inflate_state;
|
|
|
-#endif
|
|
|
+#endif
|
|
|
+#if defined(USE_ZLIB) && defined(USE_WEBSOCKET) \
|
|
|
+ && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ /* Parameters for websocket data compression according to rfc7692 */
|
|
|
+ int websocket_deflate_server_max_windows_bits;
|
|
|
+ int websocket_deflate_client_max_windows_bits;
|
|
|
+ int websocket_deflate_server_no_context_takeover;
|
|
|
+ int websocket_deflate_client_no_context_takeover;
|
|
|
+ int websocket_deflate_initialized;
|
|
|
+ int websocket_deflate_flush;
|
|
|
+ z_stream websocket_deflate_state;
|
|
|
+ z_stream websocket_inflate_state;
|
|
|
#endif
|
|
|
int handled_requests; /* Number of requests handled by this connection
|
|
|
*/
|
|
@@ -3069,6 +3058,20 @@ struct de {
|
|
|
};
|
|
|
|
|
|
|
|
|
+#define mg_cry_internal(conn, fmt, ...) \
|
|
|
+ mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
|
|
|
+
|
|
|
+#define mg_cry_ctx_internal(ctx, fmt, ...) \
|
|
|
+ mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)
|
|
|
+
|
|
|
+static void mg_cry_internal_wrap(const struct mg_connection *conn,
|
|
|
+ struct mg_context *ctx,
|
|
|
+ const char *func,
|
|
|
+ unsigned line,
|
|
|
+ const char *fmt,
|
|
|
+ ...) PRINTF_ARGS(5, 6);
|
|
|
+
|
|
|
+
|
|
|
#if !defined(NO_THREAD_NAME)
|
|
|
#if defined(_WIN32) && defined(_MSC_VER)
|
|
|
/* Set the thread name for debugging purposes in Visual Studio
|
|
@@ -4297,71 +4300,6 @@ get_req_headers(const struct mg_request_info *ri,
|
|
|
#endif
|
|
|
|
|
|
|
|
|
-#if defined(USE_WEBSOCKET) && defined(USE_ZLIB)
|
|
|
-int websocket_deflate_initialize(struct mg_connection *conn) {
|
|
|
- fflush(stdout);
|
|
|
- z_stream websocket_deflate_state;
|
|
|
- int websocket_deflate_server_max_windows_bits;
|
|
|
- int websocket_deflate_client_max_windows_bits;
|
|
|
- int websocket_deflate_server_no_context_takeover;
|
|
|
- int websocket_deflate_client_no_context_takeover;
|
|
|
-
|
|
|
- uint8_t deflate_bits;
|
|
|
- uint8_t inflate_bits;
|
|
|
-
|
|
|
- // if (server)
|
|
|
- deflate_bits = conn->websocket_deflate_server_max_windows_bits;
|
|
|
- inflate_bits = conn->websocket_deflate_client_max_windows_bits;
|
|
|
- /*
|
|
|
- deflate_bits = conn->websocket_deflate_client_max_windows_bits;
|
|
|
- inflate_bits = conn->websocket_deflate_server_max_windows_bits;
|
|
|
- */
|
|
|
-
|
|
|
- int ret = deflateInit2(&conn->websocket_deflate_state, Z_DEFAULT_COMPRESSION,
|
|
|
- Z_DEFLATED, -1 * deflate_bits,
|
|
|
- 4, // memory level 1-9
|
|
|
- Z_DEFAULT_STRATEGY);
|
|
|
- if (ret != Z_OK) return ret;
|
|
|
-
|
|
|
- ret = inflateInit2(&conn->websocket_inflate_state, -1 * inflate_bits);
|
|
|
- if (ret != Z_OK) return ret;
|
|
|
-
|
|
|
- /*
|
|
|
- if ((m_server_no_context_takeover && is_server) ||
|
|
|
- (m_client_no_context_takeover && !is_server)) {
|
|
|
- */
|
|
|
- conn->websocket_deflate_flush = Z_FULL_FLUSH;
|
|
|
- //} else
|
|
|
- // conn->websocket_deflate_flush = Z_SYNC_FLUSH;
|
|
|
-
|
|
|
- conn->websocket_deflate_initialized = 1;
|
|
|
- return Z_OK;
|
|
|
-}
|
|
|
-
|
|
|
-void websocket_deflate_negotiate(struct mg_connection *conn) {
|
|
|
- const char *extensions = mg_get_header(conn, "Sec-WebSocket-Extensions");
|
|
|
- if (extensions && strstr(extensions, "permessage-deflate")) {
|
|
|
- conn->accept_gzip = 1;
|
|
|
- conn->websocket_deflate_client_max_windows_bits = 15;
|
|
|
- conn->websocket_deflate_server_max_windows_bits = 15;
|
|
|
- } else {
|
|
|
- conn->accept_gzip = 0;
|
|
|
- conn->websocket_deflate_client_max_windows_bits = 0;
|
|
|
- conn->websocket_deflate_server_max_windows_bits = 0;
|
|
|
- }
|
|
|
- conn->websocket_deflate_initialized = 0;
|
|
|
-}
|
|
|
-
|
|
|
-void websocket_deflate_response(struct mg_connection *conn) {
|
|
|
- if (conn->accept_gzip) {
|
|
|
- mg_printf(conn,
|
|
|
- "Sec-WebSocket-Extensions: permessage-deflate; "
|
|
|
- "client_no_context_takeover; server_no_context_takeover\r\n");
|
|
|
- };
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
const char *
|
|
|
mg_get_header(const struct mg_connection *conn, const char *name)
|
|
|
{
|
|
@@ -10118,6 +10056,11 @@ fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn)
|
|
|
}
|
|
|
|
|
|
|
|
|
+#if defined(USE_ZLIB)
|
|
|
+#include "mod_zlib.inl"
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
#if !defined(NO_FILESYSTEMS)
|
|
|
static void
|
|
|
handle_static_file_request(struct mg_connection *conn,
|
|
@@ -12608,12 +12551,12 @@ send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
|
|
|
"Connection: Upgrade\r\n"
|
|
|
"Sec-WebSocket-Accept: %s\r\n",
|
|
|
b64_sha);
|
|
|
-
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- // Send negotiated compression extension parameters
|
|
|
- websocket_deflate_response(conn);
|
|
|
-#endif
|
|
|
-
|
|
|
+
|
|
|
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ // Send negotiated compression extension parameters
|
|
|
+ websocket_deflate_response(conn);
|
|
|
+#endif
|
|
|
+
|
|
|
if (conn->request_info.acceptedWebSocketSubprotocol) {
|
|
|
mg_printf(conn,
|
|
|
"Sec-WebSocket-Protocol: %s\r\n\r\n",
|
|
@@ -12852,51 +12795,73 @@ read_websocket(struct mg_connection *conn,
|
|
|
} else {
|
|
|
/* Exit the loop if callback signals to exit (server side),
|
|
|
* or "connection close" opcode received (client side). */
|
|
|
- if (ws_data_handler != NULL) {
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- if (mop & 0x40) {
|
|
|
- /* Inflate the data received if bit RSV1 is set. */
|
|
|
- if (!conn->websocket_deflate_initialized) {
|
|
|
- if (websocket_deflate_initialize(conn) != Z_OK)
|
|
|
- exit_by_callback = 1;
|
|
|
- }
|
|
|
- if (!exit_by_callback) {
|
|
|
- Bytef *inflated = mg_calloc(10 * 1024 * 1024, sizeof(Bytef));
|
|
|
- // Add trailing 0x00 0x00 0xff 0xff bytes
|
|
|
- data[data_len] = '\x00';
|
|
|
- data[data_len + 1] = '\x00';
|
|
|
- data[data_len + 2] = '\xff';
|
|
|
- data[data_len + 3] = '\xff';
|
|
|
- conn->websocket_inflate_state.avail_in = data_len + 4;
|
|
|
- conn->websocket_inflate_state.next_in = data;
|
|
|
- conn->websocket_inflate_state.avail_out = 10 * 1024 * 1024;
|
|
|
- conn->websocket_inflate_state.next_out = inflated;
|
|
|
- int ret = inflate(&conn->websocket_inflate_state, Z_SYNC_FLUSH);
|
|
|
- // TODO loop allocate bigger buffer if needed
|
|
|
- if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR ||
|
|
|
- ret == Z_MEM_ERROR || ret < 0)
|
|
|
- exit_by_callback = 1;
|
|
|
- inflated[10 * 1024 * 1024 -
|
|
|
- conn->websocket_inflate_state.avail_out] = '\0';
|
|
|
- // if (conn->websocket_inflate_state.avail_out == 0)
|
|
|
- // TODO output buffer overflow
|
|
|
- if (!ws_data_handler(conn, mop, (char *)inflated,
|
|
|
- 10 * 1024 * 1024 -
|
|
|
- conn->websocket_inflate_state.avail_out,
|
|
|
- callback_data)) {
|
|
|
- exit_by_callback = 1;
|
|
|
- }
|
|
|
- }
|
|
|
- } else
|
|
|
-#endif
|
|
|
- if (!ws_data_handler(conn,
|
|
|
- mop,
|
|
|
- (char *)data,
|
|
|
- (size_t)data_len,
|
|
|
- callback_data)) {
|
|
|
- exit_by_callback = 1;
|
|
|
+ if (ws_data_handler != NULL) {
|
|
|
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ if (mop & 0x40) {
|
|
|
+ /* Inflate the data received if bit RSV1 is set. */
|
|
|
+ if (!conn->websocket_deflate_initialized) {
|
|
|
+ if (websocket_deflate_initialize(conn, 1) != Z_OK)
|
|
|
+ exit_by_callback = 1;
|
|
|
+ }
|
|
|
+ if (!exit_by_callback) {
|
|
|
+ int inflate_buf_size_old = 0;
|
|
|
+ int inflate_buf_size =
|
|
|
+ 1024 * 1024; // Start with 1 MB and double the
|
|
|
+ // memory when needed
|
|
|
+ Bytef *inflated;
|
|
|
+ conn->websocket_inflate_state.avail_in =
|
|
|
+ data_len + 4;
|
|
|
+ conn->websocket_inflate_state.next_in = data;
|
|
|
+ // Add trailing 0x00 0x00 0xff 0xff bytes
|
|
|
+ data[data_len] = '\x00';
|
|
|
+ data[data_len + 1] = '\x00';
|
|
|
+ data[data_len + 2] = '\xff';
|
|
|
+ data[data_len + 3] = '\xff';
|
|
|
+ do {
|
|
|
+ if (inflate_buf_size_old == 0) {
|
|
|
+ inflated = mg_calloc(inflate_buf_size,
|
|
|
+ sizeof(Bytef));
|
|
|
+ } else {
|
|
|
+ inflate_buf_size *= 2;
|
|
|
+ inflated =
|
|
|
+ mg_realloc(inflated, inflate_buf_size);
|
|
|
+ }
|
|
|
+ conn->websocket_inflate_state.avail_out =
|
|
|
+ inflate_buf_size - inflate_buf_size_old;
|
|
|
+ conn->websocket_inflate_state.next_out =
|
|
|
+ inflated + inflate_buf_size_old;
|
|
|
+ int ret =
|
|
|
+ inflate(&conn->websocket_inflate_state,
|
|
|
+ Z_SYNC_FLUSH);
|
|
|
+ if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR
|
|
|
+ || ret == Z_MEM_ERROR || ret < 0)
|
|
|
+ exit_by_callback = 1;
|
|
|
+ inflate_buf_size_old = inflate_buf_size;
|
|
|
+ } while (!exit_by_callback
|
|
|
+ && conn->websocket_inflate_state.avail_out
|
|
|
+ == 0);
|
|
|
+ inflate_buf_size -=
|
|
|
+ conn->websocket_inflate_state.avail_out;
|
|
|
+ inflated[inflate_buf_size] = '\0';
|
|
|
+ if (!ws_data_handler(conn,
|
|
|
+ mop,
|
|
|
+ (char *)inflated,
|
|
|
+ inflate_buf_size,
|
|
|
+ callback_data)) {
|
|
|
+ exit_by_callback = 1;
|
|
|
+ }
|
|
|
+ mg_free(inflated);
|
|
|
+ }
|
|
|
+ } else
|
|
|
+#endif
|
|
|
+ if (!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 */
|
|
@@ -12994,47 +12959,52 @@ mg_websocket_write_exec(struct mg_connection *conn,
|
|
|
size_t headerLen;
|
|
|
int retval;
|
|
|
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- uLong deflated_size;
|
|
|
- Bytef *deflated;
|
|
|
- // Deflate websocket messages over 100kb
|
|
|
- int use_deflate = dataLen > 100 * 1024 && conn->accept_gzip;
|
|
|
-#endif
|
|
|
-
|
|
|
#if defined(GCC_DIAGNOSTIC)
|
|
|
/* Disable spurious conversion warning for GCC */
|
|
|
#pragma GCC diagnostic push
|
|
|
#pragma GCC diagnostic ignored "-Wconversion"
|
|
|
#endif
|
|
|
|
|
|
- /* TODO: Check if this lock should be moved to user land.
|
|
|
- * Currently the server sets this lock for websockets, but
|
|
|
- * not for any other connection. It must be set for every
|
|
|
- * conn read/written by more than one thread, no matter if
|
|
|
- * it is a websocket or regular connection. */
|
|
|
- (void)mg_lock_connection(conn);
|
|
|
-
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- if (use_deflate) {
|
|
|
- if (!conn->websocket_deflate_initialized) {
|
|
|
- if (websocket_deflate_initialize(conn) != Z_OK) return 0;
|
|
|
- }
|
|
|
-
|
|
|
- // Deflating the message
|
|
|
- header[0] = 0xC0u | (unsigned char)((unsigned)opcode & 0xf);
|
|
|
- conn->websocket_deflate_state.avail_in = dataLen;
|
|
|
- conn->websocket_deflate_state.next_in = (unsigned char *)data;
|
|
|
- deflated_size = compressBound(dataLen);
|
|
|
- deflated = mg_calloc(deflated_size, sizeof(Bytef));
|
|
|
- conn->websocket_deflate_state.avail_out = deflated_size;
|
|
|
- conn->websocket_deflate_state.next_out = deflated =
|
|
|
- mg_calloc(deflated_size, sizeof(Bytef));
|
|
|
- deflate(&conn->websocket_deflate_state, conn->websocket_deflate_flush);
|
|
|
- dataLen = deflated_size - conn->websocket_deflate_state.avail_out -
|
|
|
- 4; // Strip trailing 0x00 0x00 0xff 0xff bytes
|
|
|
- } else
|
|
|
-#endif
|
|
|
- header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
|
|
|
+ /* Note that POSIX/Winsock's send() is threadsafe
|
|
|
+ * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
|
|
|
+ * but mongoose's mg_printf/mg_write is not (because of the loop in
|
|
|
+ * push(), although that is only a problem if the packet is large or
|
|
|
+ * outgoing buffer is full). */
|
|
|
+
|
|
|
+ /* TODO: Check if this lock should be moved to user land.
|
|
|
+ * Currently the server sets this lock for websockets, but
|
|
|
+ * not for any other connection. It must be set for every
|
|
|
+ * conn read/written by more than one thread, no matter if
|
|
|
+ * it is a websocket or regular connection. */
|
|
|
+ (void)mg_lock_connection(conn);
|
|
|
+
|
|
|
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ uLong deflated_size;
|
|
|
+ Bytef *deflated;
|
|
|
+ // Deflate websocket messages over 100kb
|
|
|
+ int use_deflate = dataLen > 100 * 1024 && conn->accept_gzip;
|
|
|
+
|
|
|
+ if (use_deflate) {
|
|
|
+ if (!conn->websocket_deflate_initialized) {
|
|
|
+ if (websocket_deflate_initialize(conn, 1) != Z_OK)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Deflating the message
|
|
|
+ header[0] = 0xC0u | (unsigned char)((unsigned)opcode & 0xf);
|
|
|
+ conn->websocket_deflate_state.avail_in = dataLen;
|
|
|
+ conn->websocket_deflate_state.next_in = (unsigned char *)data;
|
|
|
+ deflated_size = compressBound(dataLen);
|
|
|
+ deflated = mg_calloc(deflated_size, sizeof(Bytef));
|
|
|
+ conn->websocket_deflate_state.avail_out = deflated_size;
|
|
|
+ conn->websocket_deflate_state.next_out = deflated =
|
|
|
+ mg_calloc(deflated_size, sizeof(Bytef));
|
|
|
+ deflate(&conn->websocket_deflate_state, conn->websocket_deflate_flush);
|
|
|
+ dataLen = deflated_size - conn->websocket_deflate_state.avail_out
|
|
|
+ - 4; // Strip trailing 0x00 0x00 0xff 0xff bytes
|
|
|
+ } else
|
|
|
+#endif
|
|
|
+ header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
|
|
|
|
|
|
#if defined(GCC_DIAGNOSTIC)
|
|
|
#pragma GCC diagnostic pop
|
|
@@ -13068,25 +13038,19 @@ mg_websocket_write_exec(struct mg_connection *conn,
|
|
|
headerLen += 4;
|
|
|
}
|
|
|
|
|
|
- /* Note that POSIX/Winsock's send() is threadsafe
|
|
|
- * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
|
|
|
- * but mongoose's mg_printf/mg_write is not (because of the loop in
|
|
|
- * push(), although that is only a problem if the packet is large or
|
|
|
- * outgoing buffer is full). */
|
|
|
-
|
|
|
retval = mg_write(conn, header, headerLen);
|
|
|
if (retval != (int)headerLen) {
|
|
|
/* Did not send complete header */
|
|
|
retval = -1;
|
|
|
} else {
|
|
|
if (dataLen > 0) {
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- if (use_deflate) {
|
|
|
- retval = mg_write(conn, deflated, dataLen);
|
|
|
- mg_free(deflated);
|
|
|
- } else
|
|
|
-#endif
|
|
|
- retval = mg_write(conn, data, dataLen);
|
|
|
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ if (use_deflate) {
|
|
|
+ retval = mg_write(conn, deflated, dataLen);
|
|
|
+ mg_free(deflated);
|
|
|
+ } else
|
|
|
+#endif
|
|
|
+ retval = mg_write(conn, data, dataLen);
|
|
|
}
|
|
|
/* if dataLen == 0, the header length (2) is returned */
|
|
|
}
|
|
@@ -13303,10 +13267,10 @@ handle_websocket_request(struct mg_connection *conn,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#if defined(USE_ZLIB)
|
|
|
- websocket_deflate_negotiate(conn);
|
|
|
-#endif
|
|
|
-
|
|
|
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ websocket_deflate_negotiate(conn);
|
|
|
+#endif
|
|
|
+
|
|
|
if ((ws_connect_handler != NULL)
|
|
|
&& (ws_connect_handler(conn, cbData) != 0)) {
|
|
|
/* C callback has returned non-zero, do not proceed with
|
|
@@ -13377,7 +13341,15 @@ handle_websocket_request(struct mg_connection *conn,
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
- /* Step 8: Call the close handler */
|
|
|
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
|
|
|
+ /* Step 8: Close the deflate & inflate buffers */
|
|
|
+ if (conn->websocket_deflate_initialized) {
|
|
|
+ deflateEnd(&conn->websocket_deflate_state);
|
|
|
+ inflateEnd(&conn->websocket_inflate_state);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* Step 9: Call the close handler */
|
|
|
if (ws_close_handler) {
|
|
|
ws_close_handler(conn, cbData);
|
|
|
}
|