|
@@ -18,29 +18,42 @@
|
|
|
*/
|
|
|
|
|
|
#include <errno.h>
|
|
|
-#include <fcntl.h>
|
|
|
#include <h2o.h>
|
|
|
+#include <inttypes.h>
|
|
|
+#include <limits.h>
|
|
|
+#include <netdb.h>
|
|
|
+#include <stdarg.h>
|
|
|
#include <stdbool.h>
|
|
|
#include <stdint.h>
|
|
|
+#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <string.h>
|
|
|
#include <unistd.h>
|
|
|
+#include <netinet/in.h>
|
|
|
+#include <netinet/tcp.h>
|
|
|
+#include <openssl/ssl.h>
|
|
|
#include <sys/epoll.h>
|
|
|
+#include <sys/socket.h>
|
|
|
+#include <sys/types.h>
|
|
|
|
|
|
#include "error.h"
|
|
|
#include "event_loop.h"
|
|
|
#include "thread.h"
|
|
|
#include "utility.h"
|
|
|
|
|
|
+#define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
|
|
|
+#define MAX_EPOLL_EVENTS 64
|
|
|
+
|
|
|
static void accept_connection(h2o_socket_t *listener, const char *err);
|
|
|
static void accept_http_connection(h2o_socket_t *listener, const char *err);
|
|
|
static void do_epoll_wait(h2o_socket_t *epoll_sock, const char *err);
|
|
|
+static int get_listener_socket(const char *bind_address, uint16_t port);
|
|
|
static void on_close_connection(void *data);
|
|
|
static void process_messages(h2o_multithread_receiver_t *receiver, h2o_linklist_t *messages);
|
|
|
static void shutdown_server(h2o_socket_t *listener, const char *err);
|
|
|
-static void start_accept_polling(bool is_main_thread,
|
|
|
+static void start_accept_polling(const config_t *config,
|
|
|
+ h2o_socket_cb accept_cb,
|
|
|
bool is_https,
|
|
|
- global_data_t *global_data,
|
|
|
event_loop_t *loop);
|
|
|
|
|
|
static void accept_connection(h2o_socket_t *listener, const char *err)
|
|
@@ -53,7 +66,7 @@ static void accept_connection(h2o_socket_t *listener, const char *err)
|
|
|
listener->data);
|
|
|
|
|
|
if (!ctx->global_data->shutdown) {
|
|
|
- size_t accepted = 0;
|
|
|
+ size_t accepted = ctx->config->max_accept;
|
|
|
|
|
|
do {
|
|
|
h2o_socket_t * const sock = h2o_evloop_socket_accept(listener);
|
|
@@ -65,7 +78,7 @@ static void accept_connection(h2o_socket_t *listener, const char *err)
|
|
|
sock->on_close.cb = on_close_connection;
|
|
|
sock->on_close.data = &ctx->event_loop.conn_num;
|
|
|
h2o_accept(&ctx->event_loop.h2o_accept_ctx, sock);
|
|
|
- } while (++accepted < ctx->config->max_accept);
|
|
|
+ } while (--accepted > 0);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -91,23 +104,93 @@ static void do_epoll_wait(h2o_socket_t *epoll_sock, const char *err)
|
|
|
thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
|
|
|
event_loop,
|
|
|
epoll_sock->data);
|
|
|
- const size_t num_event = ctx->db_state.db_conn_num - ctx->db_state.free_db_conn_num;
|
|
|
int ready;
|
|
|
- struct epoll_event event[num_event];
|
|
|
+ struct epoll_event event[MAX_EPOLL_EVENTS];
|
|
|
|
|
|
do
|
|
|
- ready = epoll_wait(ctx->event_loop.epoll_fd, event, num_event, 0);
|
|
|
+ ready = epoll_wait(ctx->event_loop.epoll_fd, event, MAX_EPOLL_EVENTS, 0);
|
|
|
while (ready < 0 && errno == EINTR);
|
|
|
|
|
|
if (ready > 0)
|
|
|
- for (size_t i = 0; i < (size_t) ready; i++) {
|
|
|
- void (** const on_write_ready)(void *) = event[i].data.ptr;
|
|
|
+ do {
|
|
|
+ void (** const on_write_ready)(void *) = event[--ready].data.ptr;
|
|
|
|
|
|
(*on_write_ready)(on_write_ready);
|
|
|
- }
|
|
|
+ } while (ready > 0);
|
|
|
+ else if (ready < 0)
|
|
|
+ STANDARD_ERROR("epoll_wait");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int get_listener_socket(const char *bind_address, uint16_t port)
|
|
|
+{
|
|
|
+ int ret = -1;
|
|
|
+ char buf[16];
|
|
|
+
|
|
|
+ if ((size_t) snprintf(buf, sizeof(buf), "%" PRIu16, port) >= sizeof(buf)) {
|
|
|
+ LIBRARY_ERROR("snprintf", "Truncated output.");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct addrinfo *res = NULL;
|
|
|
+ struct addrinfo hints = {.ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE};
|
|
|
+ const int error_code = getaddrinfo(bind_address, buf, &hints, &res);
|
|
|
+
|
|
|
+ if (error_code) {
|
|
|
+ LIBRARY_ERROR("getaddrinfo", gai_strerror(error_code));
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct addrinfo *iter = res;
|
|
|
+
|
|
|
+ for (; iter; iter = iter->ai_next) {
|
|
|
+ const int s = socket(iter->ai_family,
|
|
|
+ iter->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC,
|
|
|
+ iter->ai_protocol);
|
|
|
+
|
|
|
+ if (s == -1) {
|
|
|
+ STANDARD_ERROR("socket");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+#define LOCAL_CHECK_ERRNO(function, ...) \
|
|
|
+ do { \
|
|
|
+ const int error_code = (function)(__VA_ARGS__); \
|
|
|
+ \
|
|
|
+ if (error_code) { \
|
|
|
+ print_library_error(__FILE__, __LINE__, #function, errno); \
|
|
|
+ goto error; \
|
|
|
+ } \
|
|
|
+ } while(0)
|
|
|
+
|
|
|
+ int option = 1;
|
|
|
+
|
|
|
+ LOCAL_CHECK_ERRNO(setsockopt, s, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
|
|
|
+ LOCAL_CHECK_ERRNO(setsockopt, s, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
|
|
|
+ LOCAL_CHECK_ERRNO(setsockopt, s, IPPROTO_TCP, TCP_QUICKACK, &option, sizeof(option));
|
|
|
+ option = H2O_DEFAULT_HANDSHAKE_TIMEOUT_IN_SECS;
|
|
|
+ LOCAL_CHECK_ERRNO(setsockopt, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, &option, sizeof(option));
|
|
|
+ option = DEFAULT_TCP_FASTOPEN_QUEUE_LEN;
|
|
|
+ LOCAL_CHECK_ERRNO(setsockopt, s, IPPROTO_TCP, TCP_FASTOPEN, &option, sizeof(option));
|
|
|
+ LOCAL_CHECK_ERRNO(bind, s, iter->ai_addr, iter->ai_addrlen);
|
|
|
+ LOCAL_CHECK_ERRNO(listen, s, INT_MAX);
|
|
|
+ ret = s;
|
|
|
+ break;
|
|
|
+
|
|
|
+#undef LOCAL_CHECK_ERRNO
|
|
|
+
|
|
|
+error:
|
|
|
+ close(s);
|
|
|
+ }
|
|
|
+
|
|
|
+ freeaddrinfo(res);
|
|
|
+
|
|
|
+ if (ret == -1)
|
|
|
+ abort();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void on_close_connection(void *data)
|
|
|
{
|
|
|
size_t * const conn_num = data;
|
|
@@ -138,28 +221,21 @@ static void shutdown_server(h2o_socket_t *listener, const char *err)
|
|
|
ctx->global_data->shutdown = true;
|
|
|
h2o_socket_read_stop(ctx->event_loop.h2o_socket);
|
|
|
|
|
|
- for (size_t i = 1; i < ctx->config->thread_num; i++)
|
|
|
+ for (size_t i = ctx->config->thread_num - 1; i > 0; i--)
|
|
|
h2o_multithread_send_message(&ctx->global_thread_data[i].h2o_receiver, NULL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void start_accept_polling(bool is_main_thread,
|
|
|
+static void start_accept_polling(const config_t *config,
|
|
|
+ h2o_socket_cb accept_cb,
|
|
|
bool is_https,
|
|
|
- global_data_t *global_data,
|
|
|
event_loop_t *loop)
|
|
|
{
|
|
|
- int listener_sd = is_https ? global_data->https_listener_sd : global_data->listener_sd;
|
|
|
-
|
|
|
- if (!is_main_thread) {
|
|
|
- int flags;
|
|
|
-
|
|
|
- CHECK_ERRNO_RETURN(listener_sd, dup, listener_sd);
|
|
|
- CHECK_ERRNO_RETURN(flags, fcntl, listener_sd, F_GETFD);
|
|
|
- CHECK_ERRNO(fcntl, listener_sd, F_SETFD, flags | FD_CLOEXEC);
|
|
|
- }
|
|
|
-
|
|
|
+ const int listener_sd = get_listener_socket(config->bind_address,
|
|
|
+ is_https ? config->https_port : config->port);
|
|
|
// Let all the threads race to call accept() on the socket; since the latter is
|
|
|
- // non-blocking, that will effectively act as load balancing.
|
|
|
+ // non-blocking, that will virtually act as load balancing, and SO_REUSEPORT
|
|
|
+ // will make it efficient.
|
|
|
h2o_socket_t * const h2o_socket = h2o_evloop_socket_create(loop->h2o_ctx.loop,
|
|
|
listener_sd,
|
|
|
H2O_SOCKET_FLAG_DONT_READ);
|
|
@@ -170,13 +246,6 @@ static void start_accept_polling(bool is_main_thread,
|
|
|
loop->h2o_socket = h2o_socket;
|
|
|
|
|
|
h2o_socket->data = loop;
|
|
|
-
|
|
|
- // Assume that the majority of the connections use HTTPS, so HTTP can take a few
|
|
|
- // extra operations.
|
|
|
- const h2o_socket_cb accept_cb = !global_data->ssl_ctx || is_https ?
|
|
|
- accept_connection :
|
|
|
- accept_http_connection;
|
|
|
-
|
|
|
h2o_socket_read_start(h2o_socket, accept_cb);
|
|
|
}
|
|
|
|
|
@@ -202,6 +271,9 @@ void initialize_event_loop(bool is_main_thread,
|
|
|
h2o_multithread_receiver_t *h2o_receiver,
|
|
|
event_loop_t *loop)
|
|
|
{
|
|
|
+ h2o_socket_cb accept_cb = accept_connection;
|
|
|
+ const config_t * const config = global_data->global_thread_data->config;
|
|
|
+
|
|
|
memset(loop, 0, sizeof(*loop));
|
|
|
h2o_context_init(&loop->h2o_ctx, h2o_evloop_create(), &global_data->h2o_config);
|
|
|
loop->h2o_accept_ctx.ctx = &loop->h2o_ctx;
|
|
@@ -209,10 +281,13 @@ void initialize_event_loop(bool is_main_thread,
|
|
|
|
|
|
if (global_data->ssl_ctx) {
|
|
|
loop->h2o_accept_ctx.ssl_ctx = global_data->ssl_ctx;
|
|
|
- start_accept_polling(is_main_thread, true, global_data, loop);
|
|
|
+ start_accept_polling(config, accept_connection, true, loop);
|
|
|
+ // Assume that the majority of the connections use HTTPS,
|
|
|
+ // so HTTP can take a few extra operations.
|
|
|
+ accept_cb = accept_http_connection;
|
|
|
}
|
|
|
|
|
|
- start_accept_polling(is_main_thread, false, global_data, loop);
|
|
|
+ start_accept_polling(config, accept_cb, false, loop);
|
|
|
h2o_multithread_register_receiver(loop->h2o_ctx.queue,
|
|
|
h2o_receiver,
|
|
|
process_messages);
|