From 759cc79b40011587bd85e3214c741b9df5179953 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Thu, 10 Jun 2021 22:47:30 +0200 Subject: [PATCH 01/16] give headers to handler --- src/base_cfloor.c | 3 ++- src/base_cgi.c | 8 +++----- src/request.h | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/base_cfloor.c b/src/base_cfloor.c index 3dba84a..86db238 100644 --- a/src/base_cfloor.c +++ b/src/base_cfloor.c @@ -22,7 +22,8 @@ static void handler(struct request request, struct response _response) { queryString: request.metaData.queryString, peerAddr: request.peer.addr, peerPort: request.peer.port, - auth: getAuthData(request.headers) + auth: getAuthData(request.headers), + headers: *request.headers }; response_t response = routerHandler(ctx); diff --git a/src/base_cgi.c b/src/base_cgi.c index f29b85c..c90eb6b 100644 --- a/src/base_cgi.c +++ b/src/base_cgi.c @@ -102,8 +102,6 @@ int main(int argc, char** argv) { fprintf(stderr, "%s: %s\n", argv[0], strerror(errno)); exit(1); } - - // TODO use request headers ctx_t ctx = { method: getMethod(or(getenv("REQUEST_METHOD"), "GET")), @@ -111,16 +109,16 @@ int main(int argc, char** argv) { queryString: or(getenv("QUERY_STRING"), ""), peerAddr: or(getenv("REMOTE_ADDR"), ""), peerPort: 0, // TODO - auth: getAuthData(request.headers) + auth: getAuthData(request.headers), + headers: headers }; - - headers_free(&headers); response_t response = routerHandler(ctx); if (response.output == NULL) { response = errorResponse(500, "route did not provide a reponse handler"); } + headers_free(&headers); freeAuthData(ctx.auth); printf("Status: %d\n\r", response.status); diff --git a/src/request.h b/src/request.h index 41dff37..d3ae5bd 100644 --- a/src/request.h +++ b/src/request.h @@ -18,6 +18,7 @@ typedef struct { const char* peerAddr; int peerPort; struct auth auth; + struct headers headers; } ctx_t; typedef struct { From 6a5f84e701b2f89b68ff2ec28348330bfaadfbee Mon Sep 17 00:00:00 2001 From: overflowerror Date: Thu, 10 Jun 2021 22:47:57 +0200 Subject: [PATCH 02/16] updated to current version of libparcival --- libparcival | 2 +- src/request.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libparcival b/libparcival index 30c62e0..e4a8695 160000 --- a/libparcival +++ b/libparcival @@ -1 +1 @@ -Subproject commit 30c62e03b86573a3fc5df788bda4bb95d2d89f9b +Subproject commit e4a86956046c03055780e98c4e95f352e683d985 diff --git a/src/request.c b/src/request.c index 622a33a..081bb4a 100644 --- a/src/request.c +++ b/src/request.c @@ -196,8 +196,8 @@ response_t _jsonResponse(int status, const char* type, void* value) { return response; } -extern size_t _sizeTemplate(const char*, ...); -extern void _renderTemplate(const char*, FILE*, ...); +extern size_t _sizeTemplate(const char*, va_list); +extern void _renderTemplate(const char*, FILE*, va_list); response_t templateResponse(int status, const char* name, ...) { response_t response = emptyResponse(); From 7b8d76914538d27964f68fa2094a2ce85a5a3d5a Mon Sep 17 00:00:00 2001 From: overflowerror Date: Fri, 11 Jun 2021 23:57:47 +0200 Subject: [PATCH 03/16] begin of cookies --- src/cookies.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/cookies.h | 9 +++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/cookies.c create mode 100644 src/cookies.h diff --git a/src/cookies.c b/src/cookies.c new file mode 100644 index 0000000..a7d9ad5 --- /dev/null +++ b/src/cookies.c @@ -0,0 +1,52 @@ +#include +#include + +#include + +#include "cookies.h" + +char* getCookie(ctx_t ctx, const char* key) { + char* cookieHeader = headers_get(&ctx.headers, "Cookie"); + if (cookieHeader == NULL) { + return NULL; + } + + cookieHeader = strdup(cookieHeader); + if (cookieHeader == NULL) { + return NULL; + } + + char** saveptr = NULL; + char* str = cookieHeader; + size_t keyLength = strlen(key); + + char* value = NULL; + + while((str = strtok_r(str, ";", saveptr)) != NULL) { + if (strncmp(str, key, keyLength) == 0) { + str = strtok_r(NULL, "=", saveptr) + if (str == NULL) { + // illegal cookie definition; ignore + } else { + value = str; + break; + } + } + + str = NULL; + } + + if (value != NULL) { + value = strdup(value); + if (value == NULL) { + free(cookieHeader); + return NULL; + } + + free(cookieHeader); + return value; + } else { + free(cookieHeader); + return NULL; + } +} diff --git a/src/cookies.h b/src/cookies.h new file mode 100644 index 0000000..55edb30 --- /dev/null +++ b/src/cookies.h @@ -0,0 +1,9 @@ +#ifndef COOKIES_H_ +#define COOKIES_H_ + +#include "request.h" + +char* getCookie(ctx_t, const char*); +void setCookie(ctx_t, const char*, const char*); + +#endif From 9d0e533fa121e3a50157551f97581b8f21bf5ae4 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sat, 12 Jun 2021 17:50:46 +0200 Subject: [PATCH 04/16] made cookie get more elegant --- src/cookies.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/cookies.c b/src/cookies.c index a7d9ad5..d1adcb0 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -38,15 +38,8 @@ char* getCookie(ctx_t ctx, const char* key) { if (value != NULL) { value = strdup(value); - if (value == NULL) { - free(cookieHeader); - return NULL; - } - - free(cookieHeader); - return value; - } else { - free(cookieHeader); - return NULL; } + + free(cookieHeader); + return value; } From 70bf93a3edfc2a1909c4a3137bc5e117ed1e96f1 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sat, 12 Jun 2021 17:54:47 +0200 Subject: [PATCH 05/16] key check should use whole key length --- src/cookies.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/cookies.c b/src/cookies.c index d1adcb0..5d0099c 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -22,15 +22,16 @@ char* getCookie(ctx_t ctx, const char* key) { char* value = NULL; - while((str = strtok_r(str, ";", saveptr)) != NULL) { - if (strncmp(str, key, keyLength) == 0) { - str = strtok_r(NULL, "=", saveptr) - if (str == NULL) { - // illegal cookie definition; ignore - } else { - value = str; - break; - } + while((str = strtok_r(str, "; ", saveptr)) != NULL) { + char* keyCandidate = str; + str = strtok_r(NULL, "=", saveptr); + if (str == NULL) { + // illegal cookie definition; ignore + continue; + } + + if (strcmp(keyCandidate, key) == 0) { + value = str; } str = NULL; From 47747af272b9ec65da8f1e1e55420da66338be8d Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sat, 12 Jun 2021 21:47:57 +0200 Subject: [PATCH 06/16] added response header to context --- Makefile | 2 +- src/base_cfloor.c | 8 ++++++-- src/base_cgi.c | 9 +++++++-- src/common.c | 9 +++++++++ src/common.h | 2 ++ src/request.h | 3 ++- 6 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/common.c diff --git a/Makefile b/Makefile index 99dbbca..3c54f43 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ LIBARGO = libargo/libargo.a LIBPARCIVAL = libparcival/libparcival.a 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 DEPS = $(OBJS:%.o=%.d) DEMO_OBJS = obj/demo.o obj/entities.tab.o obj/template.tab.o diff --git a/src/base_cfloor.c b/src/base_cfloor.c index 86db238..8f14062 100644 --- a/src/base_cfloor.c +++ b/src/base_cfloor.c @@ -23,7 +23,8 @@ static void handler(struct request request, struct response _response) { peerAddr: request.peer.addr, peerPort: request.peer.port, auth: getAuthData(request.headers), - headers: *request.headers + requestHeaders: *request.headers, + responseHeaders: headers_create() }; response_t response = routerHandler(ctx); @@ -32,9 +33,12 @@ static void handler(struct request request, struct response _response) { } freeAuthData(ctx.auth); + + 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(&ctx.responseHeaders); if (fd < 0) { error("csite: sendHeader: %s", strerror(errno)); diff --git a/src/base_cgi.c b/src/base_cgi.c index c90eb6b..d73fa9f 100644 --- a/src/base_cgi.c +++ b/src/base_cgi.c @@ -110,7 +110,8 @@ int main(int argc, char** argv) { peerAddr: or(getenv("REMOTE_ADDR"), ""), peerPort: 0, // TODO auth: getAuthData(request.headers), - headers: headers + requestHeaders: headers, + responseHeaders: headers_create() }; response_t response = routerHandler(ctx); @@ -119,13 +120,17 @@ int main(int argc, char** argv) { } headers_free(&headers); + + headers_merge(&ctx.responseHeaders, &response.headers); + freeAuthData(ctx.auth); printf("Status: %d\n\r", response.status); - headers_dump(&response.headers, stdout); + headers_dump(&ctx.responseHeaders, stdout); printf("\n\r"); headers_free(&response.headers); + headers_free(&ctx.responseHeaders); response.output(stdout, response._userData, ctx); diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..bf2e632 --- /dev/null +++ b/src/common.c @@ -0,0 +1,9 @@ +#include + +#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); + } +} diff --git a/src/common.h b/src/common.h index e680167..7e6df47 100644 --- a/src/common.h +++ b/src/common.h @@ -15,4 +15,6 @@ typedef enum method method_t; #define HTTP_CONNECT (CONNECT) #define HTTP_PATCH (PATCH) +void headers_merge(struct headers*, struct headers*); + #endif diff --git a/src/request.h b/src/request.h index d3ae5bd..50bd4b1 100644 --- a/src/request.h +++ b/src/request.h @@ -18,7 +18,8 @@ typedef struct { const char* peerAddr; int peerPort; struct auth auth; - struct headers headers; + struct headers requestHeaders; + struct headers responseHeaders; } ctx_t; typedef struct { From 387f79e1cf55888aa03b6a0a402394a7afbb4e46 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 13 Jun 2021 12:33:21 +0200 Subject: [PATCH 07/16] changed ctx to pointer (otherwise response header changes can't be saved) --- demo/demo.c | 19 ++++++++++--------- src/base_cfloor.c | 4 ++-- src/base_cgi.c | 4 ++-- src/handler.h | 2 +- src/request.c | 12 ++++++------ src/request.h | 2 +- src/router.c | 4 ++-- src/router.h | 2 +- 8 files changed, 25 insertions(+), 24 deletions(-) diff --git a/demo/demo.c b/demo/demo.c index bebab77..c8b3ecd 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -2,39 +2,40 @@ #include #include +#include #include "entities.h" GET("/", hello); GET("/index.*", hello); -response_t hello(ctx_t ctx) { +response_t hello(ctx_t* ctx) { return rawResponse(200, "Hello World\n"); } GET("/foobar", foobar); -response_t foobar(ctx_t ctx) { +response_t foobar(ctx_t* ctx) { return fileResponse("demo/foobar.txt"); } -response_t authenticate(ctx_t ctx) { - if (ctx.auth.type != BASIC) { +response_t authenticate(ctx_t* ctx) { + if (ctx->auth.type != BASIC) { return basicAuthResponse(401, "Protected Area"); } - if (strcmp(ctx.auth.basic.user, "admin") != 0 || - strcmp(ctx.auth.basic.password, "password") != 0 + if (strcmp(ctx->auth.basic.user, "admin") != 0 || + strcmp(ctx->auth.basic.password, "password") != 0 ) { // username or password wrong return basicAuthResponse(401, "Protected Area"); } - return next(); } GET("/user", authenticate, user); -response_t user(ctx_t ctx) { +POST("/user", authenticate, user); +response_t user(ctx_t* ctx) { user_t user = { .username = "overflowerror", .github = "https://github.com/overflowerror" @@ -44,6 +45,6 @@ response_t user(ctx_t ctx) { } GET("/template", template); -response_t template(ctx_t ctx) { +response_t template(ctx_t* ctx) { return templateResponse(200, "demo.templ", "Page Title", "Overflow"); } diff --git a/src/base_cfloor.c b/src/base_cfloor.c index 8f14062..e3800d8 100644 --- a/src/base_cfloor.c +++ b/src/base_cfloor.c @@ -27,7 +27,7 @@ static void handler(struct request request, struct response _response) { responseHeaders: headers_create() }; - response_t response = routerHandler(ctx); + response_t response = routerHandler(&ctx); if (response.output == NULL) { response = errorResponse(500, "route did not provide a reponse handler"); } @@ -51,7 +51,7 @@ static void handler(struct request request, struct response _response) { return; } - response.output(out, response._userData, ctx); + response.output(out, response._userData, &ctx); fclose(out); } diff --git a/src/base_cgi.c b/src/base_cgi.c index d73fa9f..abc7d31 100644 --- a/src/base_cgi.c +++ b/src/base_cgi.c @@ -114,7 +114,7 @@ int main(int argc, char** argv) { responseHeaders: headers_create() }; - response_t response = routerHandler(ctx); + response_t response = routerHandler(&ctx); if (response.output == NULL) { response = errorResponse(500, "route did not provide a reponse handler"); } @@ -132,7 +132,7 @@ int main(int argc, char** argv) { headers_free(&response.headers); headers_free(&ctx.responseHeaders); - response.output(stdout, response._userData, ctx); + response.output(stdout, response._userData, &ctx); return 0; } diff --git a/src/handler.h b/src/handler.h index ce84db3..44f69ec 100644 --- a/src/handler.h +++ b/src/handler.h @@ -3,6 +3,6 @@ #include "request.h" -typedef response_t (handle_t)(ctx_t); +typedef response_t (handle_t)(ctx_t*); #endif diff --git a/src/request.c b/src/request.c index 081bb4a..463b3f9 100644 --- a/src/request.c +++ b/src/request.c @@ -31,13 +31,13 @@ void setDefaultErrorFormat(errorformat_t 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); 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); } @@ -70,7 +70,7 @@ struct statusdata { 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; 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), "error", json_string(statusString), "message", json_string(data->message), - "path", json_string(ctx.path) + "path", json_string(ctx->path) ); free(data); free(tmp); @@ -126,7 +126,7 @@ response_t errorResponse(int status, const char* 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; #define READ_BUFFER_SIZE (1024) @@ -171,7 +171,7 @@ response_t fileResponse(const char* file) { 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; char* result = json_stringify(json); diff --git a/src/request.h b/src/request.h index 50bd4b1..df7c24e 100644 --- a/src/request.h +++ b/src/request.h @@ -27,7 +27,7 @@ typedef struct { struct headers headers; void* _userData; - void (*output) (FILE* conenction, void* _userData, ctx_t ctx); + void (*output) (FILE* conenction, void* _userData, ctx_t* ctx); } response_t; typedef enum { diff --git a/src/router.c b/src/router.c index 5b8bc16..edddeaa 100644 --- a/src/router.c +++ b/src/router.c @@ -121,8 +121,8 @@ int registerRoute(method_t method, const char* path, ...) { return 0; } -response_t routerHandler(ctx_t ctx) { - struct route* route = findRoute(ctx.method, ctx.path); +response_t routerHandler(ctx_t* ctx) { + struct route* route = findRoute(ctx->method, ctx->path); if (route == NULL) { return errorResponse(404, "no route found"); } diff --git a/src/router.h b/src/router.h index 7e120ec..b018158 100644 --- a/src/router.h +++ b/src/router.h @@ -5,6 +5,6 @@ int registerRoute(method_t method, const char* path, ...); -response_t routerHandler(ctx_t ctx); +response_t routerHandler(ctx_t* ctx); #endif From 47cbe3db1c1f048177a2ae9b6c1cff7b11494050 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 13 Jun 2021 12:33:53 +0200 Subject: [PATCH 08/16] added cookie set function + small changes to cookie get --- Makefile | 2 +- src/cookies.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++- src/cookies.h | 23 ++++++- 3 files changed, 189 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 3c54f43..48ce236 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ LIBARGO = libargo/libargo.a LIBPARCIVAL = libparcival/libparcival.a 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 obj/common.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 DEPS = $(OBJS:%.o=%.d) DEMO_OBJS = obj/demo.o obj/entities.tab.o obj/template.tab.o diff --git a/src/cookies.c b/src/cookies.c index 5d0099c..041d36a 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -1,12 +1,15 @@ #include #include +#include +#include #include #include "cookies.h" -char* getCookie(ctx_t ctx, const char* key) { - char* cookieHeader = headers_get(&ctx.headers, "Cookie"); +char* getCookie(ctx_t* ctx, const char* key) { + // ignore const + char* cookieHeader = (char*) headers_get(&ctx->requestHeaders, "Cookie"); if (cookieHeader == NULL) { return NULL; } @@ -18,7 +21,6 @@ char* getCookie(ctx_t ctx, const char* key) { char** saveptr = NULL; char* str = cookieHeader; - size_t keyLength = strlen(key); char* value = NULL; @@ -44,3 +46,165 @@ char* getCookie(ctx_t ctx, const char* key) { 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 + 2 + 1 + 2 + 1 + 2 + 1 + 3; + } + if (settings.maxAge != COOKIE_NO_MAX_AGE) { + length += 2; + length += strlen("Max-Age="); + long tmp = settings.maxAge; + 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; +} diff --git a/src/cookies.h b/src/cookies.h index 55edb30..400fac8 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -1,9 +1,28 @@ #ifndef COOKIES_H_ #define COOKIES_H_ +#include +#include +#include + #include "request.h" -char* getCookie(ctx_t, const char*); -void setCookie(ctx_t, const char*, const char*); +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 From 55cf10890c373d2209b93a0e1e10e7baace32513 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 13 Jun 2021 12:44:24 +0200 Subject: [PATCH 09/16] fixed from buffer size when using Max-Age or Expires --- src/cookies.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cookies.c b/src/cookies.c index 041d36a..b651187 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -64,12 +64,16 @@ int setCookie(ctx_t* ctx, const char* name, const char* value, cookieSettings_t if (settings.expires != COOKIE_NO_EXPIRES) { length += 2; length += strlen("Expires="); - length += 3 + 2 + 2 + 1 + 3 + 1 + 4 + 2 + 1 + 2 + 1 + 2 + 1 + 3; + 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) { From 22f983e9fe1dbef0fd1bb6c55ac76f03a133ef10 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 13 Jun 2021 13:14:14 +0200 Subject: [PATCH 10/16] reading cookies works now --- src/cookies.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/cookies.c b/src/cookies.c index b651187..cb78bb8 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -19,21 +19,34 @@ char* getCookie(ctx_t* ctx, const char* key) { return NULL; } - char** saveptr = NULL; + char* saveptr = NULL; char* str = cookieHeader; char* value = NULL; - while((str = strtok_r(str, "; ", saveptr)) != NULL) { + while((str = strtok_r(str, ";", &saveptr)) != NULL) { + while(*str == ' ') str++; + char* keyCandidate = str; - str = strtok_r(NULL, "=", saveptr); + + 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; + value = str + 1; + break; } str = NULL; From 6565658d074e1b91a2bebbf3cc8b696fc2ab0a12 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 13 Jun 2021 13:14:27 +0200 Subject: [PATCH 11/16] added cookie stuff to demo --- demo/demo.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/demo/demo.c b/demo/demo.c index c8b3ecd..5384a0f 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -48,3 +48,20 @@ GET("/template", template); response_t template(ctx_t* ctx) { return templateResponse(200, "demo.templ", "Page Title", "Overflow"); } + +GET("/cookies", cookies); +response_t cookies(ctx_t* ctx) { + char* test = getCookie(ctx, "test"); + + setCookie(ctx, "test", "foobar", cookieSettingsNull()); + + if (test == NULL) { + return rawResponse(200, "cookie not set"); + } else if (strcmp(test, "foobar") == 0) { + free(test); + return rawResponse(200, "cookie value correct"); + } else { + free(test); + return rawResponse(200, "cookie value incorrect"); + } +} From cbe3920b12c2f5b3b5aed82df43a39d9f40fd031 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Mon, 14 Jun 2021 18:39:02 +0200 Subject: [PATCH 12/16] added basic session stuff management --- src/sessions.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sessions.h | 23 +++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/sessions.c create mode 100644 src/sessions.h diff --git a/src/sessions.c b/src/sessions.c new file mode 100644 index 0000000..39b5055 --- /dev/null +++ b/src/sessions.c @@ -0,0 +1,102 @@ +#include +#include +#include + +#include + +#include + +#include "sessions.h" + +struct session { + bool inUse; + uuid_t id; + time_t lastAccess; + void* data; +}* sessions = NULL; +static size_t sessionno = 0; + +#define SESSION_BLOCK_SIZE (128) + +int resizeSessionList() { + // TODO synchronization + struct session* tmp = realloc(session, sizeof(struct session) * (sessionno + SESSION_BLOCK_SIZE)); + if (tmp == NULL) { + return -1; + } + memset(tmp + sizeof(struct session) * sessionno, 0, sizeof(struct session) * SESSION_BLOCK_SIZE); + 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) { + // TODO synchronization + sessions[i].inUse = true; + sessions[i].data = malloc(size); + if (sessions[i].data == NULL) { + sessions[i].inUse = false; + return NULL; + } + 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; + } + + if (isValid) { + session = findSession(id); + } + + if (session == NULL) { + session = newSession(size); + if (session == NULL) { + return NULL; + } + + uuid_generate_time(session->id); + } + + session->lastAccess = time(NULL); + + return session->data; +} diff --git a/src/sessions.h b/src/sessions.h new file mode 100644 index 0000000..20c330f --- /dev/null +++ b/src/sessions.h @@ -0,0 +1,23 @@ +#ifndef SESSIONS_H_ +#define SESSIONS_H_ + +#ifndef SESSION_PTR_TYPE + #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" + +void* _session_start(ctx_t*, const char*, size_t); + +#define session_start(c) (SESSION_PTR_TYPE*) _session_start(c, SESSION_COOKIE_NAME, sizeof(SESSION_PTR_TYPE)) + +#endif From 491c4d929a438cfaab79092f418ae7e573a3599f Mon Sep 17 00:00:00 2001 From: overflowerror Date: Tue, 15 Jun 2021 18:43:20 +0200 Subject: [PATCH 13/16] fixed issues in implementation --- src/sessions.c | 17 ++++++++++++++--- src/sessions.h | 4 ++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/sessions.c b/src/sessions.c index 39b5055..6a22f80 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -1,14 +1,19 @@ #include #include #include +#include -#include +#include #include +// SESSION_PTR_TYPE doesn't matter for this file +#define SESSION_PTR_TYPE int #include "sessions.h" -struct session { +#include "cookies.h" + +static struct session { bool inUse; uuid_t id; time_t lastAccess; @@ -20,7 +25,7 @@ static size_t sessionno = 0; int resizeSessionList() { // TODO synchronization - struct session* tmp = realloc(session, sizeof(struct session) * (sessionno + SESSION_BLOCK_SIZE)); + struct session* tmp = realloc(sessions, sizeof(struct session) * (sessionno + SESSION_BLOCK_SIZE)); if (tmp == NULL) { return -1; } @@ -41,6 +46,7 @@ struct session* newSession(size_t size) { sessions[i].inUse = false; return NULL; } + memset(sessions[i].data, 0, size); return &(sessions[i]); } } @@ -94,6 +100,11 @@ void* _session_start(ctx_t* ctx, const char* cookie, size_t size) { } uuid_generate_time(session->id); + + char buffer[36 + 1]; + uuid_unparse(session->id, buffer); + + setCookie(ctx, cookie, buffer, cookieSettingsNull()); } session->lastAccess = time(NULL); diff --git a/src/sessions.h b/src/sessions.h index 20c330f..bde7f16 100644 --- a/src/sessions.h +++ b/src/sessions.h @@ -2,7 +2,7 @@ #define SESSIONS_H_ #ifndef SESSION_PTR_TYPE - #warning "session ptr type not defined" + #pragma GCC warning "session ptr type not defined" #define SESSION_PTR_TYPE int #endif @@ -18,6 +18,6 @@ void* _session_start(ctx_t*, const char*, size_t); -#define session_start(c) (SESSION_PTR_TYPE*) _session_start(c, SESSION_COOKIE_NAME, sizeof(SESSION_PTR_TYPE)) +#define session_start(c) ((SESSION_PTR_TYPE*) _session_start(c, SESSION_COOKIE_NAME, sizeof(SESSION_PTR_TYPE))) #endif From bfd04560ed845d05019ee5f42225dbdb697a3de5 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Tue, 15 Jun 2021 18:43:36 +0200 Subject: [PATCH 14/16] added demo for sessions --- Makefile | 4 ++-- demo/demo.c | 31 ++++++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 48ce236..659a53e 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ CC = gcc LD = gcc 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 LIBARGO = libargo/libargo.a LIBPARCIVAL = libparcival/libparcival.a 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 obj/common.o obj/cookies.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) DEMO_OBJS = obj/demo.o obj/entities.tab.o obj/template.tab.o diff --git a/demo/demo.c b/demo/demo.c index 5384a0f..e2eb4c7 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -1,8 +1,11 @@ #include #include +#include #include -#include + +#define SESSION_PTR_TYPE const char* +#include #include "entities.h" @@ -49,19 +52,21 @@ response_t template(ctx_t* ctx) { return templateResponse(200, "demo.templ", "Page Title", "Overflow"); } -GET("/cookies", cookies); -response_t cookies(ctx_t* ctx) { - char* test = getCookie(ctx, "test"); +GET("/sessions", sessions); +response_t sessions(ctx_t* ctx) { + const char** sessiondata = session_start(ctx); + + const char* output = "null\n"; - setCookie(ctx, "test", "foobar", cookieSettingsNull()); - - if (test == NULL) { - return rawResponse(200, "cookie not set"); - } else if (strcmp(test, "foobar") == 0) { - free(test); - return rawResponse(200, "cookie value correct"); + if (*sessiondata == NULL) { + *sessiondata = "Test\n"; } else { - free(test); - return rawResponse(200, "cookie value incorrect"); + output = *sessiondata; } + + fprintf(stderr, "%s, %d\n", output, strlen(output)); + + return rawResponse(200, output); } + + From e8a1c2dd468020712f1457409f3df9659c99c377 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Wed, 16 Jun 2021 22:49:59 +0200 Subject: [PATCH 15/16] added basic synchronization for session; still probablematic if two requests use the same session object at the same time; maybe add thread specific buffer or something --- src/sessions.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sessions.c b/src/sessions.c index 6a22f80..896911f 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -5,6 +5,8 @@ #include +#include + #include // SESSION_PTR_TYPE doesn't matter for this file @@ -20,26 +22,28 @@ static struct session { void* data; }* sessions = NULL; static size_t sessionno = 0; +static pthread_mutex_t globalLock = PTHREAD_MUTEX_INITIALIZER; #define SESSION_BLOCK_SIZE (128) int resizeSessionList() { - // TODO synchronization struct session* tmp = realloc(sessions, sizeof(struct session) * (sessionno + SESSION_BLOCK_SIZE)); if (tmp == NULL) { return -1; } - memset(tmp + sizeof(struct session) * sessionno, 0, sizeof(struct session) * SESSION_BLOCK_SIZE); + 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) { - // TODO synchronization sessions[i].inUse = true; sessions[i].data = malloc(size); if (sessions[i].data == NULL) { @@ -89,6 +93,8 @@ void* _session_start(ctx_t* ctx, const char* cookie, size_t size) { isValid = false; } + pthread_mutex_lock(&globalLock); + if (isValid) { session = findSession(id); } @@ -107,6 +113,8 @@ void* _session_start(ctx_t* ctx, const char* cookie, size_t size) { setCookie(ctx, cookie, buffer, cookieSettingsNull()); } + pthread_mutex_unlock(&globalLock); + session->lastAccess = time(NULL); return session->data; From 3d822269f860e637bb2cedd8535c11d0de27b9a2 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 5 Dec 2021 22:43:41 +0100 Subject: [PATCH 16/16] fundermental concurrency support for sessions --- demo/demo.c | 1 + src/base_cfloor.c | 6 +++++- src/base_cgi.c | 6 +++++- src/request.h | 9 ++++++++ src/sessions.c | 52 +++++++++++++++++++++++++++++++++++++++++------ src/sessions.h | 10 +++++++++ 6 files changed, 76 insertions(+), 8 deletions(-) diff --git a/demo/demo.c b/demo/demo.c index e2eb4c7..bae50e7 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -60,6 +60,7 @@ response_t sessions(ctx_t* ctx) { if (*sessiondata == NULL) { *sessiondata = "Test\n"; + session_update(ctx); } else { output = *sessiondata; } diff --git a/src/base_cfloor.c b/src/base_cfloor.c index e3800d8..d6bd49c 100644 --- a/src/base_cfloor.c +++ b/src/base_cfloor.c @@ -15,6 +15,8 @@ struct networkingConfig netConfig; +void session_end(ctx_t*); + static void handler(struct request request, struct response _response) { ctx_t ctx = { method: request.metaData.method, @@ -24,7 +26,8 @@ static void handler(struct request request, struct response _response) { peerPort: request.peer.port, auth: getAuthData(request.headers), requestHeaders: *request.headers, - responseHeaders: headers_create() + responseHeaders: headers_create(), + session: EMPTY_SESSION_CTX, }; response_t response = routerHandler(&ctx); @@ -33,6 +36,7 @@ static void handler(struct request request, struct response _response) { } freeAuthData(ctx.auth); + session_end(&ctx); headers_merge(&ctx.responseHeaders, &response.headers); diff --git a/src/base_cgi.c b/src/base_cgi.c index abc7d31..6fdcf08 100644 --- a/src/base_cgi.c +++ b/src/base_cgi.c @@ -96,6 +96,8 @@ const char* or(const char* v1, const char* v2) { } } +void session_end(ctx_t*); + int main(int argc, char** argv) { struct headers headers = headers_create(); if (setHttpHeaders(&headers)) { @@ -111,7 +113,8 @@ int main(int argc, char** argv) { peerPort: 0, // TODO auth: getAuthData(request.headers), requestHeaders: headers, - responseHeaders: headers_create() + responseHeaders: headers_create(), + session: EMPTY_SESSION_CTX, }; response_t response = routerHandler(&ctx); @@ -124,6 +127,7 @@ int main(int argc, char** argv) { headers_merge(&ctx.responseHeaders, &response.headers); freeAuthData(ctx.auth); + session_end(&ctx); printf("Status: %d\n\r", response.status); headers_dump(&ctx.responseHeaders, stdout); diff --git a/src/request.h b/src/request.h index df7c24e..6099a5d 100644 --- a/src/request.h +++ b/src/request.h @@ -11,6 +11,14 @@ #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 { method_t method; const char* path; @@ -20,6 +28,7 @@ typedef struct { struct auth auth; struct headers requestHeaders; struct headers responseHeaders; + struct session_ctx session; } ctx_t; typedef struct { diff --git a/src/sessions.c b/src/sessions.c index 896911f..c5be62d 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -1,10 +1,7 @@ #include #include -#include #include -#include - #include #include @@ -15,12 +12,15 @@ #include "cookies.h" -static struct session { +struct session { bool inUse; uuid_t id; time_t lastAccess; + time_t lastWrite; void* data; -}* sessions = NULL; +}; + +static struct session* sessions = NULL; static size_t sessionno = 0; static pthread_mutex_t globalLock = PTHREAD_MUTEX_INITIALIZER; @@ -51,6 +51,7 @@ struct session* newSession(size_t size) { return NULL; } memset(sessions[i].data, 0, size); + sessions[i].lastWrite = 0; return &(sessions[i]); } } @@ -116,6 +117,45 @@ void* _session_start(ctx_t* ctx, const char* cookie, size_t size) { 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 session->data; + 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); + } } diff --git a/src/sessions.h b/src/sessions.h index bde7f16..f7a41ae 100644 --- a/src/sessions.h +++ b/src/sessions.h @@ -1,6 +1,10 @@ #ifndef SESSIONS_H_ #define SESSIONS_H_ +#include + +#include + #ifndef SESSION_PTR_TYPE #pragma GCC warning "session ptr type not defined" #define SESSION_PTR_TYPE int @@ -16,8 +20,14 @@ #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