aboutsummaryrefslogtreecommitdiff
path: root/lib/std/http
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-04-03 15:57:35 -0700
committerAndrew Kelley <andrew@ziglang.org>2025-04-03 15:57:35 -0700
commit337e1109f5c2894aad9519ee9f7ff1f1f4c65b56 (patch)
tree222eaf622275f517d143802e616295a760f1e644 /lib/std/http
parent3b77a845f9a5409d19a9e330b82eece8af4ac18f (diff)
downloadzig-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.zig87
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;