Skip to content

Commit

Permalink
x/net: generalize tcp.Address into ip.Address
Browse files Browse the repository at this point in the history
Generalize `tcp.Address` into `ip.Address` given that multiple transport
protocols apart from TCP (i.e. UDP) operate solely over IP.
  • Loading branch information
lithdew committed May 2, 2021
1 parent f223840 commit 981dbc3
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 57 deletions.
1 change: 1 addition & 0 deletions lib/std/x.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub const os = struct {
};

pub const net = struct {
pub const ip = @import("x/net/ip.zig");
pub const tcp = @import("x/net/tcp.zig");
};

Expand Down
52 changes: 52 additions & 0 deletions lib/std/x/net/ip.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const std = @import("../../std.zig");

const IPv4 = std.x.os.IPv4;
const IPv6 = std.x.os.IPv6;
const Socket = std.x.os.Socket;

const ip = @This();

/// A union of all eligible types of IP addresses.
pub const Address = union(enum) {
ipv4: IPv4.Address,
ipv6: IPv6.Address,

/// Instantiate a new address with a IPv4 host and port.
pub fn initIPv4(host: IPv4, port: u16) Address {
return .{ .ipv4 = .{ .host = host, .port = port } };
}

/// Instantiate a new address with a IPv6 host and port.
pub fn initIPv6(host: IPv6, port: u16) Address {
return .{ .ipv6 = .{ .host = host, .port = port } };
}

/// Re-interpret a generic socket address into an IP address.
pub fn from(address: Socket.Address) ip.Address {
return switch (address) {
.ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
.ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
};
}

/// Re-interpret an IP address into a generic socket address.
pub fn into(self: ip.Address) Socket.Address {
return switch (self) {
.ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
.ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
};
}

/// Implements the `std.fmt.format` API.
pub fn format(
self: ip.Address,
comptime layout: []const u8,
opts: fmt.FormatOptions,
writer: anytype,
) !void {
switch (self) {
.ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
.ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
}
}
};
71 changes: 14 additions & 57 deletions lib/std/x/net/tcp.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
const std = @import("../../std.zig");

const os = std.os;
const ip = std.x.net.ip;

const fmt = std.fmt;
const mem = std.mem;
const builtin = std.builtin;
Expand All @@ -19,61 +21,16 @@ const Socket = std.x.os.Socket;
/// A generic TCP socket abstraction.
const tcp = @This();

/// A union of all eligible types of socket addresses over TCP.
pub const Address = union(enum) {
ipv4: IPv4.Address,
ipv6: IPv6.Address,

/// Instantiate a new address with a IPv4 host and port.
pub fn initIPv4(host: IPv4, port: u16) Address {
return .{ .ipv4 = .{ .host = host, .port = port } };
}

/// Instantiate a new address with a IPv6 host and port.
pub fn initIPv6(host: IPv6, port: u16) Address {
return .{ .ipv6 = .{ .host = host, .port = port } };
}

/// Re-interpret a generic socket address into a TCP socket address.
pub fn from(address: Socket.Address) tcp.Address {
return switch (address) {
.ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
.ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
};
}

/// Re-interpret a TCP socket address into a generic socket address.
pub fn into(self: tcp.Address) Socket.Address {
return switch (self) {
.ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
.ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
};
}

/// Implements the `std.fmt.format` API.
pub fn format(
self: tcp.Address,
comptime layout: []const u8,
opts: fmt.FormatOptions,
writer: anytype,
) !void {
switch (self) {
.ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
.ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
}
}
};

/// A TCP client-address pair.
pub const Connection = struct {
client: tcp.Client,
address: tcp.Address,
address: ip.Address,

/// Enclose a TCP client and address into a client-address pair.
pub fn from(conn: Socket.Connection) tcp.Connection {
return .{
.client = tcp.Client.from(conn.socket),
.address = tcp.Address.from(conn.address),
.address = ip.Address.from(conn.address),
};
}

Expand Down Expand Up @@ -128,7 +85,7 @@ pub const Client = struct {
}

/// Have the client attempt to the connect to an address.
pub fn connect(self: Client, address: tcp.Address) !void {
pub fn connect(self: Client, address: ip.Address) !void {
return self.socket.connect(address.into());
}

Expand Down Expand Up @@ -185,8 +142,8 @@ pub const Client = struct {
}

/// Query the address that the client's socket is locally bounded to.
pub fn getLocalAddress(self: Client) !tcp.Address {
return tcp.Address.from(try self.socket.getLocalAddress());
pub fn getLocalAddress(self: Client) !ip.Address {
return ip.Address.from(try self.socket.getLocalAddress());
}

/// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if
Expand Down Expand Up @@ -253,7 +210,7 @@ pub const Listener = struct {
}

/// Binds the listener's socket to an address.
pub fn bind(self: Listener, address: tcp.Address) !void {
pub fn bind(self: Listener, address: ip.Address) !void {
return self.socket.bind(address.into());
}

Expand All @@ -274,8 +231,8 @@ pub const Listener = struct {
}

/// Query the address that the listener's socket is locally bounded to.
pub fn getLocalAddress(self: Listener) !tcp.Address {
return tcp.Address.from(try self.socket.getLocalAddress());
pub fn getLocalAddress(self: Listener) !ip.Address {
return ip.Address.from(try self.socket.getLocalAddress());
}

/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
Expand Down Expand Up @@ -322,7 +279,7 @@ test "tcp: create client/listener pair" {
const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
defer listener.deinit();

try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0));
try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
try listener.listen(128);

const binded_address = try listener.getLocalAddress();
Expand All @@ -342,7 +299,7 @@ test "tcp/client: set read timeout of 1 millisecond on blocking client" {
const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
defer listener.deinit();

try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0));
try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
try listener.listen(128);

const binded_address = try listener.getLocalAddress();
Expand All @@ -366,7 +323,7 @@ test "tcp/listener: bind to unspecified ipv4 address" {
const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
defer listener.deinit();

try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0));
try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
try listener.listen(128);

const address = try listener.getLocalAddress();
Expand All @@ -379,7 +336,7 @@ test "tcp/listener: bind to unspecified ipv6 address" {
const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC);
defer listener.deinit();

try listener.bind(tcp.Address.initIPv6(IPv6.unspecified, 0));
try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0));
try listener.listen(128);

const address = try listener.getLocalAddress();
Expand Down

0 comments on commit 981dbc3

Please sign in to comment.