mirror of
https://github.com/sigmasternchen/CShore
synced 2025-03-15 08:08:56 +00:00
basic controller works
This commit is contained in:
commit
49be5521af
13 changed files with 408 additions and 0 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "CFloor"]
|
||||
path = CFloor
|
||||
url = git@github.com:overflowerror/CFloor.git
|
1
CFloor
Submodule
1
CFloor
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 67d398dd981c9ab3ed56a09c88e5fce63607b669
|
21
Makefile
Normal file
21
Makefile
Normal 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
10
common.h
Normal 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
15
controller.h
Normal 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
1
foobar.txt
Normal file
|
@ -0,0 +1 @@
|
|||
This seems to work.
|
8
handler.h
Normal file
8
handler.h
Normal 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
91
main.c
Normal 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
85
request.c
Normal 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
31
request.h
Normal 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
114
router.c
Normal 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
12
router.h
Normal 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
16
test.c
Normal 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");
|
||||
}
|
Loading…
Reference in a new issue