| author | |
| committer | |
| log | bdf463bee2becd84b83bb9d66725420e03680df8 |
| tree | c0a51d328eb42e28ec9315171c0f58d1f803bdc8 |
| parent | 07cc4077fba5701ef582e17c795f9f5214915179 |
5 files changed, 302 insertions(+), 56 deletions(-)
lib/std/Io.zig+8-3| ... | ... | @@ -660,7 +660,7 @@ pub const VTable = struct { |
| 660 | 660 | |
| 661 | 661 | listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.ListenOptions) net.ListenError!net.Server, |
| 662 | 662 | accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Server.Connection, |
| 663 | netRead: *const fn (?*anyopaque, src: net.Stream, dest: *Io.Writer, limit: Io.Limit) net.Stream.Reader.Error!usize, | |
| 663 | netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, | |
| 664 | 664 | netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize, |
| 665 | 665 | netClose: *const fn (?*anyopaque, stream: net.Stream) void, |
| 666 | 666 | }; |
| ... | ... | @@ -760,6 +760,11 @@ pub const File = struct { |
| 760 | 760 | } |
| 761 | 761 | return index; |
| 762 | 762 | } |
| 763 | ||
| 764 | pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError { | |
| 765 | assert(std.fs.path.isAbsolute(absolute_path)); | |
| 766 | return Dir.cwd().openFile(io, absolute_path, flags); | |
| 767 | } | |
| 763 | 768 | }; |
| 764 | 769 | |
| 765 | 770 | pub const Timestamp = enum(i96) { |
| ... | ... | @@ -1205,7 +1210,7 @@ pub fn asyncConcurrent( |
| 1205 | 1210 | const Args = @TypeOf(args); |
| 1206 | 1211 | const TypeErased = struct { |
| 1207 | 1212 | fn start(context: *const anyopaque, result: *anyopaque) void { |
| 1208 | const args_casted: *const Args = @alignCast(@ptrCast(context)); | |
| 1213 | const args_casted: *const Args = @ptrCast(@alignCast(context)); | |
| 1209 | 1214 | const result_casted: *Result = @ptrCast(@alignCast(result)); |
| 1210 | 1215 | result_casted.* = @call(.auto, function, args_casted.*); |
| 1211 | 1216 | } |
| ... | ... | @@ -1234,7 +1239,7 @@ pub fn asyncDetached(io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf |
| 1234 | 1239 | const Args = @TypeOf(args); |
| 1235 | 1240 | const TypeErased = struct { |
| 1236 | 1241 | fn start(context: *const anyopaque) void { |
| 1237 | const args_casted: *const Args = @alignCast(@ptrCast(context)); | |
| 1242 | const args_casted: *const Args = @ptrCast(@alignCast(context)); | |
| 1238 | 1243 | @call(.auto, function, args_casted.*); |
| 1239 | 1244 | } |
| 1240 | 1245 | }; |
lib/std/Io/ThreadPool.zig+34-31| ... | ... | @@ -233,7 +233,7 @@ fn async( |
| 233 | 233 | start(context.ptr, result.ptr); |
| 234 | 234 | return null; |
| 235 | 235 | } |
| 236 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 236 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 237 | 237 | const cpu_count = pool.cpu_count catch { |
| 238 | 238 | return asyncConcurrent(userdata, result.len, result_alignment, context, context_alignment, start) catch { |
| 239 | 239 | start(context.ptr, result.ptr); |
| ... | ... | @@ -244,7 +244,7 @@ fn async( |
| 244 | 244 | const context_offset = context_alignment.forward(@sizeOf(AsyncClosure)); |
| 245 | 245 | const result_offset = result_alignment.forward(context_offset + context.len); |
| 246 | 246 | const n = result_offset + result.len; |
| 247 | const closure: *AsyncClosure = @alignCast(@ptrCast(gpa.alignedAlloc(u8, .of(AsyncClosure), n) catch { | |
| 247 | const closure: *AsyncClosure = @ptrCast(@alignCast(gpa.alignedAlloc(u8, .of(AsyncClosure), n) catch { | |
| 248 | 248 | start(context.ptr, result.ptr); |
| 249 | 249 | return null; |
| 250 | 250 | })); |
| ... | ... | @@ -309,13 +309,13 @@ fn asyncConcurrent( |
| 309 | 309 | ) error{OutOfMemory}!*Io.AnyFuture { |
| 310 | 310 | if (builtin.single_threaded) unreachable; |
| 311 | 311 | |
| 312 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 312 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 313 | 313 | const cpu_count = pool.cpu_count catch 1; |
| 314 | 314 | const gpa = pool.allocator; |
| 315 | 315 | const context_offset = context_alignment.forward(@sizeOf(AsyncClosure)); |
| 316 | 316 | const result_offset = result_alignment.forward(context_offset + context.len); |
| 317 | 317 | const n = result_offset + result_len; |
| 318 | const closure: *AsyncClosure = @alignCast(@ptrCast(try gpa.alignedAlloc(u8, .of(AsyncClosure), n))); | |
| 318 | const closure: *AsyncClosure = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(AsyncClosure), n))); | |
| 319 | 319 | |
| 320 | 320 | closure.* = .{ |
| 321 | 321 | .func = start, |
| ... | ... | @@ -399,11 +399,11 @@ fn asyncDetached( |
| 399 | 399 | start: *const fn (context: *const anyopaque) void, |
| 400 | 400 | ) void { |
| 401 | 401 | if (builtin.single_threaded) return start(context.ptr); |
| 402 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 402 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 403 | 403 | const cpu_count = pool.cpu_count catch 1; |
| 404 | 404 | const gpa = pool.allocator; |
| 405 | 405 | const n = DetachedClosure.contextEnd(context_alignment, context.len); |
| 406 | const closure: *DetachedClosure = @alignCast(@ptrCast(gpa.alignedAlloc(u8, .of(DetachedClosure), n) catch { | |
| 406 | const closure: *DetachedClosure = @ptrCast(@alignCast(gpa.alignedAlloc(u8, .of(DetachedClosure), n) catch { | |
| 407 | 407 | return start(context.ptr); |
| 408 | 408 | })); |
| 409 | 409 | closure.* = .{ |
| ... | ... | @@ -451,7 +451,7 @@ fn await( |
| 451 | 451 | result_alignment: std.mem.Alignment, |
| 452 | 452 | ) void { |
| 453 | 453 | _ = result_alignment; |
| 454 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 454 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 455 | 455 | const closure: *AsyncClosure = @ptrCast(@alignCast(any_future)); |
| 456 | 456 | closure.waitAndFree(pool.allocator, result); |
| 457 | 457 | } |
| ... | ... | @@ -463,7 +463,7 @@ fn cancel( |
| 463 | 463 | result_alignment: std.mem.Alignment, |
| 464 | 464 | ) void { |
| 465 | 465 | _ = result_alignment; |
| 466 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 466 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 467 | 467 | const closure: *AsyncClosure = @ptrCast(@alignCast(any_future)); |
| 468 | 468 | switch (@atomicRmw( |
| 469 | 469 | std.Thread.Id, |
| ... | ... | @@ -486,7 +486,7 @@ fn cancel( |
| 486 | 486 | } |
| 487 | 487 | |
| 488 | 488 | fn cancelRequested(userdata: ?*anyopaque) bool { |
| 489 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 489 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 490 | 490 | _ = pool; |
| 491 | 491 | const closure = current_closure orelse return false; |
| 492 | 492 | 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 |
| 520 | 520 | } |
| 521 | 521 | |
| 522 | 522 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { |
| 523 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 523 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 524 | 524 | comptime assert(@TypeOf(cond.state) == u64); |
| 525 | 525 | const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); |
| 526 | 526 | const cond_state = &ints[0]; |
| ... | ... | @@ -567,7 +567,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I |
| 567 | 567 | } |
| 568 | 568 | |
| 569 | 569 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { |
| 570 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 570 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 571 | 571 | _ = pool; |
| 572 | 572 | comptime assert(@TypeOf(cond.state) == u64); |
| 573 | 573 | const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); |
| ... | ... | @@ -624,7 +624,7 @@ fn createFile( |
| 624 | 624 | sub_path: []const u8, |
| 625 | 625 | flags: Io.File.CreateFlags, |
| 626 | 626 | ) Io.File.OpenError!Io.File { |
| 627 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 627 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 628 | 628 | try pool.checkCancel(); |
| 629 | 629 | const fs_dir: std.fs.Dir = .{ .fd = dir.handle }; |
| 630 | 630 | const fs_file = try fs_dir.createFile(sub_path, flags); |
| ... | ... | @@ -637,7 +637,7 @@ fn openFile( |
| 637 | 637 | sub_path: []const u8, |
| 638 | 638 | flags: Io.File.OpenFlags, |
| 639 | 639 | ) Io.File.OpenError!Io.File { |
| 640 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 640 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 641 | 641 | try pool.checkCancel(); |
| 642 | 642 | const fs_dir: std.fs.Dir = .{ .fd = dir.handle }; |
| 643 | 643 | const fs_file = try fs_dir.openFile(sub_path, flags); |
| ... | ... | @@ -645,14 +645,14 @@ fn openFile( |
| 645 | 645 | } |
| 646 | 646 | |
| 647 | 647 | fn closeFile(userdata: ?*anyopaque, file: Io.File) void { |
| 648 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 648 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 649 | 649 | _ = pool; |
| 650 | 650 | const fs_file: std.fs.File = .{ .handle = file.handle }; |
| 651 | 651 | return fs_file.close(); |
| 652 | 652 | } |
| 653 | 653 | |
| 654 | 654 | fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: posix.off_t) Io.File.PReadError!usize { |
| 655 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 655 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 656 | 656 | try pool.checkCancel(); |
| 657 | 657 | const fs_file: std.fs.File = .{ .handle = file.handle }; |
| 658 | 658 | return switch (offset) { |
| ... | ... | @@ -662,7 +662,7 @@ fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: posix.off_t |
| 662 | 662 | } |
| 663 | 663 | |
| 664 | 664 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize { |
| 665 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 665 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 666 | 666 | try pool.checkCancel(); |
| 667 | 667 | const fs_file: std.fs.File = .{ .handle = file.handle }; |
| 668 | 668 | return switch (offset) { |
| ... | ... | @@ -672,14 +672,14 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi |
| 672 | 672 | } |
| 673 | 673 | |
| 674 | 674 | fn now(userdata: ?*anyopaque, clockid: posix.clockid_t) Io.ClockGetTimeError!Io.Timestamp { |
| 675 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 675 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 676 | 676 | try pool.checkCancel(); |
| 677 | 677 | const timespec = try posix.clock_gettime(clockid); |
| 678 | 678 | return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec); |
| 679 | 679 | } |
| 680 | 680 | |
| 681 | 681 | fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { |
| 682 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 682 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 683 | 683 | const deadline_nanoseconds: i96 = switch (deadline) { |
| 684 | 684 | .duration => |duration| duration.nanoseconds, |
| 685 | 685 | .timestamp => |timestamp| @intFromEnum(timestamp), |
| ... | ... | @@ -704,7 +704,7 @@ fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) |
| 704 | 704 | } |
| 705 | 705 | |
| 706 | 706 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| 707 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 707 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 708 | 708 | _ = pool; |
| 709 | 709 | |
| 710 | 710 | var reset_event: std.Thread.ResetEvent = .{}; |
| ... | ... | @@ -736,7 +736,7 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| 736 | 736 | } |
| 737 | 737 | |
| 738 | 738 | fn listen(userdata: ?*anyopaque, address: Io.net.IpAddress, options: Io.net.ListenOptions) Io.net.ListenError!Io.net.Server { |
| 739 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 739 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 740 | 740 | try pool.checkCancel(); |
| 741 | 741 | |
| 742 | 742 | 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 |
| 776 | 776 | } |
| 777 | 777 | |
| 778 | 778 | fn accept(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.AcceptError!Io.net.Server.Connection { |
| 779 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 779 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 780 | 780 | try pool.checkCancel(); |
| 781 | 781 | |
| 782 | 782 | var storage: PosixAddress = undefined; |
| ... | ... | @@ -788,17 +788,20 @@ fn accept(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.AcceptErr |
| 788 | 788 | }; |
| 789 | 789 | } |
| 790 | 790 | |
| 791 | fn netReadPosix( | |
| 792 | userdata: ?*anyopaque, | |
| 793 | stream: Io.net.Stream, | |
| 794 | w: *Io.Writer, | |
| 795 | limit: Io.Limit, | |
| 796 | ) Io.net.Stream.Reader.Error!usize { | |
| 797 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 791 | fn netReadPosix(userdata: ?*anyopaque, stream: Io.net.Stream, data: [][]u8) Io.net.Stream.Reader.Error!usize { | |
| 792 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 798 | 793 | try pool.checkCancel(); |
| 799 | 794 | |
| 800 | 795 | var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; |
| 801 | const dest = try w.writableVectorPosix(&iovecs_buffer, limit); | |
| 796 | var i: usize = 0; | |
| 797 | for (data) |buf| { | |
| 798 | if (iovecs_buffer.len - i == 0) break; | |
| 799 | if (buf.len != 0) { | |
| 800 | iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len }; | |
| 801 | i += 1; | |
| 802 | } | |
| 803 | } | |
| 804 | const dest = iovecs_buffer[0..i]; | |
| 802 | 805 | assert(dest[0].len > 0); |
| 803 | 806 | const n = try posix.readv(stream.handle, dest); |
| 804 | 807 | if (n == 0) return error.EndOfStream; |
| ... | ... | @@ -812,7 +815,7 @@ fn netWritePosix( |
| 812 | 815 | data: []const []const u8, |
| 813 | 816 | splat: usize, |
| 814 | 817 | ) Io.net.Stream.Writer.Error!usize { |
| 815 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 818 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 816 | 819 | try pool.checkCancel(); |
| 817 | 820 | |
| 818 | 821 | 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"), |
| 866 | 869 | } |
| 867 | 870 | |
| 868 | 871 | fn netClose(userdata: ?*anyopaque, stream: Io.net.Stream) void { |
| 869 | const pool: *Pool = @alignCast(@ptrCast(userdata)); | |
| 872 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | |
| 870 | 873 | _ = pool; |
| 871 | 874 | const net_stream: std.net.Stream = .{ .handle = stream.handle }; |
| 872 | 875 | return net_stream.close(); |
lib/std/Io/net.zig+244-2| ... | ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); |
| 2 | 2 | const native_os = builtin.os.tag; |
| 3 | 3 | const std = @import("../std.zig"); |
| 4 | 4 | const Io = std.Io; |
| 5 | const assert = std.debug.assert; | |
| 5 | 6 | |
| 6 | 7 | pub const ListenError = std.net.Address.ListenError || Io.Cancelable; |
| 7 | 8 | |
| ... | ... | @@ -16,10 +17,233 @@ pub const ListenOptions = struct { |
| 16 | 17 | force_nonblocking: bool = false, |
| 17 | 18 | }; |
| 18 | 19 | |
| 20 | /// An already-validated host name. | |
| 21 | pub const HostName = struct { | |
| 22 | /// Externally managed memory. Already checked to be within `max_len`. | |
| 23 | bytes: []const u8, | |
| 24 | ||
| 25 | pub const max_len = 255; | |
| 26 | ||
| 27 | pub const InitError = error{ | |
| 28 | NameTooLong, | |
| 29 | InvalidHostName, | |
| 30 | }; | |
| 31 | ||
| 32 | pub fn init(bytes: []const u8) InitError!HostName { | |
| 33 | if (bytes.len > max_len) return error.NameTooLong; | |
| 34 | if (!std.unicode.utf8ValidateSlice(bytes)) return error.InvalidHostName; | |
| 35 | for (bytes) |byte| { | |
| 36 | if (!std.ascii.isAscii(byte) or byte == '.' or byte == '-' or std.ascii.isAlphanumeric(byte)) { | |
| 37 | continue; | |
| 38 | } | |
| 39 | return error.InvalidHostName; | |
| 40 | } | |
| 41 | return .{ .bytes = bytes }; | |
| 42 | } | |
| 43 | ||
| 44 | pub const LookupOptions = struct { | |
| 45 | port: u16, | |
| 46 | /// Must have at least length 2. | |
| 47 | addresses_buffer: []IpAddress, | |
| 48 | /// If a buffer of at least `max_len` is not provided, `lookup` may | |
| 49 | /// return successfully with zero-length `LookupResult.canonical_name_len`. | |
| 50 | /// | |
| 51 | /// Suggestion: if not interested in canonical name, pass an empty buffer; | |
| 52 | /// otherwise pass a buffer of size `max_len`. | |
| 53 | canonical_name_buffer: []u8, | |
| 54 | /// `null` means either. | |
| 55 | family: ?IpAddress.Tag = null, | |
| 56 | }; | |
| 57 | ||
| 58 | pub const LookupError = Io.Cancelable || error{}; | |
| 59 | ||
| 60 | pub const LookupResult = struct { | |
| 61 | /// How many `LookupOptions.addresses_buffer` elements are populated. | |
| 62 | addresses_len: usize, | |
| 63 | /// Length zero means no canonical name returned. | |
| 64 | canonical_name_len: usize, | |
| 65 | }; | |
| 66 | ||
| 67 | pub fn lookup(host_name: HostName, io: Io, options: LookupOptions) LookupError!LookupResult { | |
| 68 | const name = host_name.bytes; | |
| 69 | assert(name.len <= max_len); | |
| 70 | assert(options.addresses_buffer.len >= 2); | |
| 71 | ||
| 72 | if (native_os == .windows) @compileError("TODO"); | |
| 73 | if (builtin.link_libc) @compileError("TODO"); | |
| 74 | if (native_os == .linux) { | |
| 75 | if (options.family != .ip6) { | |
| 76 | if (IpAddress.parseIp4(name, options.port)) |addr| { | |
| 77 | options.addresses_buffer[0] = addr; | |
| 78 | return .{ .addresses_len = 1, .canonical_name_len = 0 }; | |
| 79 | } else |_| {} | |
| 80 | } | |
| 81 | if (options.family != .ip4) { | |
| 82 | if (IpAddress.parseIp6(name, options.port)) |addr| { | |
| 83 | options.addresses_buffer[0] = addr; | |
| 84 | return .{ .addresses_len = 1, .canonical_name_len = 0 }; | |
| 85 | } else |_| {} | |
| 86 | } | |
| 87 | { | |
| 88 | const result = try lookupHosts(io, options); | |
| 89 | if (result.addresses_len > 0) return sortLookupResults(options, result); | |
| 90 | } | |
| 91 | { | |
| 92 | // RFC 6761 Section 6.3.3 | |
| 93 | // Name resolution APIs and libraries SHOULD recognize | |
| 94 | // localhost names as special and SHOULD always return the IP | |
| 95 | // loopback address for address queries and negative responses | |
| 96 | // for all other query types. | |
| 97 | ||
| 98 | // Check for equal to "localhost(.)" or ends in ".localhost(.)" | |
| 99 | const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost"; | |
| 100 | if (std.mem.endsWith(u8, name, localhost) and | |
| 101 | (name.len == localhost.len or name[name.len - localhost.len] == '.')) | |
| 102 | { | |
| 103 | var i: usize = 0; | |
| 104 | if (options.family != .ip6) { | |
| 105 | options.addresses_buffer[i] = .{ .ip4 = .localhost(options.port) }; | |
| 106 | i += 1; | |
| 107 | } | |
| 108 | if (options.family != .ip4) { | |
| 109 | options.addresses_buffer[i] = .{ .ip6 = .localhost(options.port) }; | |
| 110 | i += 1; | |
| 111 | } | |
| 112 | const canon_name = "localhost"; | |
| 113 | options.canonical_name_buffer[0..canon_name.len].* = canon_name.*; | |
| 114 | return sortLookupResults(options, .{ .addresses_len = i, .canonical_name_len = canon_name.len }); | |
| 115 | } | |
| 116 | } | |
| 117 | { | |
| 118 | const result = try lookupDns(io, options); | |
| 119 | if (result.addresses_len > 0) return sortLookupResults(options, result); | |
| 120 | } | |
| 121 | return error.UnknownHostName; | |
| 122 | } | |
| 123 | @compileError("unimplemented"); | |
| 124 | } | |
| 125 | ||
| 126 | fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult { | |
| 127 | _ = options; | |
| 128 | _ = result; | |
| 129 | @panic("TODO"); | |
| 130 | } | |
| 131 | ||
| 132 | fn lookupDns(io: Io, options: LookupOptions) !LookupResult { | |
| 133 | _ = io; | |
| 134 | _ = options; | |
| 135 | @panic("TODO"); | |
| 136 | } | |
| 137 | ||
| 138 | fn lookupHosts(io: Io, options: LookupOptions) !LookupResult { | |
| 139 | const file = Io.File.openFileAbsoluteZ(io, "/etc/hosts", .{}) catch |err| switch (err) { | |
| 140 | error.FileNotFound, | |
| 141 | error.NotDir, | |
| 142 | error.AccessDenied, | |
| 143 | => return, | |
| 144 | else => |e| return e, | |
| 145 | }; | |
| 146 | defer file.close(); | |
| 147 | ||
| 148 | var line_buf: [512]u8 = undefined; | |
| 149 | var file_reader = file.reader(io, &line_buf); | |
| 150 | return lookupHostsReader(options, &file_reader.interface) catch |err| switch (err) { | |
| 151 | error.OutOfMemory => return error.OutOfMemory, | |
| 152 | error.ReadFailed => return file_reader.err.?, | |
| 153 | }; | |
| 154 | } | |
| 155 | ||
| 156 | fn lookupHostsReader(options: LookupOptions, reader: *Io.Reader) error{ReadFailed}!LookupResult { | |
| 157 | var addresses_len: usize = 0; | |
| 158 | var canonical_name_len: usize = 0; | |
| 159 | while (true) { | |
| 160 | const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) { | |
| 161 | error.StreamTooLong => { | |
| 162 | // Skip lines that are too long. | |
| 163 | _ = reader.discardDelimiterInclusive('\n') catch |e| switch (e) { | |
| 164 | error.EndOfStream => break, | |
| 165 | error.ReadFailed => return error.ReadFailed, | |
| 166 | }; | |
| 167 | continue; | |
| 168 | }, | |
| 169 | error.ReadFailed => return error.ReadFailed, | |
| 170 | error.EndOfStream => break, | |
| 171 | }; | |
| 172 | var split_it = std.mem.splitScalar(u8, line, '#'); | |
| 173 | const no_comment_line = split_it.first(); | |
| 174 | ||
| 175 | var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t"); | |
| 176 | const ip_text = line_it.next() orelse continue; | |
| 177 | var first_name_text: ?[]const u8 = null; | |
| 178 | while (line_it.next()) |name_text| { | |
| 179 | if (std.mem.eql(u8, name_text, options.name)) { | |
| 180 | if (first_name_text == null) first_name_text = name_text; | |
| 181 | break; | |
| 182 | } | |
| 183 | } else continue; | |
| 184 | ||
| 185 | if (canonical_name_len == 0) { | |
| 186 | if (HostName.init(first_name_text)) |name_text| { | |
| 187 | if (name_text.len <= options.canonical_name_buffer.len) { | |
| 188 | @memcpy(options.canonical_name_buffer[0..name_text.len], name_text); | |
| 189 | canonical_name_len = name_text.len; | |
| 190 | } | |
| 191 | } | |
| 192 | } | |
| 193 | ||
| 194 | if (options.family != .ip6) { | |
| 195 | if (IpAddress.parseIp4(ip_text, options.port)) |addr| { | |
| 196 | options.addresses_buffer[addresses_len] = addr; | |
| 197 | addresses_len += 1; | |
| 198 | if (options.addresses_buffer.len - addresses_len == 0) return .{ | |
| 199 | .addresses_len = addresses_len, | |
| 200 | .canonical_name_len = canonical_name_len, | |
| 201 | }; | |
| 202 | } else |_| {} | |
| 203 | } | |
| 204 | if (options.family != .ip4) { | |
| 205 | if (IpAddress.parseIp6(ip_text, options.port)) |addr| { | |
| 206 | options.addresses_buffer[addresses_len] = addr; | |
| 207 | addresses_len += 1; | |
| 208 | if (options.addresses_buffer.len - addresses_len == 0) return .{ | |
| 209 | .addresses_len = addresses_len, | |
| 210 | .canonical_name_len = canonical_name_len, | |
| 211 | }; | |
| 212 | } else |_| {} | |
| 213 | } | |
| 214 | } | |
| 215 | } | |
| 216 | ||
| 217 | pub const ConnectTcpError = LookupError || IpAddress.ConnectTcpError; | |
| 218 | ||
| 219 | pub fn connectTcp(host_name: HostName, io: Io, port: u16) ConnectTcpError!Stream { | |
| 220 | var addresses_buffer: [32]IpAddress = undefined; | |
| 221 | ||
| 222 | const results = try lookup(host_name, .{ | |
| 223 | .port = port, | |
| 224 | .addresses_buffer = &addresses_buffer, | |
| 225 | .canonical_name_buffer = &.{}, | |
| 226 | }); | |
| 227 | const addresses = addresses_buffer[0..results.addresses_len]; | |
| 228 | ||
| 229 | if (addresses.len == 0) return error.UnknownHostName; | |
| 230 | ||
| 231 | for (addresses) |addr| { | |
| 232 | return addr.connectTcp(io) catch |err| switch (err) { | |
| 233 | error.ConnectionRefused => continue, | |
| 234 | else => |e| return e, | |
| 235 | }; | |
| 236 | } | |
| 237 | return error.ConnectionRefused; | |
| 238 | } | |
| 239 | }; | |
| 240 | ||
| 19 | 241 | pub const IpAddress = union(enum) { |
| 20 | 242 | ip4: Ip4Address, |
| 21 | 243 | ip6: Ip6Address, |
| 22 | 244 | |
| 245 | pub const Tag = @typeInfo(IpAddress).@"union".tag_type.?; | |
| 246 | ||
| 23 | 247 | /// Parse the given IP address string into an `IpAddress` value. |
| 24 | 248 | pub fn parse(name: []const u8, port: u16) !IpAddress { |
| 25 | 249 | if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { |
| ... | ... | @@ -94,6 +318,13 @@ pub const Ip4Address = struct { |
| 94 | 318 | bytes: [4]u8, |
| 95 | 319 | port: u16, |
| 96 | 320 | |
| 321 | pub fn localhost(port: u16) Ip4Address { | |
| 322 | return .{ | |
| 323 | .bytes = .{ 127, 0, 0, 1 }, | |
| 324 | .port = port, | |
| 325 | }; | |
| 326 | } | |
| 327 | ||
| 97 | 328 | pub const ParseError = error{ |
| 98 | 329 | Overflow, |
| 99 | 330 | InvalidEnd, |
| ... | ... | @@ -373,7 +604,10 @@ pub const Stream = struct { |
| 373 | 604 | pub fn init(stream: Stream, buffer: []u8) Reader { |
| 374 | 605 | return .{ |
| 375 | 606 | .interface = .{ |
| 376 | .vtable = &.{ .stream = streamImpl }, | |
| 607 | .vtable = &.{ | |
| 608 | .stream = streamImpl, | |
| 609 | .readVec = readVec, | |
| 610 | }, | |
| 377 | 611 | .buffer = buffer, |
| 378 | 612 | .seek = 0, |
| 379 | 613 | .end = 0, |
| ... | ... | @@ -384,9 +618,17 @@ pub const Stream = struct { |
| 384 | 618 | } |
| 385 | 619 | |
| 386 | 620 | fn streamImpl(io_r: *Io.Reader, io_w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { |
| 621 | const dest = limit.slice(try io_w.writableSliceGreedy(1)); | |
| 622 | var data: [1][]u8 = .{dest}; | |
| 623 | const n = try readVec(io_r, &data); | |
| 624 | io_w.advance(n); | |
| 625 | return n; | |
| 626 | } | |
| 627 | ||
| 628 | fn readVec(io_r: *Reader, data: [][]u8) Io.Reader.Error!usize { | |
| 387 | 629 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); |
| 388 | 630 | const io = r.io; |
| 389 | return io.vtable.netRead(io.vtable.userdata, r.stream, io_w, limit); | |
| 631 | return io.vtable.netReadVec(io.vtable.userdata, r.stream, io_r, data); | |
| 390 | 632 | } |
| 391 | 633 | }; |
| 392 | 634 |
lib/std/http/Client.zig+14-11| ... | ... | @@ -9,10 +9,10 @@ const builtin = @import("builtin"); |
| 9 | 9 | const testing = std.testing; |
| 10 | 10 | const http = std.http; |
| 11 | 11 | const mem = std.mem; |
| 12 | const net = std.net; | |
| 13 | 12 | const Uri = std.Uri; |
| 14 | 13 | const Allocator = mem.Allocator; |
| 15 | 14 | const assert = std.debug.assert; |
| 15 | const Io = std.Io; | |
| 16 | 16 | const Writer = std.Io.Writer; |
| 17 | 17 | const Reader = std.Io.Reader; |
| 18 | 18 | |
| ... | ... | @@ -22,6 +22,8 @@ pub const disable_tls = std.options.http_disable_tls; |
| 22 | 22 | |
| 23 | 23 | /// Used for all client allocations. Must be thread-safe. |
| 24 | 24 | allocator: Allocator, |
| 25 | /// Used for opening TCP connections. | |
| 26 | io: Io, | |
| 25 | 27 | |
| 26 | 28 | ca_bundle: if (disable_tls) void else std.crypto.Certificate.Bundle = if (disable_tls) {} else .{}, |
| 27 | 29 | ca_bundle_mutex: std.Thread.Mutex = .{}, |
| ... | ... | @@ -225,8 +227,8 @@ pub const Protocol = enum { |
| 225 | 227 | |
| 226 | 228 | pub const Connection = struct { |
| 227 | 229 | client: *Client, |
| 228 | stream_writer: net.Stream.Writer, | |
| 229 | stream_reader: net.Stream.Reader, | |
| 230 | stream_writer: Io.net.Stream.Writer, | |
| 231 | stream_reader: Io.net.Stream.Reader, | |
| 230 | 232 | /// Entry in `ConnectionPool.used` or `ConnectionPool.free`. |
| 231 | 233 | pool_node: std.DoublyLinkedList.Node, |
| 232 | 234 | port: u16, |
| ... | ... | @@ -242,7 +244,7 @@ pub const Connection = struct { |
| 242 | 244 | client: *Client, |
| 243 | 245 | remote_host: []const u8, |
| 244 | 246 | port: u16, |
| 245 | stream: net.Stream, | |
| 247 | stream: Io.net.Stream, | |
| 246 | 248 | ) error{OutOfMemory}!*Plain { |
| 247 | 249 | const gpa = client.allocator; |
| 248 | 250 | const alloc_len = allocLen(client, remote_host.len); |
| ... | ... | @@ -295,7 +297,7 @@ pub const Connection = struct { |
| 295 | 297 | client: *Client, |
| 296 | 298 | remote_host: []const u8, |
| 297 | 299 | port: u16, |
| 298 | stream: net.Stream, | |
| 300 | stream: Io.net.Stream, | |
| 299 | 301 | ) error{ OutOfMemory, TlsInitializationFailed }!*Tls { |
| 300 | 302 | const gpa = client.allocator; |
| 301 | 303 | const alloc_len = allocLen(client, remote_host.len); |
| ... | ... | @@ -363,7 +365,7 @@ pub const Connection = struct { |
| 363 | 365 | } |
| 364 | 366 | }; |
| 365 | 367 | |
| 366 | pub const ReadError = std.crypto.tls.Client.ReadError || std.net.Stream.ReadError; | |
| 368 | pub const ReadError = std.crypto.tls.Client.ReadError || Io.net.Stream.ReadError; | |
| 367 | 369 | |
| 368 | 370 | pub fn getReadError(c: *const Connection) ?ReadError { |
| 369 | 371 | return switch (c.protocol) { |
| ... | ... | @@ -378,8 +380,8 @@ pub const Connection = struct { |
| 378 | 380 | }; |
| 379 | 381 | } |
| 380 | 382 | |
| 381 | fn getStream(c: *Connection) net.Stream { | |
| 382 | return c.stream_reader.getStream(); | |
| 383 | fn getStream(c: *Connection) Io.net.Stream { | |
| 384 | return c.stream_reader.stream; | |
| 383 | 385 | } |
| 384 | 386 | |
| 385 | 387 | pub fn host(c: *Connection) []u8 { |
| ... | ... | @@ -1409,7 +1411,7 @@ pub fn connectTcp( |
| 1409 | 1411 | } |
| 1410 | 1412 | |
| 1411 | 1413 | pub const ConnectTcpOptions = struct { |
| 1412 | host: []const u8, | |
| 1414 | host: Io.net.HostName, | |
| 1413 | 1415 | port: u16, |
| 1414 | 1416 | protocol: Protocol, |
| 1415 | 1417 | |
| ... | ... | @@ -1418,7 +1420,7 @@ pub const ConnectTcpOptions = struct { |
| 1418 | 1420 | }; |
| 1419 | 1421 | |
| 1420 | 1422 | pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection { |
| 1421 | const host = options.host; | |
| 1423 | const host = options.host_name; | |
| 1422 | 1424 | const port = options.port; |
| 1423 | 1425 | const protocol = options.protocol; |
| 1424 | 1426 | |
| ... | ... | @@ -1431,7 +1433,7 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp |
| 1431 | 1433 | .protocol = protocol, |
| 1432 | 1434 | })) |conn| return conn; |
| 1433 | 1435 | |
| 1434 | const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { | |
| 1436 | const stream = host.connectTcp(client.io, port) catch |err| switch (err) { | |
| 1435 | 1437 | error.ConnectionRefused => return error.ConnectionRefused, |
| 1436 | 1438 | error.NetworkUnreachable => return error.NetworkUnreachable, |
| 1437 | 1439 | error.ConnectionTimedOut => return error.ConnectionTimedOut, |
| ... | ... | @@ -1440,6 +1442,7 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp |
| 1440 | 1442 | error.NameServerFailure => return error.NameServerFailure, |
| 1441 | 1443 | error.UnknownHostName => return error.UnknownHostName, |
| 1442 | 1444 | error.HostLacksNetworkAddresses => return error.HostLacksNetworkAddresses, |
| 1445 | error.Canceled => return error.Canceled, | |
| 1443 | 1446 | else => return error.UnexpectedConnectFailure, |
| 1444 | 1447 | }; |
| 1445 | 1448 | errdefer stream.close(); |
lib/std/net.zig+2-9| ... | ... | @@ -1461,15 +1461,8 @@ test parseHosts { |
| 1461 | 1461 | try std.testing.expectFmt("127.0.0.2:1234", "{f}", .{addrs.items[0].addr}); |
| 1462 | 1462 | } |
| 1463 | 1463 | |
| 1464 | pub fn isValidHostName(hostname: []const u8) bool { | |
| 1465 | if (hostname.len >= 254) return false; | |
| 1466 | if (!std.unicode.utf8ValidateSlice(hostname)) return false; | |
| 1467 | for (hostname) |byte| { | |
| 1468 | if (!std.ascii.isAscii(byte) or byte == '.' or byte == '-' or std.ascii.isAlphanumeric(byte)) { | |
| 1469 | continue; | |
| 1470 | } | |
| 1471 | return false; | |
| 1472 | } | |
| 1464 | pub fn isValidHostName(bytes: []const u8) bool { | |
| 1465 | _ = std.Io.net.HostName.init(bytes) catch return false; | |
| 1473 | 1466 | return true; |
| 1474 | 1467 | } |
| 1475 | 1468 |