From a0ec357e0a1299a30285622b206e70477ec64ee4 Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Mon, 5 Mar 2012 16:45:22 +0100 Subject: [PATCH] closes #11: first very crude session handling implementation, no persitence no memory cleanups, spread over to much files TODO: clean this --- include/http/response.h | 2 + include/http/worker.h | 3 ++ include/session.h | 47 +++++++++++++++++++ src/Makefile.am | 5 +- src/http/response/403.c | 63 +++++++++++++++++++++++++ src/http/response/login_form.c | 77 +++++++++++++++++++++++++++++++ src/http/response/me.c | 34 +++++++++----- src/http/worker.c | 45 ++++++++++++++++-- src/http/worker/process.c | 84 ++++++++++++++++++++++++++++++++-- src/session.c | 46 +++++++++++++++++++ src/session/add.c | 36 +++++++++++++++ src/session/delete.c | 23 ++++++++++ src/session/get.c | 29 ++++++++++++ 13 files changed, 475 insertions(+), 19 deletions(-) create mode 100644 include/session.h create mode 100644 src/http/response/403.c create mode 100644 src/http/response/login_form.c create mode 100644 src/session.c create mode 100644 src/session/add.c create mode 100644 src/session/delete.c create mode 100644 src/session/get.c diff --git a/include/http/response.h b/include/http/response.h index a6588b8..67b206e 100644 --- a/include/http/response.h +++ b/include/http/response.h @@ -43,7 +43,9 @@ HttpResponse httpResponse304( const char *, size_t, const char *, size_t); HttpResponse httpResponse404(); +HttpResponse httpResponse403(); HttpResponse httpResponseMe(); +HttpResponse httpResponseLoginForm(); HttpResponse httpResponseRandval(time_t, int); HttpResponse httpResponseAsset( const char *, diff --git a/include/http/worker.h b/include/http/worker.h index 669f9aa..44c412e 100644 --- a/include/http/worker.h +++ b/include/http/worker.h @@ -31,6 +31,7 @@ #include "http/parser.h" #include "http/writer.h" #include "cbuf.h" +#include "session.h" #ifndef TRUE @@ -56,6 +57,8 @@ CLASS(HttpWorker) { HttpParser parser; HttpWriter writer; + Session session; + Session * sroot; }; #endif // __HTTP_WORKER_H__ diff --git a/include/session.h b/include/session.h new file mode 100644 index 0000000..67571f3 --- /dev/null +++ b/include/session.h @@ -0,0 +1,47 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright (C) 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + +#include +#include + +#include "class.h" + +#define SESSION_LIVETIME 30 + + +CLASS(Session) { + unsigned long id; + time_t livetime; + + char * username; +}; + +Session sessionAdd(const Session *, Session); +Session sessionGet(const Session *, const unsigned long id); +void sessionDelete(const Session *, const unsigned long id); + +#endif // __SESSION_H__ + +// vim: set ts=4 sw=4: diff --git a/src/Makefile.am b/src/Makefile.am index 8970af3..884af16 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,6 +26,8 @@ REQ = http/request.c \ RESP = http/response.c \ http/response/304.c \ http/response/404.c \ + http/response/403.c \ + http/response/login_form.c \ http/response/asset.c \ http/response/me.c \ http/response/randval.c @@ -43,6 +45,7 @@ WORKER = http/worker.c \ http/worker/add_common_header.c HEADER = http/header.c http/header/get.c http/header/add.c \ http/header/to_string.c +SESSION = session.c session/add.c session/get.c session/delete.c UTILS = utils/hash.c \ utils/memory.c \ utils/http.c \ @@ -57,6 +60,6 @@ bin_PROGRAMS = testserver testserver_SOURCES = testserver.c \ $(IFACE) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \ $(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) $(CB) \ - $(UTILS) $(MSGQ) + $(UTILS) $(MSGQ) $(SESSION) testserver_CFLAGS = -Wall -I ../include/ testserver_LDFLAGS = -lrt diff --git a/src/http/response/403.c b/src/http/response/403.c new file mode 100644 index 0000000..611f271 --- /dev/null +++ b/src/http/response/403.c @@ -0,0 +1,63 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright (C) 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "class.h" +#include "interface/class.h" + +#include "http/response.h" +#include "http/message.h" +#include "http/header.h" + + +HttpResponse +httpResponse403() +{ + HttpResponse response; + HttpMessage message; + char buffer[200]; + size_t nbuf; + + response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); + message = (HttpMessage)response; + + message->type = HTTP_MESSAGE_BUFFERED; + message->nbody = 0; + message->body = NULL; + + nbuf = sprintf(buffer, "%d", message->nbody); + + httpHeaderAdd(&(message->header), + new(HttpHeader, + "Content-Length", + sizeof("Content-Length")-1, + buffer, + nbuf)); + + return response; +} + +// vim: set ts=4 sw=4: diff --git a/src/http/response/login_form.c b/src/http/response/login_form.c new file mode 100644 index 0000000..8e04b1b --- /dev/null +++ b/src/http/response/login_form.c @@ -0,0 +1,77 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright (C) 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "class.h" +#include "interface/class.h" + +#include "http/response.h" +#include "http/message.h" +#include "http/header.h" + +#define RESP_DATA "
" \ + "" \ + "" \ +"
" + +HttpResponse +httpResponseLoginForm() +{ + char buffer[200]; + HttpResponse response; + HttpMessage message; + size_t nbuf; + + response = new(HttpResponse, "HTTP/1.1", 200, "OK"); + message = (HttpMessage)response; + + httpHeaderAdd(&(message->header), + new(HttpHeader, + "Content-Type", + sizeof("Content-Type")-1, + "text/html", + sizeof("text/html")-1)); + + message->type = HTTP_MESSAGE_BUFFERED; + + message->nbody = sizeof(RESP_DATA)-1; + message->body = malloc(message->nbody); + memcpy(message->body, RESP_DATA, message->nbody); + + nbuf = sprintf(buffer, "%d", message->nbody); + + httpHeaderAdd(&(message->header), + new(HttpHeader, + "Content-Length", + sizeof("Content-Length")-1, + buffer, + nbuf)); + + return response; +} + +// vim: set ts=4 sw=4: diff --git a/src/http/response/me.c b/src/http/response/me.c index 6a49dda..3c6a333 100644 --- a/src/http/response/me.c +++ b/src/http/response/me.c @@ -60,15 +60,31 @@ "$(document).ready(function() {" \ "var intervalId;" \ "var vnext = 0;" \ + "var clickclose = function() {" \ + "clearInterval(intervalId);" \ + "vnext = 0;" \ + "$(\"#randval\").addClass(\"hide\");" \ + "};" \ "var counter = function() {" \ "if (0 >= vnext) {" \ - "$.getJSON(\"/randval/\", function(data) {" \ + "$.getJSON(\"/randval/\", function(data, xhr) {" \ "var date = new Date(data.ctime * 1000);" \ "$(\"#ctime\").empty().append(date.toString());" \ "vnext = data.vnext;" \ "$(\"#value\").empty().append(data.value);" \ "$(\"#vnext\").empty().append(vnext);" \ + "$(\"#randval\").on(\"click\", clickclose);" \ + "}).error(function(event, request, settings) {" \ + "clearInterval(intervalId);" \ + "$.get(\"/login/\", function(data) {" \ + "$(\"#randval\")" \ + ".off(\"click\", clickclose)" \ + ".empty().append(data);" \ + "});" \ "});" \ + "if ($(\"#randval\").hasClass(\"hide\")) {" \ + "$(\"#randval\").removeClass(\"hide\");" \ + "}" \ "} else {" \ "vnext--;" \ "$(\"#vnext\").empty().append(vnext);" \ @@ -83,12 +99,6 @@ "});" \ "$(\"a\").click(function() {" \ "intervalId = setInterval(counter, 1000);" \ - "$(\"#randval\").removeClass(\"hide\");" \ - "});" \ - "$(\"#randval\").click(function() {" \ - "clearInterval(intervalId);" \ - "vnext = 0;" \ - "$(\"#randval\").addClass(\"hide\");" \ "});" \ "});" \ "" \ @@ -103,6 +113,7 @@ "" \ "
" \ "

Testpage

" \ + "Welcome %s
" \ "" \ "
Link" \ "
" \ @@ -111,7 +122,7 @@ "" HttpResponse -httpResponseMe() +httpResponseMe(char * uname) { char buffer[200]; HttpResponse response; @@ -141,9 +152,10 @@ httpResponseMe() sizeof("profession=\"coder\"")-1)); message->type = HTTP_MESSAGE_BUFFERED; - message->nbody = sizeof(RESP_DATA)-1; - message->body = malloc(sizeof(RESP_DATA)-1); - memcpy(message->body, RESP_DATA, sizeof(RESP_DATA)-1); + message->nbody = sizeof(RESP_DATA)-1-2+strlen(uname); //!< the two are the %s + message->body = malloc(message->nbody+1); + sprintf(message->body, RESP_DATA, uname); + //memcpy(message->body, RESP_DATA, sizeof(RESP_DATA)-1); nbuf = sprintf(buffer, "%d", message->nbody); diff --git a/src/http/worker.c b/src/http/worker.c index 6ef098a..938493d 100644 --- a/src/http/worker.c +++ b/src/http/worker.c @@ -1,8 +1,32 @@ -#include +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright (C) 2012 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE + #include #include #include #include +#include #include "class.h" #include "http/worker.h" @@ -37,9 +61,19 @@ httpWorkerCtor(void * _this, va_list * params) this->parser = new(HttpParser, this->pbuf); this->writer = new(HttpWriter, this->wbuf); + this->sroot = &(this->session); + return 0; } +static +inline +void +tDelete(void * node) +{ + delete(node); +} + static void httpWorkerDtor(void * _this) @@ -51,8 +85,11 @@ httpWorkerDtor(void * _this) delete(this->parser); delete(this->writer); - delete(this->pbuf); //!< cloned workers have NULL, so delete won't do anything - delete(this->wbuf); //!< cloned workers have NULL, so delete won't do anything + if (NULL != this->pbuf) { + delete(this->pbuf); //!< cloned workers have NULL, so delete won't do anything + delete(this->wbuf); //!< cloned workers have NULL, so delete won't do anything + tdestroy(*(this->sroot), tDelete); + } } static @@ -66,6 +103,8 @@ httpWorkerClone(void * _this, void * _base) this->parser = new(HttpParser, base->pbuf); this->writer = new(HttpWriter, base->wbuf); + + this->sroot = &(base->session); } ssize_t httpWorkerProcess(void *, int); diff --git a/src/http/worker/process.c b/src/http/worker/process.c index a6ce588..542a35e 100644 --- a/src/http/worker/process.c +++ b/src/http/worker/process.c @@ -21,6 +21,9 @@ */ #include +#include +#include +#include #include "class.h" #include "interface/class.h" @@ -31,6 +34,7 @@ #include "http/response.h" #include "http/message/queue.h" #include "http/parser.h" +#include "session.h" HttpMessage httpWorkerGetAsset(HttpRequest, const char *, const char *, size_t); void httpWorkerAddCommonHeader(HttpMessage, HttpMessage); @@ -49,19 +53,91 @@ httpWorkerProcess(HttpWorker this, int fd) HttpMessageQueue respq = this->writer->queue; for (i=0; inmsgs; i++) { + HttpMessage rmessage = reqq->msgs[i]; HttpRequest request = (HttpRequest)(reqq->msgs[i]); HttpMessage response = NULL; + HttpHeader cookie = httpHeaderGet( + &(rmessage->header), + "cookie", + sizeof("Cookie")-1); + + if (NULL == this->session && NULL != cookie) { + int i; + + for (i=0; icvalue; i++) { + char * sidstr = strstr(cookie->value[i], "sid"); + + if (NULL != sidstr) { + unsigned long sid; + + sidstr = strchr(sidstr, '=')+1; + sid = strtoul(sidstr, NULL, 10); + + this->session = sessionGet(this->sroot, sid); + break; + } + } + } + + if (NULL != this->session) { + if (time(NULL) < this->session->livetime) { + this->session->livetime = time(NULL) + SESSION_LIVETIME; + } else { + sessionDelete(this->sroot, this->session->id); + delete(this->session); + } + } + + if (0 == strcmp("POST", request->method)) { + if (0 == strcmp("/me/", request->uri)) { + char * delim = memchr(rmessage->body, '=', rmessage->nbody); + char * key = rmessage->body; + char * val; + size_t nkey, nval; + char buffer[200]; + size_t nbuf; + + nkey = delim - rmessage->body - 1; + *delim = 0; + val = delim + 1; + nval = rmessage->nbody - (val - rmessage->body); + + this->session = sessionAdd( + this->sroot, + new(Session, val, nval)); + nbuf = sprintf(buffer, "sid=%lu;Path=/", this->session->id); + + response = (HttpMessage)httpResponseMe(this->session->username); + + httpHeaderAdd( + &(response->header), + new(HttpHeader, + "Set-Cookie", + sizeof("Set-Cookie")-1, + buffer, + nbuf)); + } + } if (0 == strcmp("GET", request->method)) { + if (0 == strcmp("/login/", request->uri)) { + response = (HttpMessage)httpResponseLoginForm(); + } + if (0 == strcmp("/me/", request->uri)) { - response = (HttpMessage)httpResponseMe(); + char * uname = (NULL != this->session)? this->session->username : ""; + response = (HttpMessage)httpResponseMe(uname); } if (0 == strcmp("/randval/", request->uri)) { - response = (HttpMessage)httpResponseRandval( - this->val->timestamp, - this->val->value); + if (NULL != this->session) { + response = (HttpMessage)httpResponseRandval( + this->val->timestamp, + this->val->value); + } else { + response = (HttpMessage)httpResponse403(); + } } if (0 == strcmp("/image/", request->uri)) { diff --git a/src/session.c b/src/session.c new file mode 100644 index 0000000..b3b4e37 --- /dev/null +++ b/src/session.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +#include "session.h" +#include "class.h" +#include "interface/class.h" + +#include "utils/hash.h" +#include "utils/memory.h" + + +static +int +sessionCtor(void * _this, va_list * params) +{ + Session this = _this; + char * uname = va_arg(* params, char *); + size_t nuname = va_arg(* params, size_t); + + this->livetime = time(NULL) + SESSION_LIVETIME; + this->id = sdbm((unsigned char *)uname, nuname) & this->livetime; + + this->username = malloc(nuname + 1); + this->username[nuname] = 0; + memcpy(this->username, uname, nuname); + + return 0; +} + +static +void +sessionDtor(void * _this) +{ + Session this = _this; + + FREE(this->username); +} + +INIT_IFACE(Class, sessionCtor, sessionDtor, NULL); +CREATE_CLASS(Session, NULL, IFACE(Class)); + +// vim: set ts=4 sw=4: diff --git a/src/session/add.c b/src/session/add.c new file mode 100644 index 0000000..07adfa2 --- /dev/null +++ b/src/session/add.c @@ -0,0 +1,36 @@ +#include + +#include "session.h" +#include "interface/class.h" + + +static +inline +int +sessionAddComp(const void * _a, const void * _b) +{ + Session a = (Session)_a; + Session b = (Session)_b; + return (a->id < b->id)? -1 : (a->id > b->id)? 1 : 0; +} + +Session +sessionAdd(const Session * root, Session session) +{ + Session * found = tsearch(session, (void**)root, sessionAddComp); + + if (NULL == found) { + return NULL; + } + + if (*found != session) { + /** + * \todo this should not happen, so do some logging here. + */ + delete(session); + } + + return *found; +} + +// vim: set ts=4 sw=4: diff --git a/src/session/delete.c b/src/session/delete.c new file mode 100644 index 0000000..ef560ee --- /dev/null +++ b/src/session/delete.c @@ -0,0 +1,23 @@ +#include + +#include "session.h" +#include "interface/class.h" + + +static +inline +int +sessionDeleteComp(const void * _a, const void * _b) +{ + unsigned long a = *(unsigned long *)_a; + Session b = (Session)_b; + return (a < b->id)? -1 : (a > b->id)? 1 : 0; +} + +void +sessionDelete(const Session * root, const unsigned long id) +{ + tdelete(&id, (void**)root, sessionDeleteComp); +} + +// vim: set ts=4 sw=4: diff --git a/src/session/get.c b/src/session/get.c new file mode 100644 index 0000000..2b622ee --- /dev/null +++ b/src/session/get.c @@ -0,0 +1,29 @@ +#include +#include + +#include "session.h" + + +static +inline +int +sessionGetComp(const void * _a, const void * _b) +{ + unsigned long a = *(unsigned long *)_a; + Session b = (Session)_b; + return (a < b->id)? -1 : (a > b->id)? 1 : 0; +} + +Session +sessionGet(const Session * root, const unsigned long id) +{ + Session * found = tfind(&id, (void**)root, sessionGetComp); + + if (NULL == found) { + return NULL; + } + + return *found; +} + +// vim: set ts=4 sw=4: