authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-10-03 14:26:06-05:00
committergravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-10-21 20:52:58-05:00
loge1c37f70d4ae9a7bfa6de92dcb26e7cfdffc17c2
tree26aa7274ade1c3fd5ffecdb477e0f83dd606b25b
parent1afeada2d95e50efe651bd6227719ca4003dad96
signaturelock-open Commit is signed but in an unrecognized format.

std.http.Client: store *Connection instead of a pool node, buffer writes


4 files changed, 110 insertions(+), 96 deletions(-)

lib/std/crypto/tls/Client.zig+1-1
...@@ -881,7 +881,7 @@ pub fn readAll(c: *Client, stream: anytype, buffer: []u8) !usize {...@@ -881,7 +881,7 @@ pub fn readAll(c: *Client, stream: anytype, buffer: []u8) !usize {
881/// The `iovecs` parameter is mutable because this function needs to mutate the fields in881/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
882/// order to handle partial reads from the underlying stream layer.882/// order to handle partial reads from the underlying stream layer.
883pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize {883pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize {
884 return readvAtLeast(c, stream, iovecs);884 return readvAtLeast(c, stream, iovecs, 1);
885}885}
886886
887/// Receives TLS-encrypted data from `stream`, which must conform to `StreamInterface`.887/// Receives TLS-encrypted data from `stream`, which must conform to `StreamInterface`.
lib/std/http/Client.zig+105-91
...@@ -54,7 +54,7 @@ pub const ConnectionPool = struct {...@@ -54,7 +54,7 @@ pub const ConnectionPool = struct {
5454
55 /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe.55 /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe.
56 /// If no connection is found, null is returned.56 /// If no connection is found, null is returned.
57 pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Node {57 pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Connection {
58 pool.mutex.lock();58 pool.mutex.lock();
59 defer pool.mutex.unlock();59 defer pool.mutex.unlock();
6060
...@@ -65,7 +65,7 @@ pub const ConnectionPool = struct {...@@ -65,7 +65,7 @@ pub const ConnectionPool = struct {
65 if (!std.ascii.eqlIgnoreCase(node.data.host, criteria.host)) continue;65 if (!std.ascii.eqlIgnoreCase(node.data.host, criteria.host)) continue;
6666
67 pool.acquireUnsafe(node);67 pool.acquireUnsafe(node);
68 return node;68 return &node.data;
69 }69 }
7070
71 return null;71 return null;
...@@ -89,10 +89,12 @@ pub const ConnectionPool = struct {...@@ -89,10 +89,12 @@ pub const ConnectionPool = struct {
8989
90 /// Tries to release a connection back to the connection pool. This function is threadsafe.90 /// Tries to release a connection back to the connection pool. This function is threadsafe.
91 /// If the connection is marked as closing, it will be closed instead.91 /// If the connection is marked as closing, it will be closed instead.
92 pub fn release(pool: *ConnectionPool, allocator: Allocator, node: *Node) void {92 pub fn release(pool: *ConnectionPool, allocator: Allocator, connection: *Connection) void {
93 pool.mutex.lock();93 pool.mutex.lock();
94 defer pool.mutex.unlock();94 defer pool.mutex.unlock();
9595
96 const node = @fieldParentPtr(Node, "data", connection);
97
96 pool.used.remove(node);98 pool.used.remove(node);
9799
98 if (node.data.closing or pool.free_size == 0) {100 if (node.data.closing or pool.free_size == 0) {
...@@ -151,6 +153,8 @@ pub const ConnectionPool = struct {...@@ -151,6 +153,8 @@ pub const ConnectionPool = struct {
151/// An interface to either a plain or TLS connection.153/// An interface to either a plain or TLS connection.
152pub const Connection = struct {154pub const Connection = struct {
153 pub const buffer_size = std.crypto.tls.max_ciphertext_record_len;155 pub const buffer_size = std.crypto.tls.max_ciphertext_record_len;
156 const BufferSize = std.math.IntFittingRange(0, buffer_size);
157
154 pub const Protocol = enum { plain, tls };158 pub const Protocol = enum { plain, tls };
155159
156 stream: net.Stream,160 stream: net.Stream,
...@@ -164,14 +168,16 @@ pub const Connection = struct {...@@ -164,14 +168,16 @@ pub const Connection = struct {
164 proxied: bool = false,168 proxied: bool = false,
165 closing: bool = false,169 closing: bool = false,
166170
167 read_start: u16 = 0,171 read_start: BufferSize = 0,
168 read_end: u16 = 0,172 read_end: BufferSize = 0,
173 write_end: BufferSize = 0,
169 read_buf: [buffer_size]u8 = undefined,174 read_buf: [buffer_size]u8 = undefined,
175 write_buf: [buffer_size]u8 = undefined,
170176
171 pub fn rawReadAtLeast(conn: *Connection, buffer: []u8, len: usize) ReadError!usize {177 pub fn readvDirect(conn: *Connection, buffers: []std.os.iovec) ReadError!usize {
172 return switch (conn.protocol) {178 return switch (conn.protocol) {
173 .plain => conn.stream.readAtLeast(buffer, len),179 .plain => conn.stream.readv(buffers),
174 .tls => conn.tls_client.readAtLeast(conn.stream, buffer, len),180 .tls => conn.tls_client.readv(conn.stream, buffers),
175 } catch |err| {181 } catch |err| {
176 // TODO: https://github.com/ziglang/zig/issues/2473182 // TODO: https://github.com/ziglang/zig/issues/2473
177 if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert;183 if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert;
...@@ -188,58 +194,52 @@ pub const Connection = struct {...@@ -188,58 +194,52 @@ pub const Connection = struct {
188 pub fn fill(conn: *Connection) ReadError!void {194 pub fn fill(conn: *Connection) ReadError!void {
189 if (conn.read_end != conn.read_start) return;195 if (conn.read_end != conn.read_start) return;
190196
191 const nread = try conn.rawReadAtLeast(conn.read_buf[0..], 1);197 var iovecs = [1]std.os.iovec{
198 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
199 };
200 const nread = try conn.readvDirect(&iovecs);
192 if (nread == 0) return error.EndOfStream;201 if (nread == 0) return error.EndOfStream;
193 conn.read_start = 0;202 conn.read_start = 0;
194 conn.read_end = @as(u16, @intCast(nread));203 conn.read_end = @intCast(nread);
195 }204 }
196205
197 pub fn peek(conn: *Connection) []const u8 {206 pub fn peek(conn: *Connection) []const u8 {
198 return conn.read_buf[conn.read_start..conn.read_end];207 return conn.read_buf[conn.read_start..conn.read_end];
199 }208 }
200209
201 pub fn drop(conn: *Connection, num: u16) void {210 pub fn drop(conn: *Connection, num: BufferSize) void {
202 conn.read_start += num;211 conn.read_start += num;
203 }212 }
204213
205 pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) ReadError!usize {214 pub fn read(conn: *Connection, buffer: []u8) ReadError!usize {
206 assert(len <= buffer.len);215 const available_read = conn.read_end - conn.read_start;
207216 const available_buffer = buffer.len;
208 var out_index: u16 = 0;
209 while (out_index < len) {
210 const available_read = conn.read_end - conn.read_start;
211 const available_buffer = buffer.len - out_index;
212
213 if (available_read > available_buffer) { // partially read buffered data
214 @memcpy(buffer[out_index..], conn.read_buf[conn.read_start..conn.read_end][0..available_buffer]);
215 out_index += @as(u16, @intCast(available_buffer));
216 conn.read_start += @as(u16, @intCast(available_buffer));
217217
218 break;218 if (available_read > available_buffer) { // partially read buffered data
219 } else if (available_read > 0) { // fully read buffered data219 @memcpy(buffer[0..available_buffer], conn.read_buf[conn.read_start..conn.read_end][0..available_buffer]);
220 @memcpy(buffer[out_index..][0..available_read], conn.read_buf[conn.read_start..conn.read_end]);220 conn.read_start += @intCast(available_buffer);
221 out_index += available_read;
222 conn.read_start += available_read;
223221
224 if (out_index >= len) break;222 return available_buffer;
225 }223 } else if (available_read > 0) { // fully read buffered data
224 @memcpy(buffer[0..available_read], conn.read_buf[conn.read_start..conn.read_end]);
225 conn.read_start += available_read;
226226
227 const leftover_buffer = available_buffer - available_read;227 return available_read;
228 const leftover_len = len - out_index;228 }
229229
230 if (leftover_buffer > conn.read_buf.len) {230 var iovecs = [2]std.os.iovec{
231 // skip the buffer if the output is large enough231 .{ .iov_base = buffer.ptr, .iov_len = buffer.len },
232 return conn.rawReadAtLeast(buffer[out_index..], leftover_len);232 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
233 }233 };
234 const nread = try conn.readvDirect(&iovecs);
234235
235 try conn.fill();236 if (nread > buffer.len) {
237 conn.read_start = 0;
238 conn.read_end = @intCast(nread - buffer.len);
239 return buffer.len;
236 }240 }
237241
238 return out_index;242 return nread;
239 }
240
241 pub fn read(conn: *Connection, buffer: []u8) ReadError!usize {
242 return conn.readAtLeast(buffer, 1);
243 }243 }
244244
245 pub const ReadError = error{245 pub const ReadError = error{
...@@ -257,7 +257,7 @@ pub const Connection = struct {...@@ -257,7 +257,7 @@ pub const Connection = struct {
257 return Reader{ .context = conn };257 return Reader{ .context = conn };
258 }258 }
259259
260 pub fn writeAll(conn: *Connection, buffer: []const u8) !void {260 pub fn writeAllDirect(conn: *Connection, buffer: []const u8) WriteError!void {
261 return switch (conn.protocol) {261 return switch (conn.protocol) {
262 .plain => conn.stream.writeAll(buffer),262 .plain => conn.stream.writeAll(buffer),
263 .tls => conn.tls_client.writeAll(conn.stream, buffer),263 .tls => conn.tls_client.writeAll(conn.stream, buffer),
...@@ -267,14 +267,27 @@ pub const Connection = struct {...@@ -267,14 +267,27 @@ pub const Connection = struct {
267 };267 };
268 }268 }
269269
270 pub fn write(conn: *Connection, buffer: []const u8) !usize {270 pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize {
271 return switch (conn.protocol) {271 if (conn.write_end + buffer.len > conn.write_buf.len) {
272 .plain => conn.stream.write(buffer),272 try conn.flush();
273 .tls => conn.tls_client.write(conn.stream, buffer),273
274 } catch |err| switch (err) {274 if (buffer.len > conn.write_buf.len) {
275 error.BrokenPipe, error.ConnectionResetByPeer => return error.ConnectionResetByPeer,275 try conn.writeAllDirect(buffer);
276 else => return error.UnexpectedWriteFailure,276 return buffer.len;
277 };277 }
278 }
279
280 @memcpy(conn.write_buf[conn.write_end..][0..buffer.len], buffer);
281 conn.write_end += @intCast(buffer.len);
282
283 return buffer.len;
284 }
285
286 pub fn flush(conn: *Connection) WriteError!void {
287 if (conn.write_end == 0) return;
288
289 try conn.writeAllDirect(conn.write_buf[0..conn.write_end]);
290 conn.write_end = 0;
278 }291 }
279292
280 pub const WriteError = error{293 pub const WriteError = error{
...@@ -455,7 +468,7 @@ pub const Request = struct {...@@ -455,7 +468,7 @@ pub const Request = struct {
455 uri: Uri,468 uri: Uri,
456 client: *Client,469 client: *Client,
457 /// is null when this connection is released470 /// is null when this connection is released
458 connection: ?*ConnectionPool.Node,471 connection: ?*Connection,
459472
460 method: http.Method,473 method: http.Method,
461 version: http.Version = .@"HTTP/1.1",474 version: http.Version = .@"HTTP/1.1",
...@@ -489,7 +502,7 @@ pub const Request = struct {...@@ -489,7 +502,7 @@ pub const Request = struct {
489 if (req.connection) |connection| {502 if (req.connection) |connection| {
490 if (!req.response.parser.done) {503 if (!req.response.parser.done) {
491 // If the response wasn't fully read, then we need to close the connection.504 // If the response wasn't fully read, then we need to close the connection.
492 connection.data.closing = true;505 connection.closing = true;
493 }506 }
494 req.client.connection_pool.release(req.client.allocator, connection);507 req.client.connection_pool.release(req.client.allocator, connection);
495 }508 }
...@@ -548,8 +561,7 @@ pub const Request = struct {...@@ -548,8 +561,7 @@ pub const Request = struct {
548 pub fn start(req: *Request, options: StartOptions) StartError!void {561 pub fn start(req: *Request, options: StartOptions) StartError!void {
549 if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding;562 if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding;
550563
551 var buffered = std.io.bufferedWriter(req.connection.?.data.writer());564 const w = req.connection.?.writer();
552 const w = buffered.writer();
553565
554 try req.method.write(w);566 try req.method.write(w);
555 try w.writeByte(' ');567 try w.writeByte(' ');
...@@ -558,9 +570,9 @@ pub const Request = struct {...@@ -558,9 +570,9 @@ pub const Request = struct {
558 try req.uri.writeToStream(.{ .authority = true }, w);570 try req.uri.writeToStream(.{ .authority = true }, w);
559 } else {571 } else {
560 try req.uri.writeToStream(.{572 try req.uri.writeToStream(.{
561 .scheme = req.connection.?.data.proxied,573 .scheme = req.connection.?.proxied,
562 .authentication = req.connection.?.data.proxied,574 .authentication = req.connection.?.proxied,
563 .authority = req.connection.?.data.proxied,575 .authority = req.connection.?.proxied,
564 .path = true,576 .path = true,
565 .query = true,577 .query = true,
566 .raw = options.raw_uri,578 .raw = options.raw_uri,
...@@ -629,8 +641,8 @@ pub const Request = struct {...@@ -629,8 +641,8 @@ pub const Request = struct {
629 try w.writeAll("\r\n");641 try w.writeAll("\r\n");
630 }642 }
631643
632 if (req.connection.?.data.proxied) {644 if (req.connection.?.proxied) {
633 const proxy_headers: ?http.Headers = switch (req.connection.?.data.protocol) {645 const proxy_headers: ?http.Headers = switch (req.connection.?.protocol) {
634 .plain => if (req.client.http_proxy) |proxy| proxy.headers else null,646 .plain => if (req.client.http_proxy) |proxy| proxy.headers else null,
635 .tls => if (req.client.https_proxy) |proxy| proxy.headers else null,647 .tls => if (req.client.https_proxy) |proxy| proxy.headers else null,
636 };648 };
...@@ -649,7 +661,7 @@ pub const Request = struct {...@@ -649,7 +661,7 @@ pub const Request = struct {
649661
650 try w.writeAll("\r\n");662 try w.writeAll("\r\n");
651663
652 try buffered.flush();664 try req.connection.?.flush();
653 }665 }
654666
655 const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;667 const TransferReadError = Connection.ReadError || proto.HeadersParser.ReadError;
...@@ -665,7 +677,7 @@ pub const Request = struct {...@@ -665,7 +677,7 @@ pub const Request = struct {
665677
666 var index: usize = 0;678 var index: usize = 0;
667 while (index == 0) {679 while (index == 0) {
668 const amt = try req.response.parser.read(&req.connection.?.data, buf[index..], req.response.skip);680 const amt = try req.response.parser.read(req.connection.?, buf[index..], req.response.skip);
669 if (amt == 0 and req.response.parser.done) break;681 if (amt == 0 and req.response.parser.done) break;
670 index += amt;682 index += amt;
671 }683 }
...@@ -683,10 +695,10 @@ pub const Request = struct {...@@ -683,10 +695,10 @@ pub const Request = struct {
683 pub fn wait(req: *Request) WaitError!void {695 pub fn wait(req: *Request) WaitError!void {
684 while (true) { // handle redirects696 while (true) { // handle redirects
685 while (true) { // read headers697 while (true) { // read headers
686 try req.connection.?.data.fill();698 try req.connection.?.fill();
687699
688 const nchecked = try req.response.parser.checkCompleteHead(req.client.allocator, req.connection.?.data.peek());700 const nchecked = try req.response.parser.checkCompleteHead(req.client.allocator, req.connection.?.peek());
689 req.connection.?.data.drop(@as(u16, @intCast(nchecked)));701 req.connection.?.drop(@intCast(nchecked));
690702
691 if (req.response.parser.state.isContent()) break;703 if (req.response.parser.state.isContent()) break;
692 }704 }
...@@ -701,7 +713,7 @@ pub const Request = struct {...@@ -701,7 +713,7 @@ pub const Request = struct {
701713
702 // we're switching protocols, so this connection is no longer doing http714 // we're switching protocols, so this connection is no longer doing http
703 if (req.response.status == .switching_protocols or (req.method == .CONNECT and req.response.status == .ok)) {715 if (req.response.status == .switching_protocols or (req.method == .CONNECT and req.response.status == .ok)) {
704 req.connection.?.data.closing = false;716 req.connection.?.closing = false;
705 req.response.parser.done = true;717 req.response.parser.done = true;
706 }718 }
707719
...@@ -712,9 +724,9 @@ pub const Request = struct {...@@ -712,9 +724,9 @@ pub const Request = struct {
712 const res_connection = req.response.headers.getFirstValue("connection");724 const res_connection = req.response.headers.getFirstValue("connection");
713 const res_keepalive = res_connection != null and !std.ascii.eqlIgnoreCase("close", res_connection.?);725 const res_keepalive = res_connection != null and !std.ascii.eqlIgnoreCase("close", res_connection.?);
714 if (res_keepalive and (req_keepalive or req_connection == null)) {726 if (res_keepalive and (req_keepalive or req_connection == null)) {
715 req.connection.?.data.closing = false;727 req.connection.?.closing = false;
716 } else {728 } else {
717 req.connection.?.data.closing = true;729 req.connection.?.closing = true;
718 }730 }
719731
720 if (req.response.transfer_encoding) |te| {732 if (req.response.transfer_encoding) |te| {
...@@ -827,10 +839,10 @@ pub const Request = struct {...@@ -827,10 +839,10 @@ pub const Request = struct {
827 const has_trail = !req.response.parser.state.isContent();839 const has_trail = !req.response.parser.state.isContent();
828840
829 while (!req.response.parser.state.isContent()) { // read trailing headers841 while (!req.response.parser.state.isContent()) { // read trailing headers
830 try req.connection.?.data.fill();842 try req.connection.?.fill();
831843
832 const nchecked = try req.response.parser.checkCompleteHead(req.client.allocator, req.connection.?.data.peek());844 const nchecked = try req.response.parser.checkCompleteHead(req.client.allocator, req.connection.?.peek());
833 req.connection.?.data.drop(@as(u16, @intCast(nchecked)));845 req.connection.?.drop(@intCast(nchecked));
834 }846 }
835847
836 if (has_trail) {848 if (has_trail) {
...@@ -868,16 +880,16 @@ pub const Request = struct {...@@ -868,16 +880,16 @@ pub const Request = struct {
868 pub fn write(req: *Request, bytes: []const u8) WriteError!usize {880 pub fn write(req: *Request, bytes: []const u8) WriteError!usize {
869 switch (req.transfer_encoding) {881 switch (req.transfer_encoding) {
870 .chunked => {882 .chunked => {
871 try req.connection.?.data.writer().print("{x}\r\n", .{bytes.len});883 try req.connection.?.writer().print("{x}\r\n", .{bytes.len});
872 try req.connection.?.data.writeAll(bytes);884 try req.connection.?.writer().writeAll(bytes);
873 try req.connection.?.data.writeAll("\r\n");885 try req.connection.?.writer().writeAll("\r\n");
874886
875 return bytes.len;887 return bytes.len;
876 },888 },
877 .content_length => |*len| {889 .content_length => |*len| {
878 if (len.* < bytes.len) return error.MessageTooLong;890 if (len.* < bytes.len) return error.MessageTooLong;
879891
880 const amt = try req.connection.?.data.write(bytes);892 const amt = try req.connection.?.write(bytes);
881 len.* -= amt;893 len.* -= amt;
882 return amt;894 return amt;
883 },895 },
...@@ -897,10 +909,12 @@ pub const Request = struct {...@@ -897,10 +909,12 @@ pub const Request = struct {
897 /// Finish the body of a request. This notifies the server that you have no more data to send.909 /// Finish the body of a request. This notifies the server that you have no more data to send.
898 pub fn finish(req: *Request) FinishError!void {910 pub fn finish(req: *Request) FinishError!void {
899 switch (req.transfer_encoding) {911 switch (req.transfer_encoding) {
900 .chunked => try req.connection.?.data.writeAll("0\r\n\r\n"),912 .chunked => try req.connection.?.writer().writeAll("0\r\n\r\n"),
901 .content_length => |len| if (len != 0) return error.MessageNotCompleted,913 .content_length => |len| if (len != 0) return error.MessageNotCompleted,
902 .none => {},914 .none => {},
903 }915 }
916
917 try req.connection.?.flush();
904 }918 }
905};919};
906920
...@@ -1024,7 +1038,7 @@ pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, Network...@@ -1024,7 +1038,7 @@ pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, Network
10241038
1025/// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open.1039/// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open.
1026/// This function is threadsafe.1040/// This function is threadsafe.
1027pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectTcpError!*ConnectionPool.Node {1041pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectTcpError!*Connection {
1028 if (client.connection_pool.findConnection(.{1042 if (client.connection_pool.findConnection(.{
1029 .host = host,1043 .host = host,
1030 .port = port,1044 .port = port,
...@@ -1074,12 +1088,12 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec...@@ -1074,12 +1088,12 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec
10741088
1075 client.connection_pool.addUsed(conn);1089 client.connection_pool.addUsed(conn);
10761090
1077 return conn;1091 return &conn.data;
1078}1092}
10791093
1080pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{ NameTooLong, Unsupported } || std.os.ConnectError;1094pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{ NameTooLong, Unsupported } || std.os.ConnectError;
10811095
1082pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*ConnectionPool.Node {1096pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connection {
1083 if (!net.has_unix_sockets) return error.Unsupported;1097 if (!net.has_unix_sockets) return error.Unsupported;
10841098
1085 if (client.connection_pool.findConnection(.{1099 if (client.connection_pool.findConnection(.{
...@@ -1108,7 +1122,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti...@@ -1108,7 +1122,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti
11081122
1109 client.connection_pool.addUsed(conn);1123 client.connection_pool.addUsed(conn);
11101124
1111 return conn;1125 return &conn.data;
1112}1126}
11131127
1114pub fn connectTunnel(1128pub fn connectTunnel(
...@@ -1116,7 +1130,7 @@ pub fn connectTunnel(...@@ -1116,7 +1130,7 @@ pub fn connectTunnel(
1116 proxy: *ProxyInformation,1130 proxy: *ProxyInformation,
1117 tunnel_host: []const u8,1131 tunnel_host: []const u8,
1118 tunnel_port: u16,1132 tunnel_port: u16,
1119) !*ConnectionPool.Node {1133) !*Connection {
1120 if (!proxy.supports_connect) return error.TunnelNotSupported;1134 if (!proxy.supports_connect) return error.TunnelNotSupported;
11211135
1122 if (client.connection_pool.findConnection(.{1136 if (client.connection_pool.findConnection(.{
...@@ -1130,7 +1144,7 @@ pub fn connectTunnel(...@@ -1130,7 +1144,7 @@ pub fn connectTunnel(
1130 _ = tunnel: {1144 _ = tunnel: {
1131 const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol);1145 const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol);
1132 errdefer {1146 errdefer {
1133 conn.data.closing = true;1147 conn.closing = true;
1134 client.connection_pool.release(client.allocator, conn);1148 client.connection_pool.release(client.allocator, conn);
1135 }1149 }
11361150
...@@ -1171,12 +1185,12 @@ pub fn connectTunnel(...@@ -1171,12 +1185,12 @@ pub fn connectTunnel(
1171 // this connection is now a tunnel, so we can't use it for anything else, it will only be released when the client is de-initialized.1185 // this connection is now a tunnel, so we can't use it for anything else, it will only be released when the client is de-initialized.
1172 req.connection = null;1186 req.connection = null;
11731187
1174 client.allocator.free(conn.data.host);1188 client.allocator.free(conn.host);
1175 conn.data.host = try client.allocator.dupe(u8, tunnel_host);1189 conn.host = try client.allocator.dupe(u8, tunnel_host);
1176 errdefer client.allocator.free(conn.data.host);1190 errdefer client.allocator.free(conn.host);
11771191
1178 conn.data.port = tunnel_port;1192 conn.port = tunnel_port;
1179 conn.data.closing = false;1193 conn.closing = false;
11801194
1181 return conn;1195 return conn;
1182 } catch {1196 } catch {
...@@ -1190,7 +1204,7 @@ pub fn connectTunnel(...@@ -1190,7 +1204,7 @@ pub fn connectTunnel(
1190const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, ConnectionRefused };1204const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, ConnectionRefused };
1191pub const ConnectError = ConnectErrorPartial || RequestError;1205pub const ConnectError = ConnectErrorPartial || RequestError;
11921206
1193pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node {1207pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*Connection {
1194 // pointer required so that `supports_connect` can be updated if a CONNECT fails1208 // pointer required so that `supports_connect` can be updated if a CONNECT fails
1195 const potential_proxy: ?*ProxyInformation = switch (protocol) {1209 const potential_proxy: ?*ProxyInformation = switch (protocol) {
1196 .plain => if (client.http_proxy) |*proxy_info| proxy_info else null,1210 .plain => if (client.http_proxy) |*proxy_info| proxy_info else null,
...@@ -1213,11 +1227,11 @@ pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connectio...@@ -1213,11 +1227,11 @@ pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connectio
1213 // fall back to using the proxy as a normal http proxy1227 // fall back to using the proxy as a normal http proxy
1214 const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol);1228 const conn = try client.connectTcp(proxy.host, proxy.port, proxy.protocol);
1215 errdefer {1229 errdefer {
1216 conn.data.closing = true;1230 conn.closing = true;
1217 client.connection_pool.release(conn);1231 client.connection_pool.release(conn);
1218 }1232 }
12191233
1220 conn.data.proxied = true;1234 conn.proxied = true;
1221 return conn;1235 return conn;
1222 }1236 }
12231237
...@@ -1240,7 +1254,7 @@ pub const RequestOptions = struct {...@@ -1240,7 +1254,7 @@ pub const RequestOptions = struct {
1240 header_strategy: StorageStrategy = .{ .dynamic = 16 * 1024 },1254 header_strategy: StorageStrategy = .{ .dynamic = 16 * 1024 },
12411255
1242 /// Must be an already acquired connection.1256 /// Must be an already acquired connection.
1243 connection: ?*ConnectionPool.Node = null,1257 connection: ?*Connection = null,
12441258
1245 pub const StorageStrategy = union(enum) {1259 pub const StorageStrategy = union(enum) {
1246 /// In this case, the client's Allocator will be used to store the1260 /// In this case, the client's Allocator will be used to store the
lib/std/http/protocol.zig+3-3
...@@ -529,7 +529,7 @@ pub const HeadersParser = struct {...@@ -529,7 +529,7 @@ pub const HeadersParser = struct {
529 try conn.fill();529 try conn.fill();
530530
531 const nread = @min(conn.peek().len, data_avail);531 const nread = @min(conn.peek().len, data_avail);
532 conn.drop(@as(u16, @intCast(nread)));532 conn.drop(@intCast(nread));
533 r.next_chunk_length -= nread;533 r.next_chunk_length -= nread;
534534
535 if (r.next_chunk_length == 0) r.done = true;535 if (r.next_chunk_length == 0) r.done = true;
...@@ -553,7 +553,7 @@ pub const HeadersParser = struct {...@@ -553,7 +553,7 @@ pub const HeadersParser = struct {
553 try conn.fill();553 try conn.fill();
554554
555 const i = r.findChunkedLen(conn.peek());555 const i = r.findChunkedLen(conn.peek());
556 conn.drop(@as(u16, @intCast(i)));556 conn.drop(@intCast(i));
557557
558 switch (r.state) {558 switch (r.state) {
559 .invalid => return error.HttpChunkInvalid,559 .invalid => return error.HttpChunkInvalid,
...@@ -582,7 +582,7 @@ pub const HeadersParser = struct {...@@ -582,7 +582,7 @@ pub const HeadersParser = struct {
582 try conn.fill();582 try conn.fill();
583583
584 const nread = @min(conn.peek().len, data_avail);584 const nread = @min(conn.peek().len, data_avail);
585 conn.drop(@as(u16, @intCast(nread)));585 conn.drop(@intCast(nread));
586 r.next_chunk_length -= nread;586 r.next_chunk_length -= nread;
587 } else if (out_avail > 0) {587 } else if (out_avail > 0) {
588 const can_read: usize = @intCast(@min(data_avail, out_avail));588 const can_read: usize = @intCast(@min(data_avail, out_avail));
test/standalone/http.zig+1-1
...@@ -680,7 +680,7 @@ pub fn main() !void {...@@ -680,7 +680,7 @@ pub fn main() !void {
680 for (0..total_connections) |i| {680 for (0..total_connections) |i| {
681 var req = try client.request(.GET, uri, .{ .allocator = calloc }, .{});681 var req = try client.request(.GET, uri, .{ .allocator = calloc }, .{});
682 req.response.parser.done = true;682 req.response.parser.done = true;
683 req.connection.?.data.closing = false;683 req.connection.?.closing = false;
684 requests[i] = req;684 requests[i] = req;
685 }685 }
686686