commit 3aba4217e755998f4246325fdf0590535fd80f59
Author: Yersa Nordman <yersa@finwo.nl>
Date: Sun, 21 May 2023 23:22:08 +0200
Initial setup, responses can be sent
Diffstat:
6 files changed, 230 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+/lib/
+*.o
+/http-server
diff --git a/Makefile b/Makefile
@@ -0,0 +1,31 @@
+BIN:=http-server
+
+CPP=g++
+CC=gcc
+
+LIBS:=
+SRC:=
+
+SRC+=src/http-server.c
+SRC+=example.c
+
+override CFLAGS?=-Wall -s -O2
+
+INCLUDES:=
+INCLUDES+=-I src
+
+include lib/.dep/config.mk
+
+OBJ:=$(SRC:.c=.o)
+OBJ:=$(OBJ:.cc=.o)
+
+override CFLAGS+=$(INCLUDES)
+
+default: $(BIN)
+
+.PHONY: clean
+clean:
+ rm -rf $(OBJ)
+
+$(BIN): $(OBJ)
+ $(CPP) $(LDFLAGS) $(OBJ) -o $@
diff --git a/example.c b/example.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "finwo/http-parser.h"
+
+#include "http-server.h"
+
+void onServing(const char **addrs, int naddrs, void *udata) {
+ for (int i = 0; i < naddrs; i++) {
+ printf("Serving at %s\n", addrs[i]);
+ }
+}
+
+void route_get_hello(struct hs_udata *reqdata) {
+ http_parser_header_set(reqdata->reqres->response, "Content-Type", "text/plain");
+ reqdata->reqres->response->body = strdup("Hello World!!");
+ reqdata->reqres->response->bodysize = strlen(reqdata->reqres->response->body);
+ http_server_response_send(reqdata, true);
+ return;
+}
+
+int main() {
+ const char *addrs[] = { "tcp://localhost:4000" };
+
+ struct http_server_events evs = {
+ .tick = NULL,
+ .serving = onServing,
+ .error = NULL,
+ .close = NULL,
+ };
+
+ http_server_route("GET", "/hello", route_get_hello);
+ http_server_main(addrs, sizeof(addrs) / sizeof(void*), &evs, NULL);
+}
diff --git a/package.ini b/package.ini
@@ -0,0 +1,6 @@
+[dependencies]
+finwo/http-parser=https://github.com/finwo/http-parser/archive/refs/tags/edge.tar.gz
+tidwall/evio=https://raw.githubusercontent.com/finwo/dep-repository/main/tidwall/evio/package.ini
+[package]
+deps=lib
+name=finwo/http-parser
diff --git a/src/http-server.c b/src/http-server.c
@@ -0,0 +1,133 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "finwo/http-parser.h"
+#include "tidwall/evio.h"
+
+#include "http-server.h"
+
+struct evio_udata {
+ struct http_server_events *hsevs;
+ void *udata;
+};
+
+struct hs_route {
+ void *next;
+ char *method;
+ char *path;
+ void (*fn)(struct hs_udata*);
+};
+
+struct hs_route *registered_routes = NULL;
+
+void _hs_onServing(const char **addrs, int naddrs, void *udata) {
+ struct evio_udata *info = udata;
+ if (info->hsevs->serving) {
+ info->hsevs->serving(addrs, naddrs, info->udata);
+ }
+}
+
+void _hs_onError(const char *message, bool fatal, void *udata) {
+ struct evio_udata *info = udata;
+ if (info->hsevs->error) {
+ info->hsevs->error(message, fatal, info->udata);
+ }
+}
+
+int64_t _hs_onTick(void *udata) {
+ struct evio_udata *info = udata;
+ if (info->hsevs->tick) {
+ return info->hsevs->tick(info->udata);
+ }
+ return 1e9; // 1 second
+}
+
+static void _hs_onRequest(struct http_parser_event *ev) {
+ struct hs_udata *hsdata = ev->udata;
+ struct hs_route *route = registered_routes;
+ struct hs_route *selected_route = NULL;
+
+ // Method/path matching, should be more intricate later
+ while(route) {
+ if (
+ (!strcmp(ev->request->method, route->method)) &&
+ (!strcmp(ev->request->path , route->path ))
+ ) {
+ selected_route = route;
+ }
+ route = route->next;
+ }
+
+ // No 404 handler (yet)
+ if (!selected_route) {
+ evio_conn_close(hsdata->connection);
+ return;
+ }
+
+ // Call the route handler
+ selected_route->fn(hsdata);
+}
+
+
+void _hs_onOpen(struct evio_conn *conn, void *udata) {
+ struct evio_udata *info = udata;
+ struct hs_udata *hsdata = malloc(sizeof(struct hs_udata));
+ hsdata->connection = conn;
+ hsdata->reqres = http_parser_pair_init(hsdata);
+ hsdata->reqres->onRequest = _hs_onRequest;
+ evio_conn_set_udata(conn, hsdata);
+}
+
+void _hs_onClose(struct evio_conn *conn, void *udata) {
+ struct evio_udata *info = udata;
+ struct hs_udata *hsdata = evio_conn_udata(conn);
+
+ if (info->hsevs->close) {
+ info->hsevs->close(hsdata, info->udata);
+ }
+
+ http_parser_pair_free(hsdata->reqres);
+ free(hsdata);
+}
+
+void _hs_onData(struct evio_conn *conn, const void *data, size_t len, void *udata) {
+ struct hs_udata *hsdata = evio_conn_udata(conn);
+ http_parser_pair_request_data(hsdata->reqres, data, len);
+}
+
+void http_server_response_send(struct hs_udata *hsdata, bool close) {
+ char *response_buffer = http_parser_sprint_response(hsdata->reqres->response);
+ evio_conn_write(hsdata->connection, response_buffer, strlen(response_buffer));
+ free(response_buffer);
+ if (close) {
+ evio_conn_close(hsdata->connection);
+ }
+}
+
+void http_server_route(char *method, char *path, void (*fn)(struct hs_udata*)) {
+ struct hs_route *route = calloc(1, sizeof(struct hs_route));
+ route->next = registered_routes;
+ route->method = method;
+ route->path = path;
+ route->fn = fn;
+ registered_routes = route;
+}
+
+void http_server_main(char **addrs, int naddrs, struct http_server_events *hsevs, void *udata) {
+ struct evio_udata *info = calloc(1, sizeof(struct evio_udata));
+ info->hsevs = hsevs;
+ info->udata = udata;
+
+ struct evio_events evs = {
+ .serving = _hs_onServing,
+ .error = _hs_onError,
+ .tick = _hs_onTick,
+ .opened = _hs_onOpen,
+ .closed = _hs_onClose,
+ .data = _hs_onData,
+ };
+
+ // This is a forever function
+ evio_main(addrs, naddrs, evs, info);
+}
diff --git a/src/http-server.h b/src/http-server.h
@@ -0,0 +1,23 @@
+#ifndef __FINWO_HTTP_SERVER_H__
+#define __FINWO_HTTP_SERVER_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct hs_udata {
+ struct evio_conn *connection; // From the underlaying connection library
+ struct http_parser_pair *reqres; // The request/response pair
+};
+
+struct http_server_events {
+ int64_t (*tick)(void *udata);
+ void (*serving)(const char **addrs, int naddrs, void *udata);
+ void (*error)(const char *message, bool fatal, void *udata);
+ void (*close)(struct hs_udata *conn, void *udata);
+};
+
+void http_server_response_send(struct hs_udata *hsdata, bool close);
+void http_server_route(char *method, char *path, void (*fn)(struct hs_udata*));
+void http_server_main(char **addrs, int naddrs, struct http_server_events *hsevs, void *udata);
+
+#endif // __FINWO_HTTP_SERVER_H__