authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-08-27 16:36:24-05:00
committergravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-08-30 13:05:45-05:00
log4689d93cb204a4143770105200eb65dcdca5d7a0
treed02590b41be334c650d01dfb1c6436bc1cb064b6
parentddef683fcb321882cc912090a25c12bc8705ff55
signaturelock-open Commit is signed but in an unrecognized format.

std.http: allow for arbitrary http methods


4 files changed, 49 insertions(+), 22 deletions(-)

lib/std/http.zig+37-12
......@@ -1,3 +1,5 @@
1const std = @import("std.zig");
2
13pub const Client = @import("http/Client.zig");
24pub const Server = @import("http/Server.zig");
35pub const protocol = @import("http/protocol.zig");
......@@ -14,16 +16,36 @@ pub const Version = enum {
1416/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
1517/// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definition
1618/// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH
17pub const Method = enum {
18 GET,
19 HEAD,
20 POST,
21 PUT,
22 DELETE,
23 CONNECT,
24 OPTIONS,
25 TRACE,
26 PATCH,
19pub const Method = enum(u64) { // TODO: should be u192 or u256, but neither is supported by the C backend, and therefore cannot pass CI
20 GET = parse("GET"),
21 HEAD = parse("HEAD"),
22 POST = parse("POST"),
23 PUT = parse("PUT"),
24 DELETE = parse("DELETE"),
25 CONNECT = parse("CONNECT"),
26 OPTIONS = parse("OPTIONS"),
27 TRACE = parse("TRACE"),
28 PATCH = parse("PATCH"),
29
30 _,
31
32 /// Converts `s` into a type that may be used as a `Method` field.
33 /// Asserts that `s` is 24 or fewer bytes.
34 pub fn parse(s: []const u8) u64 {
35 var x: u64 = 0;
36 @memcpy(std.mem.asBytes(&x)[0..s.len], s);
37 return x;
38 }
39
40 pub fn write(self: Method, w: anytype) !void {
41 const bytes = std.mem.asBytes(&@intFromEnum(self));
42 const str = std.mem.sliceTo(bytes, 0);
43 try w.writeAll(str);
44 }
45
46 pub fn format(value: Method, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
47 return try value.write(writer);
48 }
2749
2850 /// Returns true if a request of this method is allowed to have a body
2951 /// Actual behavior from servers may vary and should still be checked
......@@ -31,6 +53,7 @@ pub const Method = enum {
3153 return switch (self) {
3254 .POST, .PUT, .PATCH => true,
3355 .GET, .HEAD, .DELETE, .CONNECT, .OPTIONS, .TRACE => false,
56 else => true,
3457 };
3558 }
3659
......@@ -40,6 +63,7 @@ pub const Method = enum {
4063 return switch (self) {
4164 .GET, .POST, .DELETE, .CONNECT, .OPTIONS, .PATCH => true,
4265 .HEAD, .PUT, .TRACE => false,
66 else => true,
4367 };
4468 }
4569
......@@ -50,6 +74,7 @@ pub const Method = enum {
5074 return switch (self) {
5175 .GET, .HEAD, .OPTIONS, .TRACE => true,
5276 .POST, .PUT, .DELETE, .CONNECT, .PATCH => false,
77 else => false,
5378 };
5479 }
5580
......@@ -60,6 +85,7 @@ pub const Method = enum {
6085 return switch (self) {
6186 .GET, .HEAD, .PUT, .DELETE, .OPTIONS, .TRACE => true,
6287 .CONNECT, .POST, .PATCH => false,
88 else => false,
6389 };
6490 }
6591
......@@ -70,6 +96,7 @@ pub const Method = enum {
7096 return switch (self) {
7197 .GET, .HEAD => true,
7298 .POST, .PUT, .DELETE, .CONNECT, .OPTIONS, .TRACE, .PATCH => false,
99 else => false,
73100 };
74101 }
75102};
......@@ -269,8 +296,6 @@ pub const Connection = enum {
269296 close,
270297};
271298
272const std = @import("std.zig");
273
274299test {
275300 _ = Client;
276301 _ = Method;
lib/std/http/Client.zig+5-5
......@@ -545,7 +545,7 @@ pub const Request = struct {
545545 var buffered = std.io.bufferedWriter(req.connection.?.data.writer());
546546 const w = buffered.writer();
547547
548 try w.writeAll(@tagName(req.method));
548 try req.method.write(w);
549549 try w.writeByte(' ');
550550
551551 if (req.method == .CONNECT) {
......@@ -627,15 +627,15 @@ pub const Request = struct {
627627 try buffered.flush();
628628 }
629629
630 pub const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;
630 const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;
631631
632 pub const TransferReader = std.io.Reader(*Request, TransferReadError, transferRead);
632 const TransferReader = std.io.Reader(*Request, TransferReadError, transferRead);
633633
634 pub fn transferReader(req: *Request) TransferReader {
634 fn transferReader(req: *Request) TransferReader {
635635 return .{ .context = req };
636636 }
637637
638 pub fn transferRead(req: *Request, buf: []u8) TransferReadError!usize {
638 fn transferRead(req: *Request, buf: []u8) TransferReadError!usize {
639639 if (req.response.parser.done) return 0;
640640
641641 var index: usize = 0;
lib/std/http/Server.zig+6-4
......@@ -185,8 +185,10 @@ pub const Request = struct {
185185 return error.HttpHeadersInvalid;
186186
187187 const method_end = mem.indexOfScalar(u8, first_line, ' ') orelse return error.HttpHeadersInvalid;
188 if (method_end > 24) return error.HttpHeadersInvalid;
189
188190 const method_str = first_line[0..method_end];
189 const method = std.meta.stringToEnum(http.Method, method_str) orelse return error.UnknownHttpMethod;
191 const method: http.Method = @enumFromInt(http.Method.parse(method_str));
190192
191193 const version_start = mem.lastIndexOfScalar(u8, first_line, ' ') orelse return error.HttpHeadersInvalid;
192194 if (version_start == method_end) return error.HttpHeadersInvalid;
......@@ -467,11 +469,11 @@ pub const Response = struct {
467469 try buffered.flush();
468470 }
469471
470 pub const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;
472 const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;
471473
472 pub const TransferReader = std.io.Reader(*Response, TransferReadError, transferRead);
474 const TransferReader = std.io.Reader(*Response, TransferReadError, transferRead);
473475
474 pub fn transferReader(res: *Response) TransferReader {
476 fn transferReader(res: *Response) TransferReader {
475477 return .{ .context = res };
476478 }
477479
test/standalone/http.zig+1-1
......@@ -20,7 +20,7 @@ var server: Server = undefined;
2020fn handleRequest(res: *Server.Response) !void {
2121 const log = std.log.scoped(.server);
2222
23 log.info("{s} {s} {s}", .{ @tagName(res.request.method), @tagName(res.request.version), res.request.target });
23 log.info("{} {s} {s}", .{ res.request.method, @tagName(res.request.version), res.request.target });
2424
2525 if (res.request.headers.contains("expect")) {
2626 if (mem.eql(u8, res.request.headers.getFirstValue("expect").?, "100-continue")) {