aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHaavard <havardpe@yahoo-inc.com>2017-04-20 12:39:57 +0000
committerHaavard <havardpe@yahoo-inc.com>2017-04-20 14:05:44 +0000
commit2decf3c4e35923b739254e389c26971ee8649a20 (patch)
tree59ceb32b57e9e17abb9dd06674fe99eb395fd27a /vespalib
parent656c30b4d6c2ba47ec9818a7bf2f7c402f9daf6b (diff)
added handle-based low-level socket options with testing
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/src/tests/net/socket/socket_test.cpp86
-rw-r--r--vespalib/src/vespa/vespalib/net/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_options.cpp73
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_options.h20
-rw-r--r--vespalib/src/vespa/vespalib/test/socket_options_verifier.h60
5 files changed, 240 insertions, 0 deletions
diff --git a/vespalib/src/tests/net/socket/socket_test.cpp b/vespalib/src/tests/net/socket/socket_test.cpp
index 1c6b027a2b3..bf0a5a2f273 100644
--- a/vespalib/src/tests/net/socket/socket_test.cpp
+++ b/vespalib/src/tests/net/socket/socket_test.cpp
@@ -2,14 +2,30 @@
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/net/socket_spec.h>
#include <vespa/vespalib/net/server_socket.h>
+#include <vespa/vespalib/net/socket_options.h>
#include <vespa/vespalib/net/socket.h>
#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/test/socket_options_verifier.h>
#include <thread>
#include <functional>
#include <chrono>
using namespace vespalib;
+bool ipv4_enabled = false;
+bool ipv6_enabled = false;
+
+int my_inet() {
+ if (ipv6_enabled) {
+ return AF_INET6;
+ }
+ if (ipv4_enabled) {
+ return AF_INET;
+ }
+ TEST_ERROR("tcp/ip support not detected");
+ return AF_UNIX;
+}
+
bool is_socket(const vespalib::string &path) {
struct stat info;
if (path.empty() || (lstat(path.c_str(), &info) != 0)) {
@@ -109,6 +125,8 @@ TEST("my local address") {
for (const auto &addr: list) {
EXPECT_TRUE(addr.is_wildcard());
EXPECT_TRUE(addr.is_ipv4() || addr.is_ipv6());
+ ipv4_enabled |= addr.is_ipv4();
+ ipv6_enabled |= addr.is_ipv6();
EXPECT_TRUE(!addr.is_ipc());
EXPECT_TRUE(!addr.is_abstract());
EXPECT_EQUAL(addr.port(), 4080);
@@ -299,4 +317,72 @@ TEST_MT_FFF("require that abstract and file-based unix domain sockets are not in
TEST_DO(verify_socket_io(is_server, *socket));
}
+TEST("require that sockets can be set blocking and non-blocking") {
+ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0));
+ test::SocketOptionsVerifier verifier(handle.get());
+ EXPECT_TRUE(!SocketOptions::set_blocking(-1, true));
+ EXPECT_TRUE(SocketOptions::set_blocking(handle.get(), true));
+ TEST_DO(verifier.verify_blocking(true));
+ EXPECT_TRUE(SocketOptions::set_blocking(handle.get(), false));
+ TEST_DO(verifier.verify_blocking(false));
+}
+
+TEST("require that tcp nodelay can be enabled and disabled") {
+ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0));
+ test::SocketOptionsVerifier verifier(handle.get());
+ EXPECT_TRUE(!SocketOptions::set_nodelay(-1, true));
+ EXPECT_TRUE(SocketOptions::set_nodelay(handle.get(), true));
+ TEST_DO(verifier.verify_nodelay(true));
+ EXPECT_TRUE(SocketOptions::set_nodelay(handle.get(), false));
+ TEST_DO(verifier.verify_nodelay(false));
+}
+
+TEST("require that reuse addr can be set and cleared") {
+ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0));
+ test::SocketOptionsVerifier verifier(handle.get());
+ EXPECT_TRUE(!SocketOptions::set_reuse_addr(-1, true));
+ EXPECT_TRUE(SocketOptions::set_reuse_addr(handle.get(), true));
+ TEST_DO(verifier.verify_reuse_addr(true));
+ EXPECT_TRUE(SocketOptions::set_reuse_addr(handle.get(), false));
+ TEST_DO(verifier.verify_reuse_addr(false));
+}
+
+TEST("require that ipv6_only can be set and cleared") {
+ if (ipv6_enabled) {
+ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0));
+ test::SocketOptionsVerifier verifier(handle.get());
+ EXPECT_TRUE(!SocketOptions::set_ipv6_only(-1, true));
+ EXPECT_TRUE(SocketOptions::set_ipv6_only(handle.get(), true));
+ TEST_DO(verifier.verify_ipv6_only(true));
+ EXPECT_TRUE(SocketOptions::set_ipv6_only(handle.get(), false));
+ TEST_DO(verifier.verify_ipv6_only(false));
+ } else {
+ fprintf(stderr, "WARNING: skipping ipv6_only test since ipv6 is disabled");
+ }
+}
+
+TEST("require that tcp keepalive can be set and cleared") {
+ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0));
+ test::SocketOptionsVerifier verifier(handle.get());
+ EXPECT_TRUE(!SocketOptions::set_keepalive(-1, true));
+ EXPECT_TRUE(SocketOptions::set_keepalive(handle.get(), true));
+ TEST_DO(verifier.verify_keepalive(true));
+ EXPECT_TRUE(SocketOptions::set_keepalive(handle.get(), false));
+ TEST_DO(verifier.verify_keepalive(false));
+}
+
+TEST("require that tcp lingering can be adjusted") {
+ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0));
+ test::SocketOptionsVerifier verifier(handle.get());
+ EXPECT_TRUE(!SocketOptions::set_linger(-1, true, 0));
+ EXPECT_TRUE(SocketOptions::set_linger(handle.get(), true, 0));
+ TEST_DO(verifier.verify_linger(true, 0));
+ EXPECT_TRUE(SocketOptions::set_linger(handle.get(), true, 10));
+ TEST_DO(verifier.verify_linger(true, 10));
+ EXPECT_TRUE(SocketOptions::set_linger(handle.get(), false, 0));
+ TEST_DO(verifier.verify_linger(false, 0));
+ EXPECT_TRUE(SocketOptions::set_linger(handle.get(), false, 10));
+ TEST_DO(verifier.verify_linger(false, 0));
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/net/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/CMakeLists.txt
index 303d16db41e..4350cd49fed 100644
--- a/vespalib/src/vespa/vespalib/net/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/net/CMakeLists.txt
@@ -6,6 +6,7 @@ vespa_add_library(vespalib_vespalib_net OBJECT
socket.cpp
socket_address.cpp
socket_handle.cpp
+ socket_options.cpp
socket_spec.cpp
DEPENDS
)
diff --git a/vespalib/src/vespa/vespalib/net/socket_options.cpp b/vespalib/src/vespa/vespalib/net/socket_options.cpp
new file mode 100644
index 00000000000..6a29dbbf16d
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/socket_options.cpp
@@ -0,0 +1,73 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "socket_options.h"
+
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace vespalib {
+
+namespace {
+
+bool set_bool_opt(int fd, int level, int name, bool value) {
+ int data = value;
+ return (setsockopt(fd, level, name, &data, sizeof(data)) == 0);
+}
+
+} // namespace vespalib::<unnamed>
+
+bool
+SocketOptions::set_blocking(int fd, bool value)
+{
+ int flags = fcntl(fd, F_GETFL, NULL);
+ if (flags != -1) {
+ if (value) {
+ flags &= ~O_NONBLOCK; // clear non-blocking flag
+ } else {
+ flags |= O_NONBLOCK; // set non-blocking flag
+ }
+ return (fcntl(fd, F_SETFL, flags) == 0);
+ }
+ return false;
+}
+
+bool
+SocketOptions::set_nodelay(int fd, bool value)
+{
+ return set_bool_opt(fd, IPPROTO_TCP, TCP_NODELAY, value);
+}
+
+bool
+SocketOptions::set_reuse_addr(int fd, bool value)
+{
+ return set_bool_opt(fd, SOL_SOCKET, SO_REUSEADDR, value);
+}
+
+bool
+SocketOptions::set_ipv6_only(int fd, bool value)
+{
+ return set_bool_opt(fd, IPPROTO_IPV6, IPV6_V6ONLY, value);
+}
+
+bool
+SocketOptions::set_keepalive(int fd, bool value)
+{
+ return set_bool_opt(fd, SOL_SOCKET, SO_KEEPALIVE, value);
+}
+
+bool
+SocketOptions::set_linger(int fd, bool enable, int value)
+{
+ struct linger data;
+ memset(&data, 0, sizeof(data));
+ data.l_onoff = enable;
+ data.l_linger = value;
+ return (setsockopt(fd, SOL_SOCKET, SO_LINGER, &data, sizeof(data)) == 0);
+}
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/net/socket_options.h b/vespalib/src/vespa/vespalib/net/socket_options.h
new file mode 100644
index 00000000000..fe9b5da7325
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/socket_options.h
@@ -0,0 +1,20 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace vespalib {
+
+/**
+ * Low-level functions used to adjust various socket related
+ * options. Return values indicate success/failure.
+ **/
+struct SocketOptions {
+ static bool set_blocking(int fd, bool value);
+ static bool set_nodelay(int fd, bool value);
+ static bool set_reuse_addr(int fd, bool value);
+ static bool set_ipv6_only(int fd, bool value);
+ static bool set_keepalive(int fd, bool value);
+ static bool set_linger(int fd, bool enable, int value);
+};
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/test/socket_options_verifier.h b/vespalib/src/vespa/vespalib/test/socket_options_verifier.h
new file mode 100644
index 00000000000..e5623288f6f
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/test/socket_options_verifier.h
@@ -0,0 +1,60 @@
+// 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/testkit/test_kit.h>
+
+namespace vespalib {
+namespace test {
+
+namespace {
+
+void verify_bool_opt(int fd, int level, int name, bool expect) {
+ int data = 0;
+ socklen_t size = sizeof(data);
+ EXPECT_EQUAL(getsockopt(fd, level, name, &data, &size), 0);
+ EXPECT_EQUAL(size, sizeof(data));
+ EXPECT_EQUAL(data, int(expect));
+}
+
+} // namespace vespalib::test::<unnamed>
+
+/**
+ * Verifier of socket options for testing purposes
+ **/
+struct SocketOptionsVerifier {
+ int fd;
+ SocketOptionsVerifier(int fd_in) : fd(fd_in) {}
+ void verify_blocking(bool value) {
+ int flags = fcntl(fd, F_GETFL, NULL);
+ EXPECT_NOT_EQUAL(flags, -1);
+ EXPECT_EQUAL(((flags & O_NONBLOCK) == 0), value);
+ }
+ void verify_nodelay(bool value) {
+ TEST_DO(verify_bool_opt(fd, IPPROTO_TCP, TCP_NODELAY, value));
+ }
+ void verify_reuse_addr(bool value) {
+ TEST_DO(verify_bool_opt(fd, SOL_SOCKET, SO_REUSEADDR, value));
+ }
+ void verify_ipv6_only(bool value) {
+ TEST_DO(verify_bool_opt(fd, IPPROTO_IPV6, IPV6_V6ONLY, value));
+ }
+ void verify_keepalive(bool value) {
+ TEST_DO(verify_bool_opt(fd, SOL_SOCKET, SO_KEEPALIVE, value));
+ }
+ void verify_linger(bool enable, int value)
+ {
+ struct linger data;
+ socklen_t size = sizeof(data);
+ memset(&data, 0, sizeof(data));
+ EXPECT_EQUAL(getsockopt(fd, SOL_SOCKET, SO_LINGER, &data, &size), 0);
+ EXPECT_EQUAL(size, sizeof(data));
+ EXPECT_EQUAL(enable, data.l_onoff);
+ if (enable) {
+ EXPECT_EQUAL(value, data.l_linger);
+ }
+ }
+};
+
+} // namespace vespalib::test
+} // namespace vespalib