From 3e6dfd3d5a4afaeacf3eb4c2dfbb51d3b6dde1b8 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 Feb 2023 23:02:54 +0100 Subject: [PATCH] backend: make http request parser --- backend/http.c | 139 ++++++++++++++++++++++++++----- backend/http.h | 1 + backend/http_request_example.txt | 28 ------- backend/main.c | 15 +++- 4 files changed, 134 insertions(+), 49 deletions(-) delete mode 100644 backend/http_request_example.txt diff --git a/backend/http.c b/backend/http.c index d97bfaa..9e6b3e4 100644 --- a/backend/http.c +++ b/backend/http.c @@ -1,5 +1,6 @@ #include "http.h" #include +#include #include #include #include @@ -8,48 +9,148 @@ typedef struct { const char* message; size_t message_size; size_t i; -} HttpRequestHeaderParser; +} HttpRequestParser; -HttpMethod parse_method(HttpRequestHeaderParser* parser) +#define PARSER_PANIC(message) \ + (printf("error: http parser: %s\n at %s:%d in '%s()'\n", message, __FILE__, __LINE__, \ + __func__), \ + exit(1)) + +HttpMethod parse_method(HttpRequestParser* parser) { - if (parser->i + 3 < parser->message_size && strncmp(&parser->message[0], "GET", 3)) { + if (parser->i + 3 < parser->message_size && strncmp(&parser->message[0], "GET", 3) == 0) { parser->i += 3; return HttpMethodGet; - } else if (parser->i + 4 < parser->message_size && strncmp(&parser->message[0], "POST", 4)) { + } else if (parser->i + 4 < parser->message_size + && strncmp(&parser->message[0], "POST", 4) == 0) { parser->i += 4; return HttpMethodPost; } else { - printf("error: http parser: failed to parse http method\n"); - exit(1); + PARSER_PANIC("failed to parse http method"); } } -void skip_char(HttpRequestHeaderParser* parser, char value) +void skip_char(HttpRequestParser* parser, char value) { - if (parser->message[parser->i] != value) { - printf("error: http parser: unexpected character, expected ' '\n"); - exit(1); - } + if (parser->i >= parser->message_size) + PARSER_PANIC("unexpected end"); + if (parser->message[parser->i] != value) + PARSER_PANIC("unexpected character, expected ' '"); parser->i += 1; - if (parser->i >= parser->message_size) { - printf("error: http parser: unexpected end\n"); - exit(1); - } } typedef struct { - size_t begin, length; + size_t index, length; } PathSpan; +PathSpan parse_path(HttpRequestParser* parser) +{ + size_t index = parser->i; + while (parser->i < parser->message_size && parser->message[parser->i] != ' ' + && parser->message[parser->i] != '\r') { + parser->i += 1; + } + if (parser->i >= parser->message_size) + PARSER_PANIC("unexpected end"); + else if (parser->message[parser->i] != ' ') + PARSER_PANIC("unexpected char, expected ' '"); + return (PathSpan) { + .index = index, + .length = parser->i - index, + }; +} + +void skip_newline(HttpRequestParser* parser) +{ + skip_char(parser, '\r'); + skip_char(parser, '\n'); +} + +void skip_http_version_tag(HttpRequestParser* parser) +{ + skip_char(parser, 'H'); + skip_char(parser, 'T'); + skip_char(parser, 'T'); + skip_char(parser, 'P'); + skip_char(parser, '/'); + skip_char(parser, '1'); + skip_char(parser, '.'); + skip_char(parser, '1'); + skip_newline(parser); +} + +typedef struct { + size_t key_index, key_length; + size_t value_index, value_length; +} HeaderPair; + +HeaderPair parse_header_pair(HttpRequestParser* parser) +{ + size_t key_begin = parser->i; + while (parser->i < parser->message_size && parser->message[parser->i] != ':' + && parser->message[parser->i] != '\r') { + parser->i += 1; + } + if (parser->i >= parser->message_size) + PARSER_PANIC("unexpected end"); + size_t key_end = parser->i; + skip_char(parser, ':'); + skip_char(parser, ' '); + size_t value_begin = parser->i; + while (parser->i < parser->message_size && parser->message[parser->i] != '\r') { + parser->i += 1; + } + if (parser->i >= parser->message_size) + PARSER_PANIC("unexpected end"); + return (HeaderPair) { + .key_index = key_begin, + .key_length = key_end - key_begin, + .value_index = value_begin, + .value_length = parser->i - value_begin, + }; +} + +bool is_content_length_header(HttpRequestParser* parser, HeaderPair pair) +{ + return strncmp(&parser->message[pair.key_index], "Content-Length", pair.key_length) == 0; +} + +size_t extract_content_length_value(HttpRequestParser* parser, HeaderPair pair) +{ + char string_value[21] = { 0 }; + strncpy(string_value, &parser->message[pair.value_index], pair.value_length); + int64_t int_value = atoll(string_value); + if (int_value < 0) + PARSER_PANIC("Content-Length < 0"); + return (size_t)int_value; +} + HttpRequestHeader parse_http_request_header(const char* message, size_t message_size) { - HttpRequestHeaderParser parser = (HttpRequestHeaderParser) { + HttpRequestParser parser = (HttpRequestParser) { .message = message, .message_size = message_size, .i = 0, }; HttpMethod method = parse_method(&parser); skip_char(&parser, ' '); - - return (HttpRequestHeader) { 0 }; + PathSpan path_span = parse_path(&parser); + skip_char(&parser, ' '); + skip_http_version_tag(&parser); + size_t content_length = 0; + while (parser.i < message_size && message[parser.i] != '\r') { + HeaderPair pair = parse_header_pair(&parser); + if (is_content_length_header(&parser, pair)) { + content_length = extract_content_length_value(&parser, pair); + } + skip_newline(&parser); + } + skip_newline(&parser); + return (HttpRequestHeader) { + .method = method, + .path_index = path_span.index, + .path_length = path_span.length, + .content_length = content_length, + .body_index = parser.i, + }; } diff --git a/backend/http.h b/backend/http.h index b4e8e35..3b0337b 100644 --- a/backend/http.h +++ b/backend/http.h @@ -12,6 +12,7 @@ typedef struct { HttpMethod method; size_t path_index, path_length; size_t content_length; + size_t body_index; } HttpRequestHeader; HttpRequestHeader parse_http_request_header(const char* message, size_t message_size); diff --git a/backend/http_request_example.txt b/backend/http_request_example.txt deleted file mode 100644 index e1dd00c..0000000 --- a/backend/http_request_example.txt +++ /dev/null @@ -1,28 +0,0 @@ -starting server... -listening on port 8000 -waiting for client... -client connected -recieved: -GET / HTTP/1.1 -Host: localhost:8000 -User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109 -.0 -Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/w -ebp,*/*;q=0.8 -Accept-Language: en-US,en;q=0.5 -Accept-Encoding: gzip, deflate, br -Connection: keep-alive -Upgrade-Insecure-Requests: 1 -Sec-Fetch-Dest: document -Sec-Fetch-Mode: navigate -Sec-Fetch-Site: none -Sec-Fetch-User: ?1 - - -disconnecting client -error: could not recieve -waiting for client... -client connected -client disconnected -waiting for client... - diff --git a/backend/main.c b/backend/main.c index 59db0f7..672bc62 100644 --- a/backend/main.c +++ b/backend/main.c @@ -1,3 +1,4 @@ +#include "http.h" #include "native.h" #include #include @@ -51,7 +52,16 @@ int main(void) printf("client disconnected\n"); break; } - printf("recieved:\n%s\n", buffer); + printf("=== recieved start ===\n%s\n=== recieved end ===\n", buffer); + + HttpRequestHeader header + = parse_http_request_header((char*)buffer, strlen((char*)buffer)); + printf("method: %d\n", header.method); + printf("path_index: %ld\n", header.path_index); + printf("path_length: %ld\n", header.path_length); + printf("content_length: %ld\n", header.content_length); + printf("body_index: %ld\n", header.body_index); + uint8_t send_buffer[] = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" @@ -62,10 +72,11 @@ int main(void) ssize_t written = send(client_socket, send_buffer, sizeof(send_buffer), 0); if (written < 0) { printf("error: could not write\n"); - continue; + break; } printf("disconnecting client\n"); close_socket(client_socket); + break; } }