feat: simple echoing server
parent
a163ee5155
commit
80e972ff54
|
@ -13,9 +13,6 @@
|
||||||
#include "event_loop_internal.h"
|
#include "event_loop_internal.h"
|
||||||
#include "picohttpparser.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) {
|
static int event_loop_fd_set_nb(int fd) {
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
|
||||||
|
@ -101,6 +98,8 @@ int event_loop_accept(event_loop *loop, int fd) {
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* printf("New connection with fd %i\n", connfd); */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +175,7 @@ void event_loop_run(event_loop *el, int port) {
|
||||||
// poll for active fds
|
// poll for active fds
|
||||||
// the timeout argument doesn't matter here
|
// the timeout argument doesn't matter here
|
||||||
int rv = poll(poll_args, (nfds_t)poll_args_count, 1000);
|
int rv = poll(poll_args, (nfds_t)poll_args_count, 1000);
|
||||||
|
/* printf("poll"); */
|
||||||
|
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -188,6 +188,7 @@ void event_loop_run(event_loop *el, int port) {
|
||||||
event_loop_conn_io(conn);
|
event_loop_conn_io(conn);
|
||||||
|
|
||||||
if (conn->state == event_loop_conn_state_end) {
|
if (conn->state == event_loop_conn_state_end) {
|
||||||
|
/* printf("free %i\n", conn->fd); */
|
||||||
// client closed normally, or something bad happened.
|
// client closed normally, or something bad happened.
|
||||||
// destroy this connection
|
// destroy this connection
|
||||||
el->connections[conn->fd] = NULL;
|
el->connections[conn->fd] = NULL;
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
|
|
||||||
#include "event_loop_internal.h"
|
#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;
|
ssize_t res = 0;
|
||||||
size_t remain = conn->wbuf_size - conn->wbuf_sent;
|
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;
|
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) {
|
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_sent = 0; */
|
||||||
/* c->wbuf_size = 0; */
|
/* c->wbuf_size = 0; */
|
||||||
|
|
||||||
|
@ -49,12 +52,12 @@ bool event_loop_conn_write_to_fd(event_loop_conn *conn) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_one_request(event_loop_conn *conn) {
|
bool try_one_request(event_loop_conn *conn) {
|
||||||
if (conn->process_func != NULL) {
|
/* if (conn->process_func != NULL) { */
|
||||||
conn->process_func(conn);
|
/* conn->process_func(conn); */
|
||||||
}
|
/* } */
|
||||||
|
|
||||||
char *method, *path;
|
const char *method, *path;
|
||||||
struct phr_header headers[16];
|
struct phr_header headers[16];
|
||||||
size_t method_len, path_len, num_headers;
|
size_t method_len, path_len, num_headers;
|
||||||
int minor_version;
|
int minor_version;
|
||||||
|
@ -66,12 +69,38 @@ void try_one_request(event_loop_conn *conn) {
|
||||||
&minor_version, headers, &num_headers, 0);
|
&minor_version, headers, &num_headers, 0);
|
||||||
|
|
||||||
if (res > 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;
|
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.
|
* 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;
|
ssize_t res;
|
||||||
size_t cap = EVENT_LOOP_BUFFER_SIZE - conn->rbuf_size;
|
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;
|
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) {
|
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,
|
// 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.
|
||||||
// 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".
|
// 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
|
// We can keep reading as long as we're in request mode
|
||||||
return conn->state == event_loop_conn_state_req;
|
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) {
|
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) {
|
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) {
|
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:
|
case event_loop_conn_state_res:
|
||||||
conn_state_res(conn);
|
conn_state_res(conn);
|
||||||
break;
|
break;
|
||||||
|
case event_loop_conn_state_end:
|
||||||
|
printf("we shouldn't be here\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "event_loop.h"
|
#include "event_loop.h"
|
||||||
|
|
||||||
|
@ -16,7 +17,11 @@ typedef struct event_loop_conn {
|
||||||
size_t wbuf_size;
|
size_t wbuf_size;
|
||||||
size_t wbuf_sent;
|
size_t wbuf_sent;
|
||||||
uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE];
|
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;
|
} event_loop_conn;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue