mirror of
https://github.com/sigmasternchen/CShore
synced 2025-03-15 08:08:56 +00:00
Merge pull request #1 from overflowerror/devel
Update current state on main
This commit is contained in:
commit
7b6e4ce8a3
16 changed files with 548 additions and 37 deletions
4
Makefile
4
Makefile
|
@ -1,14 +1,14 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
LD = gcc
|
LD = gcc
|
||||||
CFLAGS = -Wall -g -std=c99 -ICFloor/src/ -Ilibargo/src/ -Ilibparcival/src/ -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500
|
CFLAGS = -Wall -g -std=c99 -ICFloor/src/ -Ilibargo/src/ -Ilibparcival/src/ -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500
|
||||||
LDFLAGS = -lpthread -lrt
|
LDFLAGS = -lpthread -lrt -luuid
|
||||||
|
|
||||||
CFLOOR_LIB = CFloor/libcfloor.a
|
CFLOOR_LIB = CFloor/libcfloor.a
|
||||||
LIBARGO = libargo/libargo.a
|
LIBARGO = libargo/libargo.a
|
||||||
LIBPARCIVAL = libparcival/libparcival.a
|
LIBPARCIVAL = libparcival/libparcival.a
|
||||||
LIBS = $(CFLOOR_LIB) $(LIBARGO) $(LIBPARCIVAL)
|
LIBS = $(CFLOOR_LIB) $(LIBARGO) $(LIBPARCIVAL)
|
||||||
|
|
||||||
OBJS = obj/router.o obj/request.o obj/base_cfloor.o obj/base_cgi.o obj/auth.o obj/base64.o
|
OBJS = obj/router.o obj/request.o obj/base_cfloor.o obj/base_cgi.o obj/auth.o obj/base64.o obj/common.o obj/cookies.o obj/sessions.o
|
||||||
DEPS = $(OBJS:%.o=%.d)
|
DEPS = $(OBJS:%.o=%.d)
|
||||||
|
|
||||||
DEMO_OBJS = obj/demo.o obj/entities.tab.o obj/template.tab.o
|
DEMO_OBJS = obj/demo.o obj/entities.tab.o obj/template.tab.o
|
||||||
|
|
42
demo/demo.c
42
demo/demo.c
|
@ -1,40 +1,44 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <controller.h>
|
#include <controller.h>
|
||||||
|
|
||||||
|
#define SESSION_PTR_TYPE const char*
|
||||||
|
#include <sessions.h>
|
||||||
|
|
||||||
#include "entities.h"
|
#include "entities.h"
|
||||||
|
|
||||||
|
|
||||||
GET("/", hello);
|
GET("/", hello);
|
||||||
GET("/index.*", hello);
|
GET("/index.*", hello);
|
||||||
response_t hello(ctx_t ctx) {
|
response_t hello(ctx_t* ctx) {
|
||||||
return rawResponse(200, "Hello World\n");
|
return rawResponse(200, "Hello World\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
GET("/foobar", foobar);
|
GET("/foobar", foobar);
|
||||||
response_t foobar(ctx_t ctx) {
|
response_t foobar(ctx_t* ctx) {
|
||||||
return fileResponse("demo/foobar.txt");
|
return fileResponse("demo/foobar.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
response_t authenticate(ctx_t ctx) {
|
response_t authenticate(ctx_t* ctx) {
|
||||||
if (ctx.auth.type != BASIC) {
|
if (ctx->auth.type != BASIC) {
|
||||||
return basicAuthResponse(401, "Protected Area");
|
return basicAuthResponse(401, "Protected Area");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(ctx.auth.basic.user, "admin") != 0 ||
|
if (strcmp(ctx->auth.basic.user, "admin") != 0 ||
|
||||||
strcmp(ctx.auth.basic.password, "password") != 0
|
strcmp(ctx->auth.basic.password, "password") != 0
|
||||||
) {
|
) {
|
||||||
// username or password wrong
|
// username or password wrong
|
||||||
return basicAuthResponse(401, "Protected Area");
|
return basicAuthResponse(401, "Protected Area");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
GET("/user", authenticate, user);
|
GET("/user", authenticate, user);
|
||||||
response_t user(ctx_t ctx) {
|
POST("/user", authenticate, user);
|
||||||
|
response_t user(ctx_t* ctx) {
|
||||||
user_t user = {
|
user_t user = {
|
||||||
.username = "overflowerror",
|
.username = "overflowerror",
|
||||||
.github = "https://github.com/overflowerror"
|
.github = "https://github.com/overflowerror"
|
||||||
|
@ -44,6 +48,26 @@ response_t user(ctx_t ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
GET("/template", template);
|
GET("/template", template);
|
||||||
response_t template(ctx_t ctx) {
|
response_t template(ctx_t* ctx) {
|
||||||
return templateResponse(200, "demo.templ", "Page Title", "Overflow");
|
return templateResponse(200, "demo.templ", "Page Title", "Overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET("/sessions", sessions);
|
||||||
|
response_t sessions(ctx_t* ctx) {
|
||||||
|
const char** sessiondata = session_start(ctx);
|
||||||
|
|
||||||
|
const char* output = "null\n";
|
||||||
|
|
||||||
|
if (*sessiondata == NULL) {
|
||||||
|
*sessiondata = "Test\n";
|
||||||
|
session_update(ctx);
|
||||||
|
} else {
|
||||||
|
output = *sessiondata;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "%s, %d\n", output, strlen(output));
|
||||||
|
|
||||||
|
return rawResponse(200, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 30c62e03b86573a3fc5df788bda4bb95d2d89f9b
|
Subproject commit e4a86956046c03055780e98c4e95f352e683d985
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
struct networkingConfig netConfig;
|
struct networkingConfig netConfig;
|
||||||
|
|
||||||
|
void session_end(ctx_t*);
|
||||||
|
|
||||||
static void handler(struct request request, struct response _response) {
|
static void handler(struct request request, struct response _response) {
|
||||||
ctx_t ctx = {
|
ctx_t ctx = {
|
||||||
method: request.metaData.method,
|
method: request.metaData.method,
|
||||||
|
@ -22,18 +24,25 @@ static void handler(struct request request, struct response _response) {
|
||||||
queryString: request.metaData.queryString,
|
queryString: request.metaData.queryString,
|
||||||
peerAddr: request.peer.addr,
|
peerAddr: request.peer.addr,
|
||||||
peerPort: request.peer.port,
|
peerPort: request.peer.port,
|
||||||
auth: getAuthData(request.headers)
|
auth: getAuthData(request.headers),
|
||||||
|
requestHeaders: *request.headers,
|
||||||
|
responseHeaders: headers_create(),
|
||||||
|
session: EMPTY_SESSION_CTX,
|
||||||
};
|
};
|
||||||
|
|
||||||
response_t response = routerHandler(ctx);
|
response_t response = routerHandler(&ctx);
|
||||||
if (response.output == NULL) {
|
if (response.output == NULL) {
|
||||||
response = errorResponse(500, "route did not provide a reponse handler");
|
response = errorResponse(500, "route did not provide a reponse handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
freeAuthData(ctx.auth);
|
freeAuthData(ctx.auth);
|
||||||
|
session_end(&ctx);
|
||||||
|
|
||||||
|
headers_merge(&ctx.responseHeaders, &response.headers);
|
||||||
|
|
||||||
int fd = _response.sendHeader(response.status, &response.headers, &request);
|
int fd = _response.sendHeader(response.status, &ctx.responseHeaders, &request);
|
||||||
headers_free(&response.headers);
|
headers_free(&response.headers);
|
||||||
|
headers_free(&ctx.responseHeaders);
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error("csite: sendHeader: %s", strerror(errno));
|
error("csite: sendHeader: %s", strerror(errno));
|
||||||
|
@ -46,7 +55,7 @@ static void handler(struct request request, struct response _response) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.output(out, response._userData, ctx);
|
response.output(out, response._userData, &ctx);
|
||||||
|
|
||||||
fclose(out);
|
fclose(out);
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,14 +96,14 @@ const char* or(const char* v1, const char* v2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void session_end(ctx_t*);
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
struct headers headers = headers_create();
|
struct headers headers = headers_create();
|
||||||
if (setHttpHeaders(&headers)) {
|
if (setHttpHeaders(&headers)) {
|
||||||
fprintf(stderr, "%s: %s\n", argv[0], strerror(errno));
|
fprintf(stderr, "%s: %s\n", argv[0], strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO use request headers
|
|
||||||
|
|
||||||
ctx_t ctx = {
|
ctx_t ctx = {
|
||||||
method: getMethod(or(getenv("REQUEST_METHOD"), "GET")),
|
method: getMethod(or(getenv("REQUEST_METHOD"), "GET")),
|
||||||
|
@ -111,25 +111,32 @@ int main(int argc, char** argv) {
|
||||||
queryString: or(getenv("QUERY_STRING"), ""),
|
queryString: or(getenv("QUERY_STRING"), ""),
|
||||||
peerAddr: or(getenv("REMOTE_ADDR"), ""),
|
peerAddr: or(getenv("REMOTE_ADDR"), ""),
|
||||||
peerPort: 0, // TODO
|
peerPort: 0, // TODO
|
||||||
auth: getAuthData(request.headers)
|
auth: getAuthData(request.headers),
|
||||||
|
requestHeaders: headers,
|
||||||
|
responseHeaders: headers_create(),
|
||||||
|
session: EMPTY_SESSION_CTX,
|
||||||
};
|
};
|
||||||
|
|
||||||
headers_free(&headers);
|
|
||||||
|
|
||||||
response_t response = routerHandler(ctx);
|
response_t response = routerHandler(&ctx);
|
||||||
if (response.output == NULL) {
|
if (response.output == NULL) {
|
||||||
response = errorResponse(500, "route did not provide a reponse handler");
|
response = errorResponse(500, "route did not provide a reponse handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headers_free(&headers);
|
||||||
|
|
||||||
|
headers_merge(&ctx.responseHeaders, &response.headers);
|
||||||
|
|
||||||
freeAuthData(ctx.auth);
|
freeAuthData(ctx.auth);
|
||||||
|
session_end(&ctx);
|
||||||
|
|
||||||
printf("Status: %d\n\r", response.status);
|
printf("Status: %d\n\r", response.status);
|
||||||
headers_dump(&response.headers, stdout);
|
headers_dump(&ctx.responseHeaders, stdout);
|
||||||
printf("\n\r");
|
printf("\n\r");
|
||||||
|
|
||||||
headers_free(&response.headers);
|
headers_free(&response.headers);
|
||||||
|
headers_free(&ctx.responseHeaders);
|
||||||
|
|
||||||
response.output(stdout, response._userData, ctx);
|
response.output(stdout, response._userData, &ctx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
9
src/common.c
Normal file
9
src/common.c
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include <headers.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void headers_merge(struct headers* dst, struct headers* src) {
|
||||||
|
for (int i = 0; i < src->number; i++) {
|
||||||
|
headers_mod(dst, src->headers[i].key, src->headers[i].value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,4 +15,6 @@ typedef enum method method_t;
|
||||||
#define HTTP_CONNECT (CONNECT)
|
#define HTTP_CONNECT (CONNECT)
|
||||||
#define HTTP_PATCH (PATCH)
|
#define HTTP_PATCH (PATCH)
|
||||||
|
|
||||||
|
void headers_merge(struct headers*, struct headers*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
227
src/cookies.c
Normal file
227
src/cookies.c
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <alloca.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <headers.h>
|
||||||
|
|
||||||
|
#include "cookies.h"
|
||||||
|
|
||||||
|
char* getCookie(ctx_t* ctx, const char* key) {
|
||||||
|
// ignore const
|
||||||
|
char* cookieHeader = (char*) headers_get(&ctx->requestHeaders, "Cookie");
|
||||||
|
if (cookieHeader == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookieHeader = strdup(cookieHeader);
|
||||||
|
if (cookieHeader == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* saveptr = NULL;
|
||||||
|
char* str = cookieHeader;
|
||||||
|
|
||||||
|
char* value = NULL;
|
||||||
|
|
||||||
|
while((str = strtok_r(str, ";", &saveptr)) != NULL) {
|
||||||
|
while(*str == ' ') str++;
|
||||||
|
|
||||||
|
char* keyCandidate = str;
|
||||||
|
|
||||||
|
for (; *str != '='; str++) {
|
||||||
|
if (*str == '\0') {
|
||||||
|
str = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str == NULL) {
|
||||||
|
// illegal cookie definition; ignore
|
||||||
|
// str is already NULL
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
if (strcmp(keyCandidate, key) == 0) {
|
||||||
|
value = str + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != NULL) {
|
||||||
|
value = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cookieHeader);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookieSettings_t cookieSettingsNull() {
|
||||||
|
return (cookieSettings_t) {
|
||||||
|
.expires = COOKIE_NO_EXPIRES,
|
||||||
|
.maxAge = COOKIE_NO_MAX_AGE,
|
||||||
|
.domain = NULL,
|
||||||
|
.path = NULL,
|
||||||
|
.secure = false,
|
||||||
|
.httpOnly = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int setCookie(ctx_t* ctx, const char* name, const char* value, cookieSettings_t settings) {
|
||||||
|
size_t length = 0;
|
||||||
|
length += strlen(name) + 1 + strlen(value);
|
||||||
|
if (settings.expires != COOKIE_NO_EXPIRES) {
|
||||||
|
length += 2;
|
||||||
|
length += strlen("Expires=");
|
||||||
|
length += 3 + 2 + 2 + 1 + 3 + 1 + 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3;
|
||||||
|
}
|
||||||
|
if (settings.maxAge != COOKIE_NO_MAX_AGE) {
|
||||||
|
length += 2;
|
||||||
|
length += strlen("Max-Age=");
|
||||||
|
long tmp = settings.maxAge;
|
||||||
|
if (tmp <= 0) {
|
||||||
|
length++;
|
||||||
|
tmp *= -1;
|
||||||
|
}
|
||||||
|
for (; tmp > 0; tmp /= 10) length++;
|
||||||
|
}
|
||||||
|
if (settings.domain != NULL) {
|
||||||
|
length += 2;
|
||||||
|
length += strlen("Domain=");
|
||||||
|
length += strlen(settings.domain);
|
||||||
|
}
|
||||||
|
if (settings.path != NULL) {
|
||||||
|
length += 2;
|
||||||
|
length += strlen("Path=");
|
||||||
|
length += strlen(settings.path);
|
||||||
|
}
|
||||||
|
if (settings.secure) {
|
||||||
|
length += 2;
|
||||||
|
length += strlen("Secure");
|
||||||
|
}
|
||||||
|
if (settings.httpOnly) {
|
||||||
|
length += 2;
|
||||||
|
length += strlen("HttpOnly");
|
||||||
|
}
|
||||||
|
|
||||||
|
char* buffer = alloca(length + 1);
|
||||||
|
char* bufferPtr = buffer;
|
||||||
|
size_t tmp;
|
||||||
|
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "%s=%s", name, value);
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
|
||||||
|
if (settings.expires != COOKIE_NO_EXPIRES) {
|
||||||
|
struct tm result;
|
||||||
|
gmtime_r(&settings.expires, &result);
|
||||||
|
|
||||||
|
char* weekday;
|
||||||
|
switch(result.tm_wday) {
|
||||||
|
case 0:
|
||||||
|
weekday = "Sun";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
weekday = "Mon";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
weekday = "Tue";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
weekday = "Wed";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
weekday = "Thu";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
weekday = "Fri";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
weekday = "Sat";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
weekday = "err";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* month;
|
||||||
|
switch(result.tm_mon) {
|
||||||
|
case 0:
|
||||||
|
month = "Jan";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
month = "Feb";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
month = "Mar";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
month = "Apr";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
month = "May";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
month = "Jun";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
month = "Jul";
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
month = "Aug";
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
month = "Sep";
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
month = "Oct";
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
month = "Nov";
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
month = "Dec";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
month = "err";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "; Expires=%s, %02d %s %d %02d:%02d:%02d GMT", weekday, result.tm_mday, month, result.tm_year + 1900, result.tm_hour, result.tm_min, result.tm_sec);
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
}
|
||||||
|
if (settings.maxAge != COOKIE_NO_MAX_AGE) {
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "; Max-Age=%ld", settings.maxAge);
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
}
|
||||||
|
if (settings.domain != NULL) {
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "; Domain=%s", settings.domain);
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
}
|
||||||
|
if (settings.path != NULL) {
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "; Path=%s", settings.path);
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
}
|
||||||
|
if (settings.secure) {
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "; Secure");
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
}
|
||||||
|
if (settings.httpOnly) {
|
||||||
|
tmp = snprintf(bufferPtr, length + 1, "; HttpOnly");
|
||||||
|
bufferPtr += tmp;
|
||||||
|
length -= tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
headers_mod(&ctx->responseHeaders, "Set-Cookie", buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
28
src/cookies.h
Normal file
28
src/cookies.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef COOKIES_H_
|
||||||
|
#define COOKIES_H_
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
char* getCookie(ctx_t*, const char*);
|
||||||
|
|
||||||
|
#define COOKIE_NO_EXPIRES (0)
|
||||||
|
#define COOKIE_NO_MAX_AGE (LONG_MIN)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
time_t expires;
|
||||||
|
long maxAge;
|
||||||
|
const char* domain;
|
||||||
|
const char* path;
|
||||||
|
bool secure;
|
||||||
|
bool httpOnly;
|
||||||
|
} cookieSettings_t;
|
||||||
|
|
||||||
|
cookieSettings_t cookieSettingsNull();
|
||||||
|
|
||||||
|
int setCookie(ctx_t*, const char*, const char*, cookieSettings_t);
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
|
|
||||||
typedef response_t (handle_t)(ctx_t);
|
typedef response_t (handle_t)(ctx_t*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,13 +31,13 @@ void setDefaultErrorFormat(errorformat_t format) {
|
||||||
errorformat = format;
|
errorformat = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rawOutputAndFree(FILE* out, void* _userData, ctx_t ctx) {
|
static void rawOutputAndFree(FILE* out, void* _userData, ctx_t* ctx) {
|
||||||
fprintf(out, "%s", (char*) _userData);
|
fprintf(out, "%s", (char*) _userData);
|
||||||
|
|
||||||
free(_userData);
|
free(_userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rawOutput(FILE* out, void* _userData, ctx_t ctx) {
|
static void rawOutput(FILE* out, void* _userData, ctx_t* ctx) {
|
||||||
fprintf(out, "%s", (const char*) _userData);
|
fprintf(out, "%s", (const char*) _userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ struct statusdata {
|
||||||
const char* message;
|
const char* message;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void statusOutput(FILE* out, void* _userData, ctx_t ctx) {
|
static void statusOutput(FILE* out, void* _userData, ctx_t* ctx) {
|
||||||
struct statusdata* data = (struct statusdata*) _userData;
|
struct statusdata* data = (struct statusdata*) _userData;
|
||||||
|
|
||||||
const char* statusString = getStatusStrings(data->status).statusString;
|
const char* statusString = getStatusStrings(data->status).statusString;
|
||||||
|
@ -80,7 +80,7 @@ static void statusOutput(FILE* out, void* _userData, ctx_t ctx) {
|
||||||
"status", json_long(data->status),
|
"status", json_long(data->status),
|
||||||
"error", json_string(statusString),
|
"error", json_string(statusString),
|
||||||
"message", json_string(data->message),
|
"message", json_string(data->message),
|
||||||
"path", json_string(ctx.path)
|
"path", json_string(ctx->path)
|
||||||
);
|
);
|
||||||
free(data);
|
free(data);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
|
@ -126,7 +126,7 @@ response_t errorResponse(int status, const char* message) {
|
||||||
return statusResponse(status, message);
|
return statusResponse(status, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fileOutput(FILE* out, void* _userData, ctx_t ctx) {
|
static void fileOutput(FILE* out, void* _userData, ctx_t* ctx) {
|
||||||
FILE* in = (FILE*) _userData;
|
FILE* in = (FILE*) _userData;
|
||||||
|
|
||||||
#define READ_BUFFER_SIZE (1024)
|
#define READ_BUFFER_SIZE (1024)
|
||||||
|
@ -171,7 +171,7 @@ response_t fileResponse(const char* file) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jsonOutput(FILE* output, void* _userData, ctx_t ctx) {
|
static void jsonOutput(FILE* output, void* _userData, ctx_t* ctx) {
|
||||||
jsonValue_t* json = (jsonValue_t*) _userData;
|
jsonValue_t* json = (jsonValue_t*) _userData;
|
||||||
|
|
||||||
char* result = json_stringify(json);
|
char* result = json_stringify(json);
|
||||||
|
@ -196,8 +196,8 @@ response_t _jsonResponse(int status, const char* type, void* value) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern size_t _sizeTemplate(const char*, ...);
|
extern size_t _sizeTemplate(const char*, va_list);
|
||||||
extern void _renderTemplate(const char*, FILE*, ...);
|
extern void _renderTemplate(const char*, FILE*, va_list);
|
||||||
|
|
||||||
response_t templateResponse(int status, const char* name, ...) {
|
response_t templateResponse(int status, const char* name, ...) {
|
||||||
response_t response = emptyResponse();
|
response_t response = emptyResponse();
|
||||||
|
|
|
@ -11,6 +11,14 @@
|
||||||
|
|
||||||
#define NEXT_RESPONSE_STATUS (0)
|
#define NEXT_RESPONSE_STATUS (0)
|
||||||
|
|
||||||
|
struct session_ctx {
|
||||||
|
void* session;
|
||||||
|
time_t accessTime;
|
||||||
|
void* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EMPTY_SESSION_CTX ((struct session_ctx) {.session = NULL})
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
method_t method;
|
method_t method;
|
||||||
const char* path;
|
const char* path;
|
||||||
|
@ -18,6 +26,9 @@ typedef struct {
|
||||||
const char* peerAddr;
|
const char* peerAddr;
|
||||||
int peerPort;
|
int peerPort;
|
||||||
struct auth auth;
|
struct auth auth;
|
||||||
|
struct headers requestHeaders;
|
||||||
|
struct headers responseHeaders;
|
||||||
|
struct session_ctx session;
|
||||||
} ctx_t;
|
} ctx_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -25,7 +36,7 @@ typedef struct {
|
||||||
struct headers headers;
|
struct headers headers;
|
||||||
|
|
||||||
void* _userData;
|
void* _userData;
|
||||||
void (*output) (FILE* conenction, void* _userData, ctx_t ctx);
|
void (*output) (FILE* conenction, void* _userData, ctx_t* ctx);
|
||||||
} response_t;
|
} response_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -121,8 +121,8 @@ int registerRoute(method_t method, const char* path, ...) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
response_t routerHandler(ctx_t ctx) {
|
response_t routerHandler(ctx_t* ctx) {
|
||||||
struct route* route = findRoute(ctx.method, ctx.path);
|
struct route* route = findRoute(ctx->method, ctx->path);
|
||||||
if (route == NULL) {
|
if (route == NULL) {
|
||||||
return errorResponse(404, "no route found");
|
return errorResponse(404, "no route found");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
|
|
||||||
int registerRoute(method_t method, const char* path, ...);
|
int registerRoute(method_t method, const char* path, ...);
|
||||||
|
|
||||||
response_t routerHandler(ctx_t ctx);
|
response_t routerHandler(ctx_t* ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
161
src/sessions.c
Normal file
161
src/sessions.c
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <headers.h>
|
||||||
|
|
||||||
|
// SESSION_PTR_TYPE doesn't matter for this file
|
||||||
|
#define SESSION_PTR_TYPE int
|
||||||
|
#include "sessions.h"
|
||||||
|
|
||||||
|
#include "cookies.h"
|
||||||
|
|
||||||
|
struct session {
|
||||||
|
bool inUse;
|
||||||
|
uuid_t id;
|
||||||
|
time_t lastAccess;
|
||||||
|
time_t lastWrite;
|
||||||
|
void* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct session* sessions = NULL;
|
||||||
|
static size_t sessionno = 0;
|
||||||
|
static pthread_mutex_t globalLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
#define SESSION_BLOCK_SIZE (128)
|
||||||
|
|
||||||
|
int resizeSessionList() {
|
||||||
|
struct session* tmp = realloc(sessions, sizeof(struct session) * (sessionno + SESSION_BLOCK_SIZE));
|
||||||
|
if (tmp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < SESSION_BLOCK_SIZE; i++) {
|
||||||
|
struct session* session = &tmp[sessionno + i];
|
||||||
|
session->inUse = 0;
|
||||||
|
}
|
||||||
|
sessions = tmp;
|
||||||
|
sessionno += SESSION_BLOCK_SIZE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct session* newSession(size_t size) {
|
||||||
|
for (size_t i = 0; i < sessionno; i++) {
|
||||||
|
if (!sessions[i].inUse) {
|
||||||
|
sessions[i].inUse = true;
|
||||||
|
sessions[i].data = malloc(size);
|
||||||
|
if (sessions[i].data == NULL) {
|
||||||
|
sessions[i].inUse = false;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(sessions[i].data, 0, size);
|
||||||
|
sessions[i].lastWrite = 0;
|
||||||
|
return &(sessions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no free session slot
|
||||||
|
if (resizeSessionList() < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSession(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct session* findSession(uuid_t id) {
|
||||||
|
for (size_t i = 0; i < sessionno; i++) {
|
||||||
|
if (!sessions[i].inUse) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (uuid_compare(sessions[i].id, id) == 0) {
|
||||||
|
return &(sessions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* _session_start(ctx_t* ctx, const char* cookie, size_t size) {
|
||||||
|
char* cookieValue = getCookie(ctx, cookie);
|
||||||
|
|
||||||
|
bool isValid = false;
|
||||||
|
uuid_t id;
|
||||||
|
struct session* session = NULL;
|
||||||
|
|
||||||
|
if (cookieValue != NULL) {
|
||||||
|
if (uuid_parse(cookieValue, id) == 0) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cookieValue);
|
||||||
|
} else {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&globalLock);
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
session = findSession(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session == NULL) {
|
||||||
|
session = newSession(size);
|
||||||
|
if (session == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid_generate_time(session->id);
|
||||||
|
|
||||||
|
char buffer[36 + 1];
|
||||||
|
uuid_unparse(session->id, buffer);
|
||||||
|
|
||||||
|
setCookie(ctx, cookie, buffer, cookieSettingsNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&globalLock);
|
||||||
|
|
||||||
|
session->lastAccess = time(NULL);
|
||||||
|
|
||||||
|
ctx->session.session = session;
|
||||||
|
ctx->session.accessTime = time(NULL);
|
||||||
|
|
||||||
|
void* requestSessionData = malloc(size);
|
||||||
|
if (requestSessionData == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(requestSessionData, session->data, size);
|
||||||
|
ctx->session.data = requestSessionData;
|
||||||
|
|
||||||
|
return requestSessionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _session_update(ctx_t* ctx, size_t size) {
|
||||||
|
struct session_ctx* sessionCtx = &(ctx->session);
|
||||||
|
struct session* session = (struct session*) sessionCtx->session;
|
||||||
|
if (session == NULL) {
|
||||||
|
return ERROR_NO_SESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&globalLock);
|
||||||
|
|
||||||
|
if (session->lastWrite > sessionCtx->accessTime) {
|
||||||
|
pthread_mutex_unlock(&globalLock);
|
||||||
|
return ERROR_CONCURRENT_SESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->lastWrite = time(NULL);
|
||||||
|
sessionCtx->accessTime = session->lastWrite;
|
||||||
|
memcpy(session->data, sessionCtx->data, size);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&globalLock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_end(ctx_t* ctx) {
|
||||||
|
if (ctx->session.session != NULL) {
|
||||||
|
free(ctx->session.data);
|
||||||
|
}
|
||||||
|
}
|
33
src/sessions.h
Normal file
33
src/sessions.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef SESSIONS_H_
|
||||||
|
#define SESSIONS_H_
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
|
#ifndef SESSION_PTR_TYPE
|
||||||
|
#pragma GCC warning "session ptr type not defined"
|
||||||
|
#define SESSION_PTR_TYPE int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SESSION_LENGTH
|
||||||
|
#define SESSION_LENGTH (24*60*60*1000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SESSION_COOKIE_NAME
|
||||||
|
#define SESSION_COOKIE_NAME "cshore_session"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#define ERROR_NO_SESSION (-2)
|
||||||
|
#define ERROR_CONCURRENT_SESSION (-1)
|
||||||
|
|
||||||
|
void* _session_start(ctx_t*, const char*, size_t);
|
||||||
|
int _session_update(ctx_t*, size_t);
|
||||||
|
void session_end(ctx_t*);
|
||||||
|
|
||||||
|
#define session_start(c) ((SESSION_PTR_TYPE*) _session_start(c, SESSION_COOKIE_NAME, sizeof(SESSION_PTR_TYPE)))
|
||||||
|
#define session_update(c) _session_update(c, sizeof(SESSION_PTR_TYPE))
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue