Commit fa92f656 authored by Adel Abouchaev's avatar Adel Abouchaev Committed by Facebook GitHub Bot

Support variable length socket options.

Summary: Add a feature to apply and read variable length socket options.

Differential Revision: D33410246

fbshipit-source-id: 3407a926c2ba9323a2318f3d34f8e1f1173bde59
parent bb743c35
......@@ -23,11 +23,17 @@
namespace folly {
const SocketOptionMap emptySocketOptionMap;
const SocketNontrivialOptionMap emptySocketNontrivialOptionMap;
int SocketOptionKey::apply(NetworkSocket fd, int val) const {
return netops::setsockopt(fd, level, optname, &val, sizeof(val));
}
int SocketOptionKey::apply(
NetworkSocket fd, const void* val, socklen_t len) const {
return netops::setsockopt(fd, level, optname, val, len);
}
int applySocketOptions(
NetworkSocket fd,
const SocketOptionMap& options,
......@@ -43,6 +49,22 @@ int applySocketOptions(
return 0;
}
int applySocketOptions(
NetworkSocket fd,
const SocketNontrivialOptionMap& options,
SocketOptionKey::ApplyPos pos) {
for (const auto& opt : options) {
if (opt.first.applyPos_ == pos) {
auto rv =
opt.first.apply(fd, (void*)opt.second.data(), opt.second.size());
if (rv) {
return errno;
}
}
}
return 0;
}
SocketOptionMap validateSocketOptions(
const SocketOptionMap& options,
sa_family_t family,
......@@ -62,4 +84,23 @@ SocketOptionMap validateSocketOptions(
return validOptions;
}
SocketNontrivialOptionMap validateSocketOptions(
const SocketNontrivialOptionMap& options,
sa_family_t family,
SocketOptionKey::ApplyPos pos) {
SocketNontrivialOptionMap validOptions;
for (const auto& option : options) {
if (pos != option.first.applyPos_) {
continue;
}
if ((family == AF_INET && option.first.level == IPPROTO_IP) ||
(family == AF_INET6 && option.first.level == IPPROTO_IPV6) ||
option.first.level == IPPROTO_UDP || option.first.level == SOL_SOCKET ||
option.first.level == SOL_UDP) {
validOptions.insert(option);
}
}
return validOptions;
}
} // namespace folly
......@@ -46,6 +46,7 @@ class SocketOptionKey {
}
int apply(NetworkSocket fd, int val) const;
int apply(NetworkSocket fd, const void* val, socklen_t len) const;
int level;
int optname;
......@@ -54,17 +55,29 @@ class SocketOptionKey {
// Maps from a socket option key to its value
using SocketOptionMap = std::map<SocketOptionKey, int>;
using SocketNontrivialOptionMap = std::map<SocketOptionKey, std::string>;
extern const SocketOptionMap emptySocketOptionMap;
extern const SocketNontrivialOptionMap emptySocketNontrivialOptionMap;
int applySocketOptions(
NetworkSocket fd,
const SocketOptionMap& options,
SocketOptionKey::ApplyPos pos);
int applySocketOptions(
NetworkSocket fd,
const SocketNontrivialOptionMap& options,
SocketOptionKey::ApplyPos pos);
SocketOptionMap validateSocketOptions(
const SocketOptionMap& options,
sa_family_t family,
SocketOptionKey::ApplyPos pos);
SocketNontrivialOptionMap validateSocketOptions(
const SocketNontrivialOptionMap& options,
sa_family_t family,
SocketOptionKey::ApplyPos pos);
} // namespace folly
......@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <folly/io/SocketOptionMap.h>
#include <folly/io/async/AsyncUDPSocket.h>
#include <cerrno>
......@@ -1371,6 +1372,17 @@ void AsyncUDPSocket::applyOptions(
}
}
void AsyncUDPSocket::applyNontrivialOptions(
const SocketNontrivialOptionMap& options, SocketOptionKey::ApplyPos pos) {
auto result = applySocketOptions(fd_, options, pos);
if (result) {
throw AsyncSocketException(
AsyncSocketException::INTERNAL_ERROR,
"failed to set nontrivial socket option",
result);
}
}
void AsyncUDPSocket::detachEventBase() {
DCHECK(eventBase_ && eventBase_->isInEventBaseThread());
registerHandler(uint16_t(NONE));
......
......@@ -17,6 +17,7 @@
#pragma once
#include <memory>
#include <folly/io/SocketOptionMap.h>
#include <folly/Function.h>
#include <folly/ScopeGuard.h>
......@@ -461,6 +462,9 @@ class AsyncUDPSocket : public EventHandler {
virtual void applyOptions(
const SocketOptionMap& options, SocketOptionKey::ApplyPos pos);
virtual void applyNontrivialOptions(
const SocketNontrivialOptionMap& options, SocketOptionKey::ApplyPos pos);
/**
* Override netops::Dispatcher to be used for netops:: calls.
*
......
......@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <folly/io/SocketOptionMap.h>
#include <folly/io/async/AsyncUDPSocket.h>
#include <thread>
......@@ -1097,3 +1098,29 @@ TEST_F(AsyncUDPSocketTest, TestWritemCmsg) {
#endif // FOLLY_HAVE_MSG_ERRQUEUE
socket_->close();
}
TEST_F(AsyncUDPSocketTest, TestApplyNontrivialOptionsPostBind) {
EventBase evb;
AsyncUDPSocket socket(&evb);
ASSERT_FALSE(socket.isBound());
folly::SocketAddress address("127.0.0.1", 443);
socket.connect(address);
ASSERT_TRUE(socket.isBound());
const auto& localAddr = socket.address();
ASSERT_TRUE(localAddr.isInitialized());
ASSERT_GT(localAddr.getPort(), 0);
folly::SocketNontrivialOptionMap options =
folly::emptySocketNontrivialOptionMap;
struct linger sl {
.l_onoff = 1, .l_linger = 123,
};
options.insert(
{folly::SocketOptionKey{SOL_SOCKET, SO_LINGER},
std::string((char*)&sl, sizeof(sl))});
socket.applyNontrivialOptions(
options, folly::SocketOptionKey::ApplyPos::POST_BIND);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment