diff --git a/.gitignore b/.gitignore index 3054110..2c0c132 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ compiled/ echoserver.beam +uvserver diff --git a/Makefile b/Makefile index e44c251..5474238 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,9 @@ -all: +all: uvserver raco make *.rkt clean: rm -rf compiled + rm -f uvserver + +uvserver: uvserver.c + gcc -o $@ $< -luv diff --git a/external-latency.rkt b/external-latency.rkt index 0be3098..35bb845 100644 --- a/external-latency.rkt +++ b/external-latency.rkt @@ -19,6 +19,7 @@ (set! max-waypoint (string->number n))] #:once-any ["--erlang" "use erlang server" (set! server-variation 'erlang)] + ["--uv" "use libuv server" (set! server-variation 'uv)] ["--minimart" "use minimart server" (set! server-variation 'minimart)] ["--other" name "use other server" (set! server-variation name)]) @@ -40,6 +41,9 @@ command)) control) +(define (capture-output command) + (string-trim (with-output-to-string (lambda () (system command))))) + (log-info "Starting server...") (define server-control (start-bg @@ -53,9 +57,11 @@ "erl -noshell -eval 'io:format(erlang:system_info(otp_release)), halt().'") (write-logbook-datum! Tmachine #:label "erlang-version" - (string-trim (with-output-to-string - (lambda () (system erlang-version-command))))) + (capture-output erlang-version-command)) "./run-erlang-server.sh"] + ['uv + (write-logbook-datum! Tmachine #:label "uv-banner" (capture-output "./uvserver -v")) + "./uvserver"] [(? string? other) (printf "Please start the other server on hostname '~a' now.\n" server-hostname) (printf "Press enter when it has started.\n") diff --git a/uvserver.c b/uvserver.c new file mode 100644 index 0000000..832c127 --- /dev/null +++ b/uvserver.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include + +static int connection_count = 0; + +static uv_buf_t alloc_buffer(uv_handle_t *handle, size_t suggested_size) { + return uv_buf_init(calloc(1, suggested_size), suggested_size); +} + +static void on_write_complete(uv_write_t *req, int status) { + /* we could check status here, but we don't care; just free, and be done */ + free(req->data); + free(req); +} + +static void on_input(uv_stream_t *conn, ssize_t nread, uv_buf_t buf) { + if (nread == -1) { + /* here we could check uv_last_error(conn->loop).code to see whether it's UV_EOF or not */ + /* but we don't care; just close, and be done */ + connection_count--; + if (connection_count == 0) { + uv_stop(conn->loop); + } + uv_close((uv_handle_t *) conn, NULL); + } else { + uv_write_t *req = calloc(1, sizeof(uv_write_t)); + /* we just echo what came in. */ + req->data = buf.base; + buf.len = nread; + uv_write(req, conn, &buf, 1, on_write_complete); + } +} + +static void on_connection(uv_stream_t *serversock, int status) { + uv_tcp_t *conn; + + if (status == -1) { + /* error? */ + return; + } + + conn = calloc(1, sizeof(uv_tcp_t)); + uv_tcp_init(serversock->loop, conn); + if (uv_accept(serversock, (uv_stream_t *) conn) == 0) { + connection_count++; + uv_read_start((uv_stream_t *) conn, alloc_buffer, on_input); + } else { + uv_close((uv_handle_t *) conn, NULL); + } +} + +static int const PORTNUMBER = 5999; + +int main(int argc, char const *argv[]) { + uv_loop_t *uv = uv_default_loop(); + uv_tcp_t serversock; + struct sockaddr_in bind_addr; + + printf("uvserver; libuv version %s\n", uv_version_string()); + + if (argc > 1) { + return 0; + } + + printf("Accepting connections on port %d.\n", PORTNUMBER); + uv_tcp_init(uv, &serversock); + bind_addr = uv_ip4_addr("0.0.0.0", PORTNUMBER); + uv_tcp_bind(&serversock, bind_addr); + uv_listen((uv_stream_t *) &serversock, 4, on_connection); + + uv_run(uv, UV_RUN_DEFAULT); + + uv_loop_delete(uv); + return 0; +}