diff --git a/http/HttpRequest.h b/http/HttpRequest.h index 87bcbb1..0fbfc44 100644 --- a/http/HttpRequest.h +++ b/http/HttpRequest.h @@ -251,11 +251,8 @@ HttpRequest* http_request_create(ThreadedChunkMemory* mem) request->size = request_buffer_count; request->protocol = HTTP_PROTOCOL_1_1; - // Create content length placehoder, this header element is always required - http_header_value_set(&request, HTTP_HEADER_KEY_CONTENT_LENGTH, " ", mem); - // Prepare the chunked sub-regions - request->header_available_count = 16; + request->header_available_count = 25; request->header_available_size = 4 * 256 * sizeof(char); request->body_offset = request->header_available_count * sizeof(HttpHeaderElement) + request->header_available_size; @@ -268,6 +265,22 @@ HttpRequest* http_request_create(ThreadedChunkMemory* mem) return request; } +FORCE_INLINE +void http_request_free(HttpRequest** request, ThreadedChunkMemory* mem) +{ + thrd_chunk_free_elements(mem, (*request)->id, (*request)->size); + *request = NULL; +} + +inline +uint32 http_request_free_body_space(const HttpRequest* request, ThreadedChunkMemory* mem) +{ + return request->size * mem->chunk_size + - request->body_offset + - request->body_used_size + - sizeof(HttpRequest); +} + inline const char* http_request_body(const HttpRequest* request) { return ((const char *) (request + 1)) + request->body_offset; @@ -356,30 +369,36 @@ void http_header_parse(HttpRequest** http_request, const char* request, Threaded http_req->method = HTTP_METHOD_UNKNOWN; } + // @bug the uri offsets are wrong (they miss the header elements) + // however, if we put them after the header elements we would also have to grow this if we ever need to grow the heder element array + + int32 header_count_bytes = http_req->header_available_count * sizeof(HttpHeaderElement); + // Parse reuqest path str_move_past(&request, ' '); - http_req->uri.path_offset = request - request_start; + http_req->uri.path_offset = request - request_start + header_count_bytes; str_skip_until_list(&request, ":?# "); - http_req->uri.path_length = (request - request_start) - http_req->uri.path_offset; + http_req->uri.path_length = (request - request_start + header_count_bytes) - http_req->uri.path_offset; // Parse port if (*request == ':') { http_req->uri.port = (uint16) str_to_int(request, &request); + str_skip_until_list(&request, "?# "); } // Parse query parameters if (*request == '?') { - http_req->uri.parameter_offset = request - request_start; + http_req->uri.parameter_offset = request - request_start + header_count_bytes; str_skip_until_list(&request, "# "); //http_req->uri.parameter_length = (request - request_start) - http_req->uri.parameter_offset; } // Parse fragment if (*request == '#') { - http_req->uri.fragment_offset = request - request_start; + http_req->uri.fragment_offset = request - request_start + header_count_bytes; str_move_to(&request, ' '); - http_req->uri.fragment_length = (request - request_start) - http_req->uri.fragment_offset; + http_req->uri.fragment_length = (request - request_start + header_count_bytes) - http_req->uri.fragment_offset; } // Parse protocol @@ -406,7 +425,53 @@ void http_header_parse(HttpRequest** http_request, const char* request, Threaded // Parsing HTTP headers ////////////////////////////////////////////////// // The HTTP headers end with \r\n\r\n (= one empty line/element) + + HttpHeaderElement* elements = (HttpHeaderElement *) (http_req + 1); + + int32 i = 0; while (request[0] != '\r' && request[1] != '\n' && request[2] != '\r' && request[3] != '\n') { + if (i >= http_req->header_available_count) { + // Ideally this if body almost never gets executed since the initial area is sufficiently large + // @todo consider to log actual usage to possibly reduce the size from 50 to maybe 25? + http_request_grow(http_request, 1, mem); + http_req = *http_request; + + int32 offset = 50 * sizeof(HttpHeaderElement); + int32 request_offset = request - request_start; + + // Grow header element area + memmove( + ((char *) (http_req + 1)) + http_req->body_offset + offset, + ((char *) (http_req + 1)) + http_req->body_offset, + http_req->header_available_size + http_req->body_used_size + ); + + // Re-adjust uri as well + http_req->uri.path_offset += offset; + + if (http_req->uri.parameter_offset) { + http_req->uri.parameter_offset += offset; + } + + if (http_req->uri.fragment_offset) { + http_req->uri.fragment_offset += offset; + } + + // Reset request after moving + request = ((char *) (http_req + 1)) + request_offset; + + // Adjust value offsets + elements = (HttpHeaderElement *) (http_req + 1); + for (int32 j = 0; j < http_req->header_used_count; ++j) { + elements[j].value_offset += offset; + } + + http_req->body_offset += offset; + + // Reset request start position + request_start = ((const char *) (http_req + 1)) + http_req->header_available_count * sizeof(HttpHeaderElement); + } + str_move_past(&request, '\n'); const char* key = request; @@ -415,8 +480,27 @@ void http_header_parse(HttpRequest** http_request, const char* request, Threaded const char* value = request; str_move_to(&request, '\r'); - http_header_value_set(http_request, http_header_key_text(key), value, mem, request - value); + elements[i].key = http_header_key_text(key); + elements[i].value_offset = http_req->header_available_count * sizeof(HttpHeaderElement) + + ((uintptr_t) value - (uintptr_t) (http_req + 1)); + elements[i].value_length = request - value; + + ++i; } + + http_req->header_used_count = i; + http_req->header_available_size = 0; + http_req->header_used_size = 0; + + if (i > 0) { + // Available size = used size + http_req->header_available_size = (request - request_start); + http_req->header_used_size = http_req->header_available_size; + } + + http_req->body_offset = http_req->header_available_count * sizeof(HttpHeaderElement) + + http_req->header_available_size + + 4; // skipping \r\n\r\n } void parse_multipart_data(const char *body, const char *boundary) { diff --git a/http/HttpResponse.h b/http/HttpResponse.h index 7b1962d..333c917 100644 --- a/http/HttpResponse.h +++ b/http/HttpResponse.h @@ -221,7 +221,7 @@ void http_header_value_set( // Set value memcpy(((char *) (resp + 1)) + element->value_offset, value, value_length); - resp->body_used_size += value_length; + // resp->body_used_size += value_length; // @bug Why was this here? resp->header_used_size += (uint16) value_length; ++resp->header_used_count; @@ -239,11 +239,8 @@ HttpResponse* http_response_create(ThreadedChunkMemory* mem) response->protocol = HTTP_PROTOCOL_1_1; response->status_code = HTTP_STATUS_CODE_200; - // Create content length placehoder, this header element is always required - http_header_value_set(&response, HTTP_HEADER_KEY_CONTENT_LENGTH, " ", mem); - // Prepare the chunked sub-regions - response->header_available_count = 16; + response->header_available_count = 25; response->header_available_size = 4 * 256 * sizeof(char); response->body_offset = response->header_available_count * sizeof(HttpHeaderElement) + response->header_available_size; @@ -256,6 +253,22 @@ HttpResponse* http_response_create(ThreadedChunkMemory* mem) return response; } +FORCE_INLINE +void http_response_free(HttpResponse** response, ThreadedChunkMemory* mem) +{ + thrd_chunk_free_elements(mem, (*response)->id, (*response)->size); + *response = NULL; +} + +inline +uint32 http_response_free_body_space(const HttpResponse* response, ThreadedChunkMemory* mem) +{ + return response->size * mem->chunk_size + - response->body_offset + - response->body_used_size + - sizeof(HttpResponse); +} + inline const char* http_response_body(const HttpResponse* response) { return ((const char *) (response + 1)) + response->body_offset; @@ -282,7 +295,11 @@ const char* http_header_value_get(const HttpResponse* response, const HttpHeader // @todo we need a streamed response version http_response_stream() // WARNING: We expect response to already contain a header element called content-length -void http_response_send(const SocketConnection* __restrict socket, HttpResponse* __restrict response) +void http_response_send( + const SocketConnection* __restrict socket, + HttpResponse* __restrict response, + ThreadedChunkMemory* mem +) { char header[4096]; char* header_ref; @@ -293,13 +310,15 @@ void http_response_send(const SocketConnection* __restrict socket, HttpResponse* header_ref += str_copy(header_ref, http_protocol_text(response->protocol)); *header_ref++ = ' '; + header_ref += int_to_str(response->status_code, header_ref); + *header_ref++ = ' '; header_ref += str_copy(header_ref, http_status_text(response->status_code)); *header_ref++ = '\r'; *header_ref++ = '\n'; char content_length[12]; int_to_str(response->body_used_size, content_length); - http_header_value_set(&response, HTTP_HEADER_KEY_CONTENT_LENGTH, content_length, NULL); + http_header_value_set(&response, HTTP_HEADER_KEY_CONTENT_LENGTH, content_length, mem); const HttpHeaderElement* elements = (HttpHeaderElement *) (response + 1); @@ -308,9 +327,11 @@ void http_response_send(const SocketConnection* __restrict socket, HttpResponse* const HttpHeaderElement* element = &elements[i]; header_ref += str_copy(header_ref, http_header_key_text(element->key)); + *header_ref++ = ':'; *header_ref++ = ' '; - memcpy(header_ref, (const char *) elements + element->value_offset, element->value_length); + // @bug What if the header array cannot fit the data from the memcpy? + memcpy(header_ref, ((const char *) elements) + element->value_offset, element->value_length); header_ref += element->value_length; *header_ref++ = '\r'; @@ -338,7 +359,7 @@ void http_response_send(const SocketConnection* __restrict socket, HttpResponse* send(socket->sd, header, header_ref - header, 0); // Do we have data remaining to be sent? - if (response->body_offset && response->body_used_size - body_size_to_add > 0) { + if (response->body_used_size - body_size_to_add > 0) { // @question Do we need chunked sends? send( socket->sd, @@ -365,7 +386,12 @@ void http_response_body_add(HttpResponse** response, const char* __restrict body response_body = (char*) (resp + 1); } - memcpy(response_body + resp->body_used_size, body, length); + memcpy( + response_body + resp->body_offset + resp->body_used_size, + body, + length + ); + resp->body_used_size += length; } diff --git a/platform/linux/threading/ThreadDefines.h b/platform/linux/threading/ThreadDefines.h index 8b6b2de..8bc93b0 100755 --- a/platform/linux/threading/ThreadDefines.h +++ b/platform/linux/threading/ThreadDefines.h @@ -18,6 +18,7 @@ #include #define THREAD_RETURN int32 +#define THREAD_RETURN_BODY int32 typedef THREAD_RETURN (*ThreadJobFunc)(void*); struct mutex { diff --git a/platform/win32/threading/ThreadDefines.h b/platform/win32/threading/ThreadDefines.h index d9751e0..771ce19 100755 --- a/platform/win32/threading/ThreadDefines.h +++ b/platform/win32/threading/ThreadDefines.h @@ -14,6 +14,7 @@ #include #define THREAD_RETURN DWORD WINAPI +#define THREAD_RETURN_BODY DWORD typedef DWORD (WINAPI *ThreadJobFunc)(void*); typedef CRITICAL_SECTION mutex; typedef void mutexattr_t; diff --git a/utils/StringUtils.h b/utils/StringUtils.h index 112bd82..83f582b 100755 --- a/utils/StringUtils.h +++ b/utils/StringUtils.h @@ -1165,7 +1165,7 @@ bool str_contains(const char* __restrict haystack, const char* __restrict needle inline constexpr bool str_contains(const char* __restrict haystack, const char* __restrict needle, size_t length) noexcept { - while (*haystack != '\0' && length > 0) { + while (*haystack != '\0' || length > 0) { const char* p1 = haystack; const char* p2 = needle; size_t remaining = length;