diff --git a/Makefile b/Makefile index f6e2c6ba..87624242 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,9 @@ boringssl: examples: default for f in examples/*.c; do $(CC) -O3 $(CFLAGS) -o $$(basename "$$f" ".c")$(EXEC_SUFFIX) "$$f" $(LDFLAGS); done +linux_examples: + for f in examples/linux/*.c; do $(CC) -O3 $(CFLAGS) -o $$(basename "$$f" ".c")$(EXEC_SUFFIX) "$$f" $(LDFLAGS); done + swift_examples: swiftc -O -I . examples/swift_http_server/main.swift uSockets.a -o swift_http_server diff --git a/examples/linux/netns_server.c b/examples/linux/netns_server.c new file mode 100644 index 00000000..c1dc2dfa --- /dev/null +++ b/examples/linux/netns_server.c @@ -0,0 +1,178 @@ +/* This is a more advanced version of the HTTP server test which + * runs the server within a Linux network namespace. The socket + * is bound and listened by the parent process, and passed to + * the namespaced child process which runs the server. + * + * Creating network namespaces (and therefore running this test) + * requires root privileges. + */ +#include + +/* Steal internal BSD socket ops */ +#include + +/* clone() */ +#define _GNU_SOURCE +#include +#include + +/* Compiling with LIBUS_NO_SSL will ignore SSL */ +const int SSL = 1; + +#include +#include +#include + +struct http_socket { + /* How far we have streamed our response */ + int offset; +}; + +struct http_context { + /* The shared response */ + char *response; + int length; +}; + +/* We don't need any of these */ +void on_wakeup(struct us_loop_t *loop) { + +} + +void on_pre(struct us_loop_t *loop) { + +} + +/* This is not HTTP POST, it is merely an event emitted post loop iteration */ +void on_post(struct us_loop_t *loop) { + +} + +struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) { + struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s); + struct http_context *http_context = (struct http_context *) us_socket_context_ext(SSL, us_socket_context(SSL, s)); + + /* Stream whatever is remaining of the response */ + http_socket->offset += us_socket_write(SSL, s, http_context->response + http_socket->offset, http_context->length - http_socket->offset, 0); + + return s; +} + +struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) { + printf("Client disconnected\n"); + + return s; +} + +struct us_socket_t *on_http_socket_end(struct us_socket_t *s) { + /* HTTP does not support half-closed sockets */ + us_socket_shutdown(SSL, s); + return us_socket_close(SSL, s, 0, NULL); +} + +struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) { + /* Get socket extension and the socket's context's extension */ + struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s); + struct http_context *http_context = (struct http_context *) us_socket_context_ext(SSL, us_socket_context(SSL, s)); + + /* We treat all data events as a request */ + http_socket->offset = us_socket_write(SSL, s, http_context->response, http_context->length, 0); + + /* Reset idle timer */ + us_socket_timeout(SSL, s, 30); + + return s; +} + +struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { + struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s); + + /* Reset offset */ + http_socket->offset = 0; + + /* Timeout idle HTTP connections */ + us_socket_timeout(SSL, s, 30); + + printf("Client connected\n"); + + return s; +} + +struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { + /* Close idle HTTP sockets */ + return us_socket_close(SSL, s, 0, NULL); +} + +int server(void* arg) { + LIBUS_SOCKET_DESCRIPTOR listen_fd = *(LIBUS_SOCKET_DESCRIPTOR*)arg; + + /* Create the event loop */ + struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); + + /* Create a socket context for HTTP */ + struct us_socket_context_options_t options = {}; + options.key_file_name = "../../misc/key.pem"; + options.cert_file_name = "../../misc/cert.pem"; + options.passphrase = "1234"; + + struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, sizeof(struct http_context), options); + + if (!http_context) { + printf("Could not load SSL cert/key\n"); + exit(1); + } + + /* Generate the shared response */ + const char body[] = "

Why hello there!

"; + + struct http_context *http_context_ext = (struct http_context *) us_socket_context_ext(SSL, http_context); + http_context_ext->response = (char *) malloc(128 + sizeof(body) - 1); + http_context_ext->length = snprintf(http_context_ext->response, 128 + sizeof(body) - 1, "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n%s", sizeof(body) - 1, body); + + /* Set up event handlers */ + us_socket_context_on_open(SSL, http_context, on_http_socket_open); + us_socket_context_on_data(SSL, http_context, on_http_socket_data); + us_socket_context_on_writable(SSL, http_context, on_http_socket_writable); + us_socket_context_on_close(SSL, http_context, on_http_socket_close); + us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout); + us_socket_context_on_end(SSL, http_context, on_http_socket_end); + + /* Start serving HTTP connections */ + struct us_listen_socket_t *listen_socket = us_socket_context_listen_direct(SSL, http_context, listen_fd, 0, sizeof(struct http_socket)); + + if (listen_socket) { + printf("Listening on passed socket...\n"); + us_loop_run(loop); + } else { + printf("Failed to listen!\n"); + } + + return 0; +} + +#define STACK_SIZE 8192 +static char child_stack[STACK_SIZE]; + +int main() { + LIBUS_SOCKET_DESCRIPTOR listen_fd = bsd_create_listen_socket(0, 3000, 0); + if (!listen_fd) { + printf("Could not bind and listen to port: %s\n", strerror(errno)); + exit(1); + } + printf("Listening on port 3000...\n"); + + int child_pid = clone( + server, + child_stack + STACK_SIZE, + CLONE_NEWNET | CLONE_FILES | SIGCHLD, + (void*)&listen_fd); + if (child_pid < 0) { + printf("Could not create namespaced server process: %s\n", strerror(errno)); + exit(1); + }; + + printf("Started server with pid %d\n", child_pid); + int status; + waitpid(child_pid, &status, 0); + return status; +} diff --git a/src/context.c b/src/context.c index 514d2e69..18048fcf 100644 --- a/src/context.c +++ b/src/context.c @@ -258,22 +258,7 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co return 0; } - struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); - us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); - us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); - - struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; - - ls->s.context = context; - ls->s.timeout = 255; - ls->s.long_timeout = 255; - ls->s.low_prio_state = 0; - ls->s.next = 0; - us_internal_socket_context_link_listen_socket(context, ls); - - ls->socket_ext_size = socket_ext_size; - - return ls; + return us_socket_context_listen_direct(ssl, context, listen_socket_fd, options, socket_ext_size); } struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) { @@ -289,6 +274,16 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock return 0; } + return us_socket_context_listen_direct(ssl, context, listen_socket_fd, options, socket_ext_size); +} + +struct us_listen_socket_t *us_socket_context_listen_direct(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR listen_socket_fd, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_listen_direct((struct us_internal_ssl_socket_context_t *) context, listen_socket_fd, options, socket_ext_size); + } +#endif + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); @@ -307,6 +302,7 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock return ls; } + struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { #ifndef LIBUS_NO_SSL if (ssl) { diff --git a/src/crypto/openssl.c b/src/crypto/openssl.c index 4718812c..d16b70a1 100644 --- a/src/crypto/openssl.c +++ b/src/crypto/openssl.c @@ -693,6 +693,10 @@ struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_ return us_socket_context_listen_unix(0, &context->sc, path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); } +struct us_listen_socket_t *us_internal_ssl_socket_context_listen_direct(struct us_internal_ssl_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR listen_socket_fd, int options, int socket_ext_size) { + return us_socket_context_listen_direct(0, &context->sc, listen_socket_fd, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { return (struct us_internal_ssl_socket_t *) us_socket_context_connect(0, &context->sc, host, port, source_host, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); } diff --git a/src/internal/internal.h b/src/internal/internal.h index b37ff4d3..115cfeb0 100644 --- a/src/internal/internal.h +++ b/src/internal/internal.h @@ -186,6 +186,9 @@ struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_inter struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, const char *path, int options, int socket_ext_size); +struct us_listen_socket_t *us_internal_ssl_socket_context_listen_direct(struct us_internal_ssl_socket_context_t *context, + LIBUS_SOCKET_DESCRIPTOR listen_socket_fd, int options, int socket_ext_size); + struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size); diff --git a/src/libusockets.h b/src/libusockets.h index f5a6c121..ea2b986b 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -184,6 +184,9 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size); +struct us_listen_socket_t *us_socket_context_listen_direct(int ssl, struct us_socket_context_t *context, + LIBUS_SOCKET_DESCRIPTOR listen_socket_fd, int options, int socket_ext_size); + /* listen_socket.c/.h */ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls);