Commit c523b5421b

Nameless <truemedian@gmail.com>
2023-10-07 04:38:05
std.http: make encoding fields non-null, store as enum variant
1 parent 16f89ea
Changed files (3)
lib/std/http/Client.zig
@@ -425,27 +425,23 @@ pub const Response = struct {
                 // Transfer-Encoding: deflate, chunked
                 var iter = mem.splitBackwardsScalar(u8, header_value, ',');
 
-                if (iter.next()) |first| {
-                    const trimmed = mem.trim(u8, first, " ");
-
-                    if (std.meta.stringToEnum(http.TransferEncoding, trimmed)) |te| {
-                        if (res.transfer_encoding != null) return error.HttpHeadersInvalid;
-                        res.transfer_encoding = te;
-                    } else if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
-                        if (res.transfer_compression != null) return error.HttpHeadersInvalid;
-                        res.transfer_compression = ce;
-                    } else {
-                        return error.HttpTransferEncodingUnsupported;
-                    }
-                }
+                const first = iter.first();
+                const trimmed_first = mem.trim(u8, first, " ");
 
-                if (iter.next()) |second| {
-                    if (res.transfer_compression != null) return error.HttpTransferEncodingUnsupported;
+                var next: ?[]const u8 = first;
+                if (std.meta.stringToEnum(http.TransferEncoding, trimmed_first)) |transfer| {
+                    if (res.transfer_encoding != .none) return error.HttpHeadersInvalid; // we already have a transfer encoding
+                    res.transfer_encoding = transfer;
 
-                    const trimmed = mem.trim(u8, second, " ");
+                    next = iter.next();
+                }
+
+                if (next) |second| {
+                    const trimmed_second = mem.trim(u8, second, " ");
 
-                    if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
-                        res.transfer_compression = ce;
+                    if (std.meta.stringToEnum(http.ContentEncoding, trimmed_second)) |transfer| {
+                        if (res.transfer_compression != .identity) return error.HttpHeadersInvalid; // double compression is not supported
+                        res.transfer_compression = transfer;
                     } else {
                         return error.HttpTransferEncodingUnsupported;
                     }
@@ -459,7 +455,7 @@ pub const Response = struct {
 
                 res.content_length = content_length;
             } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) {
-                if (res.transfer_compression != null) return error.HttpHeadersInvalid;
+                if (res.transfer_compression != .identity) return error.HttpHeadersInvalid;
 
                 const trimmed = mem.trim(u8, header_value, " ");
 
@@ -494,8 +490,8 @@ pub const Response = struct {
     reason: []const u8,
 
     content_length: ?u64 = null,
-    transfer_encoding: ?http.TransferEncoding = null,
-    transfer_compression: ?http.ContentEncoding = null,
+    transfer_encoding: http.TransferEncoding = .none,
+    transfer_compression: http.ContentEncoding = .identity,
 
     headers: http.Headers,
     parser: proto.HeadersParser,
@@ -771,8 +767,9 @@ pub const Request = struct {
                 req.connection.?.closing = true;
             }
 
-            if (req.response.transfer_encoding) |te| {
-                switch (te) {
+            if (req.response.transfer_encoding != .none) {
+                switch (req.response.transfer_encoding) {
+                    .none => unreachable,
                     .chunked => {
                         req.response.parser.next_chunk_length = 0;
                         req.response.parser.state = .chunk_head_size;
@@ -840,19 +837,19 @@ pub const Request = struct {
             } else {
                 req.response.skip = false;
                 if (!req.response.parser.done) {
-                    if (req.response.transfer_compression) |tc| switch (tc) {
+                    switch (req.response.transfer_compression) {
                         .identity => req.response.compression = .none,
-                        .compress => return error.CompressionNotSupported,
+                        .compress, .@"x-compress" => return error.CompressionNotSupported,
                         .deflate => req.response.compression = .{
                             .deflate = std.compress.zlib.decompressStream(req.client.allocator, req.transferReader()) catch return error.CompressionInitializationFailed,
                         },
-                        .gzip => req.response.compression = .{
+                        .gzip, .@"x-gzip" => req.response.compression = .{
                             .gzip = std.compress.gzip.decompress(req.client.allocator, req.transferReader()) catch return error.CompressionInitializationFailed,
                         },
                         .zstd => req.response.compression = .{
                             .zstd = std.compress.zstd.decompressStream(req.client.allocator, req.transferReader()),
                         },
-                    };
+                    }
                 }
 
                 break;
lib/std/http/Server.zig
@@ -228,27 +228,23 @@ pub const Request = struct {
                 // Transfer-Encoding: deflate, chunked
                 var iter = mem.splitBackwardsScalar(u8, header_value, ',');
 
-                if (iter.next()) |first| {
-                    const trimmed = mem.trim(u8, first, " ");
-
-                    if (std.meta.stringToEnum(http.TransferEncoding, trimmed)) |te| {
-                        if (req.transfer_encoding != null) return error.HttpHeadersInvalid;
-                        req.transfer_encoding = te;
-                    } else if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
-                        if (req.transfer_compression != null) return error.HttpHeadersInvalid;
-                        req.transfer_compression = ce;
-                    } else {
-                        return error.HttpTransferEncodingUnsupported;
-                    }
-                }
+                const first = iter.first();
+                const trimmed_first = mem.trim(u8, first, " ");
 
-                if (iter.next()) |second| {
-                    if (req.transfer_compression != null) return error.HttpTransferEncodingUnsupported;
+                var next: ?[]const u8 = first;
+                if (std.meta.stringToEnum(http.TransferEncoding, trimmed_first)) |transfer| {
+                    if (req.transfer_encoding != .none) return error.HttpHeadersInvalid; // we already have a transfer encoding
+                    req.transfer_encoding = transfer;
 
-                    const trimmed = mem.trim(u8, second, " ");
+                    next = iter.next();
+                }
+
+                if (next) |second| {
+                    const trimmed_second = mem.trim(u8, second, " ");
 
-                    if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| {
-                        req.transfer_compression = ce;
+                    if (std.meta.stringToEnum(http.ContentEncoding, trimmed_second)) |transfer| {
+                        if (req.transfer_compression != .identity) return error.HttpHeadersInvalid; // double compression is not supported
+                        req.transfer_compression = transfer;
                     } else {
                         return error.HttpTransferEncodingUnsupported;
                     }
@@ -256,7 +252,7 @@ pub const Request = struct {
 
                 if (iter.next()) |_| return error.HttpTransferEncodingUnsupported;
             } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) {
-                if (req.transfer_compression != null) return error.HttpHeadersInvalid;
+                if (req.transfer_compression != .identity) return error.HttpHeadersInvalid;
 
                 const trimmed = mem.trim(u8, header_value, " ");
 
@@ -278,8 +274,8 @@ pub const Request = struct {
     version: http.Version,
 
     content_length: ?u64 = null,
-    transfer_encoding: ?http.TransferEncoding = null,
-    transfer_compression: ?http.ContentEncoding = null,
+    transfer_encoding: http.TransferEncoding = .none,
+    transfer_compression: http.ContentEncoding = .identity,
 
     headers: http.Headers,
     parser: proto.HeadersParser,
@@ -511,8 +507,9 @@ pub const Response = struct {
         res.request.headers = .{ .allocator = res.allocator, .owned = true };
         try res.request.parse(res.request.parser.header_bytes.items);
 
-        if (res.request.transfer_encoding) |te| {
-            switch (te) {
+        if (res.request.transfer_encoding != .none) {
+            switch (res.request.transfer_encoding) {
+                .none => unreachable,
                 .chunked => {
                     res.request.parser.next_chunk_length = 0;
                     res.request.parser.state = .chunk_head_size;
@@ -527,19 +524,19 @@ pub const Response = struct {
         }
 
         if (!res.request.parser.done) {
-            if (res.request.transfer_compression) |tc| switch (tc) {
+            switch (res.request.transfer_compression) {
                 .identity => res.request.compression = .none,
-                .compress => return error.CompressionNotSupported,
+                .compress, .@"x-compress" => return error.CompressionNotSupported,
                 .deflate => res.request.compression = .{
                     .deflate = std.compress.zlib.decompressStream(res.allocator, res.transferReader()) catch return error.CompressionInitializationFailed,
                 },
-                .gzip => res.request.compression = .{
+                .gzip, .@"x-gzip" => res.request.compression = .{
                     .gzip = std.compress.gzip.decompress(res.allocator, res.transferReader()) catch return error.CompressionInitializationFailed,
                 },
                 .zstd => res.request.compression = .{
                     .zstd = std.compress.zstd.decompressStream(res.allocator, res.transferReader()),
                 },
-            };
+            }
         }
     }
 
@@ -754,7 +751,7 @@ test "HTTP server handles a chunked transfer coding request" {
             defer _ = res.reset();
             try res.wait();
 
-            try expect(res.request.transfer_encoding.? == .chunked);
+            try expect(res.request.transfer_encoding == .chunked);
 
             const server_body: []const u8 = "message from server!\n";
             res.transfer_encoding = .{ .content_length = server_body.len };
lib/std/http.zig
@@ -289,14 +289,17 @@ pub const Status = enum(u10) {
 
 pub const TransferEncoding = enum {
     chunked,
+    none,
     // compression is intentionally omitted here, as std.http.Client stores it as content-encoding
 };
 
 pub const ContentEncoding = enum {
     identity,
     compress,
+    @"x-compress",
     deflate,
     gzip,
+    @"x-gzip",
     zstd,
 };