| ... | @@ -116,7 +116,7 @@ pub const ConnectionPool = struct { | ... | @@ -116,7 +116,7 @@ pub const ConnectionPool = struct { |
| 116 | /// `allocator` must be the same one used to create `connection`. | 116 | /// `allocator` must be the same one used to create `connection`. |
| 117 | /// | 117 | /// |
| 118 | /// Threadsafe. | 118 | /// Threadsafe. |
| 119 | pub fn release(pool: *ConnectionPool, allocator: Allocator, connection: *Connection) void { | 119 | pub fn release(pool: *ConnectionPool, connection: *Connection) void { |
| 120 | if (connection.closing) return connection.destroy(); | 120 | if (connection.closing) return connection.destroy(); |
| 121 | | 121 | |
| 122 | pool.mutex.lock(); | 122 | pool.mutex.lock(); |
| ... | @@ -130,8 +130,7 @@ pub const ConnectionPool = struct { | ... | @@ -130,8 +130,7 @@ pub const ConnectionPool = struct { |
| 130 | const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); | 130 | const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); |
| 131 | pool.free_len -= 1; | 131 | pool.free_len -= 1; |
| 132 | | 132 | |
| 133 | popped.close(allocator); | 133 | popped.destroy(); |
| 134 | allocator.destroy(popped); | | |
| 135 | } | 134 | } |
| 136 | | 135 | |
| 137 | if (connection.proxied) { | 136 | if (connection.proxied) { |
| ... | @@ -434,20 +433,6 @@ pub const Connection = struct { | ... | @@ -434,20 +433,6 @@ pub const Connection = struct { |
| 434 | } | 433 | } |
| 435 | }; | 434 | }; |
| 436 | | 435 | |
| 437 | /// The decompressor for response messages. | | |
| 438 | pub const Compression = union(enum) { | | |
| 439 | pub const DeflateDecompressor = std.compress.zlib.Decompressor; | | |
| 440 | pub const GzipDecompressor = std.compress.gzip.Decompressor; | | |
| 441 | // https://github.com/ziglang/zig/issues/18937 | | |
| 442 | //pub const ZstdDecompressor = std.compress.zstd.DecompressStream(.{}); | | |
| 443 | | | |
| 444 | deflate: DeflateDecompressor, | | |
| 445 | gzip: GzipDecompressor, | | |
| 446 | // https://github.com/ziglang/zig/issues/18937 | | |
| 447 | //zstd: ZstdDecompressor, | | |
| 448 | none: void, | | |
| 449 | }; | | |
| 450 | | | |
| 451 | pub const Response = struct { | 436 | pub const Response = struct { |
| 452 | request: *Request, | 437 | request: *Request, |
| 453 | /// Pointers in this struct are invalidated with the next call to | 438 | /// Pointers in this struct are invalidated with the next call to |
| ... | @@ -469,9 +454,7 @@ pub const Response = struct { | ... | @@ -469,9 +454,7 @@ pub const Response = struct { |
| 469 | content_length: ?u64 = null, | 454 | content_length: ?u64 = null, |
| 470 | | 455 | |
| 471 | transfer_encoding: http.TransferEncoding = .none, | 456 | transfer_encoding: http.TransferEncoding = .none, |
| 472 | transfer_compression: http.ContentEncoding = .identity, | 457 | content_encoding: http.ContentEncoding = .identity, |
| 473 | | | |
| 474 | compression: Compression = .none, | | |
| 475 | | 458 | |
| 476 | pub const ParseError = error{ | 459 | pub const ParseError = error{ |
| 477 | HttpHeadersInvalid, | 460 | HttpHeadersInvalid, |
| ... | @@ -554,8 +537,8 @@ pub const Response = struct { | ... | @@ -554,8 +537,8 @@ pub const Response = struct { |
| 554 | const trimmed_second = mem.trim(u8, second, " "); | 537 | const trimmed_second = mem.trim(u8, second, " "); |
| 555 | | 538 | |
| 556 | if (std.meta.stringToEnum(http.ContentEncoding, trimmed_second)) |transfer| { | 539 | if (std.meta.stringToEnum(http.ContentEncoding, trimmed_second)) |transfer| { |
| 557 | if (res.transfer_compression != .identity) return error.HttpHeadersInvalid; // double compression is not supported | 540 | if (res.content_encoding != .identity) return error.HttpHeadersInvalid; // double compression is not supported |
| 558 | res.transfer_compression = transfer; | 541 | res.content_encoding = transfer; |
| 559 | } else { | 542 | } else { |
| 560 | return error.HttpTransferEncodingUnsupported; | 543 | return error.HttpTransferEncodingUnsupported; |
| 561 | } | 544 | } |
| ... | @@ -569,12 +552,12 @@ pub const Response = struct { | ... | @@ -569,12 +552,12 @@ pub const Response = struct { |
| 569 | | 552 | |
| 570 | res.content_length = content_length; | 553 | res.content_length = content_length; |
| 571 | } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) { | 554 | } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) { |
| 572 | if (res.transfer_compression != .identity) return error.HttpHeadersInvalid; | 555 | if (res.content_encoding != .identity) return error.HttpHeadersInvalid; |
| 573 | | 556 | |
| 574 | const trimmed = mem.trim(u8, header_value, " "); | 557 | const trimmed = mem.trim(u8, header_value, " "); |
| 575 | | 558 | |
| 576 | if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { | 559 | if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { |
| 577 | res.transfer_compression = ce; | 560 | res.content_encoding = ce; |
| 578 | } else { | 561 | } else { |
| 579 | return error.HttpTransferEncodingUnsupported; | 562 | return error.HttpTransferEncodingUnsupported; |
| 580 | } | 563 | } |
| ... | @@ -592,7 +575,7 @@ pub const Response = struct { | ... | @@ -592,7 +575,7 @@ pub const Response = struct { |
| 592 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ | 575 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ |
| 593 | "connectioN:\t keep-alive \r\n\r\n"; | 576 | "connectioN:\t keep-alive \r\n\r\n"; |
| 594 | | 577 | |
| 595 | const head = Head.parse(response_bytes); | 578 | const head = try Head.parse(response_bytes); |
| 596 | | 579 | |
| 597 | try testing.expectEqual(.@"HTTP/1.1", head.version); | 580 | try testing.expectEqual(.@"HTTP/1.1", head.version); |
| 598 | try testing.expectEqualStrings("OK", head.reason); | 581 | try testing.expectEqualStrings("OK", head.reason); |
| ... | @@ -605,7 +588,7 @@ pub const Response = struct { | ... | @@ -605,7 +588,7 @@ pub const Response = struct { |
| 605 | try testing.expectEqual(true, head.keep_alive); | 588 | try testing.expectEqual(true, head.keep_alive); |
| 606 | try testing.expectEqual(10, head.content_length.?); | 589 | try testing.expectEqual(10, head.content_length.?); |
| 607 | try testing.expectEqual(.chunked, head.transfer_encoding); | 590 | try testing.expectEqual(.chunked, head.transfer_encoding); |
| 608 | try testing.expectEqual(.deflate, head.transfer_compression); | 591 | try testing.expectEqual(.deflate, head.content_encoding); |
| 609 | } | 592 | } |
| 610 | | 593 | |
| 611 | pub fn iterateHeaders(h: Head) http.HeaderIterator { | 594 | pub fn iterateHeaders(h: Head) http.HeaderIterator { |
| ... | @@ -621,19 +604,8 @@ pub const Response = struct { | ... | @@ -621,19 +604,8 @@ pub const Response = struct { |
| 621 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ | 604 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ |
| 622 | "connectioN:\t keep-alive \r\n\r\n"; | 605 | "connectioN:\t keep-alive \r\n\r\n"; |
| 623 | | 606 | |
| 624 | var header_buffer: [1024]u8 = undefined; | 607 | const head = try Head.parse(response_bytes); |
| 625 | var res = Response{ | 608 | var it = head.iterateHeaders(); |
| 626 | .status = undefined, | | |
| 627 | .reason = undefined, | | |
| 628 | .version = undefined, | | |
| 629 | .keep_alive = false, | | |
| 630 | .parser = .init(&header_buffer), | | |
| 631 | }; | | |
| 632 | | | |
| 633 | @memcpy(header_buffer[0..response_bytes.len], response_bytes); | | |
| 634 | res.parser.header_bytes_len = response_bytes.len; | | |
| 635 | | | |
| 636 | var it = res.iterateHeaders(); | | |
| 637 | { | 609 | { |
| 638 | const header = it.next().?; | 610 | const header = it.next().?; |
| 639 | try testing.expectEqualStrings("LOcation", header.name); | 611 | try testing.expectEqualStrings("LOcation", header.name); |
| ... | @@ -695,7 +667,7 @@ pub const Response = struct { | ... | @@ -695,7 +667,7 @@ pub const Response = struct { |
| 695 | /// Asserts that this function is only called once. | 667 | /// Asserts that this function is only called once. |
| 696 | pub fn reader(response: *Response) std.io.Reader { | 668 | pub fn reader(response: *Response) std.io.Reader { |
| 697 | const head = &response.head; | 669 | const head = &response.head; |
| 698 | return response.request.reader.interface(head.transfer_encoding, head.content_length); | 670 | return response.request.reader.interface(head.transfer_encoding, head.content_length, head.content_encoding); |
| 699 | } | 671 | } |
| 700 | }; | 672 | }; |
| 701 | | 673 | |
| ... | @@ -778,16 +750,16 @@ pub const Request = struct { | ... | @@ -778,16 +750,16 @@ pub const Request = struct { |
| 778 | } | 750 | } |
| 779 | }; | 751 | }; |
| 780 | | 752 | |
| 781 | /// Frees all resources associated with the request. | 753 | /// Returns the request's `Connection` back to the pool of the `Client`. |
| 782 | pub fn deinit(req: *Request) void { | 754 | pub fn deinit(r: *Request) void { |
| 783 | if (req.connection) |connection| { | 755 | if (r.connection) |connection| { |
| 784 | if (!req.response.parser.done) { | 756 | if (r.reader.state != .ready) { |
| 785 | // If the response wasn't fully read, then we need to close the connection. | 757 | // Connection cannot be reused. |
| 786 | connection.closing = true; | 758 | connection.closing = true; |
| 787 | } | 759 | } |
| 788 | req.client.connection_pool.release(req.client.allocator, connection); | 760 | r.client.connection_pool.release(connection); |
| 789 | } | 761 | } |
| 790 | req.* = undefined; | 762 | r.* = undefined; |
| 791 | } | 763 | } |
| 792 | | 764 | |
| 793 | /// Sends and flushes a complete request as only HTTP head, no body. | 765 | /// Sends and flushes a complete request as only HTTP head, no body. |
| ... | @@ -810,12 +782,12 @@ pub const Request = struct { | ... | @@ -810,12 +782,12 @@ pub const Request = struct { |
| 810 | try sendHead(r); | 782 | try sendHead(r); |
| 811 | return .{ | 783 | return .{ |
| 812 | .http_protocol_output = &r.connection.?.writer, | 784 | .http_protocol_output = &r.connection.?.writer, |
| 813 | .transfer_encoding = if (r.transfer_encoding) |te| switch (te) { | 785 | .state = switch (r.transfer_encoding) { |
| 814 | .chunked => .{ .chunked = .init }, | 786 | .chunked => .{ .chunked = .init }, |
| 815 | .content_length => |len| .{ .content_length = len }, | 787 | .content_length => |len| .{ .content_length = len }, |
| 816 | .none => .none, | 788 | .none => .none, |
| 817 | } else .{ .chunked = .init }, | 789 | }, |
| 818 | .elide_body = false, | 790 | .elide = false, |
| 819 | }; | 791 | }; |
| 820 | } | 792 | } |
| 821 | | 793 | |
| ... | @@ -912,7 +884,7 @@ pub const Request = struct { | ... | @@ -912,7 +884,7 @@ pub const Request = struct { |
| 912 | try w.writeAll("\r\n"); | 884 | try w.writeAll("\r\n"); |
| 913 | } | 885 | } |
| 914 | | 886 | |
| 915 | pub const ReceiveHeadError = http.Reader.HeadError || error{ | 887 | pub const ReceiveHeadError = std.io.Writer.Error || http.Reader.HeadError || error{ |
| 916 | /// Server sent headers that did not conform to the HTTP protocol. | 888 | /// Server sent headers that did not conform to the HTTP protocol. |
| 917 | /// | 889 | /// |
| 918 | /// To find out more detailed diagnostics, `http.Reader.head_buffer` can be | 890 | /// To find out more detailed diagnostics, `http.Reader.head_buffer` can be |
| ... | @@ -956,7 +928,7 @@ pub const Request = struct { | ... | @@ -956,7 +928,7 @@ pub const Request = struct { |
| 956 | | 928 | |
| 957 | if (head.status == .@"continue") { | 929 | if (head.status == .@"continue") { |
| 958 | if (r.handle_continue) continue; | 930 | if (r.handle_continue) continue; |
| 959 | return; // we're not handling the 100-continue | 931 | return response; // we're not handling the 100-continue |
| 960 | } | 932 | } |
| 961 | | 933 | |
| 962 | // This while loop is for handling redirects, which means the request's | 934 | // This while loop is for handling redirects, which means the request's |
| ... | @@ -987,25 +959,17 @@ pub const Request = struct { | ... | @@ -987,25 +959,17 @@ pub const Request = struct { |
| 987 | if (r.redirect_behavior == .not_allowed) return error.TooManyHttpRedirects; | 959 | if (r.redirect_behavior == .not_allowed) return error.TooManyHttpRedirects; |
| 988 | const location = head.location orelse return error.HttpRedirectLocationMissing; | 960 | const location = head.location orelse return error.HttpRedirectLocationMissing; |
| 989 | try r.redirect(location, &aux_buf); | 961 | try r.redirect(location, &aux_buf); |
| 990 | try r.send(); | 962 | try r.sendBodiless(); |
| 991 | continue; | 963 | continue; |
| 992 | } | 964 | } |
| 993 | | 965 | |
| 994 | switch (head.transfer_compression) { | 966 | switch (head.content_encoding) { |
| 995 | .identity => response.compression = .none, | 967 | .identity, .deflate, .gzip, .@"x-gzip" => {}, |
| 996 | .compress, .@"x-compress" => return error.CompressionUnsupported, | 968 | .compress, .@"x-compress" => return error.CompressionUnsupported, |
| 997 | .deflate => response.compression = .{ | | |
| 998 | .deflate = std.compress.zlib.decompressor(r.transferReader()), | | |
| 999 | }, | | |
| 1000 | .gzip, .@"x-gzip" => response.compression = .{ | | |
| 1001 | .gzip = std.compress.gzip.decompressor(r.transferReader()), | | |
| 1002 | }, | | |
| 1003 | // https://github.com/ziglang/zig/issues/18937 | 969 | // https://github.com/ziglang/zig/issues/18937 |
| 1004 | //.zstd => response.compression = .{ | | |
| 1005 | // .zstd = std.compress.zstd.decompressStream(r.client.allocator, r.transferReader()), | | |
| 1006 | //}, | | |
| 1007 | .zstd => return error.CompressionUnsupported, | 970 | .zstd => return error.CompressionUnsupported, |
| 1008 | } | 971 | } |
| | 972 | |
| 1009 | return response; | 973 | return response; |
| 1010 | } | 974 | } |
| 1011 | } | 975 | } |
| ... | @@ -1050,7 +1014,7 @@ pub const Request = struct { | ... | @@ -1050,7 +1014,7 @@ pub const Request = struct { |
| 1050 | std.ascii.eqlIgnoreCase(r.uri.scheme, new_uri.scheme) and | 1014 | std.ascii.eqlIgnoreCase(r.uri.scheme, new_uri.scheme) and |
| 1051 | sameParentDomain(old_host, new_host); | 1015 | sameParentDomain(old_host, new_host); |
| 1052 | | 1016 | |
| 1053 | r.client.connection_pool.release(r.client.allocator, old_connection); | 1017 | r.client.connection_pool.release(old_connection); |
| 1054 | r.connection = null; | 1018 | r.connection = null; |
| 1055 | | 1019 | |
| 1056 | if (!keep_privileged_headers) { | 1020 | if (!keep_privileged_headers) { |
| ... | @@ -1327,7 +1291,7 @@ pub fn connectTunnel( | ... | @@ -1327,7 +1291,7 @@ pub fn connectTunnel( |
| 1327 | const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol); | 1291 | const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol); |
| 1328 | errdefer { | 1292 | errdefer { |
| 1329 | conn.closing = true; | 1293 | conn.closing = true; |
| 1330 | client.connection_pool.release(client.allocator, conn); | 1294 | client.connection_pool.release(conn); |
| 1331 | } | 1295 | } |
| 1332 | | 1296 | |
| 1333 | var buffer: [8096]u8 = undefined; | 1297 | var buffer: [8096]u8 = undefined; |