| author | |
| committer | |
| log | 337e1109f5c2894aad9519ee9f7ff1f1f4c65b56 |
| tree | 222eaf622275f517d143802e616295a760f1e644 |
| parent | 3b77a845f9a5409d19a9e330b82eece8af4ac18f |
this is trivial to tack on, and in my experience it is rarely wanted.2 files changed, 110 insertions(+), 99 deletions(-)
lib/std/DoublyLinkedList.zig+66-56| ... | @@ -17,7 +17,6 @@ const DoublyLinkedList = @This(); | ... | @@ -17,7 +17,6 @@ const DoublyLinkedList = @This(); |
| 17 | 17 | ||
| 18 | first: ?*Node = null, | 18 | first: ?*Node = null, |
| 19 | last: ?*Node = null, | 19 | last: ?*Node = null, |
| 20 | len: usize = 0, | ||
| 21 | 20 | ||
| 22 | /// This struct contains only the prev and next pointers and not any data | 21 | /// This struct contains only the prev and next pointers and not any data |
| 23 | /// payload. The intended usage is to embed it intrusively into another data | 22 | /// payload. The intended usage is to embed it intrusively into another data |
| ... | @@ -39,8 +38,6 @@ pub fn insertAfter(list: *DoublyLinkedList, existing_node: *Node, new_node: *Nod | ... | @@ -39,8 +38,6 @@ pub fn insertAfter(list: *DoublyLinkedList, existing_node: *Node, new_node: *Nod |
| 39 | list.last = new_node; | 38 | list.last = new_node; |
| 40 | } | 39 | } |
| 41 | existing_node.next = new_node; | 40 | existing_node.next = new_node; |
| 42 | |||
| 43 | list.len += 1; | ||
| 44 | } | 41 | } |
| 45 | 42 | ||
| 46 | pub fn insertBefore(list: *DoublyLinkedList, existing_node: *Node, new_node: *Node) void { | 43 | pub fn insertBefore(list: *DoublyLinkedList, existing_node: *Node, new_node: *Node) void { |
| ... | @@ -55,8 +52,6 @@ pub fn insertBefore(list: *DoublyLinkedList, existing_node: *Node, new_node: *No | ... | @@ -55,8 +52,6 @@ pub fn insertBefore(list: *DoublyLinkedList, existing_node: *Node, new_node: *No |
| 55 | list.first = new_node; | 52 | list.first = new_node; |
| 56 | } | 53 | } |
| 57 | existing_node.prev = new_node; | 54 | existing_node.prev = new_node; |
| 58 | |||
| 59 | list.len += 1; | ||
| 60 | } | 55 | } |
| 61 | 56 | ||
| 62 | /// Concatenate list2 onto the end of list1, removing all entries from the former. | 57 | /// Concatenate list2 onto the end of list1, removing all entries from the former. |
| ... | @@ -69,16 +64,13 @@ pub fn concatByMoving(list1: *DoublyLinkedList, list2: *DoublyLinkedList) void { | ... | @@ -69,16 +64,13 @@ pub fn concatByMoving(list1: *DoublyLinkedList, list2: *DoublyLinkedList) void { |
| 69 | if (list1.last) |l1_last| { | 64 | if (list1.last) |l1_last| { |
| 70 | l1_last.next = list2.first; | 65 | l1_last.next = list2.first; |
| 71 | l2_first.prev = list1.last; | 66 | l2_first.prev = list1.last; |
| 72 | list1.len += list2.len; | ||
| 73 | } else { | 67 | } else { |
| 74 | // list1 was empty | 68 | // list1 was empty |
| 75 | list1.first = list2.first; | 69 | list1.first = list2.first; |
| 76 | list1.len = list2.len; | ||
| 77 | } | 70 | } |
| 78 | list1.last = list2.last; | 71 | list1.last = list2.last; |
| 79 | list2.first = null; | 72 | list2.first = null; |
| 80 | list2.last = null; | 73 | list2.last = null; |
| 81 | list2.len = 0; | ||
| 82 | } | 74 | } |
| 83 | 75 | ||
| 84 | /// Insert a new node at the end of the list. | 76 | /// Insert a new node at the end of the list. |
| ... | @@ -109,8 +101,6 @@ pub fn prepend(list: *DoublyLinkedList, new_node: *Node) void { | ... | @@ -109,8 +101,6 @@ pub fn prepend(list: *DoublyLinkedList, new_node: *Node) void { |
| 109 | list.last = new_node; | 101 | list.last = new_node; |
| 110 | new_node.prev = null; | 102 | new_node.prev = null; |
| 111 | new_node.next = null; | 103 | new_node.next = null; |
| 112 | |||
| 113 | list.len = 1; | ||
| 114 | } | 104 | } |
| 115 | } | 105 | } |
| 116 | 106 | ||
| ... | @@ -134,9 +124,6 @@ pub fn remove(list: *DoublyLinkedList, node: *Node) void { | ... | @@ -134,9 +124,6 @@ pub fn remove(list: *DoublyLinkedList, node: *Node) void { |
| 134 | // Last element of the list. | 124 | // Last element of the list. |
| 135 | list.last = node.prev; | 125 | list.last = node.prev; |
| 136 | } | 126 | } |
| 137 | |||
| 138 | list.len -= 1; | ||
| 139 | assert(list.len == 0 or (list.first != null and list.last != null)); | ||
| 140 | } | 127 | } |
| 141 | 128 | ||
| 142 | /// Remove and return the last node in the list. | 129 | /// Remove and return the last node in the list. |
| ... | @@ -159,28 +146,43 @@ pub fn popFirst(list: *DoublyLinkedList) ?*Node { | ... | @@ -159,28 +146,43 @@ pub fn popFirst(list: *DoublyLinkedList) ?*Node { |
| 159 | return first; | 146 | return first; |
| 160 | } | 147 | } |
| 161 | 148 | ||
| 162 | test "basic DoublyLinkedList test" { | 149 | /// Iterate over all nodes, returning the count. |
| 163 | const L = DoublyLinkedList(u32); | 150 | /// |
| 164 | var list = L{}; | 151 | /// This operation is O(N). Consider tracking the length separately rather than |
| 165 | 152 | /// computing it. | |
| 166 | var one = L.Node{ .data = 1 }; | 153 | pub fn len(list: DoublyLinkedList) usize { |
| 167 | var two = L.Node{ .data = 2 }; | 154 | var count: usize = 0; |
| 168 | var three = L.Node{ .data = 3 }; | 155 | var it: ?*const Node = list.first; |
| 169 | var four = L.Node{ .data = 4 }; | 156 | while (it) |n| : (it = n.next) count += 1; |
| 170 | var five = L.Node{ .data = 5 }; | 157 | return count; |
| 158 | } | ||
| 171 | 159 | ||
| 172 | list.append(&two); // {2} | 160 | test "basics" { |
| 173 | list.append(&five); // {2, 5} | 161 | const L = struct { |
| 174 | list.prepend(&one); // {1, 2, 5} | 162 | data: u32, |
| 175 | list.insertBefore(&five, &four); // {1, 2, 4, 5} | 163 | node: DoublyLinkedList.Node = .{}, |
| 176 | list.insertAfter(&two, &three); // {1, 2, 3, 4, 5} | 164 | }; |
| 165 | var list: DoublyLinkedList = .{}; | ||
| 166 | |||
| 167 | var one: L = .{ .data = 1 }; | ||
| 168 | var two: L = .{ .data = 2 }; | ||
| 169 | var three: L = .{ .data = 3 }; | ||
| 170 | var four: L = .{ .data = 4 }; | ||
| 171 | var five: L = .{ .data = 5 }; | ||
| 172 | |||
| 173 | list.append(&two.node); // {2} | ||
| 174 | list.append(&five.node); // {2, 5} | ||
| 175 | list.prepend(&one.node); // {1, 2, 5} | ||
| 176 | list.insertBefore(&five.node, &four.node); // {1, 2, 4, 5} | ||
| 177 | list.insertAfter(&two.node, &three.node); // {1, 2, 3, 4, 5} | ||
| 177 | 178 | ||
| 178 | // Traverse forwards. | 179 | // Traverse forwards. |
| 179 | { | 180 | { |
| 180 | var it = list.first; | 181 | var it = list.first; |
| 181 | var index: u32 = 1; | 182 | var index: u32 = 1; |
| 182 | while (it) |node| : (it = node.next) { | 183 | while (it) |node| : (it = node.next) { |
| 183 | try testing.expect(node.data == index); | 184 | const l: *L = @fieldParentPtr("node", node); |
| 185 | try testing.expect(l.data == index); | ||
| 184 | index += 1; | 186 | index += 1; |
| 185 | } | 187 | } |
| 186 | } | 188 | } |
| ... | @@ -190,51 +192,56 @@ test "basic DoublyLinkedList test" { | ... | @@ -190,51 +192,56 @@ test "basic DoublyLinkedList test" { |
| 190 | var it = list.last; | 192 | var it = list.last; |
| 191 | var index: u32 = 1; | 193 | var index: u32 = 1; |
| 192 | while (it) |node| : (it = node.prev) { | 194 | while (it) |node| : (it = node.prev) { |
| 193 | try testing.expect(node.data == (6 - index)); | 195 | const l: *L = @fieldParentPtr("node", node); |
| 196 | try testing.expect(l.data == (6 - index)); | ||
| 194 | index += 1; | 197 | index += 1; |
| 195 | } | 198 | } |
| 196 | } | 199 | } |
| 197 | 200 | ||
| 198 | _ = list.popFirst(); // {2, 3, 4, 5} | 201 | _ = list.popFirst(); // {2, 3, 4, 5} |
| 199 | _ = list.pop(); // {2, 3, 4} | 202 | _ = list.pop(); // {2, 3, 4} |
| 200 | list.remove(&three); // {2, 4} | 203 | list.remove(&three.node); // {2, 4} |
| 201 | 204 | ||
| 202 | try testing.expect(list.first.?.data == 2); | 205 | try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?)).data == 2); |
| 203 | try testing.expect(list.last.?.data == 4); | 206 | try testing.expect(@as(*L, @fieldParentPtr("node", list.last.?)).data == 4); |
| 204 | try testing.expect(list.len == 2); | 207 | try testing.expect(list.len() == 2); |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 207 | test "DoublyLinkedList concatenation" { | 210 | test "concatenation" { |
| 208 | const L = DoublyLinkedList(u32); | 211 | const L = struct { |
| 209 | var list1 = L{}; | 212 | data: u32, |
| 210 | var list2 = L{}; | 213 | node: DoublyLinkedList.Node = .{}, |
| 211 | 214 | }; | |
| 212 | var one = L.Node{ .data = 1 }; | 215 | var list1: DoublyLinkedList = .{}; |
| 213 | var two = L.Node{ .data = 2 }; | 216 | var list2: DoublyLinkedList = .{}; |
| 214 | var three = L.Node{ .data = 3 }; | 217 | |
| 215 | var four = L.Node{ .data = 4 }; | 218 | var one: L = .{ .data = 1 }; |
| 216 | var five = L.Node{ .data = 5 }; | 219 | var two: L = .{ .data = 2 }; |
| 217 | 220 | var three: L = .{ .data = 3 }; | |
| 218 | list1.append(&one); | 221 | var four: L = .{ .data = 4 }; |
| 219 | list1.append(&two); | 222 | var five: L = .{ .data = 5 }; |
| 220 | list2.append(&three); | 223 | |
| 221 | list2.append(&four); | 224 | list1.append(&one.node); |
| 222 | list2.append(&five); | 225 | list1.append(&two.node); |
| 226 | list2.append(&three.node); | ||
| 227 | list2.append(&four.node); | ||
| 228 | list2.append(&five.node); | ||
| 223 | 229 | ||
| 224 | list1.concatByMoving(&list2); | 230 | list1.concatByMoving(&list2); |
| 225 | 231 | ||
| 226 | try testing.expect(list1.last == &five); | 232 | try testing.expect(list1.last == &five.node); |
| 227 | try testing.expect(list1.len == 5); | 233 | try testing.expect(list1.len() == 5); |
| 228 | try testing.expect(list2.first == null); | 234 | try testing.expect(list2.first == null); |
| 229 | try testing.expect(list2.last == null); | 235 | try testing.expect(list2.last == null); |
| 230 | try testing.expect(list2.len == 0); | 236 | try testing.expect(list2.len() == 0); |
| 231 | 237 | ||
| 232 | // Traverse forwards. | 238 | // Traverse forwards. |
| 233 | { | 239 | { |
| 234 | var it = list1.first; | 240 | var it = list1.first; |
| 235 | var index: u32 = 1; | 241 | var index: u32 = 1; |
| 236 | while (it) |node| : (it = node.next) { | 242 | while (it) |node| : (it = node.next) { |
| 237 | try testing.expect(node.data == index); | 243 | const l: *L = @fieldParentPtr("node", node); |
| 244 | try testing.expect(l.data == index); | ||
| 238 | index += 1; | 245 | index += 1; |
| 239 | } | 246 | } |
| 240 | } | 247 | } |
| ... | @@ -244,7 +251,8 @@ test "DoublyLinkedList concatenation" { | ... | @@ -244,7 +251,8 @@ test "DoublyLinkedList concatenation" { |
| 244 | var it = list1.last; | 251 | var it = list1.last; |
| 245 | var index: u32 = 1; | 252 | var index: u32 = 1; |
| 246 | while (it) |node| : (it = node.prev) { | 253 | while (it) |node| : (it = node.prev) { |
| 247 | try testing.expect(node.data == (6 - index)); | 254 | const l: *L = @fieldParentPtr("node", node); |
| 255 | try testing.expect(l.data == (6 - index)); | ||
| 248 | index += 1; | 256 | index += 1; |
| 249 | } | 257 | } |
| 250 | } | 258 | } |
| ... | @@ -257,7 +265,8 @@ test "DoublyLinkedList concatenation" { | ... | @@ -257,7 +265,8 @@ test "DoublyLinkedList concatenation" { |
| 257 | var it = list2.first; | 265 | var it = list2.first; |
| 258 | var index: u32 = 1; | 266 | var index: u32 = 1; |
| 259 | while (it) |node| : (it = node.next) { | 267 | while (it) |node| : (it = node.next) { |
| 260 | try testing.expect(node.data == index); | 268 | const l: *L = @fieldParentPtr("node", node); |
| 269 | try testing.expect(l.data == index); | ||
| 261 | index += 1; | 270 | index += 1; |
| 262 | } | 271 | } |
| 263 | } | 272 | } |
| ... | @@ -267,7 +276,8 @@ test "DoublyLinkedList concatenation" { | ... | @@ -267,7 +276,8 @@ test "DoublyLinkedList concatenation" { |
| 267 | var it = list2.last; | 276 | var it = list2.last; |
| 268 | var index: u32 = 1; | 277 | var index: u32 = 1; |
| 269 | while (it) |node| : (it = node.prev) { | 278 | while (it) |node| : (it = node.prev) { |
| 270 | try testing.expect(node.data == (6 - index)); | 279 | const l: *L = @fieldParentPtr("node", node); |
| 280 | try testing.expect(l.data == (6 - index)); | ||
| 271 | index += 1; | 281 | index += 1; |
| 272 | } | 282 | } |
| 273 | } | 283 | } |
lib/std/http/Client.zig+44-43| ... | @@ -46,9 +46,9 @@ https_proxy: ?*Proxy = null, | ... | @@ -46,9 +46,9 @@ https_proxy: ?*Proxy = null, |
| 46 | pub const ConnectionPool = struct { | 46 | pub const ConnectionPool = struct { |
| 47 | mutex: std.Thread.Mutex = .{}, | 47 | mutex: std.Thread.Mutex = .{}, |
| 48 | /// Open connections that are currently in use. | 48 | /// Open connections that are currently in use. |
| 49 | used: Queue = .{}, | 49 | used: std.DoublyLinkedList = .{}, |
| 50 | /// Open connections that are not currently in use. | 50 | /// Open connections that are not currently in use. |
| 51 | free: Queue = .{}, | 51 | free: std.DoublyLinkedList = .{}, |
| 52 | free_len: usize = 0, | 52 | free_len: usize = 0, |
| 53 | free_size: usize = 32, | 53 | free_size: usize = 32, |
| 54 | 54 | ||
| ... | @@ -59,9 +59,6 @@ pub const ConnectionPool = struct { | ... | @@ -59,9 +59,6 @@ pub const ConnectionPool = struct { |
| 59 | protocol: Connection.Protocol, | 59 | protocol: Connection.Protocol, |
| 60 | }; | 60 | }; |
| 61 | 61 | ||
| 62 | const Queue = std.DoublyLinkedList(Connection); | ||
| 63 | pub const Node = Queue.Node; | ||
| 64 | |||
| 65 | /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. | 62 | /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. |
| 66 | /// If no connection is found, null is returned. | 63 | /// If no connection is found, null is returned. |
| 67 | pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Connection { | 64 | pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Connection { |
| ... | @@ -70,33 +67,34 @@ pub const ConnectionPool = struct { | ... | @@ -70,33 +67,34 @@ pub const ConnectionPool = struct { |
| 70 | 67 | ||
| 71 | var next = pool.free.last; | 68 | var next = pool.free.last; |
| 72 | while (next) |node| : (next = node.prev) { | 69 | while (next) |node| : (next = node.prev) { |
| 73 | if (node.data.protocol != criteria.protocol) continue; | 70 | const connection: *Connection = @fieldParentPtr("pool_node", node); |
| 74 | if (node.data.port != criteria.port) continue; | 71 | if (connection.protocol != criteria.protocol) continue; |
| 72 | if (connection.port != criteria.port) continue; | ||
| 75 | 73 | ||
| 76 | // Domain names are case-insensitive (RFC 5890, Section 2.3.2.4) | 74 | // Domain names are case-insensitive (RFC 5890, Section 2.3.2.4) |
| 77 | if (!std.ascii.eqlIgnoreCase(node.data.host, criteria.host)) continue; | 75 | if (!std.ascii.eqlIgnoreCase(connection.host, criteria.host)) continue; |
| 78 | 76 | ||
| 79 | pool.acquireUnsafe(node); | 77 | pool.acquireUnsafe(connection); |
| 80 | return &node.data; | 78 | return connection; |
| 81 | } | 79 | } |
| 82 | 80 | ||
| 83 | return null; | 81 | return null; |
| 84 | } | 82 | } |
| 85 | 83 | ||
| 86 | /// Acquires an existing connection from the connection pool. This function is not threadsafe. | 84 | /// Acquires an existing connection from the connection pool. This function is not threadsafe. |
| 87 | pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void { | 85 | pub fn acquireUnsafe(pool: *ConnectionPool, connection: *Connection) void { |
| 88 | pool.free.remove(node); | 86 | pool.free.remove(&connection.pool_node); |
| 89 | pool.free_len -= 1; | 87 | pool.free_len -= 1; |
| 90 | 88 | ||
| 91 | pool.used.append(node); | 89 | pool.used.append(&connection.pool_node); |
| 92 | } | 90 | } |
| 93 | 91 | ||
| 94 | /// Acquires an existing connection from the connection pool. This function is threadsafe. | 92 | /// Acquires an existing connection from the connection pool. This function is threadsafe. |
| 95 | pub fn acquire(pool: *ConnectionPool, node: *Node) void { | 93 | pub fn acquire(pool: *ConnectionPool, connection: *Connection) void { |
| 96 | pool.mutex.lock(); | 94 | pool.mutex.lock(); |
| 97 | defer pool.mutex.unlock(); | 95 | defer pool.mutex.unlock(); |
| 98 | 96 | ||
| 99 | return pool.acquireUnsafe(node); | 97 | return pool.acquireUnsafe(connection); |
| 100 | } | 98 | } |
| 101 | 99 | ||
| 102 | /// Tries to release a connection back to the connection pool. This function is threadsafe. | 100 | /// Tries to release a connection back to the connection pool. This function is threadsafe. |
| ... | @@ -108,38 +106,37 @@ pub const ConnectionPool = struct { | ... | @@ -108,38 +106,37 @@ pub const ConnectionPool = struct { |
| 108 | pool.mutex.lock(); | 106 | pool.mutex.lock(); |
| 109 | defer pool.mutex.unlock(); | 107 | defer pool.mutex.unlock(); |
| 110 | 108 | ||
| 111 | const node: *Node = @fieldParentPtr("data", connection); | 109 | pool.used.remove(&connection.pool_node); |
| 112 | |||
| 113 | pool.used.remove(node); | ||
| 114 | 110 | ||
| 115 | if (node.data.closing or pool.free_size == 0) { | 111 | if (connection.closing or pool.free_size == 0) { |
| 116 | node.data.close(allocator); | 112 | connection.close(allocator); |
| 117 | return allocator.destroy(node); | 113 | return allocator.destroy(connection); |
| 118 | } | 114 | } |
| 119 | 115 | ||
| 120 | if (pool.free_len >= pool.free_size) { | 116 | if (pool.free_len >= pool.free_size) { |
| 121 | const popped = pool.free.popFirst() orelse unreachable; | 117 | const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); |
| 122 | pool.free_len -= 1; | 118 | pool.free_len -= 1; |
| 123 | 119 | ||
| 124 | popped.data.close(allocator); | 120 | popped.close(allocator); |
| 125 | allocator.destroy(popped); | 121 | allocator.destroy(popped); |
| 126 | } | 122 | } |
| 127 | 123 | ||
| 128 | if (node.data.proxied) { | 124 | if (connection.proxied) { |
| 129 | pool.free.prepend(node); // proxied connections go to the end of the queue, always try direct connections first | 125 | // proxied connections go to the end of the queue, always try direct connections first |
| 126 | pool.free.prepend(&connection.pool_node); | ||
| 130 | } else { | 127 | } else { |
| 131 | pool.free.append(node); | 128 | pool.free.append(&connection.pool_node); |
| 132 | } | 129 | } |
| 133 | 130 | ||
| 134 | pool.free_len += 1; | 131 | pool.free_len += 1; |
| 135 | } | 132 | } |
| 136 | 133 | ||
| 137 | /// Adds a newly created node to the pool of used connections. This function is threadsafe. | 134 | /// Adds a newly created node to the pool of used connections. This function is threadsafe. |
| 138 | pub fn addUsed(pool: *ConnectionPool, node: *Node) void { | 135 | pub fn addUsed(pool: *ConnectionPool, connection: *Connection) void { |
| 139 | pool.mutex.lock(); | 136 | pool.mutex.lock(); |
| 140 | defer pool.mutex.unlock(); | 137 | defer pool.mutex.unlock(); |
| 141 | 138 | ||
| 142 | pool.used.append(node); | 139 | pool.used.append(&connection.pool_node); |
| 143 | } | 140 | } |
| 144 | 141 | ||
| 145 | /// Resizes the connection pool. This function is threadsafe. | 142 | /// Resizes the connection pool. This function is threadsafe. |
| ... | @@ -170,18 +167,18 @@ pub const ConnectionPool = struct { | ... | @@ -170,18 +167,18 @@ pub const ConnectionPool = struct { |
| 170 | 167 | ||
| 171 | var next = pool.free.first; | 168 | var next = pool.free.first; |
| 172 | while (next) |node| { | 169 | while (next) |node| { |
| 173 | defer allocator.destroy(node); | 170 | const connection: *Connection = @fieldParentPtr("pool_node", node); |
| 174 | next = node.next; | 171 | next = node.next; |
| 175 | 172 | connection.close(allocator); | |
| 176 | node.data.close(allocator); | 173 | allocator.destroy(connection); |
| 177 | } | 174 | } |
| 178 | 175 | ||
| 179 | next = pool.used.first; | 176 | next = pool.used.first; |
| 180 | while (next) |node| { | 177 | while (next) |node| { |
| 181 | defer allocator.destroy(node); | 178 | const connection: *Connection = @fieldParentPtr("pool_node", node); |
| 182 | next = node.next; | 179 | next = node.next; |
| 183 | 180 | connection.close(allocator); | |
| 184 | node.data.close(allocator); | 181 | allocator.destroy(node); |
| 185 | } | 182 | } |
| 186 | 183 | ||
| 187 | pool.* = undefined; | 184 | pool.* = undefined; |
| ... | @@ -194,6 +191,9 @@ pub const Connection = struct { | ... | @@ -194,6 +191,9 @@ pub const Connection = struct { |
| 194 | /// undefined unless protocol is tls. | 191 | /// undefined unless protocol is tls. |
| 195 | tls_client: if (!disable_tls) *std.crypto.tls.Client else void, | 192 | tls_client: if (!disable_tls) *std.crypto.tls.Client else void, |
| 196 | 193 | ||
| 194 | /// Entry in `ConnectionPool.used` or `ConnectionPool.free`. | ||
| 195 | pool_node: std.DoublyLinkedList.Node, | ||
| 196 | |||
| 197 | /// The protocol that this connection is using. | 197 | /// The protocol that this connection is using. |
| 198 | protocol: Protocol, | 198 | protocol: Protocol, |
| 199 | 199 | ||
| ... | @@ -1326,9 +1326,8 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec | ... | @@ -1326,9 +1326,8 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec |
| 1326 | if (disable_tls and protocol == .tls) | 1326 | if (disable_tls and protocol == .tls) |
| 1327 | return error.TlsInitializationFailed; | 1327 | return error.TlsInitializationFailed; |
| 1328 | 1328 | ||
| 1329 | const conn = try client.allocator.create(ConnectionPool.Node); | 1329 | const conn = try client.allocator.create(Connection); |
| 1330 | errdefer client.allocator.destroy(conn); | 1330 | errdefer client.allocator.destroy(conn); |
| 1331 | conn.* = .{ .data = undefined }; | ||
| 1332 | 1331 | ||
| 1333 | const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { | 1332 | const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { |
| 1334 | error.ConnectionRefused => return error.ConnectionRefused, | 1333 | error.ConnectionRefused => return error.ConnectionRefused, |
| ... | @@ -1343,21 +1342,23 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec | ... | @@ -1343,21 +1342,23 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec |
| 1343 | }; | 1342 | }; |
| 1344 | errdefer stream.close(); | 1343 | errdefer stream.close(); |
| 1345 | 1344 | ||
| 1346 | conn.data = .{ | 1345 | conn.* = .{ |
| 1347 | .stream = stream, | 1346 | .stream = stream, |
| 1348 | .tls_client = undefined, | 1347 | .tls_client = undefined, |
| 1349 | 1348 | ||
| 1350 | .protocol = protocol, | 1349 | .protocol = protocol, |
| 1351 | .host = try client.allocator.dupe(u8, host), | 1350 | .host = try client.allocator.dupe(u8, host), |
| 1352 | .port = port, | 1351 | .port = port, |
| 1352 | |||
| 1353 | .pool_node = .{}, | ||
| 1353 | }; | 1354 | }; |
| 1354 | errdefer client.allocator.free(conn.data.host); | 1355 | errdefer client.allocator.free(conn.host); |
| 1355 | 1356 | ||
| 1356 | if (protocol == .tls) { | 1357 | if (protocol == .tls) { |
| 1357 | if (disable_tls) unreachable; | 1358 | if (disable_tls) unreachable; |
| 1358 | 1359 | ||
| 1359 | conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client); | 1360 | conn.tls_client = try client.allocator.create(std.crypto.tls.Client); |
| 1360 | errdefer client.allocator.destroy(conn.data.tls_client); | 1361 | errdefer client.allocator.destroy(conn.tls_client); |
| 1361 | 1362 | ||
| 1362 | const ssl_key_log_file: ?std.fs.File = if (std.options.http_enable_ssl_key_log_file) ssl_key_log_file: { | 1363 | const ssl_key_log_file: ?std.fs.File = if (std.options.http_enable_ssl_key_log_file) ssl_key_log_file: { |
| 1363 | const ssl_key_log_path = std.process.getEnvVarOwned(client.allocator, "SSLKEYLOGFILE") catch |err| switch (err) { | 1364 | 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 | ... | @@ -1375,19 +1376,19 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec |
| 1375 | } else null; | 1376 | } else null; |
| 1376 | errdefer if (ssl_key_log_file) |key_log_file| key_log_file.close(); | 1377 | errdefer if (ssl_key_log_file) |key_log_file| key_log_file.close(); |
| 1377 | 1378 | ||
| 1378 | conn.data.tls_client.* = std.crypto.tls.Client.init(stream, .{ | 1379 | conn.tls_client.* = std.crypto.tls.Client.init(stream, .{ |
| 1379 | .host = .{ .explicit = host }, | 1380 | .host = .{ .explicit = host }, |
| 1380 | .ca = .{ .bundle = client.ca_bundle }, | 1381 | .ca = .{ .bundle = client.ca_bundle }, |
| 1381 | .ssl_key_log_file = ssl_key_log_file, | 1382 | .ssl_key_log_file = ssl_key_log_file, |
| 1382 | }) catch return error.TlsInitializationFailed; | 1383 | }) catch return error.TlsInitializationFailed; |
| 1383 | // This is appropriate for HTTPS because the HTTP headers contain | 1384 | // This is appropriate for HTTPS because the HTTP headers contain |
| 1384 | // the content length which is used to detect truncation attacks. | 1385 | // the content length which is used to detect truncation attacks. |
| 1385 | conn.data.tls_client.allow_truncation_attacks = true; | 1386 | conn.tls_client.allow_truncation_attacks = true; |
| 1386 | } | 1387 | } |
| 1387 | 1388 | ||
| 1388 | client.connection_pool.addUsed(conn); | 1389 | client.connection_pool.addUsed(conn); |
| 1389 | 1390 | ||
| 1390 | return &conn.data; | 1391 | return conn; |
| 1391 | } | 1392 | } |
| 1392 | 1393 | ||
| 1393 | pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError; | 1394 | pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError; |