| ... | @@ -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 }; |
| 528 | | 528 | |
| 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(); |
| 533 | | 533 | |
| ... | @@ -535,14 +535,14 @@ pub const Request = struct { | ... | @@ -535,14 +535,14 @@ pub const Request = struct { |
| 535 | try w.writeByte(' '); | 535 | try w.writeByte(' '); |
| 536 | | 536 | |
| 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 uri | 542 | // 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 | } |
| 547 | | 547 | |
| 548 | try w.writeByte(' '); | 548 | try w.writeByte(' '); |
| ... | @@ -551,7 +551,7 @@ pub const Request = struct { | ... | @@ -551,7 +551,7 @@ pub const Request = struct { |
| 551 | | 551 | |
| 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 | } |
| 557 | | 557 | |
| ... | @@ -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; |
| 706 | | 706 | |
| 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 | } |
| 740 | | 739 | |
| 741 | pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || error{DecompressionFailure}; | 740 | pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || error{ DecompressionFailure, InvalidTrailers }; |
| 742 | | 741 | |
| 743 | pub const Reader = std.io.Reader(*Request, ReadError, read); | 742 | pub const Reader = std.io.Reader(*Request, ReadError, read); |
| 744 | | 743 | |
| ... | @@ -756,12 +755,22 @@ pub const Request = struct { | ... | @@ -756,12 +755,22 @@ pub const Request = struct { |
| 756 | }; | 755 | }; |
| 757 | | 756 | |
| 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 headers | 760 | while (!req.response.parser.state.isContent()) { // read trailing headers |
| 760 | try req.connection.data.buffered.fill(); | 761 | try req.connection.data.buffered.fill(); |
| 761 | | 762 | |
| 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 | } |
| 766 | | 775 | |
| 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 | }; |
| 944 | | 953 | |
| 945 | pub const Options = struct { | 954 | pub const Options = struct { |
| 946 | method: http.Method = .GET, | | |
| 947 | version: http.Version = .@"HTTP/1.1", | 955 | version: http.Version = .@"HTTP/1.1", |
| 948 | | 956 | |
| 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, .{ |
| 976 | | 984 | |
| 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. |
| 979 | pub fn request(client: *Client, uri: Uri, headers: http.Headers, options: Options) RequestError!Request { | 987 | pub 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; |
| 981 | | 989 | |
| 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, |