diff --git a/src/http.c b/src/http.c index 9f84f0f9..6defc570 100644 --- a/src/http.c +++ b/src/http.c @@ -43,6 +43,7 @@ typedef struct _tf_http_connection_t tf_http_callback_t* callback; void* user_data; + bool is_websocket; void* body; size_t body_length; size_t content_length; @@ -170,35 +171,113 @@ static void _http_reset_connection(tf_http_connection_t* connection) static void _http_add_body_bytes(tf_http_connection_t* connection, const void* data, size_t size) { - size_t fit = tf_min(connection->content_length - connection->body_length, size); - if (fit > 0) + if (connection->is_websocket) { - memcpy((char*)connection->body + connection->body_length, data, fit); - connection->body_length += fit; - } + connection->body = tf_realloc(connection->body, connection->body_length + size); + memcpy((char*)connection->body + connection->body_length, data, size); + connection->body_length += size; - if (connection->body_length == connection->content_length) - { - tf_http_request_t* request = tf_malloc(sizeof(tf_http_request_t)); - *request = (tf_http_request_t) + uint8_t* p = connection->body; + while (connection->body_length >= 2) { - .connection = connection, - .phase = k_http_callback_phase_headers_received, - .method = connection->method, - .path = connection->path, - .flags = connection->flags, - .query = connection->query, - .body = connection->body, - .content_length = connection->content_length, - .headers = connection->headers, - .headers_count = connection->headers_length, - .user_data = connection->user_data, - }; + uint8_t bits0 = p[0]; + uint8_t bits1 = p[1]; + if ((bits1 & (1 << 7)) == 0) + { + /* Unmasked message. */ + _http_connection_destroy(connection); + return; + } + uint8_t opcode = bits0 & 0xf; + bool fin = (bits0 & (1 << 7)) != 0; + size_t length = bits1 & 0x7f; + int mask_start = 2; - tf_http_request_ref(request); - connection->callback(request); - tf_http_request_release(request); - _http_reset_connection(connection); + if (length == 126) + { + length = 0; + for (int i = 0; i < 2; i++) + { + length <<= 8; + length |= p[2 + i]; + } + mask_start = 4; + } + else if (length == 127) + { + length = 0; + for (int i = 0; i < 8; i++) + { + length <<= 8; + length |= p[2 + i]; + } + mask_start = 10; + } + if (connection->body_length >= length + 2 + 4) + { + uint32_t mask = + p[mask_start + 0] | + p[mask_start + 1] << 8 | + p[mask_start + 2] << 16 | + p[mask_start + 3] << 24; + size_t i = 0; + for (i = mask_start + 4; i < length; i += 4) + { + *(uint32_t*)(p + i) ^= mask; + } + for (; i < length; i++) + { + p[i] ^= ((mask >> (8 * (i % 4))) & 0xff); + } + if (fin) + { + tf_printf("MESSAGE %d [%.*s]\n", opcode, (int)length, p + mask_start + 4); + } + size_t total_length + mask_Start + 4 + length; + memmove(connection->body, (char*)connection->body + total_length, connection->body_length - total_length); + connection->body_length -= total_length; + } + } + } + else + { + size_t fit = tf_min(connection->content_length - connection->body_length, size); + if (fit > 0) + { + memcpy((char*)connection->body + connection->body_length, data, fit); + connection->body_length += fit; + } + + if (connection->body_length == connection->content_length) + { + if (connection->flags & k_tf_http_handler_flag_websocket) + { + connection->is_websocket = true; + } + tf_http_request_t* request = tf_malloc(sizeof(tf_http_request_t)); + *request = (tf_http_request_t) + { + .connection = connection, + .phase = k_http_callback_phase_headers_received, + .method = connection->method, + .path = connection->path, + .flags = connection->flags, + .query = connection->query, + .body = connection->body, + .content_length = connection->content_length, + .headers = connection->headers, + .headers_count = connection->headers_length, + .user_data = connection->user_data, + }; + + tf_http_request_ref(request); + connection->callback(request); + tf_http_request_release(request); + if (!connection->is_websocket) + { + _http_reset_connection(connection); + } + } } }