authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-04-17 19:37:24-05:00
committergravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-04-18 10:28:53-05:00
loga23c8662b41cf6954d8294ea316fb28a88481a7e
treefb11f4e09ca8262a36a08c850eb055708f53512b
parente65cbff94d2d1d7de6f8615123c62e86f37461af
signaturelock-open Commit is signed but in an unrecognized format.

std.http: pass Method to request directly, parse trailing headers


3 files changed, 31 insertions(+), 13 deletions(-)

lib/std/http/Client.zig+20-12
...@@ -527,7 +527,7 @@ pub const Request = struct {...@@ -527,7 +527,7 @@ pub const Request = struct {
527 pub const StartError = BufferedConnection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding };527 pub const StartError = BufferedConnection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding };
528528
529 /// Send the request to the server.529 /// Send the request to the server.
530 pub fn start(req: *Request, uri: Uri) StartError!void {530 pub fn start(req: *Request) StartError!void {
531 var buffered = std.io.bufferedWriter(req.connection.data.buffered.writer());531 var buffered = std.io.bufferedWriter(req.connection.data.buffered.writer());
532 const w = buffered.writer();532 const w = buffered.writer();
533533
...@@ -535,14 +535,14 @@ pub const Request = struct {...@@ -535,14 +535,14 @@ pub const Request = struct {
535 try w.writeByte(' ');535 try w.writeByte(' ');
536536
537 if (req.method == .CONNECT) {537 if (req.method == .CONNECT) {
538 try w.writeAll(uri.host.?);538 try w.writeAll(req.uri.host.?);
539 try w.writeByte(':');539 try w.writeByte(':');
540 try w.print("{}", .{uri.port.?});540 try w.print("{}", .{req.uri.port.?});
541 } else if (req.connection.data.proxied) {541 } else if (req.connection.data.proxied) {
542 // proxied connections require the full uri542 // proxied connections require the full uri
543 try w.print("{+/}", .{uri});543 try w.print("{+/}", .{req.uri});
544 } else {544 } else {
545 try w.print("{/}", .{uri});545 try w.print("{/}", .{req.uri});
546 }546 }
547547
548 try w.writeByte(' ');548 try w.writeByte(' ');
...@@ -551,7 +551,7 @@ pub const Request = struct {...@@ -551,7 +551,7 @@ pub const Request = struct {
551551
552 if (!req.headers.contains("host")) {552 if (!req.headers.contains("host")) {
553 try w.writeAll("Host: ");553 try w.writeAll("Host: ");
554 try w.writeAll(uri.host.?);554 try w.writeAll(req.uri.host.?);
555 try w.writeAll("\r\n");555 try w.writeAll("\r\n");
556 }556 }
557557
...@@ -704,8 +704,7 @@ pub const Request = struct {...@@ -704,8 +704,7 @@ pub const Request = struct {
704 req.arena.deinit();704 req.arena.deinit();
705 req.arena = new_arena;705 req.arena = new_arena;
706706
707 const new_req = try req.client.request(resolved_url, req.headers, .{707 const new_req = try req.client.request(req.method, resolved_url, req.headers, .{
708 .method = req.method,
709 .version = req.version,708 .version = req.version,
710 .max_redirects = req.redirects_left - 1,709 .max_redirects = req.redirects_left - 1,
711 .header_strategy = if (req.response.parser.header_bytes_owned) .{710 .header_strategy = if (req.response.parser.header_bytes_owned) .{
...@@ -738,7 +737,7 @@ pub const Request = struct {...@@ -738,7 +737,7 @@ pub const Request = struct {
738 }737 }
739 }738 }
740739
741 pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || error{DecompressionFailure};740 pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || error{ DecompressionFailure, InvalidTrailers };
742741
743 pub const Reader = std.io.Reader(*Request, ReadError, read);742 pub const Reader = std.io.Reader(*Request, ReadError, read);
744743
...@@ -756,12 +755,22 @@ pub const Request = struct {...@@ -756,12 +755,22 @@ pub const Request = struct {
756 };755 };
757756
758 if (out_index == 0) {757 if (out_index == 0) {
758 const has_trail = !req.response.parser.state.isContent();
759
759 while (!req.response.parser.state.isContent()) { // read trailing headers760 while (!req.response.parser.state.isContent()) { // read trailing headers
760 try req.connection.data.buffered.fill();761 try req.connection.data.buffered.fill();
761762
762 const nchecked = try req.response.parser.checkCompleteHead(req.client.allocator, req.connection.data.buffered.peek());763 const nchecked = try req.response.parser.checkCompleteHead(req.client.allocator, req.connection.data.buffered.peek());
763 req.connection.data.buffered.clear(@intCast(u16, nchecked));764 req.connection.data.buffered.clear(@intCast(u16, nchecked));
764 }765 }
766
767 if (has_trail) {
768 req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false };
769
770 // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.
771 // This will *only* fail for a malformed trailer.
772 req.response.parse(req.response.parser.header_bytes.items) catch return error.InvalidTrailers;
773 }
765 }774 }
766775
767 return out_index;776 return out_index;
...@@ -943,7 +952,6 @@ pub const RequestError = ConnectUnproxiedError || ConnectErrorPartial || Request...@@ -943,7 +952,6 @@ pub const RequestError = ConnectUnproxiedError || ConnectErrorPartial || Request
943};952};
944953
945pub const Options = struct {954pub const Options = struct {
946 method: http.Method = .GET,
947 version: http.Version = .@"HTTP/1.1",955 version: http.Version = .@"HTTP/1.1",
948956
949 handle_redirects: bool = true,957 handle_redirects: bool = true,
...@@ -976,7 +984,7 @@ pub const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{...@@ -976,7 +984,7 @@ pub const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{
976984
977/// Form and send a http request to a server.985/// Form and send a http request to a server.
978/// This function is threadsafe.986/// This function is threadsafe.
979pub fn request(client: *Client, uri: Uri, headers: http.Headers, options: Options) RequestError!Request {987pub fn request(client: *Client, method: http.Method, uri: Uri, headers: http.Headers, options: Options) RequestError!Request {
980 const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme;988 const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme;
981989
982 const port: u16 = uri.port orelse switch (protocol) {990 const port: u16 = uri.port orelse switch (protocol) {
...@@ -1003,7 +1011,7 @@ pub fn request(client: *Client, uri: Uri, headers: http.Headers, options: Option...@@ -1003,7 +1011,7 @@ pub fn request(client: *Client, uri: Uri, headers: http.Headers, options: Option
1003 .client = client,1011 .client = client,
1004 .connection = conn,1012 .connection = conn,
1005 .headers = headers,1013 .headers = headers,
1006 .method = options.method,1014 .method = method,
1007 .version = options.version,1015 .version = options.version,
1008 .redirects_left = options.max_redirects,1016 .redirects_left = options.max_redirects,
1009 .handle_redirects = options.handle_redirects,1017 .handle_redirects = options.handle_redirects,
lib/std/http/Server.zig+10
...@@ -518,12 +518,22 @@ pub const Response = struct {...@@ -518,12 +518,22 @@ pub const Response = struct {
518 };518 };
519519
520 if (out_index == 0) {520 if (out_index == 0) {
521 const has_trail = !res.request.parser.state.isContent();
522
521 while (!res.request.parser.state.isContent()) { // read trailing headers523 while (!res.request.parser.state.isContent()) { // read trailing headers
522 try res.connection.fill();524 try res.connection.fill();
523525
524 const nchecked = try res.request.parser.checkCompleteHead(res.server.allocator, res.connection.peek());526 const nchecked = try res.request.parser.checkCompleteHead(res.server.allocator, res.connection.peek());
525 res.connection.clear(@intCast(u16, nchecked));527 res.connection.clear(@intCast(u16, nchecked));
526 }528 }
529
530 if (has_trail) {
531 res.request.headers = http.Headers{ .allocator = res.server.allocator, .owned = false };
532
533 // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.
534 // This will *only* fail for a malformed trailer.
535 res.request.parse(res.request.parser.header_bytes.items) catch return error.InvalidTrailers;
536 }
527 }537 }
528538
529 return out_index;539 return out_index;
src/Package.zig+1-1
...@@ -482,7 +482,7 @@ fn fetchAndUnpack(...@@ -482,7 +482,7 @@ fn fetchAndUnpack(
482 var h = std.http.Headers{ .allocator = gpa };482 var h = std.http.Headers{ .allocator = gpa };
483 defer h.deinit();483 defer h.deinit();
484484
485 var req = try http_client.request(uri, h, .{ .method = .GET });485 var req = try http_client.request(.GET, uri, h, .{});
486 defer req.deinit();486 defer req.deinit();
487487
488 try req.start();488 try req.start();