This commit is contained in:
yhirose 2020-06-09 19:58:01 -04:00
parent d0dc200633
commit 24bdb736f0
2 changed files with 46 additions and 18 deletions

View File

@ -515,6 +515,26 @@ private:
using Logger = std::function<void(const Request &, const Response &)>; using Logger = std::function<void(const Request &, const Response &)>;
using SocketOptions = std::function<void(socket_t sock)>;
inline void default_socket_options(socket_t sock) {
int yes = 1;
#ifdef _WIN32
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
sizeof(yes));
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<char *>(&yes), sizeof(yes));
#else
#ifdef SO_REUSEPORT
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes),
sizeof(yes));
#else
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes),
sizeof(yes));
#endif
#endif
}
class Server { class Server {
public: public:
using Handler = std::function<void(const Request &, Response &)>; using Handler = std::function<void(const Request &, Response &)>;
@ -549,9 +569,10 @@ public:
void set_file_request_handler(Handler handler); void set_file_request_handler(Handler handler);
void set_error_handler(Handler handler); void set_error_handler(Handler handler);
void set_expect_100_continue_handler(Expect100ContinueHandler handler);
void set_logger(Logger logger); void set_logger(Logger logger);
void set_expect_100_continue_handler(Expect100ContinueHandler handler); void set_socket_options(SocketOptions socket_options);
void set_keep_alive_max_count(size_t count); void set_keep_alive_max_count(size_t count);
void set_read_timeout(time_t sec, time_t usec = 0); void set_read_timeout(time_t sec, time_t usec = 0);
@ -590,8 +611,8 @@ private:
using HandlersForContentReader = using HandlersForContentReader =
std::vector<std::pair<std::regex, HandlerWithContentReader>>; std::vector<std::pair<std::regex, HandlerWithContentReader>>;
socket_t create_server_socket(const char *host, int port, socket_t create_server_socket(const char *host, int port, int socket_flags,
int socket_flags) const; SocketOptions socket_options) const;
int bind_internal(const char *host, int port, int socket_flags); int bind_internal(const char *host, int port, int socket_flags);
bool listen_internal(); bool listen_internal();
@ -639,6 +660,7 @@ private:
Handler error_handler_; Handler error_handler_;
Logger logger_; Logger logger_;
Expect100ContinueHandler expect_100_continue_handler_; Expect100ContinueHandler expect_100_continue_handler_;
SocketOptions socket_options_ = default_socket_options;
}; };
class Client { class Client {
@ -1873,9 +1895,10 @@ inline int shutdown_socket(socket_t sock) {
#endif #endif
} }
template <typename Fn> template <typename BindOrConnect>
socket_t create_socket(const char *host, int port, Fn fn, socket_t create_socket(const char *host, int port, int socket_flags,
int socket_flags = 0) { SocketOptions socket_options,
BindOrConnect bind_or_connect) {
// Get address info // Get address info
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *result; struct addrinfo *result;
@ -1923,6 +1946,8 @@ socket_t create_socket(const char *host, int port, Fn fn,
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
#endif #endif
if (socket_options) { socket_options(sock); }
// Make 'reuse address' option available // Make 'reuse address' option available
int yes = 1; int yes = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes), setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
@ -1940,7 +1965,7 @@ socket_t create_socket(const char *host, int port, Fn fn,
} }
// bind or connect // bind or connect
if (fn(sock, *rp)) { if (bind_or_connect(sock, *rp)) {
freeaddrinfo(result); freeaddrinfo(result);
return sock; return sock;
} }
@ -2017,10 +2042,12 @@ inline std::string if2ip(const std::string &ifn) {
#endif #endif
inline socket_t create_client_socket(const char *host, int port, inline socket_t create_client_socket(const char *host, int port,
SocketOptions socket_options,
time_t timeout_sec, time_t timeout_usec, time_t timeout_sec, time_t timeout_usec,
const std::string &intf) { const std::string &intf) {
return create_socket( return create_socket(
host, port, [&](socket_t sock, struct addrinfo &ai) -> bool { host, port, 0, socket_options,
[&](socket_t sock, struct addrinfo &ai) -> bool {
if (!intf.empty()) { if (!intf.empty()) {
#ifndef _WIN32 #ifndef _WIN32
auto ip = if2ip(intf); auto ip = if2ip(intf);
@ -3984,10 +4011,11 @@ inline bool Server::handle_file_request(Request &req, Response &res,
return false; return false;
} }
inline socket_t Server::create_server_socket(const char *host, int port, inline socket_t
int socket_flags) const { Server::create_server_socket(const char *host, int port, int socket_flags,
SocketOptions socket_options) const {
return detail::create_socket( return detail::create_socket(
host, port, host, port, socket_flags, socket_options,
[](socket_t sock, struct addrinfo &ai) -> bool { [](socket_t sock, struct addrinfo &ai) -> bool {
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) { if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
return false; return false;
@ -3996,14 +4024,13 @@ inline socket_t Server::create_server_socket(const char *host, int port,
return false; return false;
} }
return true; return true;
}, });
socket_flags);
} }
inline int Server::bind_internal(const char *host, int port, int socket_flags) { inline int Server::bind_internal(const char *host, int port, int socket_flags) {
if (!is_valid()) { return -1; } if (!is_valid()) { return -1; }
svr_sock_ = create_server_socket(host, port, socket_flags); svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
if (svr_sock_ == INVALID_SOCKET) { return -1; } if (svr_sock_ == INVALID_SOCKET) { return -1; }
if (port == 0) { if (port == 0) {
@ -4293,10 +4320,10 @@ inline bool Client::is_valid() const { return true; }
inline socket_t Client::create_client_socket() const { inline socket_t Client::create_client_socket() const {
if (!proxy_host_.empty()) { if (!proxy_host_.empty()) {
return detail::create_client_socket(proxy_host_.c_str(), proxy_port_, return detail::create_client_socket(proxy_host_.c_str(), proxy_port_,
connection_timeout_sec_, nullptr, connection_timeout_sec_,
connection_timeout_usec_, interface_); connection_timeout_usec_, interface_);
} }
return detail::create_client_socket(host_.c_str(), port_, return detail::create_client_socket(host_.c_str(), port_, nullptr,
connection_timeout_sec_, connection_timeout_sec_,
connection_timeout_usec_, interface_); connection_timeout_usec_, interface_);
} }

View File

@ -2294,8 +2294,9 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
// Sends a raw request to a server listening at HOST:PORT. // Sends a raw request to a server listening at HOST:PORT.
static bool send_request(time_t read_timeout_sec, const std::string &req, static bool send_request(time_t read_timeout_sec, const std::string &req,
std::string *resp = nullptr) { std::string *resp = nullptr) {
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5, 0, auto client_sock = detail::create_client_socket(
std::string()); HOST, PORT, nullptr,
/*timeout_sec=*/5, 0, std::string());
if (client_sock == INVALID_SOCKET) { return false; } if (client_sock == INVALID_SOCKET) { return false; }