summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vespalib/CMakeLists.txt1
-rw-r--r--vespalib/src/tests/net/socket_spec/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/net/socket_spec/socket_spec_test.cpp109
-rw-r--r--vespalib/src/vespa/vespalib/net/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_spec.cpp69
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_spec.h42
6 files changed, 230 insertions, 0 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index 39e56a5f97f..e5a374e4e58 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -49,6 +49,7 @@ vespa_define_module(
src/tests/memory
src/tests/net/selector
src/tests/net/socket
+ src/tests/net/socket_spec
src/tests/objects/nbostream
src/tests/optimized
src/tests/printable
diff --git a/vespalib/src/tests/net/socket_spec/CMakeLists.txt b/vespalib/src/tests/net/socket_spec/CMakeLists.txt
new file mode 100644
index 00000000000..66b4ea4d5d6
--- /dev/null
+++ b/vespalib/src/tests/net/socket_spec/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_socket_spec_test_app TEST
+ SOURCES
+ socket_spec_test.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_socket_spec_test_app COMMAND vespalib_socket_spec_test_app)
diff --git a/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp b/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp
new file mode 100644
index 00000000000..ca00377f049
--- /dev/null
+++ b/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp
@@ -0,0 +1,109 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/net/socket_spec.h>
+
+using namespace vespalib;
+
+void verify(const SocketSpec &spec, bool valid, const vespalib::string &path, const vespalib::string &host, int port) {
+ EXPECT_EQUAL(spec.valid(), valid);
+ EXPECT_EQUAL(spec.path(), path);
+ 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(const SocketSpec &spec, const vespalib::string &host, int port) {
+ TEST_DO(verify(spec, true, "", host, port));
+}
+
+void verify(const SocketSpec &spec, int port) {
+ TEST_DO(verify(spec, true, "", "", 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));
+}
+
+TEST("require that socket spec can be created directly from port only") {
+ TEST_DO(verify(SocketSpec::from_port(123), 123));
+}
+
+TEST("require that empty spec is invalid") {
+ TEST_DO(verify_invalid(SocketSpec("")));
+}
+
+TEST("require that bogus spec is invalid") {
+ TEST_DO(verify_invalid(SocketSpec("bogus")));
+}
+
+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 parse port only spec") {
+ TEST_DO(verify(SocketSpec("tcp/123"), 123));
+}
+
+TEST("require that socket spec can parse the one true listen spec") {
+ TEST_DO(verify(SocketSpec("tcp/0"), 0));
+}
+
+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 non-number port gives invalid spec") {
+ 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_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/net/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/CMakeLists.txt
index 40b8b656db7..303d16db41e 100644
--- a/vespalib/src/vespa/vespalib/net/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/net/CMakeLists.txt
@@ -6,5 +6,6 @@ vespa_add_library(vespalib_vespalib_net OBJECT
socket.cpp
socket_address.cpp
socket_handle.cpp
+ socket_spec.cpp
DEPENDS
)
diff --git a/vespalib/src/vespa/vespalib/net/socket_spec.cpp b/vespalib/src/vespa/vespalib/net/socket_spec.cpp
new file mode 100644
index 00000000000..09d26417948
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/socket_spec.cpp
@@ -0,0 +1,69 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "socket_spec.h"
+
+namespace vespalib {
+
+namespace {
+
+const vespalib::string tcp_prefix("tcp/");
+const vespalib::string ipc_prefix("ipc/file:");
+
+} // namespace vespalib::<unnamed>
+
+SocketSpec::SocketSpec(const vespalib::string &spec)
+ : SocketSpec()
+{
+ if (starts_with(spec, ipc_prefix)) {
+ _path = spec.substr(ipc_prefix.size());
+ } else if (starts_with(spec, tcp_prefix)) {
+ bool with_host = (spec.find(':') != spec.npos);
+ const char *port_str = spec.c_str() + (with_host
+ ? (spec.rfind(':') + 1)
+ : tcp_prefix.size());
+ int port = atoi(port_str);
+ if ((port > 0) || (strcmp(port_str, "0") == 0)) {
+ _port = port;
+ if (with_host) {
+ const char *host_str = spec.c_str() + tcp_prefix.size();
+ size_t host_str_len = (port_str - host_str) - 1;
+ if ((host_str_len >= 2)
+ && (host_str[0] == '[')
+ && (host_str[host_str_len - 1] == ']'))
+ {
+ ++host_str;
+ host_str_len -= 2;
+ }
+ _host.assign(host_str, host_str_len);
+ }
+ }
+ }
+}
+
+SocketAddress
+SocketSpec::client_address() const
+{
+ if (!valid()) {
+ return SocketAddress();
+ }
+ if (!_path.empty()) {
+ return SocketAddress::from_path(_path);
+ }
+ const char *node = _host.empty() ? "localhost" : _host.c_str();
+ return SocketAddress::select_remote(_port, node);
+}
+
+SocketAddress
+SocketSpec::server_address() const
+{
+ if (!valid()) {
+ return SocketAddress();
+ }
+ if (!_path.empty()) {
+ return SocketAddress::from_path(_path);
+ }
+ const char *node = _host.empty() ? nullptr : _host.c_str();
+ return SocketAddress::select_local(_port, node);
+}
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/net/socket_spec.h b/vespalib/src/vespa/vespalib/net/socket_spec.h
new file mode 100644
index 00000000000..cd8a546a9fd
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/socket_spec.h
@@ -0,0 +1,42 @@
+// Copyright 2017 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>
+#include "socket_address.h"
+
+namespace vespalib {
+
+/**
+ * High-level socket address specification.
+ **/
+class SocketSpec
+{
+private:
+ vespalib::string _path;
+ vespalib::string _host;
+ int _port;
+
+ SocketSpec() : _path(), _host(), _port(-1) {}
+ SocketSpec(const vespalib::string &path, const vespalib::string &host, int port)
+ : _path(path), _host(host), _port(port) {}
+public:
+ SocketSpec(const vespalib::string &spec);
+ static SocketSpec from_path(const vespalib::string &path) {
+ return SocketSpec(path, "", -1);
+ }
+ static SocketSpec from_host_port(const vespalib::string &host, int port) {
+ return SocketSpec("", host, port);
+ }
+ static SocketSpec from_port(int port) {
+ return SocketSpec("", "", port);
+ }
+ bool valid() const { return (!_path.empty() || (_port >= 0)); }
+ const vespalib::string &path() const { return _path; }
+ const vespalib::string &host() const { return _host; }
+ int port() const { return _port; }
+ SocketAddress client_address() const;
+ SocketAddress server_address() const;
+};
+
+} // namespace vespalib