summaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHaavard <havardpe@yahoo-inc.com>2017-04-11 13:49:59 +0000
committerHaavard <havardpe@yahoo-inc.com>2017-04-11 13:49:59 +0000
commita528c792ec975764c3b49c6a01f61f637cf22913 (patch)
tree3654238b817b8bce0ca4d8e2356b07962cfcbf27 /vespalib
parent221c4cbc0f0c1df1541f6f89ec41c4bd2f5457c6 (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.cpp81
-rw-r--r--vespalib/src/tests/net/socket_spec/socket_spec_test.cpp123
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_address.cpp82
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_address.h15
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_spec.cpp73
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_spec.h31
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); }