backend: make http request parser

This commit is contained in:
Simon 2023-02-09 23:02:54 +01:00
parent a30ee9d249
commit 3e6dfd3d5a
4 changed files with 134 additions and 49 deletions

View File

@ -1,5 +1,6 @@
#include "http.h" #include "http.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -8,48 +9,148 @@ typedef struct {
const char* message; const char* message;
size_t message_size; size_t message_size;
size_t i; 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; parser->i += 3;
return HttpMethodGet; 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; parser->i += 4;
return HttpMethodPost; return HttpMethodPost;
} else { } else {
printf("error: http parser: failed to parse http method\n"); PARSER_PANIC("failed to parse http method");
exit(1);
} }
} }
void skip_char(HttpRequestHeaderParser* parser, char value) void skip_char(HttpRequestParser* parser, char value)
{ {
if (parser->message[parser->i] != value) { if (parser->i >= parser->message_size)
printf("error: http parser: unexpected character, expected ' '\n"); PARSER_PANIC("unexpected end");
exit(1); if (parser->message[parser->i] != value)
} PARSER_PANIC("unexpected character, expected ' '");
parser->i += 1; parser->i += 1;
if (parser->i >= parser->message_size) {
printf("error: http parser: unexpected end\n");
exit(1);
}
} }
typedef struct { typedef struct {
size_t begin, length; size_t index, length;
} PathSpan; } 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) HttpRequestHeader parse_http_request_header(const char* message, size_t message_size)
{ {
HttpRequestHeaderParser parser = (HttpRequestHeaderParser) { HttpRequestParser parser = (HttpRequestParser) {
.message = message, .message = message,
.message_size = message_size, .message_size = message_size,
.i = 0, .i = 0,
}; };
HttpMethod method = parse_method(&parser); HttpMethod method = parse_method(&parser);
skip_char(&parser, ' '); skip_char(&parser, ' ');
PathSpan path_span = parse_path(&parser);
return (HttpRequestHeader) { 0 }; 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,
};
} }

View File

@ -12,6 +12,7 @@ typedef struct {
HttpMethod method; HttpMethod method;
size_t path_index, path_length; size_t path_index, path_length;
size_t content_length; size_t content_length;
size_t body_index;
} HttpRequestHeader; } HttpRequestHeader;
HttpRequestHeader parse_http_request_header(const char* message, size_t message_size); HttpRequestHeader parse_http_request_header(const char* message, size_t message_size);

View File

@ -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...

View File

@ -1,3 +1,4 @@
#include "http.h"
#include "native.h" #include "native.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -51,7 +52,16 @@ int main(void)
printf("client disconnected\n"); printf("client disconnected\n");
break; 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[] uint8_t send_buffer[]
= "HTTP/1.1 200 OK\r\n" = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\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); ssize_t written = send(client_socket, send_buffer, sizeof(send_buffer), 0);
if (written < 0) { if (written < 0) {
printf("error: could not write\n"); printf("error: could not write\n");
continue; break;
} }
printf("disconnecting client\n"); printf("disconnecting client\n");
close_socket(client_socket); close_socket(client_socket);
break;
} }
} }