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
|
||||
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
|
||||
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
|
||||
|
|
42
demo/demo.c
42
demo/demo.c
|
@ -1,40 +1,44 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <controller.h>
|
||||
|
||||
#define SESSION_PTR_TYPE const char*
|
||||
#include <sessions.h>
|
||||
|
||||
#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 +48,26 @@ 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");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
void session_end(ctx_t*);
|
||||
|
||||
static void handler(struct request request, struct response _response) {
|
||||
ctx_t ctx = {
|
||||
method: request.metaData.method,
|
||||
|
@ -22,18 +24,25 @@ 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),
|
||||
requestHeaders: *request.headers,
|
||||
responseHeaders: headers_create(),
|
||||
session: EMPTY_SESSION_CTX,
|
||||
};
|
||||
|
||||
response_t response = routerHandler(ctx);
|
||||
response_t response = routerHandler(&ctx);
|
||||
if (response.output == NULL) {
|
||||
response = errorResponse(500, "route did not provide a reponse handler");
|
||||
}
|
||||
|
||||
freeAuthData(ctx.auth);
|
||||
session_end(&ctx);
|
||||
|
||||
int fd = _response.sendHeader(response.status, &response.headers, &request);
|
||||
headers_merge(&ctx.responseHeaders, &response.headers);
|
||||
|
||||
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));
|
||||
|
@ -46,7 +55,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);
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
@ -103,33 +105,38 @@ int main(int argc, char** argv) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
// TODO use request headers
|
||||
|
||||
ctx_t ctx = {
|
||||
method: getMethod(or(getenv("REQUEST_METHOD"), "GET")),
|
||||
path: or(getenv("PATH_INFO"), "/"),
|
||||
queryString: or(getenv("QUERY_STRING"), ""),
|
||||
peerAddr: or(getenv("REMOTE_ADDR"), ""),
|
||||
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) {
|
||||
response = errorResponse(500, "route did not provide a reponse handler");
|
||||
}
|
||||
|
||||
headers_free(&headers);
|
||||
|
||||
headers_merge(&ctx.responseHeaders, &response.headers);
|
||||
|
||||
freeAuthData(ctx.auth);
|
||||
session_end(&ctx);
|
||||
|
||||
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);
|
||||
response.output(stdout, response._userData, &ctx);
|
||||
|
||||
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_PATCH (PATCH)
|
||||
|
||||
void headers_merge(struct headers*, struct headers*);
|
||||
|
||||
#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"
|
||||
|
||||
typedef response_t (handle_t)(ctx_t);
|
||||
typedef response_t (handle_t)(ctx_t*);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
@ -18,6 +26,9 @@ typedef struct {
|
|||
const char* peerAddr;
|
||||
int peerPort;
|
||||
struct auth auth;
|
||||
struct headers requestHeaders;
|
||||
struct headers responseHeaders;
|
||||
struct session_ctx session;
|
||||
} ctx_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -25,7 +36,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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
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