| ... | @@ -54,7 +54,7 @@ pub const ConnectionPool = struct { | ... | @@ -54,7 +54,7 @@ pub const ConnectionPool = struct { |
| 54 | | 54 | |
| 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(); |
| 60 | | 60 | |
| ... | @@ -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; |
| 66 | | 66 | |
| 67 | pool.acquireUnsafe(node); | 67 | pool.acquireUnsafe(node); |
| 68 | return node; | 68 | return &node.data; |
| 69 | } | 69 | } |
| 70 | | 70 | |
| 71 | return null; | 71 | return null; |
| ... | @@ -89,10 +89,12 @@ pub const ConnectionPool = struct { | ... | @@ -89,10 +89,12 @@ pub const ConnectionPool = struct { |
| 89 | | 89 | |
| 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(); |
| 95 | | 95 | |
| | 96 | const node = @fieldParentPtr(Node, "data", connection); |
| | 97 | |
| 96 | pool.used.remove(node); | 98 | pool.used.remove(node); |
| 97 | | 99 | |
| 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. |
| 152 | pub const Connection = struct { | 154 | pub 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 }; |
| 155 | | 159 | |
| 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, |
| 166 | | 170 | |
| 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, |
| 170 | | 176 | |
| 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/2473 | 182 | // 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; |
| 190 | | 196 | |
| 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 | } |
| 196 | | 205 | |
| 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 | } |
| 200 | | 209 | |
| 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 | } |
| 204 | | 213 | |
| 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; |
| 207 | | 216 | 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)); | | |
| 217 | | 217 | |
| 218 | break; | 218 | if (available_read > available_buffer) { // partially read buffered data |
| 219 | } else if (available_read > 0) { // fully read buffered data | 219 | @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; | | |
| 223 | | 221 | |
| 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; |
| 226 | | 226 | |
| 227 | const leftover_buffer = available_buffer - available_read; | 227 | return available_read; |
| 228 | const leftover_len = len - out_index; | 228 | } |
| 229 | | 229 | |
| 230 | if (leftover_buffer > conn.read_buf.len) { | 230 | var iovecs = [2]std.os.iovec{ |
| 231 | // skip the buffer if the output is large enough | 231 | .{ .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); |
| 234 | | 235 | |
| 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 | } |
| 237 | | 241 | |
| 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 | } |
| 244 | | 244 | |
| 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 | } |
| 259 | | 259 | |
| 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 | } |
| 269 | | 269 | |
| 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 | } |
| 279 | | 292 | |
| 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 released | 470 | /// is null when this connection is released |
| 458 | connection: ?*ConnectionPool.Node, | 471 | connection: ?*Connection, |
| 459 | | 472 | |
| 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; |
| 550 | | 563 | |
| 551 | var buffered = std.io.bufferedWriter(req.connection.?.data.writer()); | 564 | const w = req.connection.?.writer(); |
| 552 | const w = buffered.writer(); | | |
| 553 | | 565 | |
| 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 | } |
| 631 | | 643 | |
| 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 { |
| 649 | | 661 | |
| 650 | try w.writeAll("\r\n"); | 662 | try w.writeAll("\r\n"); |
| 651 | | 663 | |
| 652 | try buffered.flush(); | 664 | try req.connection.?.flush(); |
| 653 | } | 665 | } |
| 654 | | 666 | |
| 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 { |
| 665 | | 677 | |
| 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 redirects | 696 | while (true) { // handle redirects |
| 685 | while (true) { // read headers | 697 | while (true) { // read headers |
| 686 | try req.connection.?.data.fill(); | 698 | try req.connection.?.fill(); |
| 687 | | 699 | |
| 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)); |
| 690 | | 702 | |
| 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 { |
| 701 | | 713 | |
| 702 | // we're switching protocols, so this connection is no longer doing http | 714 | // 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 | } |
| 707 | | 719 | |
| ... | @@ -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 | } |
| 719 | | 731 | |
| 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(); |
| 828 | | 840 | |
| 829 | while (!req.response.parser.state.isContent()) { // read trailing headers | 841 | while (!req.response.parser.state.isContent()) { // read trailing headers |
| 830 | try req.connection.?.data.fill(); | 842 | try req.connection.?.fill(); |
| 831 | | 843 | |
| 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 | } |
| 835 | | 847 | |
| 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"); |
| 874 | | 886 | |
| 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; |
| 879 | | 891 | |
| 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 | }; |
| 906 | | 920 | |
| ... | @@ -1024,7 +1038,7 @@ pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, Network | ... | @@ -1024,7 +1038,7 @@ pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, Network |
| 1024 | | 1038 | |
| 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. |
| 1027 | pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectTcpError!*ConnectionPool.Node { | 1041 | pub 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 |
| 1074 | | 1088 | |
| 1075 | client.connection_pool.addUsed(conn); | 1089 | client.connection_pool.addUsed(conn); |
| 1076 | | 1090 | |
| 1077 | return conn; | 1091 | return &conn.data; |
| 1078 | } | 1092 | } |
| 1079 | | 1093 | |
| 1080 | pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{ NameTooLong, Unsupported } || std.os.ConnectError; | 1094 | pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{ NameTooLong, Unsupported } || std.os.ConnectError; |
| 1081 | | 1095 | |
| 1082 | pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*ConnectionPool.Node { | 1096 | pub 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; |
| 1084 | | 1098 | |
| 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 |
| 1108 | | 1122 | |
| 1109 | client.connection_pool.addUsed(conn); | 1123 | client.connection_pool.addUsed(conn); |
| 1110 | | 1124 | |
| 1111 | return conn; | 1125 | return &conn.data; |
| 1112 | } | 1126 | } |
| 1113 | | 1127 | |
| 1114 | pub fn connectTunnel( | 1128 | pub 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; |
| 1121 | | 1135 | |
| 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 | } |
| 1136 | | 1150 | |
| ... | @@ -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; |
| 1173 | | 1187 | |
| 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); |
| 1177 | | 1191 | |
| 1178 | conn.data.port = tunnel_port; | 1192 | conn.port = tunnel_port; |
| 1179 | conn.data.closing = false; | 1193 | conn.closing = false; |
| 1180 | | 1194 | |
| 1181 | return conn; | 1195 | return conn; |
| 1182 | } catch { | 1196 | } catch { |
| ... | @@ -1190,7 +1204,7 @@ pub fn connectTunnel( | ... | @@ -1190,7 +1204,7 @@ pub fn connectTunnel( |
| 1190 | const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, ConnectionRefused }; | 1204 | const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, ConnectionRefused }; |
| 1191 | pub const ConnectError = ConnectErrorPartial || RequestError; | 1205 | pub const ConnectError = ConnectErrorPartial || RequestError; |
| 1192 | | 1206 | |
| 1193 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node { | 1207 | pub 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 fails | 1208 | // 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 proxy | 1227 | // 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 | } |
| 1219 | | 1233 | |
| 1220 | conn.data.proxied = true; | 1234 | conn.proxied = true; |
| 1221 | return conn; | 1235 | return conn; |
| 1222 | } | 1236 | } |
| 1223 | | 1237 | |
| ... | @@ -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 }, |
| 1241 | | 1255 | |
| 1242 | /// Must be an already acquired connection. | 1256 | /// Must be an already acquired connection. |
| 1243 | connection: ?*ConnectionPool.Node = null, | 1257 | connection: ?*Connection = null, |
| 1244 | | 1258 | |
| 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 the | 1260 | /// In this case, the client's Allocator will be used to store the |