From 80e972ff540da1815f5d03a4d3adfb44838cfaa8 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 24 May 2023 14:49:08 +0200 Subject: [PATCH] feat: simple echoing server --- src/event_loop/event_loop.c | 7 +-- src/event_loop/event_loop_conn.c | 68 +++++++++++++++++++++------- src/event_loop/event_loop_internal.h | 7 ++- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/event_loop/event_loop.c b/src/event_loop/event_loop.c index b653efa..5887407 100644 --- a/src/event_loop/event_loop.c +++ b/src/event_loop/event_loop.c @@ -13,9 +13,6 @@ #include "event_loop_internal.h" #include "picohttpparser.h" -const char http_200_ok[] = "HTTP/1.1 200 OK\n" - "Connection: close\n"; - static int event_loop_fd_set_nb(int fd) { int flags = fcntl(fd, F_GETFL, 0); @@ -101,6 +98,8 @@ int event_loop_accept(event_loop *loop, int fd) { return -4; } + /* printf("New connection with fd %i\n", connfd); */ + return 0; } @@ -176,6 +175,7 @@ void event_loop_run(event_loop *el, int port) { // poll for active fds // the timeout argument doesn't matter here int rv = poll(poll_args, (nfds_t)poll_args_count, 1000); + /* printf("poll"); */ if (rv < 0) { return; @@ -188,6 +188,7 @@ void event_loop_run(event_loop *el, int port) { event_loop_conn_io(conn); if (conn->state == event_loop_conn_state_end) { + /* printf("free %i\n", conn->fd); */ // client closed normally, or something bad happened. // destroy this connection el->connections[conn->fd] = NULL; diff --git a/src/event_loop/event_loop_conn.c b/src/event_loop/event_loop_conn.c index 4a299d8..afc4f79 100644 --- a/src/event_loop/event_loop_conn.c +++ b/src/event_loop/event_loop_conn.c @@ -14,7 +14,9 @@ #include "event_loop_internal.h" -bool event_loop_conn_write_to_fd(event_loop_conn *conn) { +const char http_200_ok[] = "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 5\n\nhello"; + +bool event_loop_conn_io_res(event_loop_conn *conn) { ssize_t res = 0; size_t remain = conn->wbuf_size - conn->wbuf_sent; @@ -36,9 +38,10 @@ bool event_loop_conn_write_to_fd(event_loop_conn *conn) { conn->wbuf_sent += (size_t)res; - // Everything is written from the buffer, so we exit + // After writing a response, we switch back to request mode to process a next + // request if (conn->wbuf_sent == conn->wbuf_size) { - conn->state = event_loop_conn_state_end; + conn->state = conn->close_after_write ? event_loop_conn_state_end : event_loop_conn_state_req; /* c->wbuf_sent = 0; */ /* c->wbuf_size = 0; */ @@ -49,12 +52,12 @@ bool event_loop_conn_write_to_fd(event_loop_conn *conn) { return true; } -void try_one_request(event_loop_conn *conn) { - if (conn->process_func != NULL) { - conn->process_func(conn); - } +bool try_one_request(event_loop_conn *conn) { + /* if (conn->process_func != NULL) { */ + /* conn->process_func(conn); */ + /* } */ - char *method, *path; + const char *method, *path; struct phr_header headers[16]; size_t method_len, path_len, num_headers; int minor_version; @@ -66,12 +69,38 @@ void try_one_request(event_loop_conn *conn) { &minor_version, headers, &num_headers, 0); if (res > 0) { + // TODO allow HTTP pipelining + conn->close_after_write = true; + /* for (int i = 0; i < num_headers; i++) { */ + /* printf("%.*s: ", headers[i].name_len, headers[i].name); */ + /* printf("%.*s\n", headers[i].value_len, headers[i].value); */ + /* if (strncmp("Connection", headers[i].name, headers[i].name_len) == 0) { */ + /* if (strncmp("close", headers[i].value, headers[i].value_len) == 0) { */ + /* printf("close\n"); */ + /* break; */ + /* } */ + /* } */ + /* } */ - } else if (res == -1) { + memcpy(conn->wbuf, http_200_ok, sizeof(http_200_ok) - 1); + + // Move the new request up to the front of the read buffer + memmove(conn->rbuf, &conn->rbuf[res], conn->rbuf_size - res); + conn->rbuf_size -= res; + + conn->state = event_loop_conn_state_res; + conn->wbuf_size = sizeof(http_200_ok) - 1; + conn->wbuf_sent = 0; + + event_loop_conn_io_res(conn); + + // End the connection if the http request is invalid, or when the response + // is too large to fit into a single read buffer + } else if (res == -1 || (res == -2 && conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) { conn->state = event_loop_conn_state_end; - } else if (res == -2) { - // We don't do anything here } + + return conn->state == event_loop_conn_state_req; } /** @@ -80,7 +109,7 @@ void try_one_request(event_loop_conn *conn) { * * Returns whether the function should be retried immediately or not. */ -bool event_loop_conn_read_from_fd(event_loop_conn *conn) { +bool event_loop_conn_io_req(event_loop_conn *conn) { ssize_t res; size_t cap = EVENT_LOOP_BUFFER_SIZE - conn->rbuf_size; @@ -101,8 +130,11 @@ bool event_loop_conn_read_from_fd(event_loop_conn *conn) { return false; } - // An output of 0 zero means we've reached the end of the input + // Output of 0 and no error means we've reached the end of the input. We run + // try_one_request one more time and close the connection if this doesn't + // change the result. if (res == 0) { + conn->state = event_loop_conn_state_end; } // We switch to processing mode if we've reached the end of the data stream, @@ -119,7 +151,7 @@ bool event_loop_conn_read_from_fd(event_loop_conn *conn) { // Try to process requests one by one. // Try to process requests one by one. // Why is there a loop? Please read the explanation of "pipelining". - try_one_request(conn); + while (try_one_request(conn)) {}; // We can keep reading as long as we're in request mode return conn->state == event_loop_conn_state_req; @@ -128,13 +160,14 @@ bool event_loop_conn_read_from_fd(event_loop_conn *conn) { } void conn_state_res(event_loop_conn *conn) { - while (event_loop_conn_write_to_fd(conn)) { + while (event_loop_conn_io_res(conn)) { } } void conn_state_req(event_loop_conn *conn) { - while (event_loop_conn_read_from_fd(conn)) { + while (event_loop_conn_io_req(conn)) { } + /* printf("done req"); */ } void event_loop_conn_io(event_loop_conn *conn) { @@ -145,5 +178,8 @@ void event_loop_conn_io(event_loop_conn *conn) { case event_loop_conn_state_res: conn_state_res(conn); break; + case event_loop_conn_state_end: + printf("we shouldn't be here\n"); + break; } } diff --git a/src/event_loop/event_loop_internal.h b/src/event_loop/event_loop_internal.h index 9189ce7..2ee8af3 100644 --- a/src/event_loop/event_loop_internal.h +++ b/src/event_loop/event_loop_internal.h @@ -3,6 +3,7 @@ #include #include +#include #include "event_loop.h" @@ -16,7 +17,11 @@ typedef struct event_loop_conn { size_t wbuf_size; size_t wbuf_sent; uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE]; - void (*process_func)(struct event_loop_conn *); + + // If true, the server will close the connection after the final write buffer + // has been written + bool close_after_write; + /* void (*process_func)(struct event_loop_conn *); */ } event_loop_conn; /*