diff --git a/include/application/adapter/http.h b/include/application/adapter/http.h new file mode 100644 index 0000000..134b093 --- /dev/null +++ b/include/application/adapter/http.h @@ -0,0 +1,36 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 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 __APPLICATION_ADAPTER_HTTP_H__ +#define __APPLICATION_ADAPTER_HTTP_H__ + +#include "class.h" +#include "application.h" + + +CLASS(ApplicationAdapterHttp) { + Application application; +}; + +#endif // __APPLICATION_ADAPTER_HTTP_H__ + +// vim: set ts=4 sw=4: diff --git a/include/application/application.h b/include/application/application.h new file mode 100644 index 0000000..5a7f9b9 --- /dev/null +++ b/include/application/application.h @@ -0,0 +1,49 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 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 __APPLICATION_H__ +#define __APPLICATION_H__ + +#include "class.h" + +#include "auth/credential.h" + +struct randval { + time_t timestamp; + int value; +} + +CLASS(Application) { + Hash active_sessions; + void * auth; + struct randval * val; +}; + +// this should return a user account....now it only return success or failure. +int applicationLogin(Application, Credential); +unsigned long applicationSessionStart(Application); +void applicationSessionStop(Application, unsigned long); +Session applicationSessionGet(Application, unsigned long); + +#endif // __HTTP_HEADER_H__ + +// vim: set ts=4 sw=4: diff --git a/include/http/worker.h b/include/http/worker.h index f131260..af935e6 100644 --- a/include/http/worker.h +++ b/include/http/worker.h @@ -34,28 +34,24 @@ #include "cbuf.h" #include "session.h" +#include "http/request.h" +#include "http/response.h" #include "commons.h" -struct randval { - time_t timestamp; - int value; -}; - CLASS(HttpWorker) { - char * id; - struct randval * val; + char * id; - Cbuf pbuf; + Cbuf pbuf; + Hash asset_pool; - Hash asset_pool; + void * application_adapter; + + HttpRequest current_request; + HttpMessage current_response; HttpParser parser; HttpWriter writer; - Session session; - Session * sroot; - - void * auth; }; #endif // __HTTP_WORKER_H__ diff --git a/include/interface/observer.h b/include/interface/observer.h index 077823c..927c5d8 100644 --- a/include/interface/observer.h +++ b/include/interface/observer.h @@ -24,16 +24,16 @@ #ifndef __OBSERVER_H__ #define __OBSERVER_H__ -typedef void (* fptr_observerNotify)(void *, void*); +typedef void (* fptr_observerUpdate)(void *, void*); extern const struct interface i_Observer; struct i_Observer { const struct interface * const _; - fptr_observerNotify notify; + fptr_observerUpdate update; }; -extern void observerNotify(void *, void *); +extern void observerUpdate(void *, void *); #endif // __OBSERVER_H__ diff --git a/src/application/adapter/http/http.c b/src/application/adapter/http/http.c new file mode 100644 index 0000000..346e7ff --- /dev/null +++ b/src/application/adapter/http/http.c @@ -0,0 +1,63 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 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 "class.h" +#include "application/application.h" +#include "application/adapter/http.h" + +#include "utils/memory.h" +#include "interface/observer.h" + +static +int +applicationAdapterHttpCtor(void * _this, va_list * params) +{ + ApplicationAdapterHttp this = _this; + + this->application = va_arg(*params, Application); + + return 0; +} + +static +void +applicationAdapterHttpDtor(void * _this) +{ +} + + +void applicationAdapterHttpUpdate(ApplicationAdapterHttp, void *); + + +INIT_IFACE(Class, applicationAdapterHttpCtor, applicationAdapterHttpDtor); +INIT_IFACE(Observer, applicationAdapterHttpUpdate); +CREATE_CLASS( + ApplicationAdapterHttp, + NULL, + IFACE(Class), + IFACE(Observer)); + +// vim: set ts=4 sw=4: diff --git a/src/application/adapter/http/update.c b/src/application/adapter/http/update.c new file mode 100644 index 0000000..ebfcc88 --- /dev/null +++ b/src/application/adapter/http/update.c @@ -0,0 +1,161 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 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 "class.h" +#include "application/application.h" +#include "application/adapter/http.h" +#include "hash.h" +#include "http/worker.h" +#include "http/header.h" +#include "http/response.h" +#include "auth/credential.h" + +#include "utils/memory.h" + + +#define NO_SESSION_SID 0 + + +static +inline +unsigned long +getSessionId(Hash cookies) +{ + HashValue sidstr = hashGet(cookies, CSTRA("sid")); + + if (NULL != sidstr) { + return strtoul((char*)(sidstr->value), NULL, 10); + } + + return NO_SESSION_SID; +} + +static +void +loginAdapter(Application application, HttpWorker worker) +{ + HashValue username; + HashValue passeord; + Credential credential; + + username = hashGet( + worker->current_reqeust->post, + CSTRA("username")); + password = hashGet( + worker->current_reqeust->post, + CSTRA("password")); + + if (NULL == username || NULL == password) { + worker->current_response = + new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); + return; + } + + credential = new(Credential, + CRED_PASSWORD, + (char *)(username->value), username->nvalue, + (char *)(password->value), password->nvalue); + + if (applicationLogin(application, credential)) { + char buffer[200]; + size_t nbuf; + + if (NO_SESSION_SID == sid) { + sid = applicationSessionStart(application); + } else { + applicationSessionUpdate( + application, + sid, + username->value, + username->nvalue); + } + + nbuf = sprintf(buffer, "sid=%lu;Path=/", sid); + + worker->current_response = + (HttpMessage)httpResponseSession( + applicationSessionGet(sid)); + + hashAdd( + worker->current_response->header, + new(HttpHeader. CSTRA("Set-Cookie"), buffer, nbuf)); + } else { + worker->current_response = + new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); + } + + delete(credential) +} + + +void +applicationAdapterHttpUpdate(ApplicationAdapterHttp this, void * subject) +{ + HttpWorker worker = (HttpWorker)subject; + unsigned long sid = getSessionId(worker->current_request->cookies); + + if (0 == strcmp("POST", worker->current_request->method)) { + if (0 == strcmp("/login/", worker->current_request->path)) { + loginAdapter(this->application, worker); + return; + } + } + + if (0 == strcmp("GET", worker->current_request->method)) { + if (0 == strcmp("/sessinfo/", worker->current_request->path)) { + worker->current_response = + (HttpMessage)httpResponseSession( + applicationSessionGet(sid)); + return; + } + + if (0 == strcmp("/sess/", worker->current_request->path)) { + if (NO_SESSION_SID == sid) { + sid = applicationSessionStart(application); + } + + worker->current_response = + (HttpMessage)httpResponseSession( + applicationSessionGet(sid)); + return; + } + + if (0 == strcmp("/randval/", worker->current_request->path)) { + if (NO_SESSION_SID != sid) { + worker->current_response = + (HttpMessage)httpResponseRandval( + this->application->val->timestamp, + this->application->val->value); + } else { + worker->current_response = (HttpMessage)httpResponse403(); + } + } + } +} + +// vim: set ts=4 sw=4: diff --git a/src/application/application.c b/src/application/application.c new file mode 100644 index 0000000..6831004 --- /dev/null +++ b/src/application/application.c @@ -0,0 +1,54 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 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 "class.h" +#include "application/application.h" + +#include "utils/memory.h" + +static +int +applicationCtor(void * _this, va_list * params) +{ + Application this = _this; + + this->val = va_arg(*params, struct randval *); + this->auth = va_arg(* params, void *); + + return 0; +} + +static +void +applicationDtor(void * _this) +{ +} + + +INIT_IFACE(Class, applicationCtor, applicationDtor); +CREATE_CLASS(Application, NULL, IFACE(Class)); + +// vim: set ts=4 sw=4: diff --git a/src/http/worker.c b/src/http/worker.c index 71182e5..6ccb07f 100644 --- a/src/http/worker.c +++ b/src/http/worker.c @@ -38,6 +38,8 @@ #include "http/writer.h" #include "utils/memory.h" +#include "interface/subject.h" +#include "interface/observer.h" static int @@ -50,8 +52,6 @@ httpWorkerCtor(void * _this, va_list * params) this->id = memMalloc(strlen(id) + 1); strcpy(this->id, id); - this->val = va_arg(*params, struct randval *); - this->asset_pool = new(Hash); sprintf(cbuf_id, "%s_%s", "parser", id); @@ -60,20 +60,9 @@ httpWorkerCtor(void * _this, va_list * params) this->parser = new(HttpParser, this->pbuf); this->writer = new(HttpWriter); - this->sroot = &(this->session); - this->auth = va_arg(* params, void *); - return 0; } -static -inline -void -tDelete(void * node) -{ - delete(node); -} - static void httpWorkerDtor(void * _this) @@ -88,7 +77,6 @@ httpWorkerDtor(void * _this) if (NULL != this->pbuf) { delete(this->asset_pool); delete(this->pbuf); //!< cloned workers have NULL, so delete won't do anything - tdestroy(*(this->sroot), tDelete); } } @@ -99,9 +87,8 @@ httpWorkerClone(void * _this, void * _base) HttpWorker this = _this; HttpWorker base = _base; - this->val = base->val; - - this->asset_pool = base->asset_pool; + this->asset_pool = base->asset_pool; + this->application_adapter = base->application_adapter; this->parser = new(HttpParser, base->pbuf); /* @@ -124,22 +111,59 @@ httpWorkerClone(void * _this, void * _base) * it is. */ this->writer = new(HttpWriter); - - this->sroot = &(base->session); - this->auth = base->auth; } ssize_t httpWorkerProcess(void *, Stream); ssize_t httpWorkerWrite(void *, Stream); +static +void +httpWorkerDetach(void * _this, void * adapter) +{ + HttpWorker this = (HttpWorker)_this; + + if (NULL != this->application_adapter) { + delete(this->application_adapter); + } +} + +static +void +httpWorkerAttach(void * _this, void * adapter) +{ + HttpWorker this = (HttpWorker)_this; + + /* + * right now only one adapter is allowed and the last + * added will be used....all others will be deleted in + * assumption that no other handle does exist anymore + * (because it was added as an adapter and thus is good + * for nothing else.) + */ + httpWorkerDetach(_this, adapter); + + this->application_adapter = adapter; +} + +static +void +httpWorkerNotify(void * _this) +{ + HttpWorker this = (HttpWorker)_this; + + observerUpdate(this->application_adapter, _this); +} + INIT_IFACE(Class, httpWorkerCtor, httpWorkerDtor, httpWorkerClone); INIT_IFACE(StreamReader, httpWorkerProcess); INIT_IFACE(StreamWriter, httpWorkerWrite); +INIT_IFACE(Subject, httpWorkerAttach, httpWorkerDetach, httpWorkerNotify); CREATE_CLASS( HttpWorker, NULL, IFACE(Class), IFACE(StreamReader), - IFACE(StreamWriter)); + IFACE(StreamWriter), + IFACE(Subject)); // vim: set ts=4 sw=4: diff --git a/src/http/worker/add_common_header.c b/src/http/worker/add_common_header.c index 74890d8..5af7dd5 100644 --- a/src/http/worker/add_common_header.c +++ b/src/http/worker/add_common_header.c @@ -26,7 +26,7 @@ #include "http/message.h" #include "http/header.h" -#include "http/response.h" +#include "http/worker.h" #include "hash.h" @@ -35,35 +35,35 @@ void -httpWorkerAddCommonHeader(HttpMessage request, HttpMessage response) +httpWorkerAddCommonHeader(HttpWorker this) { char buffer[200]; size_t nbuf; - if (httpMessageHasKeepAlive(request)) { - hashAdd(response->header, + if (httpMessageHasKeepAlive((HttpMessage)this->current_request)) { + hashAdd(this->current_response->header, new(HttpHeader, CSTRA("Connection"), CSTRA("Keep-Alive"))); } else { - hashAdd(response->header, + hashAdd(this->current_response->header, new(HttpHeader, CSTRA("Connection"), CSTRA("Close"))); } - hashAdd(response->header, + hashAdd(this->current_response->header, new(HttpHeader, CSTRA("Server"), CSTRA("testserver"))); - switch(((HttpResponse)response)->status) { + switch(((HttpResponse)this->current_response)->status) { case 304: break; default: - nbuf = sprintf(buffer, "%d", response->nbody); - hashAdd(response->header, + nbuf = sprintf(buffer, "%d", this->current_response->nbody); + hashAdd(this->current_response->header, new(HttpHeader, CSTRA("Content-Length"), buffer, nbuf)); } nbuf = rfc1123GmtNow(buffer, sizeof(buffer)); - hashAdd(response->header, + hashAdd(this->current_response->header, new(HttpHeader, CSTRA("Date"), buffer, nbuf)); } diff --git a/src/http/worker/get_asset.c b/src/http/worker/get_asset.c index 66fd0c5..bcc82dd 100644 --- a/src/http/worker/get_asset.c +++ b/src/http/worker/get_asset.c @@ -32,10 +32,7 @@ #include "hash.h" HttpMessage -httpWorkerGetAsset( - HttpWorker this, - HttpRequest request, - const char * fname) +httpWorkerGetAsset(HttpWorker this, const char * fname) { char * match; size_t nmatch; @@ -45,7 +42,7 @@ httpWorkerGetAsset( size_t nfname = strlen(fname); header = hashGet( - ((HttpMessage)request)->header, + ((HttpMessage)this->current_request)->header, CSTRA("If-None-Match")); if (NULL == header) { diff --git a/src/http/worker/process.c b/src/http/worker/process.c index ee38054..05b8c1d 100644 --- a/src/http/worker/process.c +++ b/src/http/worker/process.c @@ -46,9 +46,8 @@ #include "commons.h" -HttpMessage httpWorkerGetAsset(HttpWorker, HttpRequest, const char *); -void httpWorkerAddCommonHeader(HttpMessage, HttpMessage); -char * httpWorkerGetMimeType(HttpWorker, const char * extension); +HttpMessage httpWorkerGetAsset(HttpWorker, const char *); +void httpWorkerAddCommonHeader(HttpWorker); ssize_t @@ -62,162 +61,61 @@ httpWorkerProcess(HttpWorker this, Stream st) if (0 < requests) { while (! queueEmpty(this->parser->queue)) { - HttpRequest request = queueGet(this->parser->queue); - HttpMessage response = NULL; + this->current_request = queueGet(this->parser->queue); + this->current_response = NULL; - /** - * \todo store the cookie count in the request to make a simple - * check possible to prevent this lookup if no cookies exists - * at all + /* + * let our observers...application (or better their + * http adapter) try to create an answer. */ - if (NULL == this->session) { - HashValue sidstr = hashGet(request->cookies, CSTRA("sid")); + subjectNotify(this); - if (NULL != sidstr) { - unsigned long sid; - - sid = strtoul((char*)(sidstr->value), NULL, 10); - this->session = sessionGet(this->sroot, sid); - } - } - - 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", this->current_request->method)) { + /* + * we can't do post requests on our own... + */ + this->current_response = (HttpMessage)httpResponse500(); } - if (0 == strcmp("POST", request->method)) { - if (0 == strcmp("/login/", request->path)) { - char buffer[200]; - size_t nbuf; - - HashValue username = hashGet(request->post, CSTRA("username")); - HashValue password = hashGet(request->post, CSTRA("password")); - - /** - * \todo This is an application authorization not an HTTP - * authorization...anyway think about sending HTTP 401 - * messages if authorization is required and think about - * sending the credentials via header as described in the - * HTTP protocol. Most likely this will lead to hacky thing - * with javascript as i am not sure how far this is implemented - * within browsers. - * Anyway, for now we simply ignore a failed login within the - * response except that no session is initialized. We send - * an empty 200 OK - */ - if (NULL == password || NULL == username) { - response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); - } - - if (NULL == response) { - Credential cred = new(Credential, - CRED_PASSWORD, - (char*)(username->value), username->nvalue, - (char*)(password->value), password->nvalue); - - if (!authenticate(this->auth, cred)) { - response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden"); - } else { - if (NULL == this->session) { - this->session = sessionAdd( - this->sroot, - new(Session, - username->value, - username->nvalue)); - } else { - this->session->username = memMalloc(username->nvalue + 1); - this->session->username[username->nvalue] = 0; - memcpy(this->session->username, - username->value, - username->nvalue); - } - - nbuf = sprintf(buffer, - "sid=%lu;Path=/", - this->session->id); - - response = (HttpMessage)httpResponseSession( - this->session); - - hashAdd(response->header, - new(HttpHeader, - CSTRA("Set-Cookie"), - buffer, nbuf)); - } - delete(cred); - } - } - } + if (0 == strcmp("GET", this->current_request->method)) { + char html_asset[2048] = "./assets/html"; + char base_asset[2048] = "./assets"; + char main_asset[] = "/main.html"; - if (0 == strcmp("GET", request->method)) { - if (0 == strcmp("/sessinfo/", request->path)) { - response = (HttpMessage)httpResponseSession(this->session); - } + char * asset_path = base_asset; + char * asset; + char * mime_type; - else if (0 == strcmp("/sess/", request->path)) { - if (NULL == this->session) { - this->session = sessionAdd( - this->sroot, - new(Session, NULL, 0)); - } - response = (HttpMessage)httpResponseSession(this->session); + if (0 == strcmp("/", this->current_request->path)) { + asset = main_asset; + } else { + asset = this->current_request->path; } - else if (0 == strcmp("/randval/", request->path)) { - if (NULL != this->session) { - response = (HttpMessage)httpResponseRandval( - this->val->timestamp, - this->val->value); - } else { - response = (HttpMessage)httpResponse403(); - } + mime_type = strrchr(asset, '.'); + if (NULL != mime_type) { + mime_type++; + mime_type = getMimeType(mime_type, strlen(mime_type)); } - else { - char html_asset[2048] = "./assets/html"; - char base_asset[2048] = "./assets"; - char main_asset[] = "/main.html"; - - char * asset_path = base_asset; - char * asset; - char * mime_type; - - if (0 == strcmp("/", request->path)) { - asset = main_asset; - } else { - asset = request->path; - } - - mime_type = strrchr(asset, '.'); - if (NULL != mime_type) { - mime_type++; - mime_type = getMimeType(mime_type, strlen(mime_type)); - } - - if (NULL != mime_type && - 0 == memcmp(mime_type, CSTRA("text/html"))) { - asset_path = html_asset; - } - - strcat(asset_path, asset); - response = httpWorkerGetAsset(this, request, asset_path); + if (NULL != mime_type && + 0 == memcmp(mime_type, CSTRA("text/html"))) { + asset_path = html_asset; } + strcat(asset_path, asset); + this->current_response = + httpWorkerGetAsset(this, asset_path); } - if (NULL == response) { - response = (HttpMessage)httpResponse404(); + if (NULL == this->current_response) { + this->current_response = (HttpMessage)httpResponse404(); } - httpWorkerAddCommonHeader((HttpMessage)request, response); - delete(request); - queuePut(this->writer->queue, response); - response = NULL; + httpWorkerAddCommonHeader(this); + delete(this->current_request); + queuePut(this->writer->queue, this->current_response); + this->current_response = NULL; } } diff --git a/src/interface/observer.c b/src/interface/observer.c index 7775c6b..90b1d19 100644 --- a/src/interface/observer.c +++ b/src/interface/observer.c @@ -29,9 +29,9 @@ const struct interface i_Observer = { }; void -observerNotify(void * observer, void * subject) +observerUpdate(void * observer, void * subject) { - CALL(observer, Observer, notify, subject); + CALL(observer, Observer, update, subject); } // vim: set ts=4 sw=4: diff --git a/src/taskrambler.c b/src/taskrambler.c index 3eaf459..80569bd 100644 --- a/src/taskrambler.c +++ b/src/taskrambler.c @@ -134,9 +134,11 @@ main() default: { - AuthLdap auth; - HttpWorker worker; - Server server; + AuthLdap auth; + //Application application; + //ApplicationAdapterHttp adapterHttp; + HttpWorker worker; + Server server; value = mmap (0, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, shm, 0); @@ -146,9 +148,13 @@ main() logger = new(LoggerSyslog, LOGGER_DEBUG); auth = new(AuthLdap, - "ldap://localhost/", + "ldap://hosted/", CSTRA("ou=user,dc=yabrog,dc=weird-web-workers,dc=org")); - worker = new(HttpWorker, "testserver", value, auth); + worker = new(HttpWorker, "testserver"); + application = new(Application, value, auth); + adapterHttp = new(ApplicationAdapterHttp, application); + subjectAttach(worker, adapterHttp); + server = new(Server, logger, worker, 11212, SOMAXCONN); if (NULL != server) {