fediglam/src/http/request/test_parser.zig

265 lines
6.2 KiB
Zig
Raw Normal View History

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
\\
,
.{},
));
}