basic controller works

This commit is contained in:
overflowerror 2021-04-29 18:40:41 +02:00
commit 49be5521af
13 changed files with 408 additions and 0 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "CFloor"]
path = CFloor
url = git@github.com:overflowerror/CFloor.git

1
CFloor Submodule

@ -0,0 +1 @@
Subproject commit 67d398dd981c9ab3ed56a09c88e5fce63607b669

21
Makefile Normal file
View file

@ -0,0 +1,21 @@
CC = gcc
LD = gcc
CFLAGS = -Wall -g -std=c99 -ICFloor/src/ -D_POSIX_SOURCE
LDFLAGS = -lpthread -lrt
test: main.o router.o request.o test.o CFloor/libcfloor.a
$(LD) $(LDFLAGS) -o $@ $^
CFloor/libcfloor.a:
$(MAKE) -C CFloor/ libcfloor.a
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
#main.o: main.c router.h
#router.o: router.c router.h
#test.o: controller.h
clean:
rm -f *.o test
$(MAKE) -C CFloor/ clean

10
common.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef MISC_H_
#define MISC_H_
#include <misc.h>
typedef enum method method_t;
#define HTTP_GET (GET)
#endif

15
controller.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <stdio.h>
#include "handler.h"
#include "router.h"
#include "common.h"
#define _CAT(a, b, c, d) a ## b ## c ## d
#define _CAT2(a, b, c, d) _CAT(a, b, c, d)
#define GET(p, f) handle_t f; __attribute__((constructor)) static void _CAT2(_register_route_, f, _, __LINE__) () { if (registerRoute(HTTP_GET, p, &f) < 0) { fprintf(stderr, "ERROR: couldn't add route %s %s (%s): already registered\n", "GET", p, #f); }; }
#endif

1
foobar.txt Normal file
View file

@ -0,0 +1 @@
This seems to work.

8
handler.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef HANDLER_H
#define HANDLER_H
#include "request.h"
typedef response_t (handle_t)(ctx_t);
#endif

91
main.c Normal file
View file

@ -0,0 +1,91 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <networking.h>
#include <logging.h>
#include "router.h"
struct networkingConfig netConfig;
static void handler(struct request request, struct response _response) {
method_t method = request.metaData.method;
const char* path = request.metaData.path;
const char* queryString = request.metaData.queryString;
//const char* peer = request.peer.addr;
//int port = request.peer.port;
ctx_t ctx = {
method: method,
path: path,
queryString: queryString
};
response_t response = routerHandler(ctx);
if (response.output == NULL) {
response.status = 500;
response.headers = headers_create();
}
int fd = _response.sendHeader(response.status, &response.headers, &request);
headers_free(&response.headers);
if (fd < 0) {
error("csite: sendHeader: %s", strerror(errno));
return;
}
FILE* out = fdopen(fd, "w");
if (out == NULL) {
error("csite: fdopen: %s", strerror(errno));
return;
}
if (response.output == NULL) {
fprintf(out, "Internal Server Error\n");
} else {
response.output(out, response._userData);
}
fclose(out);
}
static struct handler handlerGetter(struct metaData metaData, const char* host, struct bind* bind) {
return (struct handler) {
handler: &handler
};
}
int main() {
struct bind bind = {
address: "0.0.0.0",
port: "1337",
ssl: false
};
struct headers headers = headers_create();
headers_mod(&headers, "Server", "CSite 0.1 on CFloor");
netConfig = (struct networkingConfig) {
binds: {
number: 1,
binds: &bind
},
connectionTimeout: DEFAULT_CONNECTION_TIMEOUT,
maxConnections: DEFAULT_MAX_CONNECTIONS,
defaultHeaders: headers,
getHandler: handlerGetter
};
setLogging(stdout, WARN, true);
//setLogging(stdout, HTTP_ACCESS, false);
networking_init(netConfig);
while (true) {
sleep(0xffff);
}
return 0;
}

85
request.c Normal file
View file

@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <alloca.h>
#include <sys/stat.h>
#include <headers.h>
#include <util.h>
#include <mime.h>
#include "request.h"
response_t emptyResponse() {
return (response_t) {
status: 500,
headers: headers_create(),
output: NULL
};
}
void _rawOutput(FILE* out, void* _userData) {
fprintf(out, "%s", (const char*) _userData);
}
response_t rawResponse(int status, const char* txt) {
response_t response = emptyResponse();
response.status = status;
size_t size = strlen(txt);
int length = strlenOfNumber(size);
char* tmp = alloca(length + 1);
snprintf(tmp, length + 1, "%ld", size);
headers_mod(&response.headers, "Content-Length", tmp);
response._userData = (void*) txt;
response.output = _rawOutput;
return response;
}
void _fileOutput(FILE* out, void* _userData) {
FILE* in = (FILE*) _userData;
#define READ_BUFFER_SIZE (1024)
char buffer[READ_BUFFER_SIZE];
while((fgets(buffer, READ_BUFFER_SIZE, in)) != NULL) {
fputs(buffer, out);
}
fclose(in);
}
response_t fileResponse(const char* file) {
response_t response = emptyResponse();
struct stat statObj;
if (stat(file, &statObj) < 0) {
return rawResponse(500, strerror(errno));
}
if (!S_ISREG(statObj.st_mode)) {
return rawResponse(500, "not a file");
}
FILE* stream = fopen(file, "r");
if (stream == NULL) {
return rawResponse(500, "");
}
int length = strlenOfNumber(statObj.st_size);
char* tmp = alloca(length + 1);
snprintf(tmp, length + 1, "%ld", statObj.st_size);
headers_mod(&response.headers, "Content-Type", getMineFromFileName(file));
headers_mod(&response.headers, "Content-Length", tmp);
response.status = 200;
response._userData = stream;
response.output = _fileOutput;
return response;
}

31
request.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef REQUEST_H
#define REQUEST_H
#include <stdio.h>
#include <stdbool.h>
#include <headers.h>
#include "common.h"
typedef struct {
method_t method;
const char* path;
const char* queryString;
} ctx_t;
typedef struct {
int status;
struct headers headers;
void* _userData;
void (*output) (FILE* conenction, void* _userData);
} response_t;
response_t emptyResponse();
response_t rawResponse(int status, const char* txt);
response_t fileResponse(const char* file);
#endif

114
router.c Normal file
View file

@ -0,0 +1,114 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "router.h"
#define MAX_ROUTES (128)
struct route {
method_t method;
const char* path;
handle_t* handle;
} routes[MAX_ROUTES];
int n = 0;
bool pathMatch(const char* routePath, const char* path) {
size_t routePathLength = strlen(routePath);
size_t pathLength = strlen(path);
bool matches = true;
// need to go 1 char further for the path length, because of wildcards
// shouldn't break anything as there has to be a 0-byte anyway
for(size_t i = 0, j = 0; i < routePathLength && j < pathLength + 1; i++, j++) {
if (routePath[i] == '*') {
if (routePath[i + 1] == '*') {
break;
}
if (path[j] == '/') {
if (routePath[i + 1] == '/') {
// continue without i--; match next path char against *
} else {
// no match
matches = false;
break;
}
} else {
// matches this char in path, try next
i--;
}
continue;
}
if (routePath[i] == path[j]) {
// matches; go to next
} else {
// no match
matches = false;
break;
}
if (j == pathLength - 1 && i == routePathLength - 1) {
// both path are finished with no conflict
break;
}
if ((j == pathLength) != (i == routePathLength - 1)) {
// path length is exhausted but not the route path
// or route path is exhausted but not the path
// we are not in a wildcard
// => no match
matches = false;
break;
}
}
return matches;
}
struct route* findRoute(method_t method, const char* path) {
for (int i = 0; i < n; i++) {
if (routes[i].method == method && pathMatch(routes[i].path, path)) {
return &routes[i];
}
}
return NULL;
}
struct route* reverseFindRoute(method_t method, const char* path) {
for (int i = 0; i < n; i++) {
if (routes[i].method == method && pathMatch(path, routes[i].path)) {
return &routes[i];
}
}
return NULL;
}
int registerRoute(method_t method, const char* path, handle_t* handle) {
if (n >= MAX_ROUTES) {
return -1;
}
if (findRoute(method, path) != NULL || reverseFindRoute(method, path) != NULL) {
return -2;
}
routes[n++] = (struct route) {
method: method,
path: path,
handle: handle
};
return 0;
}
response_t routerHandler(ctx_t ctx) {
struct route* route = findRoute(ctx.method, ctx.path);
if (route == NULL) {
return rawResponse(404, "Not Found\n");
}
return route->handle(ctx);
}

12
router.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef ROUTER_H
#define ROUTER_H
#include <misc.h>
#include "handler.h"
int registerRoute(method_t method, const char* path, handle_t handle);
response_t routerHandler(ctx_t ctx);
#endif

16
test.c Normal file
View file

@ -0,0 +1,16 @@
#include <stdlib.h>
#include <stdio.h>
#include "controller.h"
GET("/", hello);
GET("/index.*", hello);
response_t hello(ctx_t ctx) {
return rawResponse(200, "Hello World\n");
}
GET("/foobar", foobar);
response_t foobar(ctx_t ctx) {
return fileResponse("foobar.txt");
}