postnummer-app/backend/http.c

157 lines
4.6 KiB
C
Raw Permalink Normal View History

2023-02-09 12:50:26 +00:00
#include "http.h"
2023-02-09 14:55:57 +00:00
#include <stdbool.h>
2023-02-09 22:02:54 +00:00
#include <stdint.h>
2023-02-09 14:55:57 +00:00
#include <stdio.h>
2023-02-09 12:50:26 +00:00
#include <stdlib.h>
2023-02-09 14:55:57 +00:00
#include <string.h>
2023-02-09 12:50:26 +00:00
2023-02-09 14:55:57 +00:00
typedef struct {
const char* message;
size_t message_size;
size_t i;
2023-02-09 22:02:54 +00:00
} HttpRequestParser;
2023-02-09 12:50:26 +00:00
2023-02-09 22:02:54 +00:00
#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)
2023-02-09 14:55:57 +00:00
{
2023-02-09 22:02:54 +00:00
if (parser->i + 3 < parser->message_size && strncmp(&parser->message[0], "GET", 3) == 0) {
2023-02-09 14:55:57 +00:00
parser->i += 3;
return HttpMethodGet;
2023-02-09 22:02:54 +00:00
} else if (parser->i + 4 < parser->message_size
&& strncmp(&parser->message[0], "POST", 4) == 0) {
2023-02-09 14:55:57 +00:00
parser->i += 4;
return HttpMethodPost;
2023-02-09 12:50:26 +00:00
} else {
2023-02-09 22:02:54 +00:00
PARSER_PANIC("failed to parse http method");
2023-02-09 14:55:57 +00:00
}
}
2023-02-09 22:02:54 +00:00
void skip_char(HttpRequestParser* parser, char value)
2023-02-09 14:55:57 +00:00
{
2023-02-09 22:02:54 +00:00
if (parser->i >= parser->message_size)
PARSER_PANIC("unexpected end");
if (parser->message[parser->i] != value)
PARSER_PANIC("unexpected character, expected ' '");
2023-02-09 14:55:57 +00:00
parser->i += 1;
}
2023-02-09 12:50:26 +00:00
2023-02-09 14:55:57 +00:00
typedef struct {
2023-02-09 22:02:54 +00:00
size_t index, length;
2023-02-09 14:55:57 +00:00
} PathSpan;
2023-02-09 22:02:54 +00:00
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;
}
2023-02-09 14:55:57 +00:00
HttpRequestHeader parse_http_request_header(const char* message, size_t message_size)
{
2023-02-09 22:02:54 +00:00
HttpRequestParser parser = (HttpRequestParser) {
2023-02-09 14:55:57 +00:00
.message = message,
.message_size = message_size,
.i = 0,
};
HttpMethod method = parse_method(&parser);
skip_char(&parser, ' ');
2023-02-09 22:02:54 +00:00
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,
};
2023-02-09 12:50:26 +00:00
}