Commit e62b0773cc

Nameless <truemedian@gmail.com>
2024-02-27 21:22:34
std.http: add tests against regressions for conforming fields
1 parent 81713f2
Changed files (3)
lib/std/http/Client.zig
@@ -551,6 +551,43 @@ pub const Response = struct {
         return error.HttpHeadersInvalid; // missing empty line
     }
 
+    test parse {
+        const response_bytes = "HTTP/1.1 200 OK\r\n" ++
+            "LOcation:url\r\n" ++
+            "content-tYpe: text/plain\r\n" ++
+            "content-disposition:attachment; filename=example.txt \r\n" ++
+            "content-Length:10\r\n" ++
+            "TRansfer-encoding:\tdeflate, chunked \r\n" ++
+            "connectioN:\t keep-alive \r\n\r\n";
+
+        var header_buffer: [1024]u8 = undefined;
+        var res = Response{
+            .status = undefined,
+            .reason = undefined,
+            .version = undefined,
+            .keep_alive = false,
+            .parser = proto.HeadersParser.init(&header_buffer),
+        };
+
+        @memcpy(header_buffer[0..response_bytes.len], response_bytes);
+        res.parser.header_bytes_len = response_bytes.len;
+
+        try res.parse(response_bytes);
+
+        try testing.expectEqual(.@"HTTP/1.1", res.version);
+        try testing.expectEqualStrings("OK", res.reason);
+        try testing.expectEqual(.ok, res.status);
+
+        try testing.expectEqualStrings("url", res.location.?);
+        try testing.expectEqualStrings("text/plain", res.content_type.?);
+        try testing.expectEqualStrings("attachment; filename=example.txt", res.content_disposition.?);
+
+        try testing.expectEqual(true, res.keep_alive);
+        try testing.expectEqual(10, res.content_length.?);
+        try testing.expectEqual(.chunked, res.transfer_encoding);
+        try testing.expectEqual(.deflate, res.transfer_compression);
+    }
+
     inline fn int64(array: *const [8]u8) u64 {
         return @bitCast(array.*);
     }
@@ -575,6 +612,67 @@ pub const Response = struct {
     pub fn iterateHeaders(r: Response) http.HeaderIterator {
         return http.HeaderIterator.init(r.parser.get());
     }
+
+    test iterateHeaders {
+        const response_bytes = "HTTP/1.1 200 OK\r\n" ++
+            "LOcation:url\r\n" ++
+            "content-tYpe: text/plain\r\n" ++
+            "content-disposition:attachment; filename=example.txt \r\n" ++
+            "content-Length:10\r\n" ++
+            "TRansfer-encoding:\tdeflate, chunked \r\n" ++
+            "connectioN:\t keep-alive \r\n\r\n";
+
+        var header_buffer: [1024]u8 = undefined;
+        var res = Response{
+            .status = undefined,
+            .reason = undefined,
+            .version = undefined,
+            .keep_alive = false,
+            .parser = proto.HeadersParser.init(&header_buffer),
+        };
+
+        @memcpy(header_buffer[0..response_bytes.len], response_bytes);
+        res.parser.header_bytes_len = response_bytes.len;
+
+        var it = res.iterateHeaders();
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("LOcation", header.name);
+            try testing.expectEqualStrings("url", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("content-tYpe", header.name);
+            try testing.expectEqualStrings("text/plain", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("content-disposition", header.name);
+            try testing.expectEqualStrings("attachment; filename=example.txt", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("content-Length", header.name);
+            try testing.expectEqualStrings("10", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("TRansfer-encoding", header.name);
+            try testing.expectEqualStrings("deflate, chunked", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("connectioN", header.name);
+            try testing.expectEqualStrings("keep-alive", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        try testing.expectEqual(null, it.next());
+    }
 };
 
 /// A HTTP request that has been sent.
lib/std/http/Server.zig
@@ -271,6 +271,29 @@ pub const Request = struct {
             return error.MissingFinalNewline;
         }
 
+        test parse {
+            const request_bytes = "GET /hi HTTP/1.0\r\n" ++
+                "content-tYpe: text/plain\r\n" ++
+                "content-Length:10\r\n" ++
+                "expeCt:   100-continue \r\n" ++
+                "TRansfer-encoding:\tdeflate, chunked \r\n" ++
+                "connectioN:\t keep-alive \r\n\r\n";
+
+            const req = try parse(request_bytes);
+
+            try testing.expectEqual(.GET, req.method);
+            try testing.expectEqual(.@"HTTP/1.0", req.version);
+            try testing.expectEqualStrings("/hi", req.target);
+
+            try testing.expectEqualStrings("text/plain", req.content_type.?);
+            try testing.expectEqualStrings("100-continue", req.expect.?);
+
+            try testing.expectEqual(true, req.keep_alive);
+            try testing.expectEqual(10, req.content_length.?);
+            try testing.expectEqual(.chunked, req.transfer_encoding);
+            try testing.expectEqual(.deflate, req.transfer_compression);
+        }
+
         inline fn int64(array: *const [8]u8) u64 {
             return @bitCast(array.*);
         }
@@ -280,6 +303,66 @@ pub const Request = struct {
         return http.HeaderIterator.init(r.server.read_buffer[0..r.head_end]);
     }
 
+    test iterateHeaders {
+        const request_bytes = "GET /hi HTTP/1.0\r\n" ++
+            "content-tYpe: text/plain\r\n" ++
+            "content-Length:10\r\n" ++
+            "expeCt:   100-continue \r\n" ++
+            "TRansfer-encoding:\tdeflate, chunked \r\n" ++
+            "connectioN:\t keep-alive \r\n\r\n";
+
+        var read_buffer: [500]u8 = undefined;
+        @memcpy(read_buffer[0..request_bytes.len], request_bytes);
+
+        var server: Server = .{
+            .connection = undefined,
+            .state = .ready,
+            .read_buffer = &read_buffer,
+            .read_buffer_len = request_bytes.len,
+            .next_request_start = 0,
+        };
+
+        var request: Request = .{
+            .server = &server,
+            .head_end = request_bytes.len,
+            .head = undefined,
+            .reader_state = undefined,
+        };
+
+        var it = request.iterateHeaders();
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("content-tYpe", header.name);
+            try testing.expectEqualStrings("text/plain", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("content-Length", header.name);
+            try testing.expectEqualStrings("10", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("expeCt", header.name);
+            try testing.expectEqualStrings("100-continue", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("TRansfer-encoding", header.name);
+            try testing.expectEqualStrings("deflate, chunked", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        {
+            const header = it.next().?;
+            try testing.expectEqualStrings("connectioN", header.name);
+            try testing.expectEqualStrings("keep-alive", header.value);
+            try testing.expect(!it.is_trailer);
+        }
+        try testing.expectEqual(null, it.next());
+    }
+
     pub const RespondOptions = struct {
         version: http.Version = .@"HTTP/1.1",
         status: http.Status = .ok,
@@ -1060,5 +1143,6 @@ const mem = std.mem;
 const net = std.net;
 const Uri = std.Uri;
 const assert = std.debug.assert;
+const testing = std.testing;
 
 const Server = @This();
lib/std/http/test.zig
@@ -328,8 +328,8 @@ test "receiving arbitrary http headers from the client" {
     defer test_server.destroy();
 
     const request_bytes = "GET /bar HTTP/1.1\r\n" ++
-        "CoNneCtIoN: close\r\n" ++
-        "aoeu: asdf\r\n" ++
+        "CoNneCtIoN:close\r\n" ++
+        "aoeu:  asdf \r\n" ++
         "\r\n";
     const gpa = std.testing.allocator;
     const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());