diff --git a/README.md b/README.md index e58cd04..3e27532 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,6 @@ type information at compile-time, to transform from one type to another. It has also different algorithms for iterating, searching and modifying template arguments. * `net`: URL-Parsing, network programming. -* `network`: Socket implementation. `network` is currently under rework. -After finishing the new socket implementation will land in the `net` package and -`network` will be deprecated. * `os`: Platform-independent interfaces to operating system functionality. * `range`: Generic functions and templates for D ranges. * `test`: Test suite for unittest-blocks. @@ -167,7 +164,7 @@ parameter is used) | DMD | GCC | |:-------:|:---------:| -| 2.091.1 | gdc trunk | +| 2.096.0 | 10.3 | ## Further characteristics diff --git a/source/tanya/async/event/epoll.d b/source/tanya/async/event/epoll.d index 7d3854d..c81d6ff 100644 --- a/source/tanya/async/event/epoll.d +++ b/source/tanya/async/event/epoll.d @@ -31,7 +31,7 @@ import tanya.async.transport; import tanya.async.watcher; import tanya.container.array; import tanya.memory.allocator; -import tanya.network.socket; +import tanya.net.socket; extern (C) nothrow @nogc { diff --git a/source/tanya/async/event/selector.d b/source/tanya/async/event/selector.d index 897ad99..f954e8c 100644 --- a/source/tanya/async/event/selector.d +++ b/source/tanya/async/event/selector.d @@ -26,7 +26,7 @@ import tanya.async.watcher; import tanya.container.array; import tanya.container.buffer; import tanya.memory.allocator; -import tanya.network.socket; +import tanya.net.socket; /** * Transport for stream sockets. diff --git a/source/tanya/async/loop.d b/source/tanya/async/loop.d index 6653282..1d88575 100644 --- a/source/tanya/async/loop.d +++ b/source/tanya/async/loop.d @@ -34,8 +34,9 @@ * * void main() * { - * auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192); - * + * auto address = address4("127.0.0.1"); + * auto endpoint = Endpoint(address.get, cast(ushort) 8192); + * * version (Windows) * { * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet); @@ -46,19 +47,18 @@ * sock.blocking = false; * } * - * sock.bind(address); + * sock.bind(endpoint); * sock.listen(5); - * + * * auto io = defaultAllocator.make!ConnectionWatcher(sock); * io.setProtocol!EchoProtocol; - * + * * defaultLoop.start(io); * defaultLoop.run(); - * + * * sock.shutdown(); * defaultAllocator.dispose(io); * defaultAllocator.dispose(sock); - * defaultAllocator.dispose(address); * } * --- * @@ -78,7 +78,7 @@ import tanya.bitmanip; import tanya.container.buffer; import tanya.container.list; import tanya.memory.allocator; -import tanya.network.socket; +import tanya.net.socket; version (DisableBackends) { diff --git a/source/tanya/async/protocol.d b/source/tanya/async/protocol.d index 4a1b72c..c51dae5 100644 --- a/source/tanya/async/protocol.d +++ b/source/tanya/async/protocol.d @@ -19,7 +19,7 @@ module tanya.async.protocol; import tanya.async.transport; -import tanya.network.socket; +import tanya.net.socket; /** * Common protocol interface. diff --git a/source/tanya/async/transport.d b/source/tanya/async/transport.d index 349abc3..87051e8 100644 --- a/source/tanya/async/transport.d +++ b/source/tanya/async/transport.d @@ -16,7 +16,7 @@ module tanya.async.transport; import tanya.async.protocol; -import tanya.network.socket; +import tanya.net.socket; /** * Base transport interface. diff --git a/source/tanya/async/watcher.d b/source/tanya/async/watcher.d index 8656c94..1159779 100644 --- a/source/tanya/async/watcher.d +++ b/source/tanya/async/watcher.d @@ -20,7 +20,7 @@ import tanya.async.transport; import tanya.container.buffer; import tanya.container.list; import tanya.memory.allocator; -import tanya.network.socket; +import tanya.net.socket; /** * A watcher is an opaque structure that you allocate and register to record diff --git a/source/tanya/net/iface.d b/source/tanya/net/iface.d index 9338745..8e2675a 100644 --- a/source/tanya/net/iface.d +++ b/source/tanya/net/iface.d @@ -142,3 +142,23 @@ String indexToName(uint index) @nogc nothrow @trusted return String(findNullTerminated(buffer)); } } + +/** + * $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects + * the protocol family which will be used for communication. + */ +enum AddressFamily : int +{ + unspec = 0, /// Unspecified. + local = 1, /// Local to host (pipes and file-domain). + unix = local, /// POSIX name for PF_LOCAL. + inet = 2, /// IP protocol family. + ax25 = 3, /// Amateur Radio AX.25. + ipx = 4, /// Novell Internet Protocol. + appletalk = 5, /// Appletalk DDP. + netrom = 6, /// Amateur radio NetROM. + bridge = 7, /// Multiprotocol bridge. + atmpvc = 8, /// ATM PVCs. + x25 = 9, /// Reserved for X.25 project. + inet6 = 10, /// IP version 6. +} diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 8299f73..54a27e3 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -14,7 +14,6 @@ */ module tanya.net.ip; -import core.sys.posix.sys.socket; import std.algorithm.comparison; import std.ascii; import std.typecons; @@ -1331,7 +1330,7 @@ struct Address */ struct Endpoint { - private sa_family_t family = AF_UNSPEC; + private AddressFamily family = AddressFamily.unspec; private ubyte[ushort.sizeof] service; private Address4 address4; // Unused sin6_flowinfo if IPv6 private Address6 address6; // Unused if IPv4 @@ -1345,10 +1344,12 @@ struct Endpoint * Constructs an endpoint. * * Params: + * T = Address type (IPv4 or IPv6). * address = IP address that should be associated with the endpoint. * port = Port number in network byte order. */ - this(Address address, const ushort port = anyPort) + this(T)(T address, const ushort port = anyPort) + if (is(T == Address) || is(T == Address4) || is(T == Address6)) { this.address = address; this.port = port; @@ -1357,7 +1358,7 @@ struct Endpoint /** * Returns: Port number in network byte order. */ - @property ushort port() const @nogc nothrow pure @safe + @property inout(ushort) port() inout const @nogc nothrow pure @safe { return this.service[].toHostOrder!ushort(); } @@ -1374,13 +1375,13 @@ struct Endpoint /** * Returns: IP address associated with the endpoint. */ - @property Address address() @nogc nothrow pure @safe + @property inout(Address) address() inout @nogc nothrow pure @safe { - if (this.family == AF_INET) + if (this.family == AddressFamily.inet) { return Address(this.address4); } - else if (this.family == AF_INET6) + else if (this.family == AddressFamily.inet6) { return Address(this.address6); } @@ -1395,14 +1396,26 @@ struct Endpoint { if (address.isV4()) { - this.family = AF_INET; - this.address4 = address.toV4(); + this.address = address.toV4(); } else if (address.isV6()) { - this.family = AF_INET6; - this.address4 = Address4(0); - this.address6 = address.toV6(); + this.address = address.toV6(); } } + + /// ditto + @property void address(Address4 address) @nogc nothrow pure @safe + { + this.family = AddressFamily.inet; + this.address4 = address; + } + + /// ditto + @property void address(Address6 address) @nogc nothrow pure @safe + { + this.family = AddressFamily.inet6; + this.address4 = Address4(0); + this.address6 = address; + } } diff --git a/source/tanya/net/package.d b/source/tanya/net/package.d index 536715c..37ac2cc 100644 --- a/source/tanya/net/package.d +++ b/source/tanya/net/package.d @@ -17,4 +17,5 @@ module tanya.net; public import tanya.net.iface; public import tanya.net.inet; public import tanya.net.ip; +public import tanya.net.socket; public import tanya.net.uri; diff --git a/source/tanya/network/socket.d b/source/tanya/net/socket.d similarity index 85% rename from source/tanya/network/socket.d rename to source/tanya/net/socket.d index bfd192f..0bdb84a 100644 --- a/source/tanya/network/socket.d +++ b/source/tanya/net/socket.d @@ -7,37 +7,6 @@ * * Current API supports only server-side TCP communication. * - * Here is an example of a cross-platform blocking server: - * - * --- - * import std.stdio; - * import tanya.memory; - * import tanya.network; - * - * void main() - * { - * auto socket = defaultAllocator.make!StreamSocket(AddressFamily.inet); - * auto address = defaultAllocator.make!InternetAddress("127.0.0.1", - * cast(ushort) 8192); - * - * socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true); - * socket.blocking = true; - * socket.bind(address); - * socket.listen(5); - * - * auto client = socket.accept(); - * client.send(cast(const(ubyte)[]) "Test\n"); - * - * ubyte[100] buf; - * auto response = client.receive(buf[]); - * - * writeln(cast(const(char)[]) buf[0 .. response]); - * - * defaultAllocator.dispose(client); - * defaultAllocator.dispose(socket); - * } - * --- - * * For an example of an asynchronous server refer to the documentation of the * $(D_PSYMBOL tanya.async.loop) module. * @@ -48,7 +17,7 @@ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/socket.d, * tanya/network/socket.d) */ -module tanya.network.socket; +module tanya.net.socket; import core.stdc.errno; import core.time; @@ -57,6 +26,8 @@ import tanya.bitmanip; import tanya.memory.allocator; import tanya.meta.trait; import tanya.os.error; +import tanya.net.iface; +import tanya.net.ip; /// Value returned by socket operations on error. enum int socketError = -1; @@ -588,26 +559,6 @@ shared static this() } } -/** - * $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects - * the protocol family which will be used for communication. - */ -enum AddressFamily : int -{ - unspec = 0, /// Unspecified. - local = 1, /// Local to host (pipes and file-domain). - unix = local, /// POSIX name for PF_LOCAL. - inet = 2, /// IP protocol family. - ax25 = 3, /// Amateur Radio AX.25. - ipx = 4, /// Novell Internet Protocol. - appletalk = 5, /// Appletalk DDP. - netrom = 6, /// Amateur radio NetROM. - bridge = 7, /// Multiprotocol bridge. - atmpvc = 8, /// ATM PVCs. - x25 = 9, /// Reserved for X.25 project. - inet6 = 10, /// IP version 6. -} - /** * $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions * $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because @@ -1064,13 +1015,13 @@ class StreamSocket : Socket, ConnectionOrientedSocket * Associate a local address with this socket. * * Params: - * address = Local address. + * endpoint = Local address. * * Throws: $(D_PSYMBOL SocketException) if unable to bind. */ - void bind(Address address) const @trusted @nogc + void bind(const Endpoint endpoint) const @nogc { - if (.bind(handle_, address.name, address.length) == socketError) + if (.bind(handle_, cast(const(sockaddr)*) &endpoint, Endpoint.sizeof)) { throw defaultAllocator.make!SocketException("Unable to bind socket"); } @@ -1268,157 +1219,6 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket } } -/** - * Socket address representation. - */ -abstract class Address -{ - /** - * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. - */ - abstract @property inout(sockaddr)* name() inout pure nothrow @nogc; - - /** - * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. - */ - abstract @property inout(socklen_t) length() inout const pure nothrow @nogc; -} - -class InternetAddress : Address -{ - version (Windows) - { - /// Internal internet address representation. - protected SOCKADDR_STORAGE storage; - } - else version (Posix) - { - /// Internal internet address representation. - protected sockaddr_storage storage; - } - const ushort port_; - - enum ushort anyPort = 0; - - this(string host, const ushort port = anyPort) @nogc - { - if (getaddrinfoPointer is null || freeaddrinfoPointer is null) - { - throw make!SocketException(defaultAllocator, - "Address info lookup is not available on this system"); - } - addrinfo* ai_res; - this.port_ = port; - - // Make C-string from host. - auto node = cast(char[]) allocator.allocate(host.length + 1); - node[0 .. $ - 1] = host; - node[$ - 1] = '\0'; - scope (exit) - { - allocator.deallocate(node); - } - - // Convert port to a C-string. - char[6] service = [0, 0, 0, 0, 0, 0]; - const(char)* servicePointer; - if (port) - { - ushort originalPort = port; - ushort start; - for (ushort j = 10, i = 4; i > 0; j *= 10, --i) - { - ushort rest = originalPort % 10; - if (rest != 0) - { - service[i] = cast(char) (rest + '0'); - start = i; - } - originalPort /= 10; - } - servicePointer = service[start .. $].ptr; - } - - auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res); - if (ret) - { - throw defaultAllocator.make!SocketException("Address info lookup failed"); - } - scope (exit) - { - freeaddrinfoPointer(ai_res); - } - - ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr; - for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++) - { - *dp = *sp; - } - if (ai_res.ai_family != AddressFamily.inet && ai_res.ai_family != AddressFamily.inet6) - { - throw defaultAllocator.make!SocketException("Wrong address family"); - } - } - - /// - unittest - { - auto address = defaultAllocator.make!InternetAddress("127.0.0.1"); - assert(address.port == InternetAddress.anyPort); - assert(address.name !is null); - assert(address.family == AddressFamily.inet); - - defaultAllocator.dispose(address); - } - - /** - * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. - */ - override @property inout(sockaddr)* name() inout pure nothrow @nogc - { - return cast(sockaddr*) &storage; - } - - /** - * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. - */ - override @property inout(socklen_t) length() inout const pure nothrow @nogc - { - // FreeBSD wants to know the exact length of the address on bind. - switch (family) - { - case AddressFamily.inet: - return sockaddr_in.sizeof; - case AddressFamily.inet6: - return sockaddr_in6.sizeof; - default: - assert(false); - } - } - - /** - * Returns: Family of this address. - */ - @property inout(AddressFamily) family() inout const pure nothrow @nogc - { - return cast(AddressFamily) storage.ss_family; - } - - @property inout(ushort) port() inout const pure nothrow @nogc - { - return port_; - } - - /// - unittest - { - auto address = defaultAllocator.make!InternetAddress("127.0.0.1", - cast(ushort) 1234); - assert(address.port == 1234); - defaultAllocator.dispose(address); - } -} - /** * Checks if the last error is a serious error or just a special * behaviour error of non-blocking sockets (for example an error diff --git a/source/tanya/network/package.d b/source/tanya/network/package.d deleted file mode 100644 index 8c349ec..0000000 --- a/source/tanya/network/package.d +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Network programming. - * - * Copyright: Eugene Wissner 2016-2020. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/package.d, - * tanya/network/package.d) - */ -module tanya.network; - -public import tanya.network.socket;