diff --git a/lib/std/Io.zig b/lib/std/Io.zig index b11ee66cee..ae4634c812 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -660,7 +660,7 @@ pub const VTable = struct { listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.ListenOptions) net.ListenError!net.Server, accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Server.Connection, - netRead: *const fn (?*anyopaque, src: net.Stream, dest: *Io.Writer, limit: Io.Limit) net.Stream.Reader.Error!usize, + netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize, netClose: *const fn (?*anyopaque, stream: net.Stream) void, }; @@ -760,6 +760,11 @@ pub const File = struct { } return index; } + + pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError { + assert(std.fs.path.isAbsolute(absolute_path)); + return Dir.cwd().openFile(io, absolute_path, flags); + } }; pub const Timestamp = enum(i96) { @@ -1205,7 +1210,7 @@ pub fn asyncConcurrent( const Args = @TypeOf(args); const TypeErased = struct { fn start(context: *const anyopaque, result: *anyopaque) void { - const args_casted: *const Args = @alignCast(@ptrCast(context)); + const args_casted: *const Args = @ptrCast(@alignCast(context)); const result_casted: *Result = @ptrCast(@alignCast(result)); result_casted.* = @call(.auto, function, args_casted.*); } @@ -1234,7 +1239,7 @@ pub fn asyncDetached(io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf const Args = @TypeOf(args); const TypeErased = struct { fn start(context: *const anyopaque) void { - const args_casted: *const Args = @alignCast(@ptrCast(context)); + const args_casted: *const Args = @ptrCast(@alignCast(context)); @call(.auto, function, args_casted.*); } }; diff --git a/lib/std/Io/ThreadPool.zig b/lib/std/Io/ThreadPool.zig index 13eb2e7695..2affcdafb3 100644 --- a/lib/std/Io/ThreadPool.zig +++ b/lib/std/Io/ThreadPool.zig @@ -233,7 +233,7 @@ fn async( start(context.ptr, result.ptr); return null; } - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); const cpu_count = pool.cpu_count catch { return asyncConcurrent(userdata, result.len, result_alignment, context, context_alignment, start) catch { start(context.ptr, result.ptr); @@ -244,7 +244,7 @@ fn async( const context_offset = context_alignment.forward(@sizeOf(AsyncClosure)); const result_offset = result_alignment.forward(context_offset + context.len); const n = result_offset + result.len; - const closure: *AsyncClosure = @alignCast(@ptrCast(gpa.alignedAlloc(u8, .of(AsyncClosure), n) catch { + const closure: *AsyncClosure = @ptrCast(@alignCast(gpa.alignedAlloc(u8, .of(AsyncClosure), n) catch { start(context.ptr, result.ptr); return null; })); @@ -309,13 +309,13 @@ fn asyncConcurrent( ) error{OutOfMemory}!*Io.AnyFuture { if (builtin.single_threaded) unreachable; - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); const cpu_count = pool.cpu_count catch 1; const gpa = pool.allocator; const context_offset = context_alignment.forward(@sizeOf(AsyncClosure)); const result_offset = result_alignment.forward(context_offset + context.len); const n = result_offset + result_len; - const closure: *AsyncClosure = @alignCast(@ptrCast(try gpa.alignedAlloc(u8, .of(AsyncClosure), n))); + const closure: *AsyncClosure = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(AsyncClosure), n))); closure.* = .{ .func = start, @@ -399,11 +399,11 @@ fn asyncDetached( start: *const fn (context: *const anyopaque) void, ) void { if (builtin.single_threaded) return start(context.ptr); - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); const cpu_count = pool.cpu_count catch 1; const gpa = pool.allocator; const n = DetachedClosure.contextEnd(context_alignment, context.len); - const closure: *DetachedClosure = @alignCast(@ptrCast(gpa.alignedAlloc(u8, .of(DetachedClosure), n) catch { + const closure: *DetachedClosure = @ptrCast(@alignCast(gpa.alignedAlloc(u8, .of(DetachedClosure), n) catch { return start(context.ptr); })); closure.* = .{ @@ -451,7 +451,7 @@ fn await( result_alignment: std.mem.Alignment, ) void { _ = result_alignment; - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); const closure: *AsyncClosure = @ptrCast(@alignCast(any_future)); closure.waitAndFree(pool.allocator, result); } @@ -463,7 +463,7 @@ fn cancel( result_alignment: std.mem.Alignment, ) void { _ = result_alignment; - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); const closure: *AsyncClosure = @ptrCast(@alignCast(any_future)); switch (@atomicRmw( std.Thread.Id, @@ -486,7 +486,7 @@ fn cancel( } fn cancelRequested(userdata: ?*anyopaque) bool { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); _ = pool; const closure = current_closure orelse return false; return @atomicLoad(std.Thread.Id, &closure.cancel_tid, .acquire) == AsyncClosure.canceling_tid; @@ -520,7 +520,7 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut } fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); comptime assert(@TypeOf(cond.state) == u64); const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); const cond_state = &ints[0]; @@ -567,7 +567,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I } fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); _ = pool; comptime assert(@TypeOf(cond.state) == u64); const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); @@ -624,7 +624,7 @@ fn createFile( sub_path: []const u8, flags: Io.File.CreateFlags, ) Io.File.OpenError!Io.File { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); const fs_dir: std.fs.Dir = .{ .fd = dir.handle }; const fs_file = try fs_dir.createFile(sub_path, flags); @@ -637,7 +637,7 @@ fn openFile( sub_path: []const u8, flags: Io.File.OpenFlags, ) Io.File.OpenError!Io.File { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); const fs_dir: std.fs.Dir = .{ .fd = dir.handle }; const fs_file = try fs_dir.openFile(sub_path, flags); @@ -645,14 +645,14 @@ fn openFile( } fn closeFile(userdata: ?*anyopaque, file: Io.File) void { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); _ = pool; const fs_file: std.fs.File = .{ .handle = file.handle }; return fs_file.close(); } fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: posix.off_t) Io.File.PReadError!usize { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); const fs_file: std.fs.File = .{ .handle = file.handle }; return switch (offset) { @@ -662,7 +662,7 @@ fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: posix.off_t } fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); const fs_file: std.fs.File = .{ .handle = file.handle }; return switch (offset) { @@ -672,14 +672,14 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi } fn now(userdata: ?*anyopaque, clockid: posix.clockid_t) Io.ClockGetTimeError!Io.Timestamp { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); const timespec = try posix.clock_gettime(clockid); return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec); } fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); const deadline_nanoseconds: i96 = switch (deadline) { .duration => |duration| duration.nanoseconds, .timestamp => |timestamp| @intFromEnum(timestamp), @@ -704,7 +704,7 @@ fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) } fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); _ = pool; var reset_event: std.Thread.ResetEvent = .{}; @@ -736,7 +736,7 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { } fn listen(userdata: ?*anyopaque, address: Io.net.IpAddress, options: Io.net.ListenOptions) Io.net.ListenError!Io.net.Server { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); const nonblock: u32 = if (options.force_nonblocking) posix.SOCK.NONBLOCK else 0; @@ -776,7 +776,7 @@ fn listen(userdata: ?*anyopaque, address: Io.net.IpAddress, options: Io.net.List } fn accept(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.AcceptError!Io.net.Server.Connection { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); var storage: PosixAddress = undefined; @@ -788,17 +788,20 @@ fn accept(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.AcceptErr }; } -fn netReadPosix( - userdata: ?*anyopaque, - stream: Io.net.Stream, - w: *Io.Writer, - limit: Io.Limit, -) Io.net.Stream.Reader.Error!usize { - const pool: *Pool = @alignCast(@ptrCast(userdata)); +fn netReadPosix(userdata: ?*anyopaque, stream: Io.net.Stream, data: [][]u8) Io.net.Stream.Reader.Error!usize { + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; - const dest = try w.writableVectorPosix(&iovecs_buffer, limit); + var i: usize = 0; + for (data) |buf| { + if (iovecs_buffer.len - i == 0) break; + if (buf.len != 0) { + iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len }; + i += 1; + } + } + const dest = iovecs_buffer[0..i]; assert(dest[0].len > 0); const n = try posix.readv(stream.handle, dest); if (n == 0) return error.EndOfStream; @@ -812,7 +815,7 @@ fn netWritePosix( data: []const []const u8, splat: usize, ) Io.net.Stream.Writer.Error!usize { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); try pool.checkCancel(); var iovecs: [max_iovecs_len]posix.iovec_const = undefined; @@ -866,7 +869,7 @@ fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), } fn netClose(userdata: ?*anyopaque, stream: Io.net.Stream) void { - const pool: *Pool = @alignCast(@ptrCast(userdata)); + const pool: *Pool = @ptrCast(@alignCast(userdata)); _ = pool; const net_stream: std.net.Stream = .{ .handle = stream.handle }; return net_stream.close(); diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index 24c310edde..6f198fc449 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -2,6 +2,7 @@ const builtin = @import("builtin"); const native_os = builtin.os.tag; const std = @import("../std.zig"); const Io = std.Io; +const assert = std.debug.assert; pub const ListenError = std.net.Address.ListenError || Io.Cancelable; @@ -16,10 +17,233 @@ pub const ListenOptions = struct { force_nonblocking: bool = false, }; +/// An already-validated host name. +pub const HostName = struct { + /// Externally managed memory. Already checked to be within `max_len`. + bytes: []const u8, + + pub const max_len = 255; + + pub const InitError = error{ + NameTooLong, + InvalidHostName, + }; + + pub fn init(bytes: []const u8) InitError!HostName { + if (bytes.len > max_len) return error.NameTooLong; + if (!std.unicode.utf8ValidateSlice(bytes)) return error.InvalidHostName; + for (bytes) |byte| { + if (!std.ascii.isAscii(byte) or byte == '.' or byte == '-' or std.ascii.isAlphanumeric(byte)) { + continue; + } + return error.InvalidHostName; + } + return .{ .bytes = bytes }; + } + + pub const LookupOptions = struct { + port: u16, + /// Must have at least length 2. + addresses_buffer: []IpAddress, + /// If a buffer of at least `max_len` is not provided, `lookup` may + /// return successfully with zero-length `LookupResult.canonical_name_len`. + /// + /// Suggestion: if not interested in canonical name, pass an empty buffer; + /// otherwise pass a buffer of size `max_len`. + canonical_name_buffer: []u8, + /// `null` means either. + family: ?IpAddress.Tag = null, + }; + + pub const LookupError = Io.Cancelable || error{}; + + pub const LookupResult = struct { + /// How many `LookupOptions.addresses_buffer` elements are populated. + addresses_len: usize, + /// Length zero means no canonical name returned. + canonical_name_len: usize, + }; + + pub fn lookup(host_name: HostName, io: Io, options: LookupOptions) LookupError!LookupResult { + const name = host_name.bytes; + assert(name.len <= max_len); + assert(options.addresses_buffer.len >= 2); + + if (native_os == .windows) @compileError("TODO"); + if (builtin.link_libc) @compileError("TODO"); + if (native_os == .linux) { + if (options.family != .ip6) { + if (IpAddress.parseIp4(name, options.port)) |addr| { + options.addresses_buffer[0] = addr; + return .{ .addresses_len = 1, .canonical_name_len = 0 }; + } else |_| {} + } + if (options.family != .ip4) { + if (IpAddress.parseIp6(name, options.port)) |addr| { + options.addresses_buffer[0] = addr; + return .{ .addresses_len = 1, .canonical_name_len = 0 }; + } else |_| {} + } + { + const result = try lookupHosts(io, options); + if (result.addresses_len > 0) return sortLookupResults(options, result); + } + { + // RFC 6761 Section 6.3.3 + // Name resolution APIs and libraries SHOULD recognize + // localhost names as special and SHOULD always return the IP + // loopback address for address queries and negative responses + // for all other query types. + + // Check for equal to "localhost(.)" or ends in ".localhost(.)" + const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost"; + if (std.mem.endsWith(u8, name, localhost) and + (name.len == localhost.len or name[name.len - localhost.len] == '.')) + { + var i: usize = 0; + if (options.family != .ip6) { + options.addresses_buffer[i] = .{ .ip4 = .localhost(options.port) }; + i += 1; + } + if (options.family != .ip4) { + options.addresses_buffer[i] = .{ .ip6 = .localhost(options.port) }; + i += 1; + } + const canon_name = "localhost"; + options.canonical_name_buffer[0..canon_name.len].* = canon_name.*; + return sortLookupResults(options, .{ .addresses_len = i, .canonical_name_len = canon_name.len }); + } + } + { + const result = try lookupDns(io, options); + if (result.addresses_len > 0) return sortLookupResults(options, result); + } + return error.UnknownHostName; + } + @compileError("unimplemented"); + } + + fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult { + _ = options; + _ = result; + @panic("TODO"); + } + + fn lookupDns(io: Io, options: LookupOptions) !LookupResult { + _ = io; + _ = options; + @panic("TODO"); + } + + fn lookupHosts(io: Io, options: LookupOptions) !LookupResult { + const file = Io.File.openFileAbsoluteZ(io, "/etc/hosts", .{}) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.AccessDenied, + => return, + else => |e| return e, + }; + defer file.close(); + + var line_buf: [512]u8 = undefined; + var file_reader = file.reader(io, &line_buf); + return lookupHostsReader(options, &file_reader.interface) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ReadFailed => return file_reader.err.?, + }; + } + + fn lookupHostsReader(options: LookupOptions, reader: *Io.Reader) error{ReadFailed}!LookupResult { + var addresses_len: usize = 0; + var canonical_name_len: usize = 0; + while (true) { + const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) { + error.StreamTooLong => { + // Skip lines that are too long. + _ = reader.discardDelimiterInclusive('\n') catch |e| switch (e) { + error.EndOfStream => break, + error.ReadFailed => return error.ReadFailed, + }; + continue; + }, + error.ReadFailed => return error.ReadFailed, + error.EndOfStream => break, + }; + var split_it = std.mem.splitScalar(u8, line, '#'); + const no_comment_line = split_it.first(); + + var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t"); + const ip_text = line_it.next() orelse continue; + var first_name_text: ?[]const u8 = null; + while (line_it.next()) |name_text| { + if (std.mem.eql(u8, name_text, options.name)) { + if (first_name_text == null) first_name_text = name_text; + break; + } + } else continue; + + if (canonical_name_len == 0) { + if (HostName.init(first_name_text)) |name_text| { + if (name_text.len <= options.canonical_name_buffer.len) { + @memcpy(options.canonical_name_buffer[0..name_text.len], name_text); + canonical_name_len = name_text.len; + } + } + } + + if (options.family != .ip6) { + if (IpAddress.parseIp4(ip_text, options.port)) |addr| { + options.addresses_buffer[addresses_len] = addr; + addresses_len += 1; + if (options.addresses_buffer.len - addresses_len == 0) return .{ + .addresses_len = addresses_len, + .canonical_name_len = canonical_name_len, + }; + } else |_| {} + } + if (options.family != .ip4) { + if (IpAddress.parseIp6(ip_text, options.port)) |addr| { + options.addresses_buffer[addresses_len] = addr; + addresses_len += 1; + if (options.addresses_buffer.len - addresses_len == 0) return .{ + .addresses_len = addresses_len, + .canonical_name_len = canonical_name_len, + }; + } else |_| {} + } + } + } + + pub const ConnectTcpError = LookupError || IpAddress.ConnectTcpError; + + pub fn connectTcp(host_name: HostName, io: Io, port: u16) ConnectTcpError!Stream { + var addresses_buffer: [32]IpAddress = undefined; + + const results = try lookup(host_name, .{ + .port = port, + .addresses_buffer = &addresses_buffer, + .canonical_name_buffer = &.{}, + }); + const addresses = addresses_buffer[0..results.addresses_len]; + + if (addresses.len == 0) return error.UnknownHostName; + + for (addresses) |addr| { + return addr.connectTcp(io) catch |err| switch (err) { + error.ConnectionRefused => continue, + else => |e| return e, + }; + } + return error.ConnectionRefused; + } +}; + pub const IpAddress = union(enum) { ip4: Ip4Address, ip6: Ip6Address, + pub const Tag = @typeInfo(IpAddress).@"union".tag_type.?; + /// Parse the given IP address string into an `IpAddress` value. pub fn parse(name: []const u8, port: u16) !IpAddress { if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { @@ -94,6 +318,13 @@ pub const Ip4Address = struct { bytes: [4]u8, port: u16, + pub fn localhost(port: u16) Ip4Address { + return .{ + .bytes = .{ 127, 0, 0, 1 }, + .port = port, + }; + } + pub const ParseError = error{ Overflow, InvalidEnd, @@ -373,7 +604,10 @@ pub const Stream = struct { pub fn init(stream: Stream, buffer: []u8) Reader { return .{ .interface = .{ - .vtable = &.{ .stream = streamImpl }, + .vtable = &.{ + .stream = streamImpl, + .readVec = readVec, + }, .buffer = buffer, .seek = 0, .end = 0, @@ -384,9 +618,17 @@ pub const Stream = struct { } fn streamImpl(io_r: *Io.Reader, io_w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { + const dest = limit.slice(try io_w.writableSliceGreedy(1)); + var data: [1][]u8 = .{dest}; + const n = try readVec(io_r, &data); + io_w.advance(n); + return n; + } + + fn readVec(io_r: *Reader, data: [][]u8) Io.Reader.Error!usize { const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); const io = r.io; - return io.vtable.netRead(io.vtable.userdata, r.stream, io_w, limit); + return io.vtable.netReadVec(io.vtable.userdata, r.stream, io_r, data); } }; diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 5d6a75cb28..81bfbdcc2e 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -9,10 +9,10 @@ const builtin = @import("builtin"); const testing = std.testing; const http = std.http; const mem = std.mem; -const net = std.net; const Uri = std.Uri; const Allocator = mem.Allocator; const assert = std.debug.assert; +const Io = std.Io; const Writer = std.Io.Writer; const Reader = std.Io.Reader; @@ -22,6 +22,8 @@ pub const disable_tls = std.options.http_disable_tls; /// Used for all client allocations. Must be thread-safe. allocator: Allocator, +/// Used for opening TCP connections. +io: Io, ca_bundle: if (disable_tls) void else std.crypto.Certificate.Bundle = if (disable_tls) {} else .{}, ca_bundle_mutex: std.Thread.Mutex = .{}, @@ -225,8 +227,8 @@ pub const Protocol = enum { pub const Connection = struct { client: *Client, - stream_writer: net.Stream.Writer, - stream_reader: net.Stream.Reader, + stream_writer: Io.net.Stream.Writer, + stream_reader: Io.net.Stream.Reader, /// Entry in `ConnectionPool.used` or `ConnectionPool.free`. pool_node: std.DoublyLinkedList.Node, port: u16, @@ -242,7 +244,7 @@ pub const Connection = struct { client: *Client, remote_host: []const u8, port: u16, - stream: net.Stream, + stream: Io.net.Stream, ) error{OutOfMemory}!*Plain { const gpa = client.allocator; const alloc_len = allocLen(client, remote_host.len); @@ -295,7 +297,7 @@ pub const Connection = struct { client: *Client, remote_host: []const u8, port: u16, - stream: net.Stream, + stream: Io.net.Stream, ) error{ OutOfMemory, TlsInitializationFailed }!*Tls { const gpa = client.allocator; const alloc_len = allocLen(client, remote_host.len); @@ -363,7 +365,7 @@ pub const Connection = struct { } }; - pub const ReadError = std.crypto.tls.Client.ReadError || std.net.Stream.ReadError; + pub const ReadError = std.crypto.tls.Client.ReadError || Io.net.Stream.ReadError; pub fn getReadError(c: *const Connection) ?ReadError { return switch (c.protocol) { @@ -378,8 +380,8 @@ pub const Connection = struct { }; } - fn getStream(c: *Connection) net.Stream { - return c.stream_reader.getStream(); + fn getStream(c: *Connection) Io.net.Stream { + return c.stream_reader.stream; } pub fn host(c: *Connection) []u8 { @@ -1409,7 +1411,7 @@ pub fn connectTcp( } pub const ConnectTcpOptions = struct { - host: []const u8, + host: Io.net.HostName, port: u16, protocol: Protocol, @@ -1418,7 +1420,7 @@ pub const ConnectTcpOptions = struct { }; pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection { - const host = options.host; + const host = options.host_name; const port = options.port; const protocol = options.protocol; @@ -1431,7 +1433,7 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp .protocol = protocol, })) |conn| return conn; - const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { + const stream = host.connectTcp(client.io, port) catch |err| switch (err) { error.ConnectionRefused => return error.ConnectionRefused, error.NetworkUnreachable => return error.NetworkUnreachable, error.ConnectionTimedOut => return error.ConnectionTimedOut, @@ -1440,6 +1442,7 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp error.NameServerFailure => return error.NameServerFailure, error.UnknownHostName => return error.UnknownHostName, error.HostLacksNetworkAddresses => return error.HostLacksNetworkAddresses, + error.Canceled => return error.Canceled, else => return error.UnexpectedConnectFailure, }; errdefer stream.close(); diff --git a/lib/std/net.zig b/lib/std/net.zig index 37fe2734d5..bddb99b886 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1461,15 +1461,8 @@ test parseHosts { try std.testing.expectFmt("127.0.0.2:1234", "{f}", .{addrs.items[0].addr}); } -pub fn isValidHostName(hostname: []const u8) bool { - if (hostname.len >= 254) return false; - if (!std.unicode.utf8ValidateSlice(hostname)) return false; - for (hostname) |byte| { - if (!std.ascii.isAscii(byte) or byte == '.' or byte == '-' or std.ascii.isAlphanumeric(byte)) { - continue; - } - return false; - } +pub fn isValidHostName(bytes: []const u8) bool { + _ = std.Io.net.HostName.init(bytes) catch return false; return true; }