Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0a00631
cookie: add free()
ccdle12 Jan 21, 2026
b3f01c1
headers: add @each_header
ccdle12 Jan 21, 2026
22c9862
add http-request and tests
ccdle12 Jan 21, 2026
2f31bc7
http1: add request line parser, http request and tests
ccdle12 Jan 24, 2026
b3f0859
rename HttpStepParser
ccdle12 Jan 24, 2026
247cba5
common: move HttpParserResult and MALFORMED_CRLF
ccdle12 Jan 31, 2026
bda7b68
header_parser: remove interface and make step a macro fn
ccdle12 Jan 31, 2026
5b34066
http_request: add wip parse_and_set_header()
ccdle12 Jan 31, 2026
a9d252b
http_response_parser: move HttpParserResult, MALFORMED_CRLF, and use …
ccdle12 Jan 31, 2026
3e77d90
request_line_parser: remove interface
ccdle12 Jan 31, 2026
82a1a3b
add http_request_parser and update tests
ccdle12 Jan 31, 2026
24e81cb
parser_test_fixture: remove interface
ccdle12 Jan 31, 2026
633de02
udpate tests
ccdle12 Jan 31, 2026
bebb0fc
http_request: refactor parse_and_set_header
ccdle12 Jan 31, 2026
9c04952
remove unused interface
ccdle12 Jan 31, 2026
800f399
remove TODOs
ccdle12 Jan 31, 2026
e8e8635
http_response: assign everything to arena
ccdle12 Feb 4, 2026
29acc9f
http_request: add arena
ccdle12 Feb 4, 2026
d2e3437
body_parser: make step a macro
ccdle12 Feb 21, 2026
30bce7a
chunk_body_parser: update step to macro
ccdle12 Feb 21, 2026
1c8c1f9
content_length_body_parser: update step to macro
ccdle12 Feb 21, 2026
91be60c
header_parser: add handling for host value
ccdle12 Feb 21, 2026
adf8dfc
http_request: add header handling and body type
ccdle12 Feb 21, 2026
d8efae3
http_request_parser: add body state in header
ccdle12 Feb 21, 2026
d9c56c8
http_response_parser: use body_parser macro
ccdle12 Feb 21, 2026
e156101
test_chunk_body_parser: use macro
ccdle12 Feb 21, 2026
4811ed4
test_content_length_body_parser: use macro
ccdle12 Feb 21, 2026
40c73af
test_http_request_parser: add post request test
ccdle12 Feb 21, 2026
c760eed
test_http_request: add wip ser_der test
ccdle12 Feb 21, 2026
b6d9683
update doc comments
ccdle12 Feb 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/http1/body_parser.c3
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module req::http1;

import std::io;

struct BodyParser (HttpStepParser)
struct BodyParser
{
Allocator allocator;
ChunkBodyParser chunk_body_parser;
Expand All @@ -16,13 +16,13 @@ fn void BodyParser.init(&self, Allocator allocator)
self.content_length_body_parser.init();
}

fn HttpParserResult? BodyParser.step(&self, char byte, HttpResponse* http_response) @dynamic
macro HttpParserResult? BodyParser.@step(&self, char byte, http_request_or_response)
{
switch(http_response.body_type)
switch(http_request_or_response.body_type)
{
case NULL: break;
case CHUNKED: return self.chunk_body_parser.step(byte, http_response)!;
case CONTENT_LENGTH: return self.content_length_body_parser.step(byte, http_response)!;
case CHUNKED: return self.chunk_body_parser.@step(byte, http_request_or_response)!;
case CONTENT_LENGTH: return self.content_length_body_parser.@step(byte, http_request_or_response)!;
}

return COMPLETE;
Expand Down
12 changes: 8 additions & 4 deletions src/http1/chunk_body_parser.c3
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ enum ChunkBodyParserState
COMPLETE,
}

struct ChunkBodyParser (HttpStepParser)
struct ChunkBodyParser
{
Allocator allocator;
ChunkBodyParserState state;
Expand All @@ -36,8 +36,12 @@ fn void ChunkBodyParser.free(&self)
self.extensions_cache.free();
}

fn HttpParserResult? ChunkBodyParser.step(&self, char byte, HttpResponse* http_response) @dynamic
macro HttpParserResult? ChunkBodyParser.@step(&self, char byte, http_request_or_response)
{
// TODO: Could use an interface instead?
$assert($defined(http_request_or_response.append_to_body));
$assert($defined(http_request_or_response.parse_and_set_extensions));

switch(self.state)
{
case HEX_NUM:
Expand Down Expand Up @@ -71,7 +75,7 @@ fn HttpParserResult? ChunkBodyParser.step(&self, char byte, HttpResponse* http_r
case READ_CHUNK:
if (self.chunk_size > 0)
{
http_response.append_to_body(byte);
http_request_or_response.append_to_body(byte);
self.chunk_size--;
break;
}
Expand All @@ -89,7 +93,7 @@ fn HttpParserResult? ChunkBodyParser.step(&self, char byte, HttpResponse* http_r
case EXTENSION:
if (byte == CR)
{
http_response.parse_and_set_extensions(self.extensions_cache.str_view());
http_request_or_response.parse_and_set_extensions(self.extensions_cache.str_view());
defer self.extensions_cache.clear();

self.state = HEX_LF;
Expand Down
8 changes: 8 additions & 0 deletions src/http1/common.c3
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ const char CR = '\r';
const char LF = '\n';
const char SP = ' ';

faultdef MALFORMED_CRLF;

enum HttpParserResult
{
REQUEST_MORE,
COMPLETE,
}

<*
Checks for valid characters allowed for HTTP Header keys.
*>
Expand Down
15 changes: 9 additions & 6 deletions src/http1/content_length_body_parser.c3
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ enum ContentLengthBodyParserState
COMPLETE,
}

struct ContentLengthBodyParser (HttpStepParser)
struct ContentLengthBodyParser
{
ulong content_length_read;
ContentLengthBodyParserState state;
Expand All @@ -18,19 +18,22 @@ fn void ContentLengthBodyParser.init(&self)
self.state = READ_CONTENT;
}

fn HttpParserResult? ContentLengthBodyParser.step(&self, char byte, HttpResponse* http_response) @dynamic
macro HttpParserResult? ContentLengthBodyParser.@step(&self, char byte, http_request_or_response)
{
if (http_response.content_length == 0) return COMPLETE;
$assert($defined(http_request_or_response.content_length));
$assert($defined(http_request_or_response.append_to_body));

if (http_request_or_response.content_length == 0) return COMPLETE;

switch(self.state)
{
case READ_CONTENT:
if (self.content_length_read < http_response.content_length)
if (self.content_length_read < http_request_or_response.content_length)
{
http_response.append_to_body(byte);
http_request_or_response.append_to_body(byte);
self.content_length_read++;

if (self.content_length_read == http_response.content_length)
if (self.content_length_read == http_request_or_response.content_length)
{
self.state = COMPLETE;
return COMPLETE;
Expand Down
13 changes: 11 additions & 2 deletions src/http1/cookie.c3
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ struct Cookie
// TODO: - Whats the difference between expires and max_age?
String expires_raw;
DateTime expires;
// TODO: - Create a date time object for expires.

// The cookie must only be sent over HTTPS - optional.
bool secure;
Expand Down Expand Up @@ -85,6 +84,17 @@ fn void Cookie.clear(&self)
self.same_site = SameSite.LAX;
}

fn void Cookie.free(&self)
{
self.clear();
if (!self.allocator) return;
self.name.free(self.allocator);
self.value.free(self.allocator);
self.domain.free(self.allocator);
self.path.free(self.allocator);
self.expires_raw.free(self.allocator);
}

fn void? Cookie.parse_str(&self, String value) => @pool()
{
if (value.len < 1) return MALFORMED_EMPTY_COOKIE?;
Expand Down Expand Up @@ -151,7 +161,6 @@ fn void? Cookie.parse_str(&self, String value) => @pool()
{
self.expires_raw = attr.trim().copy(self.allocator);
self.expires = parse_and_get_date_time(self.expires_raw) ?? MALFORMED_COOKIE_INVALID_EXPIRY_DATE?!;

}

if (tnormalize_key(key) == "max-age")
Expand Down
21 changes: 18 additions & 3 deletions src/http1/header_parser.c3
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ enum HeaderState
COMPLETE,
}

struct HeaderParser (HttpStepParser)
struct HeaderParser
{
Allocator allocator;
HeaderState state;
Expand All @@ -35,8 +35,10 @@ fn void HeaderParser.free(&self)
self.header_val_cache.free();
}

fn HttpParserResult? HeaderParser.step(&self, char byte, HttpResponse* response) @dynamic
macro HttpParserResult? HeaderParser.@step(&self, char byte, http_request_or_response)
{
$assert($defined(http_request_or_response.parse_and_set_header));

switch(self.state)
{
case HEADER_KEY:
Expand All @@ -59,13 +61,26 @@ fn HttpParserResult? HeaderParser.step(&self, char byte, HttpResponse* response)
if (byte == LF) return MALFORMED_CRLF?;
if (byte == CR)
{
// TODO: If http_request_or_response is request:
// If header is Host, assign to host NOT push to headers.
String key = self.header_key_cache.str_view();
defer self.header_key_cache.clear();

String value = self.header_val_cache.str_view();
defer self.header_val_cache.clear();

response.parse_and_set_header(key, value)!;
// TODO: Push to headers, but if we have a http_request, which has "host", we need to assing the value there.
// TODO: Is there some logic where we can not push it to headers but just assign instead?
if (tnormalize_key(key) == "host")
{
$if $defined(http_request_or_response.parse_host):
http_request_or_response.parse_host(value)!;
$endif
}
else
{
http_request_or_response.parse_and_set_header(key, value)!;
}

self.state = HEADER_LF;
break;
Expand Down
12 changes: 11 additions & 1 deletion src/http1/headers.c3
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ fn bool is_special_case_header_key(String key) => @pool()
return special_cases.contains(key);
}


struct HttpHeaders
{
Allocator allocator;
Expand Down Expand Up @@ -188,6 +187,17 @@ fn void? HttpHeaders.enforce_content_length_special_case(&self, Header* header,
}
}

<*
foreach iterator for the headers.
*>
macro HttpHeaders.@each_header(&self; @body(key, value))
{
self.headers.@each(; String key, Header header)
{
@body(key, header);
};
}

enum HeaderType
{
SINGLETON,
Expand Down
Loading