diff options
author | Haavard <havardpe@yahoo-inc.com> | 2017-04-20 12:39:57 +0000 |
---|---|---|
committer | Haavard <havardpe@yahoo-inc.com> | 2017-04-20 14:05:44 +0000 |
commit | 2decf3c4e35923b739254e389c26971ee8649a20 (patch) | |
tree | 59ceb32b57e9e17abb9dd06674fe99eb395fd27a /vespalib | |
parent | 656c30b4d6c2ba47ec9818a7bf2f7c402f9daf6b (diff) |
added handle-based low-level socket options with testing
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/src/tests/net/socket/socket_test.cpp | 86 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/CMakeLists.txt | 1 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/socket_options.cpp | 73 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/socket_options.h | 20 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/test/socket_options_verifier.h | 60 |
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 |