2022-11-05 07:26:53 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const parser = @import("./parser.zig");
|
|
|
|
const http = @import("../lib.zig");
|
|
|
|
const t = std.testing;
|
|
|
|
|
|
|
|
const test_case = struct {
|
|
|
|
fn parse(text: []const u8, expected: struct {
|
|
|
|
protocol: http.Request.Protocol = .http_1_1,
|
|
|
|
method: http.Method = .GET,
|
|
|
|
headers: []const std.meta.Tuple(&.{ []const u8, []const u8 }) = &.{},
|
|
|
|
uri: []const u8 = "",
|
|
|
|
}) !void {
|
|
|
|
var stream = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(text) };
|
|
|
|
var actual = try parser.parse(t.allocator, stream.reader());
|
|
|
|
defer actual.parseFree(t.allocator);
|
|
|
|
|
|
|
|
try t.expectEqual(expected.protocol, actual.protocol);
|
|
|
|
try t.expectEqual(expected.method, actual.method);
|
|
|
|
try t.expectEqualStrings(expected.uri, actual.uri);
|
|
|
|
|
|
|
|
try t.expectEqual(expected.headers.len, actual.headers.count());
|
|
|
|
for (expected.headers) |hdr| {
|
|
|
|
if (actual.headers.get(hdr[0])) |val| {
|
|
|
|
try t.expectEqualStrings(hdr[1], val);
|
|
|
|
} else {
|
|
|
|
std.debug.print("Error: Header {s} expected to be present, was not.\n", .{hdr[0]});
|
|
|
|
try t.expect(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
fn toCrlf(comptime str: []const u8) []const u8 {
|
|
|
|
comptime {
|
|
|
|
var buf: [str.len * 2]u8 = undefined;
|
|
|
|
|
|
|
|
@setEvalBranchQuota(@intCast(u32, str.len * 2)); // TODO: why does this need to be *2
|
|
|
|
|
|
|
|
var buf_len: usize = 0;
|
|
|
|
for (str) |ch| {
|
|
|
|
if (ch == '\n') {
|
|
|
|
buf[buf_len] = '\r';
|
|
|
|
buf_len += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[buf_len] = ch;
|
|
|
|
buf_len += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf[0..buf_len];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
test "HTTP/1.x parse - No body" {
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET / HTTP/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/",
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\POST / HTTP/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .POST,
|
|
|
|
.uri = "/",
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET /url/abcd HTTP/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/url/abcd",
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET / HTTP/1.0
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_0,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/",
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET /url/abcd HTTP/1.1
|
|
|
|
\\Content-Type: application/json
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/url/abcd",
|
|
|
|
.headers = &.{.{ "Content-Type", "application/json" }},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET /url/abcd HTTP/1.1
|
|
|
|
\\Content-Type: application/json
|
|
|
|
\\Authorization: bearer <token>
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/url/abcd",
|
|
|
|
.headers = &.{
|
|
|
|
.{ "Content-Type", "application/json" },
|
|
|
|
.{ "Authorization", "bearer <token>" },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test without CRLF
|
|
|
|
try test_case.parse(
|
|
|
|
\\GET /url/abcd HTTP/1.1
|
|
|
|
\\Content-Type: application/json
|
|
|
|
\\Authorization: bearer <token>
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/url/abcd",
|
|
|
|
.headers = &.{
|
|
|
|
.{ "Content-Type", "application/json" },
|
|
|
|
.{ "Authorization", "bearer <token>" },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
\\POST / HTTP/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .POST,
|
|
|
|
.uri = "/",
|
|
|
|
},
|
|
|
|
);
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET / HTTP/1.2
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_x,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/",
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "HTTP/1.x parse - unsupported protocol" {
|
|
|
|
try t.expectError(error.UnknownProtocol, test_case.parse(
|
|
|
|
\\GET / JSON/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.UnknownProtocol, test_case.parse(
|
|
|
|
\\GET / SOMETHINGELSE/3.5
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.UnknownProtocol, test_case.parse(
|
|
|
|
\\GET / /1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.HttpVersionNotSupported, test_case.parse(
|
|
|
|
\\GET / HTTP/2.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
test "HTTP/1.x parse - Unknown method" {
|
|
|
|
try t.expectError(error.MethodNotImplemented, test_case.parse(
|
|
|
|
\\ABCD / HTTP/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.MethodNotImplemented, test_case.parse(
|
|
|
|
\\PATCHPATCHPATCH / HTTP/1.1
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
test "HTTP/1.x parse - Too long" {
|
|
|
|
try t.expectError(error.RequestUriTooLong, test_case.parse(
|
|
|
|
std.fmt.comptimePrint("GET {s} HTTP/1.1\n\n", .{"a" ** 8192}),
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.HeaderLineTooLong, test_case.parse(
|
|
|
|
std.fmt.comptimePrint("GET / HTTP/1.1\r\n{s}: abcd", .{"a" ** 8192}),
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.HeaderLineTooLong, test_case.parse(
|
|
|
|
std.fmt.comptimePrint("GET / HTTP/1.1\r\nabcd: {s}", .{"a" ** 8192}),
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
test "HTTP/1.x parse - bad requests" {
|
|
|
|
try t.expectError(error.BadRequest, test_case.parse(
|
|
|
|
\\GET / HTTP/1.1 blah blah
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.BadRequest, test_case.parse(
|
|
|
|
\\GET / HTTP/1.1
|
|
|
|
\\abcd : lksjdfkl
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
try t.expectError(error.BadRequest, test_case.parse(
|
|
|
|
\\GET / HTTP/1.1
|
|
|
|
\\ lksjfklsjdfklj
|
|
|
|
\\
|
|
|
|
,
|
|
|
|
.{},
|
|
|
|
));
|
|
|
|
}
|
2022-11-05 08:54:00 +00:00
|
|
|
|
|
|
|
test "HTTP/1.x parse - Headers" {
|
|
|
|
try test_case.parse(
|
|
|
|
toCrlf(
|
|
|
|
\\GET /url/abcd HTTP/1.1
|
|
|
|
\\Content-Type: application/json
|
|
|
|
\\Content-Type: application/xml
|
|
|
|
\\
|
|
|
|
\\
|
|
|
|
),
|
|
|
|
.{
|
|
|
|
.protocol = .http_1_1,
|
|
|
|
.method = .GET,
|
|
|
|
.uri = "/url/abcd",
|
|
|
|
.headers = &.{.{ "Content-Type", "application/json, application/xml" }},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|