| ... | @@ -206,34 +206,16 @@ pub fn connect( | ... | @@ -206,34 +206,16 @@ pub fn connect( |
| 206 | port: u16, | 206 | port: u16, |
| 207 | options: IpAddress.ConnectOptions, | 207 | options: IpAddress.ConnectOptions, |
| 208 | ) ConnectError!Stream { | 208 | ) ConnectError!Stream { |
| 209 | var canonical_name_buffer: [max_len]u8 = undefined; | 209 | var connect_many_buffer: [32]ConnectManyResult = undefined; |
| 210 | var results_buffer: [32]HostName.LookupResult = undefined; | 210 | var connect_many_queue: Io.Queue(ConnectManyResult) = .init(&connect_many_buffer); |
| 211 | var results: Io.Queue(LookupResult) = .init(&results_buffer); | | |
| 212 | | | |
| 213 | var lookup_task = io.async(HostName.lookup, .{ host_name, io, &results, .{ | | |
| 214 | .port = port, | | |
| 215 | .canonical_name_buffer = &canonical_name_buffer, | | |
| 216 | } }); | | |
| 217 | defer lookup_task.cancel(io); | | |
| 218 | | | |
| 219 | const Result = union(enum) { connect_result: IpAddress.ConnectError!Stream }; | | |
| 220 | var finished_task_buffer: [results_buffer.len]Result = undefined; | | |
| 221 | var select: Io.Select(Result) = .init(io, &finished_task_buffer); | | |
| 222 | defer select.cancel(); | | |
| 223 | | 211 | |
| 224 | while (results.getOne(io)) |result| switch (result) { | 212 | var connect_many = io.async(connectMany, .{ host_name, io, port, &connect_many_queue, options }); |
| 225 | .address => |address| select.async(.connect_result, IpAddress.connect, .{ address, io, options }), | 213 | defer connect_many.cancel(io); |
| 226 | .canonical_name => continue, | | |
| 227 | .end => |lookup_result| { | | |
| 228 | try lookup_result; | | |
| 229 | break; | | |
| 230 | }, | | |
| 231 | } else |err| return err; | | |
| 232 | | 214 | |
| 233 | var aggregate_error: ConnectError = error.UnknownHostName; | 215 | var aggregate_error: ConnectError = error.UnknownHostName; |
| 234 | | 216 | |
| 235 | while (select.outstanding != 0) switch (try select.wait()) { | 217 | while (connect_many_queue.getOne(io)) |result| switch (result) { |
| 236 | .connect_result => |connect_result| if (connect_result) |stream| return stream else |err| switch (err) { | 218 | .connection => |connection| if (connection) |stream| return stream else |err| switch (err) { |
| 237 | error.SystemResources => |e| return e, | 219 | error.SystemResources => |e| return e, |
| 238 | error.OptionUnsupported => |e| return e, | 220 | error.OptionUnsupported => |e| return e, |
| 239 | error.ProcessFdQuotaExceeded => |e| return e, | 221 | error.ProcessFdQuotaExceeded => |e| return e, |
| ... | @@ -242,9 +224,59 @@ pub fn connect( | ... | @@ -242,9 +224,59 @@ pub fn connect( |
| 242 | error.WouldBlock => return error.Unexpected, | 224 | error.WouldBlock => return error.Unexpected, |
| 243 | else => |e| aggregate_error = e, | 225 | else => |e| aggregate_error = e, |
| 244 | }, | 226 | }, |
| 245 | }; | 227 | .end => |end| { |
| | 228 | try end; |
| | 229 | return aggregate_error; |
| | 230 | }, |
| | 231 | } else |err| return err; |
| | 232 | } |
| | 233 | |
| | 234 | pub const ConnectManyResult = union(enum) { |
| | 235 | connection: IpAddress.ConnectError!Stream, |
| | 236 | end: ConnectError!void, |
| | 237 | }; |
| | 238 | |
| | 239 | /// Asynchronously establishes a connection to all IP addresses associated with |
| | 240 | /// a host name, adding them to a results queue upon completion. |
| | 241 | pub fn connectMany( |
| | 242 | host_name: HostName, |
| | 243 | io: Io, |
| | 244 | port: u16, |
| | 245 | results: *Io.Queue(ConnectManyResult), |
| | 246 | options: IpAddress.ConnectOptions, |
| | 247 | ) void { |
| | 248 | var canonical_name_buffer: [max_len]u8 = undefined; |
| | 249 | var lookup_buffer: [32]HostName.LookupResult = undefined; |
| | 250 | var lookup_queue: Io.Queue(LookupResult) = .init(&lookup_buffer); |
| | 251 | |
| | 252 | host_name.lookup(io, &lookup_queue, .{ |
| | 253 | .port = port, |
| | 254 | .canonical_name_buffer = &canonical_name_buffer, |
| | 255 | }); |
| | 256 | |
| | 257 | var group: Io.Group = .init; |
| | 258 | defer group.cancel(io); |
| | 259 | |
| | 260 | while (lookup_queue.getOne(io)) |dns_result| switch (dns_result) { |
| | 261 | .address => |address| group.async(io, enqueueConnection, .{ address, io, results, options }), |
| | 262 | .canonical_name => continue, |
| | 263 | .end => |lookup_result| { |
| | 264 | group.wait(io); |
| | 265 | results.putOneUncancelable(io, .{ .end = lookup_result }); |
| | 266 | return; |
| | 267 | }, |
| | 268 | } else |err| switch (err) { |
| | 269 | error.Canceled => |e| results.putOneUncancelable(io, .{ .end = e }), |
| | 270 | } |
| | 271 | } |
| 246 | | 272 | |
| 247 | return aggregate_error; | 273 | fn enqueueConnection( |
| | 274 | address: IpAddress, |
| | 275 | io: Io, |
| | 276 | queue: *Io.Queue(ConnectManyResult), |
| | 277 | options: IpAddress.ConnectOptions, |
| | 278 | ) void { |
| | 279 | queue.putOneUncancelable(io, .{ .connection = address.connect(io, options) }); |
| 248 | } | 280 | } |
| 249 | | 281 | |
| 250 | pub const ResolvConf = struct { | 282 | pub const ResolvConf = struct { |