| ... | @@ -15,7 +15,7 @@ const proto = @import("protocol.zig"); | ... | @@ -15,7 +15,7 @@ const proto = @import("protocol.zig"); |
| 15 | pub const disable_tls = std.options.http_disable_tls; | 15 | pub const disable_tls = std.options.http_disable_tls; |
| 16 | | 16 | |
| 17 | allocator: Allocator, | 17 | allocator: Allocator, |
| 18 | ca_bundle: std.crypto.Certificate.Bundle = .{}, | 18 | ca_bundle: if (disable_tls) void else std.crypto.Certificate.Bundle = if (disable_tls) {} else .{}, |
| 19 | ca_bundle_mutex: std.Thread.Mutex = .{}, | 19 | ca_bundle_mutex: std.Thread.Mutex = .{}, |
| 20 | | 20 | |
| 21 | /// When this is `true`, the next time this client performs an HTTPS request, | 21 | /// When this is `true`, the next time this client performs an HTTPS request, |
| ... | @@ -386,7 +386,7 @@ pub const Response = struct { | ... | @@ -386,7 +386,7 @@ pub const Response = struct { |
| 386 | }; | 386 | }; |
| 387 | | 387 | |
| 388 | pub fn parse(res: *Response, bytes: []const u8, trailing: bool) ParseError!void { | 388 | pub fn parse(res: *Response, bytes: []const u8, trailing: bool) ParseError!void { |
| 389 | var it = mem.tokenizeAny(u8, bytes[0 .. bytes.len - 4], "\r\n"); | 389 | var it = mem.tokenizeAny(u8, bytes, "\r\n"); |
| 390 | | 390 | |
| 391 | const first_line = it.next() orelse return error.HttpHeadersInvalid; | 391 | const first_line = it.next() orelse return error.HttpHeadersInvalid; |
| 392 | if (first_line.len < 12) | 392 | if (first_line.len < 12) |
| ... | @@ -405,6 +405,8 @@ pub const Response = struct { | ... | @@ -405,6 +405,8 @@ pub const Response = struct { |
| 405 | res.status = status; | 405 | res.status = status; |
| 406 | res.reason = reason; | 406 | res.reason = reason; |
| 407 | | 407 | |
| | 408 | res.headers.clearRetainingCapacity(); |
| | 409 | |
| 408 | while (it.next()) |line| { | 410 | while (it.next()) |line| { |
| 409 | if (line.len == 0) return error.HttpHeadersInvalid; | 411 | if (line.len == 0) return error.HttpHeadersInvalid; |
| 410 | switch (line[0]) { | 412 | switch (line[0]) { |
| ... | @@ -525,6 +527,7 @@ pub const Request = struct { | ... | @@ -525,6 +527,7 @@ pub const Request = struct { |
| 525 | | 527 | |
| 526 | redirects_left: u32, | 528 | redirects_left: u32, |
| 527 | handle_redirects: bool, | 529 | handle_redirects: bool, |
| | 530 | handle_continue: bool, |
| 528 | | 531 | |
| 529 | response: Response, | 532 | response: Response, |
| 530 | | 533 | |
| ... | @@ -758,6 +761,10 @@ pub const Request = struct { | ... | @@ -758,6 +761,10 @@ pub const Request = struct { |
| 758 | if (req.response.status == .@"continue") { | 761 | if (req.response.status == .@"continue") { |
| 759 | req.response.parser.done = true; // we're done parsing the continue response, reset to prepare for the real response | 762 | req.response.parser.done = true; // we're done parsing the continue response, reset to prepare for the real response |
| 760 | req.response.parser.reset(); | 763 | req.response.parser.reset(); |
| | 764 | |
| | 765 | if (req.handle_continue) |
| | 766 | continue; |
| | 767 | |
| 761 | break; | 768 | break; |
| 762 | } | 769 | } |
| 763 | | 770 | |
| ... | @@ -897,8 +904,6 @@ pub const Request = struct { | ... | @@ -897,8 +904,6 @@ pub const Request = struct { |
| 897 | } | 904 | } |
| 898 | | 905 | |
| 899 | if (has_trail) { | 906 | if (has_trail) { |
| 900 | req.response.headers.clearRetainingCapacity(); | | |
| 901 | | | |
| 902 | // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error. | 907 | // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error. |
| 903 | // This will *only* fail for a malformed trailer. | 908 | // This will *only* fail for a malformed trailer. |
| 904 | req.response.parse(req.response.parser.header_bytes.items, true) catch return error.InvalidTrailers; | 909 | req.response.parse(req.response.parser.header_bytes.items, true) catch return error.InvalidTrailers; |
| ... | @@ -999,7 +1004,9 @@ pub fn deinit(client: *Client) void { | ... | @@ -999,7 +1004,9 @@ pub fn deinit(client: *Client) void { |
| 999 | proxy.headers.deinit(); | 1004 | proxy.headers.deinit(); |
| 1000 | } | 1005 | } |
| 1001 | | 1006 | |
| 1002 | client.ca_bundle.deinit(client.allocator); | 1007 | if (!disable_tls) |
| | 1008 | client.ca_bundle.deinit(client.allocator); |
| | 1009 | |
| 1003 | client.* = undefined; | 1010 | client.* = undefined; |
| 1004 | } | 1011 | } |
| 1005 | | 1012 | |
| ... | @@ -1315,6 +1322,14 @@ pub const RequestError = ConnectTcpError || ConnectErrorPartial || Request.SendE | ... | @@ -1315,6 +1322,14 @@ pub const RequestError = ConnectTcpError || ConnectErrorPartial || Request.SendE |
| 1315 | pub const RequestOptions = struct { | 1322 | pub const RequestOptions = struct { |
| 1316 | version: http.Version = .@"HTTP/1.1", | 1323 | version: http.Version = .@"HTTP/1.1", |
| 1317 | | 1324 | |
| | 1325 | /// Automatically ignore 100 Continue responses. This assumes you don't care, and will have sent the body before you |
| | 1326 | /// wait for the response. |
| | 1327 | /// |
| | 1328 | /// If this is not the case AND you know the server will send a 100 Continue, set this to false and wait for a |
| | 1329 | /// response before sending the body. If you wait AND the server does not send a 100 Continue before you finish the |
| | 1330 | /// request, then the request *will* deadlock. |
| | 1331 | handle_continue: bool = true, |
| | 1332 | |
| 1318 | handle_redirects: bool = true, | 1333 | handle_redirects: bool = true, |
| 1319 | max_redirects: u32 = 3, | 1334 | max_redirects: u32 = 3, |
| 1320 | header_strategy: StorageStrategy = .{ .dynamic = 16 * 1024 }, | 1335 | header_strategy: StorageStrategy = .{ .dynamic = 16 * 1024 }, |
| ... | @@ -1361,6 +1376,8 @@ pub fn open(client: *Client, method: http.Method, uri: Uri, headers: http.Header | ... | @@ -1361,6 +1376,8 @@ pub fn open(client: *Client, method: http.Method, uri: Uri, headers: http.Header |
| 1361 | const host = uri.host orelse return error.UriMissingHost; | 1376 | const host = uri.host orelse return error.UriMissingHost; |
| 1362 | | 1377 | |
| 1363 | if (protocol == .tls and @atomicLoad(bool, &client.next_https_rescan_certs, .Acquire)) { | 1378 | if (protocol == .tls and @atomicLoad(bool, &client.next_https_rescan_certs, .Acquire)) { |
| | 1379 | if (disable_tls) unreachable; |
| | 1380 | |
| 1364 | client.ca_bundle_mutex.lock(); | 1381 | client.ca_bundle_mutex.lock(); |
| 1365 | defer client.ca_bundle_mutex.unlock(); | 1382 | defer client.ca_bundle_mutex.unlock(); |
| 1366 | | 1383 | |
| ... | @@ -1381,6 +1398,7 @@ pub fn open(client: *Client, method: http.Method, uri: Uri, headers: http.Header | ... | @@ -1381,6 +1398,7 @@ pub fn open(client: *Client, method: http.Method, uri: Uri, headers: http.Header |
| 1381 | .version = options.version, | 1398 | .version = options.version, |
| 1382 | .redirects_left = options.max_redirects, | 1399 | .redirects_left = options.max_redirects, |
| 1383 | .handle_redirects = options.handle_redirects, | 1400 | .handle_redirects = options.handle_redirects, |
| | 1401 | .handle_continue = options.handle_continue, |
| 1384 | .response = .{ | 1402 | .response = .{ |
| 1385 | .status = undefined, | 1403 | .status = undefined, |
| 1386 | .reason = undefined, | 1404 | .reason = undefined, |