feat: inching our way towards http routing
parent
5dc772e507
commit
6d4b94c55e
|
@ -1,6 +1,12 @@
|
||||||
#ifndef LANDER_EVENT_LOOP
|
#ifndef LANDER_EVENT_LOOP
|
||||||
#define LANDER_EVENT_LOOP
|
#define LANDER_EVENT_LOOP
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "http_req.h"
|
||||||
|
|
||||||
// Size of the read and write buffers for each connection, in bytes
|
// Size of the read and write buffers for each connection, in bytes
|
||||||
#define EVENT_LOOP_BUFFER_SIZE 1024
|
#define EVENT_LOOP_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
@ -30,4 +36,52 @@ event_loop *event_loop_init();
|
||||||
*/
|
*/
|
||||||
void event_loop_run(event_loop *el, int port);
|
void event_loop_run(event_loop *el, int port);
|
||||||
|
|
||||||
|
typedef struct event_loop_conn {
|
||||||
|
int fd;
|
||||||
|
event_loop_conn_state state;
|
||||||
|
// buffer for reading
|
||||||
|
size_t rbuf_size;
|
||||||
|
size_t rbuf_read;
|
||||||
|
uint8_t rbuf[EVENT_LOOP_BUFFER_SIZE];
|
||||||
|
// buffer for writing
|
||||||
|
size_t wbuf_size;
|
||||||
|
size_t wbuf_sent;
|
||||||
|
uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE];
|
||||||
|
|
||||||
|
// 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 *); */
|
||||||
|
http_request req;
|
||||||
|
} event_loop_conn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a new event_loop_conn struct
|
||||||
|
*/
|
||||||
|
event_loop_conn *event_loop_conn_init();
|
||||||
|
|
||||||
|
typedef struct event_loop {
|
||||||
|
event_loop_conn **connections;
|
||||||
|
size_t connection_count;
|
||||||
|
} event_loop;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a new event_loop struct
|
||||||
|
*/
|
||||||
|
event_loop *event_loop_init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Place a new connection into the event loop's internal array.
|
||||||
|
*
|
||||||
|
* Returns -1 if the internal realloc failed
|
||||||
|
*/
|
||||||
|
int event_loop_put(event_loop *loop, event_loop_conn *conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a new connection for the given file descriptor.
|
||||||
|
*/
|
||||||
|
int event_loop_accept(event_loop *loop, int fd);
|
||||||
|
|
||||||
|
void event_loop_conn_io(event_loop_conn *conn);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,31 +3,16 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "picohttpparser.h"
|
||||||
|
|
||||||
|
#include "event_loop.h"
|
||||||
|
#include "http_req.h"
|
||||||
|
|
||||||
extern const char http_404[];
|
extern const char http_404[];
|
||||||
extern const size_t http_404_len;
|
extern const size_t http_404_len;
|
||||||
extern const char http_500[];
|
extern const char http_500[];
|
||||||
extern const size_t http_500_len;
|
extern const size_t http_500_len;
|
||||||
|
|
||||||
typedef enum http_request_entity {
|
|
||||||
event_loop_request_type_unknown = 0
|
|
||||||
} http_request_entity;
|
|
||||||
|
|
||||||
typedef enum http_request_method {
|
|
||||||
http_request_method_get = 0,
|
|
||||||
http_request_method_post = 1,
|
|
||||||
http_request_method_put = 2,
|
|
||||||
http_request_method_patch = 3,
|
|
||||||
http_request_method_delete = 4
|
|
||||||
} http_request_method;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Struct representing the specific type of request
|
|
||||||
*/
|
|
||||||
typedef struct http_request {
|
|
||||||
http_request_method type;
|
|
||||||
http_request_entity entity;
|
|
||||||
} http_request;
|
|
||||||
|
|
||||||
typedef enum http_parse_error {
|
typedef enum http_parse_error {
|
||||||
http_parse_error_ok = 0,
|
http_parse_error_ok = 0,
|
||||||
http_parse_error_incomplete = 1,
|
http_parse_error_incomplete = 1,
|
||||||
|
@ -37,4 +22,6 @@ typedef enum http_parse_error {
|
||||||
http_parse_error http_parse_request(http_request *req, const char *path,
|
http_parse_error http_parse_request(http_request *req, const char *path,
|
||||||
size_t path_len);
|
size_t path_len);
|
||||||
|
|
||||||
|
void http_route(event_loop_conn *conn);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef HTTP_REQ
|
||||||
|
#define HTTP_REQ
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "picohttpparser.h"
|
||||||
|
|
||||||
|
#define HTTP_MAX_ALLOWED_HEADERS 16
|
||||||
|
|
||||||
|
typedef enum http_request_method {
|
||||||
|
http_request_method_get = 0,
|
||||||
|
http_request_method_post = 1,
|
||||||
|
http_request_method_put = 2,
|
||||||
|
http_request_method_patch = 3,
|
||||||
|
http_request_method_delete = 4
|
||||||
|
} http_request_method;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Struct representing the specific type of request
|
||||||
|
*/
|
||||||
|
typedef struct http_request {
|
||||||
|
size_t len;
|
||||||
|
int minor_version;
|
||||||
|
const char *method;
|
||||||
|
size_t method_len;
|
||||||
|
const char *path;
|
||||||
|
size_t path_len;
|
||||||
|
struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS];
|
||||||
|
} http_request;
|
||||||
|
|
||||||
|
#endif
|
|
@ -10,7 +10,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "event_loop_internal.h"
|
#include "event_loop.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "picohttpparser.h"
|
#include "picohttpparser.h"
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "picohttpparser.h"
|
#include "picohttpparser.h"
|
||||||
#include "http.h"
|
|
||||||
|
|
||||||
#include "event_loop_internal.h"
|
#include "event_loop.h"
|
||||||
|
#include "http.h"
|
||||||
|
|
||||||
void event_loop_conn_io_res(event_loop_conn *conn) {
|
void event_loop_conn_io_res(event_loop_conn *conn) {
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -59,58 +59,34 @@ bool event_loop_handle_request(event_loop_conn *conn) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if (conn->process_func != NULL) { */
|
http_parse_error res =
|
||||||
/* conn->process_func(conn); */
|
http_parse_request(&conn->req, (const char *)&conn->rbuf[conn->rbuf_read],
|
||||||
/* } */
|
conn->rbuf_size - conn->rbuf_read);
|
||||||
|
|
||||||
const char *method, *path;
|
if (res == http_parse_error_ok) {
|
||||||
struct phr_header headers[16];
|
conn->rbuf_read += conn->req.len;
|
||||||
size_t method_len, path_len, num_headers;
|
|
||||||
int minor_version;
|
|
||||||
|
|
||||||
num_headers = sizeof(headers) / sizeof(headers[0]);
|
|
||||||
|
|
||||||
int res = phr_parse_request((const char *)&conn->rbuf[conn->rbuf_read], conn->rbuf_size - conn->rbuf_read,
|
|
||||||
&method, &method_len, &path, &path_len,
|
|
||||||
&minor_version, headers, &num_headers, 0);
|
|
||||||
|
|
||||||
if (res > 0) {
|
|
||||||
/* for (size_t i = 0; i < num_headers; i++) { */
|
|
||||||
/* /1* printf("%.*s: ", headers[i].name_len, headers[i].name); *1/ */
|
|
||||||
/* /1* printf("%.*s\n", headers[i].value_len, headers[i].value); *1/ */
|
|
||||||
/* if (strncmp("Connection", headers[i].name, headers[i].name_len) == 0) {
|
|
||||||
*/
|
|
||||||
/* if (strncmp("close", headers[i].value, headers[i].value_len) == 0) {
|
|
||||||
*/
|
|
||||||
/* conn->close_after_write = true; */
|
|
||||||
/* break; */
|
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
// Advance the read buffer
|
|
||||||
conn->rbuf_read += res;
|
|
||||||
|
|
||||||
memcpy(conn->wbuf, http_404, http_404_len);
|
memcpy(conn->wbuf, http_404, http_404_len);
|
||||||
|
|
||||||
// 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->state = event_loop_conn_state_res;
|
||||||
conn->wbuf_size = http_404_len;
|
conn->wbuf_size = http_404_len;
|
||||||
conn->wbuf_sent = 0;
|
conn->wbuf_sent = 0;
|
||||||
|
|
||||||
event_loop_conn_io_res(conn);
|
/* event_loop_conn_io_res(conn); */
|
||||||
|
|
||||||
/* // End the connection if the http request is invalid, or when the
|
}
|
||||||
* response */
|
// Both in the case of an invalid HTTP request or one that's larger than the
|
||||||
/* // is too large to fit into a single read buffer */
|
// read buffer, we cannot determine when the next, possibly valid, HTTP
|
||||||
} else if (res == -1 ||
|
// request begins in the data stream. Therefore, we close the connection,
|
||||||
(res == -2 && conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) {
|
// even if additional pipelined requests are incoming.
|
||||||
|
else if (res == http_parse_error_invalid ||
|
||||||
|
(res == http_parse_error_incomplete &&
|
||||||
|
conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) {
|
||||||
conn->state = event_loop_conn_state_end;
|
conn->state = event_loop_conn_state_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO in highly concurrent situations, it might actually be better to always
|
||||||
|
// return false here, as this allows cycling better through all connections
|
||||||
return conn->state == event_loop_conn_state_req;
|
return conn->state == event_loop_conn_state_req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +99,8 @@ bool event_loop_handle_request(event_loop_conn *conn) {
|
||||||
void event_loop_conn_io_req(event_loop_conn *conn) {
|
void event_loop_conn_io_req(event_loop_conn *conn) {
|
||||||
do {
|
do {
|
||||||
// Move remaining data to start of buffer
|
// Move remaining data to start of buffer
|
||||||
memmove(conn->rbuf, &conn->rbuf[conn->rbuf_read], conn->rbuf_size - conn->rbuf_read);
|
memmove(conn->rbuf, &conn->rbuf[conn->rbuf_read],
|
||||||
|
conn->rbuf_size - conn->rbuf_read);
|
||||||
conn->rbuf_size -= conn->rbuf_read;
|
conn->rbuf_size -= conn->rbuf_read;
|
||||||
conn->rbuf_read = 0;
|
conn->rbuf_read = 0;
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
#ifndef LANDER_EVENT_LOOP_INTERNAL
|
|
||||||
#define LANDER_EVENT_LOOP_INTERNAL
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "event_loop.h"
|
|
||||||
|
|
||||||
typedef struct event_loop_conn {
|
|
||||||
int fd;
|
|
||||||
event_loop_conn_state state;
|
|
||||||
// buffer for reading
|
|
||||||
size_t rbuf_size;
|
|
||||||
size_t rbuf_read;
|
|
||||||
uint8_t rbuf[EVENT_LOOP_BUFFER_SIZE];
|
|
||||||
// buffer for writing
|
|
||||||
size_t wbuf_size;
|
|
||||||
size_t wbuf_sent;
|
|
||||||
uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE];
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize a new event_loop_conn struct
|
|
||||||
*/
|
|
||||||
event_loop_conn *event_loop_conn_init();
|
|
||||||
|
|
||||||
typedef struct event_loop {
|
|
||||||
event_loop_conn **connections;
|
|
||||||
size_t connection_count;
|
|
||||||
} event_loop;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize a new event_loop struct
|
|
||||||
*/
|
|
||||||
event_loop *event_loop_init();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Place a new connection into the event loop's internal array.
|
|
||||||
*
|
|
||||||
* Returns -1 if the internal realloc failed
|
|
||||||
*/
|
|
||||||
int event_loop_put(event_loop *loop, event_loop_conn *conn);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accept a new connection for the given file descriptor.
|
|
||||||
*/
|
|
||||||
int event_loop_accept(event_loop *loop, int fd);
|
|
||||||
|
|
||||||
void event_loop_conn_io(event_loop_conn *conn);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -7,25 +7,19 @@
|
||||||
http_parse_error http_parse_request(http_request *req, const char *buf,
|
http_parse_error http_parse_request(http_request *req, const char *buf,
|
||||||
size_t buf_size) {
|
size_t buf_size) {
|
||||||
// First we try to parse the incoming HTTP request
|
// First we try to parse the incoming HTTP request
|
||||||
const char *method, *path;
|
size_t num_headers = HTTP_MAX_ALLOWED_HEADERS;
|
||||||
struct phr_header headers[16];
|
|
||||||
size_t method_len, path_len, num_headers;
|
|
||||||
int minor_version;
|
|
||||||
|
|
||||||
int res =
|
int res = phr_parse_request(buf, buf_size, &req->method, &req->method_len,
|
||||||
phr_parse_request(buf, buf_size, &method, &method_len, &path, &path_len,
|
&req->path, &req->path_len, &req->minor_version,
|
||||||
&minor_version, headers, &num_headers, 0);
|
req->headers, &num_headers, 0);
|
||||||
|
|
||||||
if (res == -2) {
|
if (res == -1) {
|
||||||
|
return http_parse_error_invalid;
|
||||||
|
} else if (res == -2) {
|
||||||
return http_parse_error_incomplete;
|
return http_parse_error_incomplete;
|
||||||
} else if (res < 0) {
|
|
||||||
return http_parse_error_invalid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, we parse the HTTP request as a lander-specific request
|
req->len = res;
|
||||||
if (path_len == 0 || path[0] != '/') {
|
|
||||||
return http_parse_error_invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return http_parse_error_ok;
|
return http_parse_error_ok;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "event_loop.h"
|
||||||
|
|
||||||
|
typedef void (*routing_func)(event_loop_conn *);
|
||||||
|
|
||||||
|
routing_func http_route();
|
Loading…
Reference in New Issue