Remove router
This commit is contained in:
-447
@@ -1,447 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "router.h"
|
||||
#endif
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
bool is_alpha(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
bool is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
#endif // HTTP_AMALGAMATION
|
||||
|
||||
typedef enum {
|
||||
ROUTE_STATIC_DIR,
|
||||
ROUTE_DYNAMIC,
|
||||
} RouteType;
|
||||
|
||||
typedef struct {
|
||||
RouteType type;
|
||||
HTTP_String endpoint;
|
||||
HTTP_String path;
|
||||
HTTP_RouterFunc func;
|
||||
void *ptr;
|
||||
} Route;
|
||||
|
||||
struct HTTP_Router {
|
||||
int num_routes;
|
||||
int max_routes;
|
||||
Route routes[];
|
||||
};
|
||||
|
||||
HTTP_Router *http_router_init(void)
|
||||
{
|
||||
int max_routes = 32;
|
||||
HTTP_Router *router = malloc(sizeof(HTTP_Router) + max_routes * sizeof(Route));
|
||||
if (router == NULL)
|
||||
return NULL;
|
||||
router->max_routes = max_routes;
|
||||
router->num_routes = 0;
|
||||
return router;
|
||||
}
|
||||
|
||||
void http_router_free(HTTP_Router *router)
|
||||
{
|
||||
free(router);
|
||||
}
|
||||
|
||||
void http_router_dir(HTTP_Router *router, HTTP_String endpoint, HTTP_String path)
|
||||
{
|
||||
if (router->num_routes == router->max_routes)
|
||||
abort();
|
||||
Route *route = &router->routes[router->num_routes++];
|
||||
route->type = ROUTE_STATIC_DIR;
|
||||
route->endpoint = endpoint;
|
||||
route->path = path;
|
||||
}
|
||||
|
||||
void http_router_func(HTTP_Router *router, HTTP_Method method,
|
||||
HTTP_String endpoint, HTTP_RouterFunc func, void *ptr)
|
||||
{
|
||||
if (router->num_routes == router->max_routes)
|
||||
abort();
|
||||
Route *route = &router->routes[router->num_routes++];
|
||||
(void) method; // TODO: Don't ignore the method
|
||||
route->type = ROUTE_DYNAMIC;
|
||||
route->endpoint = endpoint;
|
||||
route->func = func;
|
||||
route->ptr = ptr;
|
||||
}
|
||||
|
||||
static int valid_component_char(char c)
|
||||
{
|
||||
return is_alpha(c) || is_digit(c) || c == '-' || c == '_' || c == '.'; // TODO
|
||||
}
|
||||
|
||||
static int parse_and_sanitize_path(HTTP_String path, HTTP_String *comps, int max_comps)
|
||||
{
|
||||
// We treat relative and absolute paths the same
|
||||
if (path.len > 0 && path.ptr[0] == '/') {
|
||||
path.ptr++;
|
||||
path.len--;
|
||||
if (path.len == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int num = 0;
|
||||
int cur = 0;
|
||||
for (;;) {
|
||||
if (cur == path.len || !valid_component_char(path.ptr[cur]))
|
||||
return -1; // Empty component
|
||||
int start = cur;
|
||||
do
|
||||
cur++;
|
||||
while (cur < path.len && valid_component_char(path.ptr[cur]));
|
||||
HTTP_String comp = { path.ptr + start, cur - start };
|
||||
|
||||
if (http_streq(comp, HTTP_STR(".."))) {
|
||||
if (num == 0)
|
||||
return -1;
|
||||
num--;
|
||||
} else if (!http_streq(comp, HTTP_STR("."))) {
|
||||
if (num == max_comps)
|
||||
return -1;
|
||||
comps[num++] = comp;
|
||||
}
|
||||
|
||||
if (cur < path.len) {
|
||||
if (path.ptr[cur] != '/')
|
||||
return -1;
|
||||
cur++;
|
||||
}
|
||||
|
||||
if (cur == path.len)
|
||||
break;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static int
|
||||
serialize_parsed_path(HTTP_String *comps, int num_comps, char *dst, int max)
|
||||
{
|
||||
int len = 0;
|
||||
for (int i = 0; i < num_comps; i++)
|
||||
len += comps[i].len + 1;
|
||||
|
||||
if (len >= max)
|
||||
return -1;
|
||||
|
||||
int copied = 0;
|
||||
for (int i = 0; i < num_comps; i++) {
|
||||
|
||||
if (i > 0)
|
||||
dst[copied++] = '/';
|
||||
|
||||
memcpy(dst + copied,
|
||||
comps[i].ptr,
|
||||
comps[i].len);
|
||||
|
||||
copied += comps[i].len;
|
||||
}
|
||||
|
||||
dst[copied] = '\0';
|
||||
return copied;
|
||||
}
|
||||
|
||||
#define MAX_COMPS 32
|
||||
|
||||
static int sanitize_path(HTTP_String path, char *dst, int max)
|
||||
{
|
||||
HTTP_String comps[MAX_COMPS];
|
||||
int num_comps = parse_and_sanitize_path(path, comps, MAX_COMPS);
|
||||
if (num_comps < 0) return -1;
|
||||
|
||||
return serialize_parsed_path(comps, num_comps, dst, max);
|
||||
}
|
||||
|
||||
static int swap_parents(HTTP_String original_parent_path, HTTP_String new_parent_path, HTTP_String path, char *mem, int max)
|
||||
{
|
||||
int num_original_parent_path_comps;
|
||||
HTTP_String original_parent_path_comps[MAX_COMPS];
|
||||
|
||||
int num_new_parent_path_comps;
|
||||
HTTP_String new_parent_path_comps[MAX_COMPS];
|
||||
|
||||
int num_path_comps;
|
||||
HTTP_String path_comps[MAX_COMPS];
|
||||
|
||||
num_original_parent_path_comps = parse_and_sanitize_path(original_parent_path, original_parent_path_comps, MAX_COMPS);
|
||||
num_new_parent_path_comps = parse_and_sanitize_path(new_parent_path, new_parent_path_comps, MAX_COMPS);
|
||||
num_path_comps = parse_and_sanitize_path(path, path_comps, MAX_COMPS);
|
||||
if (num_original_parent_path_comps < 0 || num_new_parent_path_comps < 0 || num_path_comps < 0)
|
||||
return -1;
|
||||
|
||||
int match = 1;
|
||||
if (num_path_comps < num_original_parent_path_comps)
|
||||
match = 0;
|
||||
else {
|
||||
for (int i = 0; i < num_original_parent_path_comps; i++)
|
||||
if (!http_streq(original_parent_path_comps[i], path_comps[i])) {
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
return 0;
|
||||
|
||||
int num_result_comps = num_new_parent_path_comps + num_path_comps - num_original_parent_path_comps;
|
||||
if (num_result_comps < 0 || num_result_comps > MAX_COMPS)
|
||||
return -1;
|
||||
|
||||
HTTP_String result_comps[MAX_COMPS];
|
||||
for (int i = 0; i < num_new_parent_path_comps; i++)
|
||||
result_comps[i] = new_parent_path_comps[i];
|
||||
|
||||
for (int i = 0; i < num_path_comps; i++)
|
||||
result_comps[num_new_parent_path_comps + i] = path_comps[num_original_parent_path_comps + i];
|
||||
|
||||
return serialize_parsed_path(result_comps, num_result_comps, mem, max);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
typedef HANDLE File;
|
||||
#else
|
||||
typedef int File;
|
||||
#endif
|
||||
|
||||
static int file_open(const char *path, File *handle, int *size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
*handle = CreateFileA(
|
||||
path,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL
|
||||
);
|
||||
if (*handle == INVALID_HANDLE_VALUE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_FILE_NOT_FOUND)
|
||||
return 1;
|
||||
if (error == ERROR_ACCESS_DENIED)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
LARGE_INTEGER fileSize;
|
||||
if (!GetFileSizeEx(*handle, &fileSize)) {
|
||||
CloseHandle(*handle);
|
||||
return -1;
|
||||
}
|
||||
if (fileSize.QuadPart > INT_MAX) {
|
||||
CloseHandle(*handle);
|
||||
return -1;
|
||||
}
|
||||
*size = (int) fileSize.QuadPart;
|
||||
return 0;
|
||||
#else
|
||||
*handle = open(path, O_RDONLY);
|
||||
if (*handle < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
struct stat info;
|
||||
if (fstat(*handle, &info) < 0) {
|
||||
close(*handle);
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR(info.st_mode)) {
|
||||
close(*handle);
|
||||
return 1;
|
||||
}
|
||||
if (info.st_size > INT_MAX) {
|
||||
close(*handle);
|
||||
return -1;
|
||||
}
|
||||
*size = (int) info.st_size;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void file_close(File file)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CloseHandle(file);
|
||||
#else
|
||||
close(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int file_read(File file, char *dst, int max)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD num;
|
||||
BOOL ok = ReadFile(file, dst, max, &num, NULL);
|
||||
if (!ok)
|
||||
return -1;
|
||||
return (int) num;
|
||||
#else
|
||||
return read(file, dst, max);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int serve_file_or_index(HTTP_ResponseBuilder res, HTTP_String base_endpoint, HTTP_String base_path, HTTP_String endpoint)
|
||||
{
|
||||
char mem[1<<12];
|
||||
int ret = swap_parents(base_endpoint, base_path, endpoint, mem, sizeof(mem));
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
HTTP_String path = {mem, ret}; // Note that this is zero terminated
|
||||
|
||||
int size;
|
||||
File file;
|
||||
ret = file_open(path.ptr, &file, &size);
|
||||
if (ret == -1) {
|
||||
http_response_builder_status(res, 500);
|
||||
http_response_builder_done(res);
|
||||
return 1;
|
||||
}
|
||||
if (ret == 1) {
|
||||
|
||||
// File missing
|
||||
|
||||
char index[] = "index.html";
|
||||
if (path.len + sizeof(index) + 1 > sizeof(mem)) {
|
||||
http_response_builder_status(res, 500);
|
||||
http_response_builder_done(res);
|
||||
return 1;
|
||||
}
|
||||
path.ptr[path.len++] = '/';
|
||||
memcpy(path.ptr + path.len, index, sizeof(index));
|
||||
path.len += sizeof(index)-1;
|
||||
|
||||
ret = file_open(path.ptr, &file, &size);
|
||||
if (ret == -1) {
|
||||
http_response_builder_status(res, 500);
|
||||
http_response_builder_done(res);
|
||||
return 1;
|
||||
}
|
||||
if (ret == 1)
|
||||
return 0; // File missing
|
||||
}
|
||||
HTTP_ASSERT(ret == 0);
|
||||
|
||||
int cap;
|
||||
char *dst;
|
||||
http_response_builder_status(res, 200);
|
||||
http_response_builder_bodycap(res, size);
|
||||
dst = http_response_builder_bodybuf(res, &cap);
|
||||
if (dst) {
|
||||
int copied = 0;
|
||||
while (copied < size) {
|
||||
int ret = file_read(file, dst + copied, size - copied);
|
||||
if (ret < 0) goto err;
|
||||
if (ret == 0) break;
|
||||
copied += ret;
|
||||
}
|
||||
if (copied < size) goto err;
|
||||
http_response_builder_bodyack(res, size);
|
||||
}
|
||||
http_response_builder_done(res);
|
||||
file_close(file);
|
||||
return 1;
|
||||
err:
|
||||
http_response_builder_bodyack(res, 0);
|
||||
http_response_builder_undo(res);
|
||||
http_response_builder_status(res, 500);
|
||||
http_response_builder_done(res);
|
||||
file_close(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int serve_dynamic_route(Route *route, HTTP_Request *req, HTTP_ResponseBuilder res)
|
||||
{
|
||||
char path_mem[1<<12];
|
||||
int path_len = sanitize_path(req->url.path, path_mem, (int) sizeof(path_mem));
|
||||
if (path_len < 0) {
|
||||
http_response_builder_status(res, 400);
|
||||
http_response_builder_body(res, HTTP_STR("Invalid path"));
|
||||
http_response_builder_done(res);
|
||||
return 1;
|
||||
}
|
||||
HTTP_String path = {path_mem, path_len};
|
||||
|
||||
if (!http_streq(path, route->endpoint))
|
||||
return 0;
|
||||
|
||||
route->func(req, res, route->ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void http_router_resolve(HTTP_Router *router, HTTP_Request *req, HTTP_ResponseBuilder res)
|
||||
{
|
||||
for (int i = 0; i < router->num_routes; i++) {
|
||||
Route *route = &router->routes[i];
|
||||
switch (route->type) {
|
||||
case ROUTE_STATIC_DIR:
|
||||
if (serve_file_or_index(res,
|
||||
route->endpoint,
|
||||
route->path,
|
||||
req->url.path))
|
||||
return;
|
||||
break;
|
||||
|
||||
case ROUTE_DYNAMIC:
|
||||
if (serve_dynamic_route(route, req, res))
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
http_response_builder_status(res, 500);
|
||||
http_response_builder_done(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
http_response_builder_status(res, 404);
|
||||
http_response_builder_done(res);
|
||||
}
|
||||
|
||||
int http_serve(char *addr, int port, HTTP_Router *router)
|
||||
{
|
||||
int ret;
|
||||
|
||||
HTTP_Server *server = http_server_init_ex((HTTP_String) { addr, strlen(addr) }, port, 0, (HTTP_String) {}, (HTTP_String) {});
|
||||
if (server == NULL) {
|
||||
http_router_free(router);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
HTTP_Request *req;
|
||||
HTTP_ResponseBuilder res;
|
||||
ret = http_server_wait(server, &req, &res);
|
||||
if (ret < 0) {
|
||||
http_server_free(server);
|
||||
http_router_free(router);
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0)
|
||||
continue;
|
||||
http_router_resolve(router, req, res);
|
||||
}
|
||||
|
||||
http_server_free(server);
|
||||
http_router_free(router);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef HTTP_ROUTER_INCLUDED
|
||||
#define HTTP_ROUTER_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "server.h"
|
||||
#endif
|
||||
|
||||
typedef struct HTTP_Router HTTP_Router;
|
||||
typedef void (*HTTP_RouterFunc)(HTTP_Request*, HTTP_ResponseBuilder, void*);;
|
||||
|
||||
HTTP_Router* http_router_init (void);
|
||||
void http_router_free (HTTP_Router *router);
|
||||
void http_router_resolve (HTTP_Router *router, HTTP_Request *req, HTTP_ResponseBuilder res);
|
||||
void http_router_dir (HTTP_Router *router, HTTP_String endpoint, HTTP_String path);
|
||||
void http_router_func (HTTP_Router *router, HTTP_Method method, HTTP_String endpoint, HTTP_RouterFunc func, void*);
|
||||
int http_serve (char *addr, int port, HTTP_Router *router);
|
||||
|
||||
#endif // HTTP_ROUTER_INCLUDED
|
||||
+6
-6
@@ -105,12 +105,12 @@ int http_server_wait(HTTP_Server *server, HTTP_Request **req, HTTP_ResponseBuild
|
||||
case SOCKET_EVENT_DIED:
|
||||
{
|
||||
Connection *conn = event.user_data;
|
||||
HTTP_ASSERT(conn);
|
||||
|
||||
http_engine_free(&conn->engine);
|
||||
conn->used = false;
|
||||
conn->gen++;
|
||||
server->num_conns--;
|
||||
if (conn) {
|
||||
http_engine_free(&conn->engine);
|
||||
conn->used = false;
|
||||
conn->gen++;
|
||||
server->num_conns--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user