| ... | ... | @@ -116,7 +116,7 @@ pub const ConnectionPool = struct { |
| 116 | 116 | /// `allocator` must be the same one used to create `connection`. |
| 117 | 117 | /// |
| 118 | 118 | /// Threadsafe. |
| 119 | | pub fn release(pool: *ConnectionPool, allocator: Allocator, connection: *Connection) void { |
| 119 | pub fn release(pool: *ConnectionPool, connection: *Connection) void { |
| 120 | 120 | if (connection.closing) return connection.destroy(); |
| 121 | 121 | |
| 122 | 122 | pool.mutex.lock(); |
| ... | ... | @@ -130,8 +130,7 @@ pub const ConnectionPool = struct { |
| 130 | 130 | const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); |
| 131 | 131 | pool.free_len -= 1; |
| 132 | 132 | |
| 133 | | popped.close(allocator); |
| 134 | | allocator.destroy(popped); |
| 133 | popped.destroy(); |
| 135 | 134 | } |
| 136 | 135 | |
| 137 | 136 | if (connection.proxied) { |
| ... | ... | @@ -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 | 436 | pub const Response = struct { |
| 452 | 437 | request: *Request, |
| 453 | 438 | /// Pointers in this struct are invalidated with the next call to |
| ... | ... | @@ -469,9 +454,7 @@ pub const Response = struct { |
| 469 | 454 | content_length: ?u64 = null, |
| 470 | 455 | |
| 471 | 456 | transfer_encoding: http.TransferEncoding = .none, |
| 472 | | transfer_compression: http.ContentEncoding = .identity, |
| 473 | | |
| 474 | | compression: Compression = .none, |
| 457 | content_encoding: http.ContentEncoding = .identity, |
| 475 | 458 | |
| 476 | 459 | pub const ParseError = error{ |
| 477 | 460 | HttpHeadersInvalid, |
| ... | ... | @@ -554,8 +537,8 @@ pub const Response = struct { |
| 554 | 537 | const trimmed_second = mem.trim(u8, second, " "); |
| 555 | 538 | |
| 556 | 539 | if (std.meta.stringToEnum(http.ContentEncoding, trimmed_second)) |transfer| { |
| 557 | | if (res.transfer_compression != .identity) return error.HttpHeadersInvalid; // double compression is not supported |
| 558 | | res.transfer_compression = transfer; |
| 540 | if (res.content_encoding != .identity) return error.HttpHeadersInvalid; // double compression is not supported |
| 541 | res.content_encoding = transfer; |
| 559 | 542 | } else { |
| 560 | 543 | return error.HttpTransferEncodingUnsupported; |
| 561 | 544 | } |
| ... | ... | @@ -569,12 +552,12 @@ pub const Response = struct { |
| 569 | 552 | |
| 570 | 553 | res.content_length = content_length; |
| 571 | 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 | 557 | const trimmed = mem.trim(u8, header_value, " "); |
| 575 | 558 | |
| 576 | 559 | if (std.meta.stringToEnum(http.ContentEncoding, trimmed)) |ce| { |
| 577 | | res.transfer_compression = ce; |
| 560 | res.content_encoding = ce; |
| 578 | 561 | } else { |
| 579 | 562 | return error.HttpTransferEncodingUnsupported; |
| 580 | 563 | } |
| ... | ... | @@ -592,7 +575,7 @@ pub const Response = struct { |
| 592 | 575 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ |
| 593 | 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 | 580 | try testing.expectEqual(.@"HTTP/1.1", head.version); |
| 598 | 581 | try testing.expectEqualStrings("OK", head.reason); |
| ... | ... | @@ -605,7 +588,7 @@ pub const Response = struct { |
| 605 | 588 | try testing.expectEqual(true, head.keep_alive); |
| 606 | 589 | try testing.expectEqual(10, head.content_length.?); |
| 607 | 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 | 594 | pub fn iterateHeaders(h: Head) http.HeaderIterator { |
| ... | ... | @@ -621,19 +604,8 @@ pub const Response = struct { |
| 621 | 604 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ |
| 622 | 605 | "connectioN:\t keep-alive \r\n\r\n"; |
| 623 | 606 | |
| 624 | | var header_buffer: [1024]u8 = undefined; |
| 625 | | var res = Response{ |
| 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(); |
| 607 | const head = try Head.parse(response_bytes); |
| 608 | var it = head.iterateHeaders(); |
| 637 | 609 | { |
| 638 | 610 | const header = it.next().?; |
| 639 | 611 | try testing.expectEqualStrings("LOcation", header.name); |
| ... | ... | @@ -695,7 +667,7 @@ pub const Response = struct { |
| 695 | 667 | /// Asserts that this function is only called once. |
| 696 | 668 | pub fn reader(response: *Response) std.io.Reader { |
| 697 | 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 | 750 | } |
| 779 | 751 | }; |
| 780 | 752 | |
| 781 | | /// Frees all resources associated with the request. |
| 782 | | pub fn deinit(req: *Request) void { |
| 783 | | if (req.connection) |connection| { |
| 784 | | if (!req.response.parser.done) { |
| 785 | | // If the response wasn't fully read, then we need to close the connection. |
| 753 | /// Returns the request's `Connection` back to the pool of the `Client`. |
| 754 | pub fn deinit(r: *Request) void { |
| 755 | if (r.connection) |connection| { |
| 756 | if (r.reader.state != .ready) { |
| 757 | // Connection cannot be reused. |
| 786 | 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 | 765 | /// Sends and flushes a complete request as only HTTP head, no body. |
| ... | ... | @@ -810,12 +782,12 @@ pub const Request = struct { |
| 810 | 782 | try sendHead(r); |
| 811 | 783 | return .{ |
| 812 | 784 | .http_protocol_output = &r.connection.?.writer, |
| 813 | | .transfer_encoding = if (r.transfer_encoding) |te| switch (te) { |
| 785 | .state = switch (r.transfer_encoding) { |
| 814 | 786 | .chunked => .{ .chunked = .init }, |
| 815 | 787 | .content_length => |len| .{ .content_length = len }, |
| 816 | 788 | .none => .none, |
| 817 | | } else .{ .chunked = .init }, |
| 818 | | .elide_body = false, |
| 789 | }, |
| 790 | .elide = false, |
| 819 | 791 | }; |
| 820 | 792 | } |
| 821 | 793 | |
| ... | ... | @@ -912,7 +884,7 @@ pub const Request = struct { |
| 912 | 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 | 888 | /// Server sent headers that did not conform to the HTTP protocol. |
| 917 | 889 | /// |
| 918 | 890 | /// To find out more detailed diagnostics, `http.Reader.head_buffer` can be |
| ... | ... | @@ -956,7 +928,7 @@ pub const Request = struct { |
| 956 | 928 | |
| 957 | 929 | if (head.status == .@"continue") { |
| 958 | 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 | 934 | // This while loop is for handling redirects, which means the request's |
| ... | ... | @@ -987,25 +959,17 @@ pub const Request = struct { |
| 987 | 959 | if (r.redirect_behavior == .not_allowed) return error.TooManyHttpRedirects; |
| 988 | 960 | const location = head.location orelse return error.HttpRedirectLocationMissing; |
| 989 | 961 | try r.redirect(location, &aux_buf); |
| 990 | | try r.send(); |
| 962 | try r.sendBodiless(); |
| 991 | 963 | continue; |
| 992 | 964 | } |
| 993 | 965 | |
| 994 | | switch (head.transfer_compression) { |
| 995 | | .identity => response.compression = .none, |
| 966 | switch (head.content_encoding) { |
| 967 | .identity, .deflate, .gzip, .@"x-gzip" => {}, |
| 996 | 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 | 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 | 970 | .zstd => return error.CompressionUnsupported, |
| 1008 | 971 | } |
| 972 | |
| 1009 | 973 | return response; |
| 1010 | 974 | } |
| 1011 | 975 | } |
| ... | ... | @@ -1050,7 +1014,7 @@ pub const Request = struct { |
| 1050 | 1014 | std.ascii.eqlIgnoreCase(r.uri.scheme, new_uri.scheme) and |
| 1051 | 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 | 1018 | r.connection = null; |
| 1055 | 1019 | |
| 1056 | 1020 | if (!keep_privileged_headers) { |
| ... | ... | @@ -1327,7 +1291,7 @@ pub fn connectTunnel( |
| 1327 | 1291 | const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol); |
| 1328 | 1292 | errdefer { |
| 1329 | 1293 | conn.closing = true; |
| 1330 | | client.connection_pool.release(client.allocator, conn); |
| 1294 | client.connection_pool.release(conn); |
| 1331 | 1295 | } |
| 1332 | 1296 | |
| 1333 | 1297 | var buffer: [8096]u8 = undefined; |