diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-04-03 15:57:35 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-04-03 15:57:35 -0700 |
| commit | 337e1109f5c2894aad9519ee9f7ff1f1f4c65b56 (patch) | |
| tree | 222eaf622275f517d143802e616295a760f1e644 /lib/std/http | |
| parent | 3b77a845f9a5409d19a9e330b82eece8af4ac18f (diff) | |
| download | zig-337e1109f5c2894aad9519ee9f7ff1f1f4c65b56.tar.gz zig-337e1109f5c2894aad9519ee9f7ff1f1f4c65b56.zip | |
std.DoublyLinkedList: remove length tracking
this is trivial to tack on, and in my experience it is rarely wanted.
Diffstat (limited to 'lib/std/http')
| -rw-r--r-- | lib/std/http/Client.zig | 87 |
1 files changed, 44 insertions, 43 deletions
diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index ecc1893194..d3041619b0 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -46,9 +46,9 @@ https_proxy: ?*Proxy = null, pub const ConnectionPool = struct { mutex: std.Thread.Mutex = .{}, /// Open connections that are currently in use. - used: Queue = .{}, + used: std.DoublyLinkedList = .{}, /// Open connections that are not currently in use. - free: Queue = .{}, + free: std.DoublyLinkedList = .{}, free_len: usize = 0, free_size: usize = 32, @@ -59,9 +59,6 @@ pub const ConnectionPool = struct { protocol: Connection.Protocol, }; - const Queue = std.DoublyLinkedList(Connection); - pub const Node = Queue.Node; - /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. /// If no connection is found, null is returned. pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Connection { @@ -70,33 +67,34 @@ pub const ConnectionPool = struct { var next = pool.free.last; while (next) |node| : (next = node.prev) { - if (node.data.protocol != criteria.protocol) continue; - if (node.data.port != criteria.port) continue; + const connection: *Connection = @fieldParentPtr("pool_node", node); + if (connection.protocol != criteria.protocol) continue; + if (connection.port != criteria.port) continue; // Domain names are case-insensitive (RFC 5890, Section 2.3.2.4) - if (!std.ascii.eqlIgnoreCase(node.data.host, criteria.host)) continue; + if (!std.ascii.eqlIgnoreCase(connection.host, criteria.host)) continue; - pool.acquireUnsafe(node); - return &node.data; + pool.acquireUnsafe(connection); + return connection; } return null; } /// Acquires an existing connection from the connection pool. This function is not threadsafe. - pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void { - pool.free.remove(node); + pub fn acquireUnsafe(pool: *ConnectionPool, connection: *Connection) void { + pool.free.remove(&connection.pool_node); pool.free_len -= 1; - pool.used.append(node); + pool.used.append(&connection.pool_node); } /// Acquires an existing connection from the connection pool. This function is threadsafe. - pub fn acquire(pool: *ConnectionPool, node: *Node) void { + pub fn acquire(pool: *ConnectionPool, connection: *Connection) void { pool.mutex.lock(); defer pool.mutex.unlock(); - return pool.acquireUnsafe(node); + return pool.acquireUnsafe(connection); } /// Tries to release a connection back to the connection pool. This function is threadsafe. @@ -108,38 +106,37 @@ pub const ConnectionPool = struct { pool.mutex.lock(); defer pool.mutex.unlock(); - const node: *Node = @fieldParentPtr("data", connection); - - pool.used.remove(node); + pool.used.remove(&connection.pool_node); - if (node.data.closing or pool.free_size == 0) { - node.data.close(allocator); - return allocator.destroy(node); + if (connection.closing or pool.free_size == 0) { + connection.close(allocator); + return allocator.destroy(connection); } if (pool.free_len >= pool.free_size) { - const popped = pool.free.popFirst() orelse unreachable; + const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); pool.free_len -= 1; - popped.data.close(allocator); + popped.close(allocator); allocator.destroy(popped); } - if (node.data.proxied) { - pool.free.prepend(node); // proxied connections go to the end of the queue, always try direct connections first + if (connection.proxied) { + // proxied connections go to the end of the queue, always try direct connections first + pool.free.prepend(&connection.pool_node); } else { - pool.free.append(node); + pool.free.append(&connection.pool_node); } pool.free_len += 1; } /// Adds a newly created node to the pool of used connections. This function is threadsafe. - pub fn addUsed(pool: *ConnectionPool, node: *Node) void { + pub fn addUsed(pool: *ConnectionPool, connection: *Connection) void { pool.mutex.lock(); defer pool.mutex.unlock(); - pool.used.append(node); + pool.used.append(&connection.pool_node); } /// Resizes the connection pool. This function is threadsafe. @@ -170,18 +167,18 @@ pub const ConnectionPool = struct { var next = pool.free.first; while (next) |node| { - defer allocator.destroy(node); + const connection: *Connection = @fieldParentPtr("pool_node", node); next = node.next; - - node.data.close(allocator); + connection.close(allocator); + allocator.destroy(connection); } next = pool.used.first; while (next) |node| { - defer allocator.destroy(node); + const connection: *Connection = @fieldParentPtr("pool_node", node); next = node.next; - - node.data.close(allocator); + connection.close(allocator); + allocator.destroy(node); } pool.* = undefined; @@ -194,6 +191,9 @@ pub const Connection = struct { /// undefined unless protocol is tls. tls_client: if (!disable_tls) *std.crypto.tls.Client else void, + /// Entry in `ConnectionPool.used` or `ConnectionPool.free`. + pool_node: std.DoublyLinkedList.Node, + /// The protocol that this connection is using. protocol: Protocol, @@ -1326,9 +1326,8 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec if (disable_tls and protocol == .tls) return error.TlsInitializationFailed; - const conn = try client.allocator.create(ConnectionPool.Node); + const conn = try client.allocator.create(Connection); errdefer client.allocator.destroy(conn); - conn.* = .{ .data = undefined }; const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { error.ConnectionRefused => return error.ConnectionRefused, @@ -1343,21 +1342,23 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec }; errdefer stream.close(); - conn.data = .{ + conn.* = .{ .stream = stream, .tls_client = undefined, .protocol = protocol, .host = try client.allocator.dupe(u8, host), .port = port, + + .pool_node = .{}, }; - errdefer client.allocator.free(conn.data.host); + errdefer client.allocator.free(conn.host); if (protocol == .tls) { if (disable_tls) unreachable; - conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client); - errdefer client.allocator.destroy(conn.data.tls_client); + conn.tls_client = try client.allocator.create(std.crypto.tls.Client); + errdefer client.allocator.destroy(conn.tls_client); const ssl_key_log_file: ?std.fs.File = if (std.options.http_enable_ssl_key_log_file) ssl_key_log_file: { const ssl_key_log_path = std.process.getEnvVarOwned(client.allocator, "SSLKEYLOGFILE") catch |err| switch (err) { @@ -1375,19 +1376,19 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec } else null; errdefer if (ssl_key_log_file) |key_log_file| key_log_file.close(); - conn.data.tls_client.* = std.crypto.tls.Client.init(stream, .{ + conn.tls_client.* = std.crypto.tls.Client.init(stream, .{ .host = .{ .explicit = host }, .ca = .{ .bundle = client.ca_bundle }, .ssl_key_log_file = ssl_key_log_file, }) catch return error.TlsInitializationFailed; // This is appropriate for HTTPS because the HTTP headers contain // the content length which is used to detect truncation attacks. - conn.data.tls_client.allow_truncation_attacks = true; + conn.tls_client.allow_truncation_attacks = true; } client.connection_pool.addUsed(conn); - return &conn.data; + return conn; } pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError; |
