backend: make http request parser
This commit is contained in:
parent
a30ee9d249
commit
3e6dfd3d5a
139
backend/http.c
139
backend/http.c
@ -1,5 +1,6 @@
|
||||
#include "http.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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...
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "http.h"
|
||||
#include "native.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user