Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ SET(TH_CORE_SRC
src/th_send.c
src/th_sendvec.c
src/th_sendfile.c
src/th_address.c
src/th_acceptor.c
src/th_accept.c
src/th_tcp_conn.c
Expand Down Expand Up @@ -234,9 +235,11 @@ if (NOT TH_DISABLE_TESTS)
src/th_send_test.c
src/th_sendvec_test.c
src/th_sendfile_test.c
src/th_address_test.c
src/th_acceptor_test.c
src/th_accept_test.c
src/th_conn_test.c
src/th_conn_tracker_test.c
src/th_tcp_conn_test.c
src/th_router_test.c
src/th_allocator_test.c
Expand Down
2 changes: 2 additions & 0 deletions include/th.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ typedef struct th_buffer {
#define TH_ERR_CATEGORY_SYSTEM 1 // system error, corresponds to errno or GetLastError
#define TH_ERR_CATEGORY_HTTP 2 // http protocol error
#define TH_ERR_CATEGORY_SSL 3 // ssl error
#define TH_ERR_CATEGORY_EAI 4 // getaddrinfo error, EAI_* code (not errno)

/* other error codes */
#define TH_ERRC_OK 0
Expand Down Expand Up @@ -78,6 +79,7 @@ typedef enum th_err {
#define TH_ERR_SYSTEM(code) TH_ERR(TH_ERR_CATEGORY_SYSTEM, code)
#define TH_ERR_HTTP(code) TH_ERR(TH_ERR_CATEGORY_HTTP, code)
#define TH_ERR_SSL(code) TH_ERR(TH_ERR_CATEGORY_SSL, code)
#define TH_ERR_EAI(code) TH_ERR(TH_ERR_CATEGORY_EAI, code)
#define TH_ERR_CATEGORY(err) (err >> TH_ERR_CATEGORY_SHIFT)
#define TH_ERR_CODE(err) (err & TH_ERR_CODE_MASK)

Expand Down
63 changes: 58 additions & 5 deletions src/th_accept_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,62 @@ typedef struct th_fake_acceptor_ops {
} th_fake_acceptor_ops;

static th_err
th_fake_acceptor_open(void* self, const char* addr, const char* port, int* out_fd)
th_fake_acceptor_socket(void* self, int domain, int type, int protocol, int* out_fd)
{
(void)addr;
(void)port;
(void)domain;
(void)type;
(void)protocol;
th_fake_acceptor_ops* ops = self;
*out_fd = ops->open_fd;
return TH_ERR_OK;
}

static th_err
th_fake_acceptor_setsockopt(void* self, int fd, int level, int optname, const void* optval, socklen_t optlen)
{
(void)self;
(void)fd;
(void)level;
(void)optname;
(void)optval;
(void)optlen;
return TH_ERR_OK;
}

static th_err
th_fake_acceptor_set_nonblocking(void* self, int fd)
{
(void)self;
(void)fd;
return TH_ERR_OK;
}

static th_err
th_fake_acceptor_bind(void* self, int fd, const struct sockaddr* addr, socklen_t addrlen)
{
(void)self;
(void)fd;
(void)addr;
(void)addrlen;
return TH_ERR_OK;
}

static th_err
th_fake_acceptor_listen(void* self, int fd, int backlog)
{
(void)self;
(void)fd;
(void)backlog;
return TH_ERR_OK;
}

static void
th_fake_acceptor_close(void* self, int fd)
{
(void)self;
(void)fd;
}

static th_err
th_fake_acceptor_accept(void* self, int fd, th_address* addr, int* out_fd)
{
Expand All @@ -114,7 +161,12 @@ th_fake_acceptor_accept(void* self, int fd, th_address* addr, int* out_fd)
static void
th_fake_acceptor_ops_init(th_fake_acceptor_ops* ops)
{
ops->base.open = th_fake_acceptor_open;
ops->base.socket = th_fake_acceptor_socket;
ops->base.setsockopt = th_fake_acceptor_setsockopt;
ops->base.set_nonblocking = th_fake_acceptor_set_nonblocking;
ops->base.bind = th_fake_acceptor_bind;
ops->base.listen = th_fake_acceptor_listen;
ops->base.close = th_fake_acceptor_close;
ops->base.accept = th_fake_acceptor_accept;
ops->open_fd = 9;
ops->accept_err = TH_ERR_OK;
Expand Down Expand Up @@ -158,7 +210,8 @@ TH_TEST_BEGIN(accept)
th_fake_acceptor_ops_init(&ops);
th_acceptor acceptor;
th_acceptor_init(&acceptor, &loop, &ops.base);
th_acceptor_open(&acceptor, "127.0.0.1", "8080");
th_addrinfo info = {0};
th_acceptor_open(&acceptor, &info);

TH_TEST_CASE_BEGIN(accept_completes_with_new_fd)
{
Expand Down
154 changes: 91 additions & 63 deletions src/th_acceptor.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,63 @@
#if defined(TH_CONFIG_OS_POSIX)
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

TH_LOCAL(th_err)
th_acceptor_ops_os_set_nonblocking(int fd)
th_acceptor_ops_os_socket(void* self, int domain, int type, int protocol, int* out_fd)
{
(void)self;
int fd = socket(domain, type, protocol);
if (fd < 0)
return TH_ERR_SYSTEM(errno);
*out_fd = fd;
return TH_ERR_OK;
}

TH_LOCAL(th_err)
th_acceptor_ops_os_setsockopt(void* self, int fd, int level, int optname, const void* optval, socklen_t optlen)
{
(void)self;
if (setsockopt(fd, level, optname, optval, optlen) < 0)
return TH_ERR_SYSTEM(errno);
return TH_ERR_OK;
}

TH_LOCAL(th_err)
th_acceptor_ops_os_set_nonblocking(void* self, int fd)
{
(void)self;
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0)
return TH_ERR_SYSTEM(errno);
return TH_ERR_OK;
}

TH_LOCAL(th_err)
th_acceptor_ops_os_open(void* self, const char* addr, const char* port, int* out_fd)
th_acceptor_ops_os_bind(void* self, int fd, const struct sockaddr* addr, socklen_t addrlen)
{
(void)self;
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
struct addrinfo* res = NULL;
if (getaddrinfo(addr, port, &hints, &res) != 0)
if (bind(fd, addr, addrlen) < 0)
return TH_ERR_SYSTEM(errno);
return TH_ERR_OK;
}

th_err err = TH_ERR_OK;
int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0) {
err = TH_ERR_SYSTEM(errno);
goto cleanup_addrinfo;
}
#if TH_CONFIG_REUSE_ADDR
{
int optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
err = TH_ERR_SYSTEM(errno);
goto cleanup_fd;
}
}
#endif
#if TH_CONFIG_REUSE_PORT
{
#if defined(SO_REUSEPORT)
int optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) {
err = TH_ERR_SYSTEM(errno);
goto cleanup_fd;
}
#else
TH_LOG_FATAL("SO_REUSEPORT is not supported on this platform");
err = TH_ERR_NOSUPPORT;
goto cleanup_fd;
#endif
}
#endif
if ((err = th_acceptor_ops_os_set_nonblocking(fd)) != TH_ERR_OK)
goto cleanup_fd;
if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
err = TH_ERR_SYSTEM(errno);
goto cleanup_fd;
}
if (listen(fd, 1024) < 0) {
err = TH_ERR_SYSTEM(errno);
goto cleanup_fd;
}
freeaddrinfo(res);
*out_fd = fd;
TH_LOCAL(th_err)
th_acceptor_ops_os_listen(void* self, int fd, int backlog)
{
(void)self;
if (listen(fd, backlog) < 0)
return TH_ERR_SYSTEM(errno);
return TH_ERR_OK;
cleanup_fd:
}

TH_LOCAL(void)
th_acceptor_ops_os_close(void* self, int fd)
{
(void)self;
close(fd);
cleanup_addrinfo:
freeaddrinfo(res);
return err;
}

TH_LOCAL(th_err)
Expand All @@ -89,7 +71,7 @@ th_acceptor_ops_os_accept(void* self, int fd, th_address* addr, int* out_fd)
int conn_fd = accept(fd, (struct sockaddr*)&addr->addr, &addr->addrlen);
if (conn_fd < 0)
return TH_ERR_SYSTEM(errno);
th_err err = th_acceptor_ops_os_set_nonblocking(conn_fd);
th_err err = th_acceptor_ops_os_set_nonblocking(self, conn_fd);
if (err != TH_ERR_OK) {
close(conn_fd);
return err;
Expand All @@ -102,14 +84,62 @@ TH_PRIVATE(th_acceptor_ops*)
th_acceptor_ops_os(void)
{
static th_acceptor_ops ops = {
.open = th_acceptor_ops_os_open,
.socket = th_acceptor_ops_os_socket,
.setsockopt = th_acceptor_ops_os_setsockopt,
.set_nonblocking = th_acceptor_ops_os_set_nonblocking,
.bind = th_acceptor_ops_os_bind,
.listen = th_acceptor_ops_os_listen,
.close = th_acceptor_ops_os_close,
.accept = th_acceptor_ops_os_accept,
};
return &ops;
}

#endif /* TH_CONFIG_OS_POSIX */

TH_LOCAL(th_err)
th_acceptor_open_socket(th_acceptor* acceptor, const th_addrinfo* info, int* out_fd)
{
th_acceptor_ops* ops = acceptor->ops;
int fd = -1;
th_err err = ops->socket(ops, info->family, info->socktype, info->protocol, &fd);
if (err != TH_ERR_OK)
return err;

#if TH_CONFIG_REUSE_ADDR
{
int optval = 1;
if ((err = ops->setsockopt(ops, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) != TH_ERR_OK)
goto cleanup_fd;
}
#endif
#if TH_CONFIG_REUSE_PORT
{
#if defined(SO_REUSEPORT)
int optval = 1;
if ((err = ops->setsockopt(ops, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval))) != TH_ERR_OK)
goto cleanup_fd;
#else
TH_LOG_FATAL("SO_REUSEPORT is not supported on this platform");
err = TH_ERR_NOSUPPORT;
goto cleanup_fd;
#endif
}
#endif
if ((err = ops->set_nonblocking(ops, fd)) != TH_ERR_OK)
goto cleanup_fd;
if ((err = ops->bind(ops, fd, (const struct sockaddr*)&info->addr.addr, info->addr.addrlen)) != TH_ERR_OK)
goto cleanup_fd;
if ((err = ops->listen(ops, fd, 1024)) != TH_ERR_OK)
goto cleanup_fd;

*out_fd = fd;
return TH_ERR_OK;
cleanup_fd:
ops->close(ops, fd);
return err;
}

TH_PRIVATE(void)
th_acceptor_init(th_acceptor* acceptor, th_loop* loop, th_acceptor_ops* ops)
{
Expand All @@ -119,18 +149,16 @@ th_acceptor_init(th_acceptor* acceptor, th_loop* loop, th_acceptor_ops* ops)
}

TH_PRIVATE(th_err)
th_acceptor_open(th_acceptor* acceptor, const char* addr, const char* port)
th_acceptor_open(th_acceptor* acceptor, const th_addrinfo* info)
{
int fd = -1;
th_err err = acceptor->ops->open(acceptor->ops, addr, port, &fd);
th_err err = th_acceptor_open_socket(acceptor, info, &fd);
if (err != TH_ERR_OK)
return err;
th_acceptor_close(acceptor);
err = th_reactor_create_handle(acceptor->loop->reactor, &acceptor->handle, fd);
if (err != TH_ERR_OK) {
#if defined(TH_CONFIG_OS_POSIX)
close(fd);
#endif
acceptor->ops->close(acceptor->ops, fd);
return err;
}
th_handle_enable_timeout(acceptor->handle, false);
Expand Down
28 changes: 12 additions & 16 deletions src/th_acceptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,17 @@
#include "th_socket.h"

/** th_acceptor_ops
* @brief The raw listen-socket syscalls a th_acceptor performs. Injected
* at construction time so tests can fake an acceptor without a real fd.
* @brief Mirrors the raw listen-socket syscalls directly, one op each,
* so tests can fake a th_acceptor without a real fd.
*/
typedef struct th_acceptor_ops {
/** open
* @brief Resolves addr/port, creates a non-blocking listening socket
* bound and listening on it, and writes its fd to *fd.
*/
th_err (*open)(void* self, const char* addr, const char* port, int* fd);

/** accept
* @brief Accepts one pending connection on fd, writes the peer
* address to addr and the new non-blocking socket's fd to *out_fd.
* TH_ERR_SYSTEM(TH_EAGAIN)/TH_EWOULDBLOCK when none is pending.
*/
th_err (*socket)(void* self, int domain, int type, int protocol, int* out_fd);
th_err (*setsockopt)(void* self, int fd, int level, int optname, const void* optval, socklen_t optlen);
th_err (*set_nonblocking)(void* self, int fd);
th_err (*bind)(void* self, int fd, const struct sockaddr* addr, socklen_t addrlen);
th_err (*listen)(void* self, int fd, int backlog);
void (*close)(void* self, int fd);
// TH_ERR_SYSTEM(TH_EAGAIN)/TH_EWOULDBLOCK when nothing is pending.
th_err (*accept)(void* self, int fd, th_address* addr, int* out_fd);
} th_acceptor_ops;

Expand All @@ -46,11 +42,11 @@ TH_PRIVATE(void)
th_acceptor_init(th_acceptor* acceptor, th_loop* loop, th_acceptor_ops* ops);

/** th_acceptor_open
* @brief Resolves addr/port and registers the resulting listening socket
* with the acceptor's reactor, replacing any fd previously set.
* @brief Opens a listening socket matching info and registers it with
* the acceptor's reactor, replacing any fd previously set.
*/
TH_PRIVATE(th_err)
th_acceptor_open(th_acceptor* acceptor, const char* addr, const char* port);
th_acceptor_open(th_acceptor* acceptor, const th_addrinfo* info);

TH_INLINE(int)
th_acceptor_get_fd(const th_acceptor* acceptor)
Expand Down
Loading
Loading