Skip to content
Open
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
40 changes: 38 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ add_library(ioxd
lib/json/json.c
lib/tls/certs.c
lib/tls/handshake.c
lib/quic/quic.c
lib/quic/stream.c
third_party/picohttpparser/picohttpparser.c)
add_library(ioxd::ioxd ALIAS ioxd)

Expand All @@ -89,6 +91,35 @@ if(IOXD_TLS)
else()
target_compile_definitions(ioxd PRIVATE IOXD_TLS=0)
endif()
# QUIC: ngtcp2 over OpenSSL 3.5's QUIC TLS API. AUTO takes it when pkg-config finds libngtcp2 and
# its ossl backend; ON insists; OFF leaves it out. Needs IOXD_TLS.
set(IOXD_QUIC AUTO CACHE STRING "QUIC ports (needs libngtcp2 with its OpenSSL backend, OpenSSL 3.5+): AUTO, ON or OFF")
set(IOXD_HAVE_QUIC OFF)
if(NOT IOXD_QUIC STREQUAL "OFF" AND IOXD_TLS)
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
if(IOXD_QUIC STREQUAL "ON")
pkg_check_modules(NGTCP2 REQUIRED libngtcp2 libngtcp2_crypto_ossl)
else()
pkg_check_modules(NGTCP2 QUIET libngtcp2 libngtcp2_crypto_ossl)
endif()
if(NGTCP2_FOUND)
set(IOXD_HAVE_QUIC ON)
endif()
elseif(IOXD_QUIC STREQUAL "ON")
message(FATAL_ERROR "IOXD_QUIC=ON needs pkg-config to find libngtcp2")
endif()
elseif(IOXD_QUIC STREQUAL "ON")
message(FATAL_ERROR "IOXD_QUIC=ON needs IOXD_TLS: QUIC is TLS 1.3 from the same certificate store")
endif()
if(IOXD_HAVE_QUIC)
target_compile_definitions(ioxd PRIVATE IOXD_QUIC=1)
target_include_directories(ioxd PRIVATE ${NGTCP2_INCLUDE_DIRS})
target_link_libraries(ioxd PUBLIC ${NGTCP2_LINK_LIBRARIES})
message(STATUS "QUIC: ngtcp2 ${NGTCP2_libngtcp2_VERSION}")
else()
target_compile_definitions(ioxd PRIVATE IOXD_QUIC=0)
endif()
target_compile_options(ioxd PRIVATE -Wall -Wextra)
# Fat LTO objects when supported: the archive stays plain-linkable, and consumers that link with
# -flto get cross-file inlining (the demo below does).
Expand Down Expand Up @@ -144,7 +175,11 @@ if(IOXD_EXAMPLES)
enable_testing()
set(IOXD_CHECK_PORT 8099 CACHE STRING "first port the fixture listens on; it takes the two after it")
set(IOXD_PIPE_PORT 8102 CACHE STRING "port the pipe fixture listens on")
set(IOXD_TLS_PYTHON python3 CACHE STRING "a python with tlslite-ng, for tests/tls_early.py")
set(IOXD_TLS_PYTHON python3 CACHE STRING "a python with tlslite-ng and aioquic, for tests/tls_early.py and tests/quic.py")
set(IOXD_QUIC_FLAG 0)
if(IOXD_HAVE_QUIC)
set(IOXD_QUIC_FLAG 1)
endif()
add_test(NAME unit COMMAND ioxd-unit)
add_test(NAME router COMMAND ioxd-router-test)
add_test(NAME suites COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-suites.sh
Expand All @@ -154,9 +189,10 @@ if(IOXD_EXAMPLES)
--tls-python ${IOXD_TLS_PYTHON}
--work ${CMAKE_CURRENT_BINARY_DIR}/check/suites)
add_test(NAME pipes COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-suites.sh
--suite pipes
--suite pipes --suite quic --quic ${IOXD_QUIC_FLAG}
--pipe-port ${IOXD_PIPE_PORT}
--pipe-server $<TARGET_FILE:ioxd-pipe-server>
--tls-python ${IOXD_TLS_PYTHON}
--work ${CMAKE_CURRENT_BINARY_DIR}/check/pipes)
set_tests_properties(suites pipes PROPERTIES RUN_SERIAL TRUE)
add_custom_target(check
Expand Down
45 changes: 30 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# make build libioxd.a, libioxd.so and the examples
# make lib just the libraries
# make check the unit test and the suites, against the fixture servers
# make check the unit test and the suites, against the fixture servers (QUIC's too, in a QUIC build)
# make check-tiny the stress suite against a build with the buffers starved on purpose
# make check-all both
# make tidy clang-tidy over the library
Expand Down Expand Up @@ -33,14 +33,29 @@ WARN := -Wall -Wextra $(STD)
HARDEN := $(shell $(CC) -Werror -fstack-clash-protection -x c -c /dev/null -o /dev/null 2>/dev/null && echo -fstack-clash-protection)
CPP := -D_GNU_SOURCE -Iinclude -Ilib -Ithird_party/picohttpparser
# TLS: OpenSSL for the handshake only; the kernel does the records. make TLS=0 leaves it out.
# pkg-config finds the OpenSSL to build against when there is one to find (PKG_CONFIG_PATH picks
# a private build); otherwise the toolchain's default is what -lssl names.
TLS ?= 1
ifeq ($(TLS),1)
CPP += -DIOXD_TLS=1
LIBS := -lssl -lcrypto
CPP += -DIOXD_TLS=1 $(shell pkg-config --cflags openssl 2>/dev/null)
LIBS := $(shell pkg-config --libs openssl 2>/dev/null || echo -lssl -lcrypto)
else
CPP += -DIOXD_TLS=0
LIBS :=
endif
# QUIC: ngtcp2 over OpenSSL 3.5's QUIC TLS API (libngtcp2_crypto_ossl). In by default when
# pkg-config finds both; make QUIC=1 insists, QUIC=0 leaves it out. Needs TLS=1.
QUIC ?= $(if $(filter 1,$(TLS)),$(shell pkg-config --exists 'libngtcp2 libngtcp2_crypto_ossl' 2>/dev/null && echo 1 || echo 0),0)
ifeq ($(QUIC),1)
ifeq ($(TLS),0)
$(error QUIC=1 needs TLS=1: QUIC is TLS 1.3 from the same certificate store)
endif
CPP += -DIOXD_QUIC=1 $(shell pkg-config --cflags libngtcp2 libngtcp2_crypto_ossl)
LIBS += $(shell pkg-config --libs libngtcp2 libngtcp2_crypto_ossl)
else
CPP += -DIOXD_QUIC=0
endif
LDFLAGS ?=
HDRS := $(wildcard include/*.h include/ioxd/*.h lib/*/*.h)
PTHREAD := -pthread

Expand All @@ -52,7 +67,7 @@ LIBDIR := $(PREFIX)/lib
INCDIR := $(PREFIX)/include
PCDIR := $(LIBDIR)/pkgconfig

UNITS := io/uring io/coro io/bufring io/conn io/proactor io/pipe clients/timer clients/socket http/engine http/api http/router http/run json/json tls/certs tls/handshake
UNITS := io/uring io/coro io/bufring io/conn io/proactor io/pipe clients/timer clients/socket http/engine http/api http/router http/run json/json tls/certs tls/handshake quic/quic quic/stream
OBJ := $(addprefix obj/,$(addsuffix .o,$(UNITS))) obj/io/switch_x86_64.o obj/picohttpparser.o
PICOBJ := $(addprefix obj/pic/,$(addsuffix .o,$(UNITS))) obj/pic/io/switch_x86_64.o obj/pic/picohttpparser.o

Expand All @@ -68,7 +83,7 @@ MAP := cmake/ioxd.map
# Every flag an object is built with, in a file. TLS=0/1 - or a different CC or CFLAGS - changes
# what the objects must be, and a stamp they all depend on is what makes that a build dependency:
# the recipe rewrites it only when it differs, so an unchanged build stays untouched.
FLAGS := $(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LIBS)
FLAGS := $(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $(LIBS)

.PHONY: all lib examples check check-tiny check-all tidy manual clean install uninstall force
all: lib examples
Expand All @@ -86,7 +101,7 @@ libioxd.a: $(OBJ)
# The version script keeps the library's own names out of the dynamic symbol table: what a
# consumer may bind to is ioxd_*, and nothing else (nm -D libioxd.so says so).
libioxd.so: $(PICOBJ) $(MAP)
$(CC) $(CFLAGS) -shared -Wl,-soname,$(SONAME) -Wl,--version-script,$(MAP) -o $@ $(PICOBJ) $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(LDFLAGS) -shared -Wl,-soname,$(SONAME) -Wl,--version-script,$(MAP) -o $@ $(PICOBJ) $(PTHREAD) $(LIBS)

# --- static objects (used by libioxd.a and the examples) ---
obj/%.o: lib/%.c $(HDRS) obj/flags
Expand Down Expand Up @@ -114,20 +129,20 @@ obj/pic/picohttpparser.o: third_party/picohttpparser/picohttpparser.c obj/flags
examples: $(EXAMPLES)
# Link the static archive directly so the example runs in-tree without installing the .so.
ioxd-hello: playground/hello/main.c libioxd.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
# The manual's examples, playground/examples/<name>.c, each a whole program: ioxd-example-<name>.
ioxd-example-%: playground/examples/%.c libioxd.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)

# --- tests: the unit test, then the fixture server with both suites against it ---
$(TESTSRV): tests/server.c libioxd.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(UNIT): tests/unit.c libioxd.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(PIPESRV): tests/pipe-server.c libioxd.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(ROUTER): tests/router_test.c libioxd.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(PTHREAD) $(LDFLAGS) $< libioxd.a -o $@ $(PTHREAD) $(LIBS)

CHECK_PORT ?= 8099
PIPE_PORT ?= 8102 # the fixture takes CHECK_PORT and the two after (plain, TLS)
Expand All @@ -136,7 +151,7 @@ TLSFUZZER ?= # a tlsfuzzer checkout, for `make check
# The sequence itself is tests/run-suites.sh, so CMake's `check` target runs exactly this one.
check: $(TESTSRV) $(UNIT) $(PIPESRV) $(ROUTER)
@./$(ROUTER) || exit 1; \
sh tests/run-suites.sh --port $(CHECK_PORT) --pipe-port $(PIPE_PORT) \
sh tests/run-suites.sh --port $(CHECK_PORT) --pipe-port $(PIPE_PORT) --quic $(QUIC) \
--unit ./$(UNIT) --server ./$(TESTSRV) --pipe-server ./$(PIPESRV) --tls-python $(TLS_PYTHON)

# --- the same stress suite, against a build starved on purpose ---
Expand All @@ -161,7 +176,7 @@ obj-tiny/picohttpparser.o: third_party/picohttpparser/picohttpparser.c obj/flags
libioxd-tiny.a: $(TINYOBJ)
$(AR) rcs $@ $^
$(TINYSRV): tests/server.c libioxd-tiny.a
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(TINY) $(PTHREAD) $< libioxd-tiny.a -o $@ $(PTHREAD) $(LIBS)
$(CC) $(CFLAGS) $(WARN) $(HARDEN) $(CPP) $(TINY) $(PTHREAD) $(LDFLAGS) $< libioxd-tiny.a -o $@ $(PTHREAD) $(LIBS)

check-tiny: $(TINYSRV)
@IOXD_RECV_BUFFERS=8 IOXD_RECV_BUFFER_SIZE=64 sh tests/run-suites.sh --suite stress --port $(TINY_PORT) --server ./$(TINYSRV) --work obj-tiny/check
Expand All @@ -173,7 +188,7 @@ check-all: check check-tiny
# checks). CLion's bundled binary ships without clang's builtin headers, so gcc's own are handed
# to it: make tidy TIDY=<clion>/bin/clang/linux/x64/bin/clang-tidy ---
TIDY ?= clang-tidy
TIDY_ARGS ?= --extra-arg=-isystem$(shell $(CC) -print-file-name=include)
TIDY_ARGS ?= --extra-arg=-isystem$(shell $(CC) -print-file-name=include) $(if $(filter 1,$(QUIC)),--extra-arg=-isystem$(shell pkg-config --variable=includedir libngtcp2))
TIDY_SRC := $(wildcard lib/*/*.c) tests/server.c tests/pipe-server.c tests/unit.c
tidy:
$(TIDY) $(TIDY_ARGS) $(TIDY_SRC) -- $(STD) $(CPP) $(PTHREAD)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# libioxd

An HTTP/1.1 server library in C for Linux: io_uring underneath, a thread per core, a stackful
coroutine per connection, TLS 1.3 terminated in the kernel. A handler reads the request and writes
the reply in straight-line code; the runtime does the waiting.
coroutine per connection, TLS 1.3 terminated in the kernel, QUIC on ngtcp2 with every stream a pipe.
A handler reads the request and writes the reply in straight-line code; the runtime does the waiting.

```c
static void hello(ioxd_ctx *ctx)
Expand Down
1 change: 1 addition & 0 deletions include/ioxd.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
#include "ioxd/timer.h" /* a delay that parks the connection, not the worker */
#include "ioxd/socket.h" /* outbound connections, as pipes */
#include "ioxd/tls.h" /* certificates, for a TLS listener */
#include "ioxd/quic.h" /* a QUIC port: its streams as pipes */
20 changes: 20 additions & 0 deletions include/ioxd/quic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* ioxd/quic.h - QUIC: a UDP port whose connections carry streams, each stream handed to the pipe
* handler as a pipe of its own. TLS 1.3 comes from the same certificate store a TLS port uses,
* run by ngtcp2 with OpenSSL; the transport lives in the library, since the kernel offers no
* QUIC of its own.
*/
#pragma once

#include "ioxd/tls.h"

/* Bind a UDP port for QUIC, with a certificate store (ioxd/tls.h) and the application protocols
* the port answers, most preferred first, ended by NULL - QUIC requires one, so a client offering
* none of them is refused at the handshake. Every stream a peer opens is served by the handler of
* ioxd_run_pipes (ioxd/run.h) as a pipe: what the peer sent on the stream is what the pipe reads,
* what the handler writes goes back on it, and the handler returning ends the stream. A stream
* the peer opened one-way reads but cannot be written. ioxd_run, the HTTP server, does not serve
* a QUIC port yet - HTTP/3 is a layer that is not there - and refuses to start with one bound.
* -1 if refused: a bad port, no store, no protocol, the table full, or a build without QUIC
* (make QUIC=1 needs libngtcp2 with its OpenSSL backend, and OpenSSL 3.5 or newer). */
int ioxd_bind_quic(int port, ioxd_certs *certs, const char *const *alpn);
2 changes: 1 addition & 1 deletion lib/clients/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ ioxd_pipe *ioxd__socket_connect(proactor_t *p, const struct sockaddr *sa, sockle
ioxd__conn_setsockopt(c, IPPROTO_TCP, TCP_NODELAY, &one, sizeof one);
ioxd__conn_arm_recv(p, c);
cp->conn = c;
ioxd__pipe_init(&cp->pipe, c, cp->gather, sizeof cp->gather, cp->slab, IOXD_PIPE_LEAD, IOXD_PIPE_CAP, IOXD_PIPE_SLACK);
ioxd__pipe_init(&cp->pipe, c, &ioxd__conn_link, c, cp->gather, sizeof cp->gather, cp->slab, IOXD_PIPE_LEAD, IOXD_PIPE_CAP, IOXD_PIPE_SLACK);
*err = 0;
return &cp->pipe;
}
Expand Down
67 changes: 63 additions & 4 deletions lib/http/run.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,26 @@ int ioxd_bind(int port, ioxd_certs *certs)
return 0;
}

static int run_workers(int workers, handler_fn handler)
static bool any_quic(void)
{
for (int i = 0; i < g_n_listeners; i++)
if (g_listeners[i].quic)
return true;
return false;
}

static int run_workers(int workers, handler_fn handler, handler_fn stream_handler)
{
if (workers <= 0)
workers = cpu_count();
if (g_n_listeners == 0) {
fprintf(stderr, "ioxd_run: nothing bound: ioxd_bind a port first\n");
return 2;
}
if (!stream_handler && any_quic()) {
fprintf(stderr, "ioxd_run: a QUIC port is bound, and HTTP/3 is not here yet: ioxd_run_pipes serves QUIC streams\n");
return 2;
}

g_stop = 0;
raise_nofile();
Expand Down Expand Up @@ -135,6 +147,8 @@ static int run_workers(int workers, handler_fn handler)
ws[i].cpu = i;
ws[i].cfg = cfg;
ws[i].handler = handler;
ws[i].stream_handler = stream_handler;
ws[i].n_workers = workers;
ws[i].n_listeners = g_n_listeners;
memcpy(ws[i].listeners, g_listeners, sizeof g_listeners);
ws[i].stop = &g_stop;
Expand All @@ -149,7 +163,8 @@ static int run_workers(int workers, handler_fn handler)
if (started) {
fprintf(stderr, "ioxd: %d workers on", started);
for (int i = 0; i < g_n_listeners; i++)
fprintf(stderr, " :%u%s", g_listeners[i].port, g_listeners[i].certs ? "/tls" : "");
fprintf(stderr, " :%u%s", g_listeners[i].port,
g_listeners[i].quic ? "/quic" : g_listeners[i].certs ? "/tls" : "");
fputc('\n', stderr);
}

Expand Down Expand Up @@ -186,7 +201,7 @@ int ioxd__run_http(int workers, size_t ctx_size)
return 1;
}
ioxd__router_build();
return run_workers(workers, serve_http);
return run_workers(workers, serve_http, nullptr);
}

static ioxd_pipe_handler g_pipe_handler;
Expand All @@ -203,5 +218,49 @@ static void serve_pipe(ioxd_pipe *pipe)
int ioxd_run_pipes(int workers, ioxd_pipe_handler fn)
{
g_pipe_handler = fn;
return run_workers(workers, serve_pipe);
return run_workers(workers, serve_pipe, fn);
}

#if IOXD_QUIC
int ioxd_bind_quic(int port, ioxd_certs *certs, const char *const *alpn)
{
if (port < 1 || port > 65535 || g_n_listeners == IOXD_MAX_LISTENERS) {
fprintf(stderr, "ioxd_bind_quic: port %d refused (1..65535, at most %d ports)\n", port, IOXD_MAX_LISTENERS);
return -1;
}
if (!certs) {
fprintf(stderr, "ioxd_bind_quic: port %d refused: QUIC is always TLS, and no certificate store was given\n", port);
return -1;
}
size_t total = 0;
int n = 0;
for (; alpn && alpn[n]; n++) {
size_t len = strlen(alpn[n]);
if (len == 0 || len > 255) {
fprintf(stderr, "ioxd_bind_quic: port %d refused: a protocol name must be 1 to 255 bytes\n", port);
return -1;
}
total += 1 + len;
}
if (n == 0 || total > 65535) {
fprintf(stderr, "ioxd_bind_quic: port %d refused: QUIC needs an application protocol to answer (\"h3\", or one of your own)\n", port);
return -1;
}
uint8_t *wire = malloc(2 + total);
if (!wire) {
perror("ioxd_bind_quic");
return -1;
}
wire[0] = (uint8_t)(total & 0xFF);
wire[1] = (uint8_t)(total >> 8);
size_t at = 2;
for (int i = 0; i < n; i++) {
size_t len = strlen(alpn[i]);
wire[at++] = (uint8_t)len;
memcpy(wire + at, alpn[i], len);
at += len;
}
g_listeners[g_n_listeners++] = (struct listener){ .port = (uint16_t)port, .certs = certs, .quic = true, .alpn = wire, .alpn_len = total };
return 0;
}
#endif
31 changes: 30 additions & 1 deletion lib/io/conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,42 @@ void ioxd__conn_close(conn_t *c)
conn_unref(c);
}

static int link_recv_item(void *link, struct rx_item *out)
{
return ioxd__conn_recv_item(link, out);
}

static bool link_has_item(void *link)
{
conn_t *c = link;
return !ioxd__spsc_empty(&c->rx);
}

static void link_release(void *link, const struct rx_item *item)
{
conn_t *c = link;
ioxd__bufring_return(&c->p->bufs, item->buf_id);
}

static int link_send(void *link, const void *data, size_t n)
{
return ioxd__conn_send(link, data, n);
}

const ioxd_pipe_link ioxd__conn_link = {
.recv_item = link_recv_item,
.has_item = link_has_item,
.release = link_release,
.send = link_send,
};

void ioxd__conn_main(void *arg)
{
conn_t *c = arg;
char gather[IOXD_PIPE_GATHER];
char slab[IOXD_PIPE_LEAD + IOXD_PIPE_CAP + IOXD_PIPE_SLACK];
struct ioxd_pipe pipe;
ioxd__pipe_init(&pipe, c, gather, sizeof gather, slab, IOXD_PIPE_LEAD, IOXD_PIPE_CAP, IOXD_PIPE_SLACK);
ioxd__pipe_init(&pipe, c, &ioxd__conn_link, c, gather, sizeof gather, slab, IOXD_PIPE_LEAD, IOXD_PIPE_CAP, IOXD_PIPE_SLACK);
c->p->handler(&pipe);
ioxd__pipe_close(&pipe);
ioxd__conn_close(c);
Expand Down
4 changes: 4 additions & 0 deletions lib/io/conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ int ioxd__conn_recv_exact (conn_t *c, void *dst, size_t n); /* n bytes into d
int ioxd__conn_setsockopt (conn_t *c, int level, int name, const void *val, size_t len); /* 0 or -errno; over the ring */
int ioxd__conn_sendmsg (conn_t *c, const struct msghdr *msg); /* one sendmsg, for a message with control data */

/* The connection as a pipe's link (io/pipe.h): its delivered buffers in, its socket out. */
struct ioxd_pipe_link;
extern const struct ioxd_pipe_link ioxd__conn_link;

/* For the loop (proactor.c): a connection's life from accept to the pool. */
conn_t *ioxd__conn_new(proactor_t *p, struct listener *l, int fd); /* from the pool, or fresh */
void ioxd__conn_main(void *arg); /* the connection's coroutine body */
Expand Down
Loading