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 "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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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 "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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user