diff options
author | Haavard <havardpe@yahoo-inc.com> | 2017-04-11 13:49:59 +0000 |
---|---|---|
committer | Haavard <havardpe@yahoo-inc.com> | 2017-04-11 13:49:59 +0000 |
commit | a528c792ec975764c3b49c6a01f61f637cf22913 (patch) | |
tree | 3654238b817b8bce0ca4d8e2356b07962cfcbf27 /vespalib | |
parent | 221c4cbc0f0c1df1541f6f89ec41c4bd2f5457c6 (diff) |
support unix domain sockets with abstract names
clean up corner cases in spec parsing
simplify spec testing
robustify and extend socket testing
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/src/tests/net/socket/socket_test.cpp | 81 | ||||
-rw-r--r-- | vespalib/src/tests/net/socket_spec/socket_spec_test.cpp | 123 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/socket_address.cpp | 82 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/socket_address.h | 15 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/socket_spec.cpp | 73 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/socket_spec.h | 31 |
6 files changed, 282 insertions, 123 deletions
diff --git a/vespalib/src/tests/net/socket/socket_test.cpp b/vespalib/src/tests/net/socket/socket_test.cpp index 5447f0de9bf..1c6b027a2b3 100644 --- a/vespalib/src/tests/net/socket/socket_test.cpp +++ b/vespalib/src/tests/net/socket/socket_test.cpp @@ -52,6 +52,9 @@ vespalib::string get_meta(const SocketAddress &addr) { if (addr.is_wildcard()) { meta += " wildcard"; } + if (addr.is_abstract()) { + meta += " abstract"; + } return meta; } @@ -105,6 +108,9 @@ TEST("my local address") { fprintf(stderr, "resolve(4080):\n"); for (const auto &addr: list) { EXPECT_TRUE(addr.is_wildcard()); + EXPECT_TRUE(addr.is_ipv4() || addr.is_ipv6()); + EXPECT_TRUE(!addr.is_ipc()); + EXPECT_TRUE(!addr.is_abstract()); EXPECT_EQUAL(addr.port(), 4080); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } @@ -115,17 +121,38 @@ TEST("yahoo.com address") { fprintf(stderr, "resolve(80, 'yahoo.com'):\n"); for (const auto &addr: list) { EXPECT_TRUE(!addr.is_wildcard()); + EXPECT_TRUE(addr.is_ipv4() || addr.is_ipv6()); + EXPECT_TRUE(!addr.is_ipc()); + EXPECT_TRUE(!addr.is_abstract()); EXPECT_EQUAL(addr.port(), 80); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } } -TEST("ipc address") { +TEST("ipc address (path)") { auto addr = SocketAddress::from_path("my_socket"); + EXPECT_TRUE(!addr.is_ipv4()); + EXPECT_TRUE(!addr.is_ipv6()); EXPECT_TRUE(addr.is_ipc()); + EXPECT_TRUE(!addr.is_abstract()); EXPECT_TRUE(!addr.is_wildcard()); EXPECT_EQUAL(addr.port(), -1); EXPECT_EQUAL(vespalib::string("my_socket"), addr.path()); + EXPECT_TRUE(addr.name().empty()); + fprintf(stderr, "from_path(my_socket)\n"); + fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); +} + +TEST("ipc address (name)") { + auto addr = SocketAddress::from_name("my_socket"); + EXPECT_TRUE(!addr.is_ipv4()); + EXPECT_TRUE(!addr.is_ipv6()); + EXPECT_TRUE(addr.is_ipc()); + EXPECT_TRUE(addr.is_abstract()); + EXPECT_TRUE(!addr.is_wildcard()); + EXPECT_EQUAL(addr.port(), -1); + EXPECT_TRUE(addr.path().empty()); + EXPECT_EQUAL(vespalib::string("my_socket"), addr.name()); fprintf(stderr, "from_path(my_socket)\n"); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } @@ -147,19 +174,29 @@ struct ServerWrapper { ServerWrapper(const vespalib::string &spec) : server(ServerSocket::listen(SocketSpec(spec))) {} }; -TEST_MT_F("require that basic socket io works", 2, ServerWrapper("tcp/0")) { +TEST_MT_FF("require that basic socket io works", 2, ServerWrapper("tcp/0"), TimeBomb(60)) { bool is_server = (thread_id == 0); Socket::UP socket = connect_sockets(is_server, *f1.server); TEST_DO(verify_socket_io(is_server, *socket)); } -TEST_MT_F("require that basic unix domain socket io works", 2, ServerWrapper("ipc/file:my_socket")) { +TEST_MT_FF("require that basic unix domain socket io works (path)", 2, + ServerWrapper("ipc/file:my_socket"), TimeBomb(60)) +{ bool is_server = (thread_id == 0); Socket::UP socket = connect_sockets(is_server, *f1.server); TEST_DO(verify_socket_io(is_server, *socket)); } -TEST_MT_F("require that server accept can be interrupted", 2, ServerWrapper("tcp/0")) { +TEST_MT_FF("require that basic unix domain socket io works (name)", 2, + ServerWrapper(make_string("ipc/name:my_socket-%d", int(getpid()))), TimeBomb(60)) +{ + bool is_server = (thread_id == 0); + Socket::UP socket = connect_sockets(is_server, *f1.server); + TEST_DO(verify_socket_io(is_server, *socket)); +} + +TEST_MT_FF("require that server accept can be interrupted", 2, ServerWrapper("tcp/0"), TimeBomb(60)) { bool is_server = (thread_id == 0); if (is_server) { fprintf(stderr, "--> calling accept\n"); @@ -226,4 +263,40 @@ TEST("require that a server socket will remove an old socket file if it cannot b EXPECT_TRUE(!is_socket("my_socket")); } +TEST("require that two server sockets cannot have the same abstract unix domain socket name") { + vespalib::string spec = make_string("ipc/name:my_socket-%d", int(getpid())); + ServerSocket::UP server1 = ServerSocket::listen(SocketSpec(spec)); + ServerSocket::UP server2 = ServerSocket::listen(SocketSpec(spec)); + EXPECT_TRUE(server1->valid()); + EXPECT_TRUE(!server2->valid()); +} + +TEST("require that abstract socket names are freed when the server socket is destructed") { + vespalib::string spec = make_string("ipc/name:my_socket-%d", int(getpid())); + ServerSocket::UP server1 = ServerSocket::listen(SocketSpec(spec)); + EXPECT_TRUE(server1->valid()); + server1.reset(); + ServerSocket::UP server2 = ServerSocket::listen(SocketSpec(spec)); + EXPECT_TRUE(server2->valid()); +} + +TEST("require that abstract sockets do not have socket files") { + vespalib::string name = make_string("my_socket-%d", int(getpid())); + vespalib::string spec = make_string("ipc/name:%s", name.c_str()); + ServerSocket::UP server = ServerSocket::listen(SocketSpec(spec)); + EXPECT_TRUE(server->valid()); + EXPECT_TRUE(!is_socket(name)); + EXPECT_TRUE(!is_file(name)); +} + +TEST_MT_FFF("require that abstract and file-based unix domain sockets are not in conflict", 4, + ServerWrapper(make_string("ipc/file:my_socket-%d", int(getpid()))), + ServerWrapper(make_string("ipc/name:my_socket-%d", int(getpid()))), TimeBomb(60)) +{ + bool is_server = ((thread_id % 2) == 0); + ServerSocket &server_socket = ((thread_id / 2) == 0) ? *f1.server : *f2.server; + Socket::UP socket = connect_sockets(is_server, server_socket); + TEST_DO(verify_socket_io(is_server, *socket)); +} + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp b/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp index ca00377f049..9cb88993c97 100644 --- a/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp +++ b/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp @@ -4,106 +4,99 @@ using namespace vespalib; -void verify(const SocketSpec &spec, bool valid, const vespalib::string &path, const vespalib::string &host, int port) { +void verify(const SocketSpec &spec, bool valid, + const vespalib::string &path, const vespalib::string &name, + const vespalib::string &host, int port) +{ EXPECT_EQUAL(spec.valid(), valid); EXPECT_EQUAL(spec.path(), path); + EXPECT_EQUAL(spec.name(), name); EXPECT_EQUAL(spec.host(), host); EXPECT_EQUAL(spec.port(), port); } -void verify(const SocketSpec &spec, const vespalib::string &path) { - TEST_DO(verify(spec, true, path, "", -1)); +void verify_path(const SocketSpec &spec, const vespalib::string &path) { + TEST_DO(verify(spec, true, path, "", "", -1)); } -void verify(const SocketSpec &spec, const vespalib::string &host, int port) { - TEST_DO(verify(spec, true, "", host, port)); +void verify_name(const SocketSpec &spec, const vespalib::string &name) { + TEST_DO(verify(spec, true, "", name, "", -1)); } -void verify(const SocketSpec &spec, int port) { - TEST_DO(verify(spec, true, "", "", port)); +void verify_host_port(const SocketSpec &spec, const vespalib::string &host, int port) { + TEST_DO(verify(spec, true, "", "", host, port)); } -void verify_invalid(const SocketSpec &spec) { - TEST_DO(verify(spec, false, "", "", -1)); -} - -//----------------------------------------------------------------------------- - -TEST("require that socket spec can be created directly from path") { - TEST_DO(verify(SocketSpec::from_path("my_path"), "my_path")); -} - -TEST("require that socket spec can be created directly from host and port") { - TEST_DO(verify(SocketSpec::from_host_port("my_host", 123), "my_host", 123)); +void verify_port(const SocketSpec &spec, int port) { + TEST_DO(verify(spec, true, "", "", "", port)); } -TEST("require that socket spec can be created directly from port only") { - TEST_DO(verify(SocketSpec::from_port(123), 123)); +void verify_invalid(const SocketSpec &spec) { + TEST_DO(verify(spec, false, "", "", "", -1)); } -TEST("require that empty spec is invalid") { - TEST_DO(verify_invalid(SocketSpec(""))); +void verify_spec(const vespalib::string &str, const vespalib::string &expect) { + vespalib::string actual = SocketSpec(str).spec(); + EXPECT_EQUAL(actual, expect); } -TEST("require that bogus spec is invalid") { - TEST_DO(verify_invalid(SocketSpec("bogus"))); +void verify_spec(const vespalib::string &str) { + TEST_DO(verify_spec(str, str)); } -TEST("require that socket spec can parse ipc spec") { - TEST_DO(verify(SocketSpec("ipc/file:my_path"), "my_path")); -} - -TEST("require that empty ipc path gives invalid socket spec") { - TEST_DO(verify_invalid(SocketSpec("ipc/file:"))); -} +//----------------------------------------------------------------------------- -TEST("require that socket spec can parse host/port spec") { - TEST_DO(verify(SocketSpec("tcp/my_host:123"), "my_host", 123)); +TEST("require that socket spec can be created directly from path") { + TEST_DO(verify_path(SocketSpec::from_path("my_path"), "my_path")); } -TEST("require that socket spec can parse port only spec") { - TEST_DO(verify(SocketSpec("tcp/123"), 123)); +TEST("require that socket spec can be created directly from name") { + TEST_DO(verify_name(SocketSpec::from_name("my_name"), "my_name")); } -TEST("require that socket spec can parse the one true listen spec") { - TEST_DO(verify(SocketSpec("tcp/0"), 0)); +TEST("require that socket spec can be created directly from host and port") { + TEST_DO(verify_host_port(SocketSpec::from_host_port("my_host", 123), "my_host", 123)); } -TEST("require that host port separator can be given also without host") { - TEST_DO(verify(SocketSpec("tcp/:123"), 123)); - TEST_DO(verify(SocketSpec("tcp/:0"), 0)); +TEST("require that socket spec can be created directly from port only") { + TEST_DO(verify_port(SocketSpec::from_port(123), 123)); } -TEST("require that non-number port gives invalid spec") { +TEST("require that socket spec parsing works as expected") { + TEST_DO(verify_invalid(SocketSpec(""))); + TEST_DO(verify_invalid(SocketSpec("bogus"))); + TEST_DO(verify_path(SocketSpec("ipc/file:my_path"), "my_path")); + TEST_DO(verify_invalid(SocketSpec("ipc/file:"))); + TEST_DO(verify_name(SocketSpec("ipc/name:my_name"), "my_name")); + TEST_DO(verify_invalid(SocketSpec("ipc/name:"))); + TEST_DO(verify_host_port(SocketSpec("tcp/my_host:123"), "my_host", 123)); + TEST_DO(verify_port(SocketSpec("tcp/123"), 123)); + TEST_DO(verify_port(SocketSpec("tcp/0"), 0)); + TEST_DO(verify_invalid(SocketSpec("tcp/:123"))); + TEST_DO(verify_invalid(SocketSpec("tcp/:0"))); TEST_DO(verify_invalid(SocketSpec("tcp/host:xyz"))); TEST_DO(verify_invalid(SocketSpec("tcp/xyz"))); -} - -TEST("require that negative port gives invalid spec") { TEST_DO(verify_invalid(SocketSpec("tcp/host:-123"))); TEST_DO(verify_invalid(SocketSpec("tcp/-123"))); -} - -TEST("require that missing port number gives invalid spec") { TEST_DO(verify_invalid(SocketSpec("tcp/host:"))); TEST_DO(verify_invalid(SocketSpec("tcp/"))); -} - -TEST("require that host can be quoted") { - TEST_DO(verify(SocketSpec("tcp/[my:host]:123"), "my:host", 123)); -} - -TEST("require that missing host can be quoted") { - TEST_DO(verify(SocketSpec("tcp/[]:123"), 123)); -} - -TEST("require that partial quotes are treated as host") { - TEST_DO(verify(SocketSpec("tcp/[:123"), "[", 123)); - TEST_DO(verify(SocketSpec("tcp/]:123"), "]", 123)); -} - -TEST("require that inconvenient hosts can be parsed without quotes") { - TEST_DO(verify(SocketSpec("tcp/my:host:123"), "my:host", 123)); + TEST_DO(verify_host_port(SocketSpec("tcp/[my:host]:123"), "my:host", 123)); + TEST_DO(verify_invalid(SocketSpec("tcp/[]:123"))); + TEST_DO(verify_host_port(SocketSpec("tcp/[:123"), "[", 123)); + TEST_DO(verify_host_port(SocketSpec("tcp/]:123"), "]", 123)); + TEST_DO(verify_host_port(SocketSpec("tcp/my:host:123"), "my:host", 123)); +} + +TEST("require that socket spec to string transform works as expected") { + TEST_DO(verify_spec("invalid")); + TEST_DO(verify_spec("bogus", "invalid")); + TEST_DO(verify_spec("ipc/file:my_path")); + TEST_DO(verify_spec("ipc/name:my_name")); + TEST_DO(verify_spec("tcp/123")); + TEST_DO(verify_spec("tcp/0")); + TEST_DO(verify_spec("tcp/host:123")); + TEST_DO(verify_spec("tcp/[my:host]:123")); + TEST_DO(verify_spec("tcp/[host]:123", "tcp/host:123")); } TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/vespa/vespalib/net/socket_address.cpp b/vespalib/src/vespa/vespalib/net/socket_address.cpp index 1484c650156..6a70491beb8 100644 --- a/vespalib/src/vespa/vespalib/net/socket_address.cpp +++ b/vespalib/src/vespa/vespalib/net/socket_address.cpp @@ -1,6 +1,5 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - #include "socket_address.h" #include <vespa/vespalib/util/stringfmt.h> #include <sys/types.h> @@ -21,28 +20,36 @@ bool SocketAddress::is_wildcard() const { if (is_ipv4()) { - const sockaddr_in *addr = reinterpret_cast<const sockaddr_in *>(&_addr); - return (addr->sin_addr.s_addr == htonl(INADDR_ANY)); + return (addr_in()->sin_addr.s_addr == htonl(INADDR_ANY)); } if (is_ipv6()) { - const sockaddr_in6 *addr = reinterpret_cast<const sockaddr_in6 *>(&_addr); - return (memcmp(&addr->sin6_addr, &ipv6_wildcard, sizeof(in6_addr)) == 0); + return (memcmp(&addr_in6()->sin6_addr, &ipv6_wildcard, sizeof(in6_addr)) == 0); } return false; } +bool +SocketAddress::is_abstract() const +{ + bool result = false; + if (is_ipc()) { + const char *path_limit = (reinterpret_cast<const char *>(addr_un()) + _size); + const char *pos = &addr_un()->sun_path[0]; + result = ((path_limit > pos) && (pos[0] == '\0')); + } + return result; +} + vespalib::string SocketAddress::ip_address() const { - vespalib::string result = "invalid"; + vespalib::string result; if (is_ipv4()) { char buf[INET_ADDRSTRLEN]; - const sockaddr_in *addr = reinterpret_cast<const sockaddr_in *>(&_addr); - result = inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)); + result = inet_ntop(AF_INET, &addr_in()->sin_addr, buf, sizeof(buf)); } else if (is_ipv6()) { char buf[INET6_ADDRSTRLEN]; - const sockaddr_in6 *addr = reinterpret_cast<const sockaddr_in6 *>(&_addr); - result = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof(buf)); + result = inet_ntop(AF_INET6, &addr_in6()->sin6_addr, buf, sizeof(buf)); } return result; } @@ -50,11 +57,26 @@ SocketAddress::ip_address() const vespalib::string SocketAddress::path() const { - vespalib::string result = ""; - if (is_ipc()) { - const sockaddr_un *addr = reinterpret_cast<const sockaddr_un *>(&_addr); - const char *path_limit = (reinterpret_cast<const char *>(addr) + _size); - const char *pos = &addr->sun_path[0]; + vespalib::string result; + if (is_ipc() && !is_abstract()) { + const char *path_limit = (reinterpret_cast<const char *>(addr_un()) + _size); + const char *pos = &addr_un()->sun_path[0]; + const char *end = pos; + while ((end < path_limit) && (*end != 0)) { + ++end; + } + result.assign(pos, end - pos); + } + return result; +} + +vespalib::string +SocketAddress::name() const +{ + vespalib::string result; + if (is_ipc() && is_abstract()) { + const char *path_limit = (reinterpret_cast<const char *>(addr_un()) + _size); + const char *pos = &addr_un()->sun_path[1]; const char *end = pos; while ((end < path_limit) && (*end != 0)) { ++end; @@ -76,12 +98,10 @@ int SocketAddress::port() const { if (is_ipv4()) { - const sockaddr_in *addr = reinterpret_cast<const sockaddr_in *>(&_addr); - return ntohs(addr->sin_port); + return ntohs(addr_in()->sin_port); } if (is_ipv6()) { - const sockaddr_in6 *addr = reinterpret_cast<const sockaddr_in6 *>(&_addr); - return ntohs(addr->sin6_port); + return ntohs(addr_in6()->sin6_port); } return -1; } @@ -99,7 +119,11 @@ SocketAddress::spec() const return make_string("tcp/[%s]:%d", ip_address().c_str(), port()); } if (is_ipc()) { - return make_string("ipc/file:%s", path().c_str()); + if (is_abstract()) { + return make_string("ipc/name:%s", name().c_str()); + } else { + return make_string("ipc/file:%s", path().c_str()); + } } return "invalid"; } @@ -205,10 +229,22 @@ SocketAddress::from_path(const vespalib::string &path) { SocketAddress result; sockaddr_un &addr_un = reinterpret_cast<sockaddr_un &>(result._addr); - assert(sizeof(sockaddr_un) <= sizeof(result._addr)); - if (path.size() < sizeof(addr_un.sun_path)) { + if (!path.empty() && (path.size() < sizeof(addr_un.sun_path))) { + addr_un.sun_family = AF_UNIX; + memcpy(&addr_un.sun_path[0], path.data(), path.size()); + result._size = sizeof(sockaddr_un); + } + return result; +} + +SocketAddress +SocketAddress::from_name(const vespalib::string &name) +{ + SocketAddress result; + sockaddr_un &addr_un = reinterpret_cast<sockaddr_un &>(result._addr); + if (!name.empty() && (name.size() < sizeof(addr_un.sun_path))) { addr_un.sun_family = AF_UNIX; - strcpy(&addr_un.sun_path[0], path.c_str()); + memcpy(&addr_un.sun_path[1], name.data(), name.size()); result._size = sizeof(sockaddr_un); } return result; diff --git a/vespalib/src/vespa/vespalib/net/socket_address.h b/vespalib/src/vespa/vespalib/net/socket_address.h index e614aca5365..c6810ad6108 100644 --- a/vespalib/src/vespa/vespalib/net/socket_address.h +++ b/vespalib/src/vespa/vespalib/net/socket_address.h @@ -1,6 +1,5 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - #pragma once #include <vespa/vespalib/stllike/string.h> @@ -8,10 +7,14 @@ #include <vector> #include <sys/socket.h> +struct sockaddr_in; +struct sockaddr_in6; +struct sockaddr_un; + namespace vespalib { /** - * Wrapper class for low-level TCP/IP socket addresses. + * Wrapper class for low-level TCP/IP and IPC socket addresses. **/ class SocketAddress { @@ -20,7 +23,10 @@ private: sockaddr_storage _addr; const sockaddr *addr() const { return reinterpret_cast<const sockaddr *>(&_addr); } - explicit SocketAddress(const sockaddr *addr_in, socklen_t addrlen_in); + const sockaddr_in *addr_in() const { return reinterpret_cast<const sockaddr_in *>(&_addr); } + const sockaddr_in6 *addr_in6() const { return reinterpret_cast<const sockaddr_in6 *>(&_addr); } + const sockaddr_un *addr_un() const { return reinterpret_cast<const sockaddr_un *>(&_addr); } + SocketAddress(const sockaddr *addr_in, socklen_t addrlen_in); public: SocketAddress() { memset(this, 0, sizeof(SocketAddress)); } SocketAddress(const SocketAddress &rhs) { memcpy(this, &rhs, sizeof(SocketAddress)); } @@ -33,9 +39,11 @@ public: bool is_ipv6() const { return (valid() && (_addr.ss_family == AF_INET6)); } bool is_ipc() const { return (valid() && (_addr.ss_family == AF_UNIX)); } bool is_wildcard() const; + bool is_abstract() const; int port() const; vespalib::string ip_address() const; vespalib::string path() const; + vespalib::string name() const; vespalib::string spec() const; SocketHandle connect() const; SocketHandle listen(int backlog = 500) const; @@ -59,6 +67,7 @@ public: return SocketAddress(); } static SocketAddress from_path(const vespalib::string &path); + static SocketAddress from_name(const vespalib::string &name); }; } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/socket_spec.cpp b/vespalib/src/vespa/vespalib/net/socket_spec.cpp index 061ba5879f5..0964e3f8b8f 100644 --- a/vespalib/src/vespa/vespalib/net/socket_spec.cpp +++ b/vespalib/src/vespa/vespalib/net/socket_spec.cpp @@ -1,39 +1,55 @@ // Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include "socket_spec.h" +#include <vespa/vespalib/util/stringfmt.h> namespace vespalib { namespace { const vespalib::string tcp_prefix("tcp/"); -const vespalib::string ipc_prefix("ipc/file:"); +const vespalib::string ipc_path_prefix("ipc/file:"); +const vespalib::string ipc_name_prefix("ipc/name:"); + +SocketAddress make_address(const char *node, int port, bool server) { + if (server) { + return SocketAddress::select_local(port, node); + } else { + return SocketAddress::select_remote(port, node); + } +} + +SocketAddress make_address(int port, bool server) { + const char *node = server ? nullptr : "localhost"; + return make_address(node, port, server); +} } // namespace vespalib::<unnamed> +const vespalib::string SocketSpec::_empty; + SocketAddress SocketSpec::address(bool server) const { - if (!valid()) { - return SocketAddress(); - } - if (!_path.empty()) { - return SocketAddress::from_path(_path); - } - const char *fallback = server ? nullptr : "localhost"; - const char *node = _host.empty() ? fallback : _host.c_str(); - if (server) { - return SocketAddress::select_local(_port, node); - } else { - return SocketAddress::select_remote(_port, node); + switch (_type) { + case Type::PATH: return SocketAddress::from_path(_node); + case Type::NAME: return SocketAddress::from_name(_node); + case Type::HOST_PORT: return make_address(_node.c_str(), _port, server); + case Type::PORT: return make_address(_port, server); + case Type::INVALID: ; } + return SocketAddress(); } SocketSpec::SocketSpec(const vespalib::string &spec) : SocketSpec() { - if (starts_with(spec, ipc_prefix)) { - _path = spec.substr(ipc_prefix.size()); + if (starts_with(spec, ipc_path_prefix)) { + _node = spec.substr(ipc_path_prefix.size()); + _type = Type::PATH; + } else if (starts_with(spec, ipc_name_prefix)) { + _node = spec.substr(ipc_name_prefix.size()); + _type = Type::NAME; } else if (starts_with(spec, tcp_prefix)) { bool with_host = (spec.find(':') != spec.npos); const char *port_str = spec.c_str() + (with_host @@ -52,10 +68,35 @@ SocketSpec::SocketSpec(const vespalib::string &spec) ++host_str; host_str_len -= 2; } - _host.assign(host_str, host_str_len); + _node.assign(host_str, host_str_len); + _type = Type::HOST_PORT; + } else { + _type = Type::PORT; } } } + if ((_type != Type::PORT) && _node.empty()) { + _type = Type::INVALID; + _port = -1; + } +} + +vespalib::string +SocketSpec::spec() const +{ + switch (_type) { + case Type::PATH: return make_string("ipc/file:%s", _node.c_str()); + case Type::NAME: return make_string("ipc/name:%s", _node.c_str()); + case Type::HOST_PORT: + if (_node.find(':') != _node.npos) { + return make_string("tcp/[%s]:%d", _node.c_str(), _port); + } else { + return make_string("tcp/%s:%d", _node.c_str(), _port); + } + case Type::PORT: return make_string("tcp/%d", _port); + case Type::INVALID: ; + } + return "invalid"; } } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/socket_spec.h b/vespalib/src/vespa/vespalib/net/socket_spec.h index 43e7cb8c4b9..488cbf28fe7 100644 --- a/vespalib/src/vespa/vespalib/net/socket_spec.h +++ b/vespalib/src/vespa/vespalib/net/socket_spec.h @@ -13,28 +13,35 @@ namespace vespalib { class SocketSpec { private: - vespalib::string _path; - vespalib::string _host; - int _port; + enum class Type { INVALID, PATH, NAME, HOST_PORT, PORT }; + static const vespalib::string _empty; + Type _type; + vespalib::string _node; + int _port; - SocketSpec() : _path(), _host(), _port(-1) {} - SocketSpec(const vespalib::string &path, const vespalib::string &host, int port) - : _path(path), _host(host), _port(port) {} + SocketSpec() : _type(Type::INVALID), _node(), _port(-1) {} + SocketSpec(Type type, const vespalib::string &node, int port) + : _type(type), _node(node), _port(port) {} SocketAddress address(bool server) const; public: explicit SocketSpec(const vespalib::string &spec); + vespalib::string spec() const; static SocketSpec from_path(const vespalib::string &path) { - return SocketSpec(path, "", -1); + return SocketSpec(Type::PATH, path, -1); + } + static SocketSpec from_name(const vespalib::string &name) { + return SocketSpec(Type::NAME, name, -1); } static SocketSpec from_host_port(const vespalib::string &host, int port) { - return SocketSpec("", host, port); + return SocketSpec(Type::HOST_PORT, host, port); } static SocketSpec from_port(int port) { - return SocketSpec("", "", port); + return SocketSpec(Type::PORT, "", port); } - bool valid() const { return (!_path.empty() || (_port >= 0)); } - const vespalib::string &path() const { return _path; } - const vespalib::string &host() const { return _host; } + bool valid() const { return (_type != Type::INVALID); } + const vespalib::string &path() const { return (_type == Type::PATH) ? _node : _empty; } + const vespalib::string &name() const { return (_type == Type::NAME) ? _node : _empty; } + const vespalib::string &host() const { return (_type == Type::HOST_PORT) ? _node : _empty; } int port() const { return _port; } SocketAddress client_address() const { return address(false); } SocketAddress server_address() const { return address(true); } |