diff --git a/ChangeLog b/ChangeLog index 9958d02..1eeec15 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,14 @@ +2012-02-15 09:38:32 +0100 Georg Hopp + + * separated the server completely from the http processing (HEAD, master) + 2012-02-15 06:19:52 +0100 Georg Hopp - * add subject/observer interface (HEAD, master) + * add subject/observer interface 2012-02-15 04:55:46 +0100 Georg Hopp - * fix infinite busy loop in run (origin/master, origin/HEAD) + * fix infinite busy loop in run 2012-02-15 04:44:38 +0100 Georg Hopp diff --git a/TODO b/TODO index 36521bd..7f6e1f0 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,6 @@ VERY BIG TODO: - give a contructor a way to fail, so that no object will be created at all + +- right now i will use long polling ajax calls when feedback from to the client + is needed. In the long term this should be changed to websockets (ws). But + right now ws specification is not final anyway. :) diff --git a/include/http/request/parser.h b/include/http/request/parser.h index f37ae0c..09b5d11 100644 --- a/include/http/request/parser.h +++ b/include/http/request/parser.h @@ -34,7 +34,7 @@ CLASS(HttpRequestParser) { }; size_t httpRequestParserRead(HttpRequestParser, int); -void httpRequestParserParse(HttpRequestParser); +size_t httpRequestParserParse(HttpRequestParser); void httpRequestParserGetBody(HttpRequestParser); void httpRequestParserGetRequestLine(HttpRequest, char *); diff --git a/include/http/response/writer.h b/include/http/response/writer.h index 76bd63a..ac9e300 100644 --- a/include/http/response/writer.h +++ b/include/http/response/writer.h @@ -29,7 +29,7 @@ CLASS(HttpResponseWriter) { HttpResponseState state; }; -HttpResponse httpResponseWriterWrite(HttpResponseWriter, int); +size_t httpResponseWriterWrite(HttpResponseWriter, int); #endif // __HTTP_RESPONSE_WRITER_H__ diff --git a/include/http/worker.h b/include/http/worker.h index 1cbaa42..59703df 100644 --- a/include/http/worker.h +++ b/include/http/worker.h @@ -1,13 +1,20 @@ #ifndef __HTTP_WORKER_H__ #define __HTTP_WORKER_H__ +#include + #include "class.h" #include "http/request/parser.h" +#include "http/response/writer.h" CLASS(HttpWorker) { - HttpRequestParser parser; + HttpRequestParser parser; + HttpResponseWriter writer; }; +size_t httpWorkerProcess(HttpWorker, int); +size_t httpWorkerWrite(HttpWorker, int); + #endif // __HTTP_WORKER_H__ // vim: set ts=4 sw=4: diff --git a/include/server.h b/include/server.h index 4cb9878..dbc0737 100644 --- a/include/server.h +++ b/include/server.h @@ -27,13 +27,15 @@ CLASS(Server) { Logger logger; Sock sock; + void * worker; + nfds_t nfds; struct pollfd fds[POLL_FD_NSIZE]; struct { Sock sock; - void * reader; - void * writer; + + void * worker; char keep_alive; } conns[POLL_FD_NSIZE]; diff --git a/src/Makefile.am b/src/Makefile.am index 4e6c690..ac86379 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,6 +15,7 @@ RESP = http/response.c \ http/response/404.c \ http/response/image.c \ http/response/me.c +WORKER = http/worker.c http/worker/process.c http/worker/write.c WRITER = http/response/writer.c http/response/writer/write.c HEADER = http/header.c http/header/get.c http/header/add.c \ http/header/size_get.c http/header/to_string.c @@ -29,5 +30,6 @@ bin_PROGRAMS = testserver testserver_SOURCES = testserver.c \ $(IFACE) $(CLASS) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \ - $(WRITER) $(RESP) $(HEADER) $(PARSER) signalHandling.c daemonize.c + $(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) \ + signalHandling.c daemonize.c testserver_CFLAGS = -Wall -I ../include/ diff --git a/src/http/request/parser.c b/src/http/request/parser.c index d196ab1..60c9b7f 100644 --- a/src/http/request/parser.c +++ b/src/http/request/parser.c @@ -59,8 +59,7 @@ _clone(void * _this, void * _base) } INIT_IFACE(Class, ctor, dtor, _clone); -INIT_IFACE(StreamReader, - (fptr_streamReaderRead)httpRequestParserRead); +INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpRequestParserRead); CREATE_CLASS(HttpRequestParser, NULL, IFACE(Class), IFACE(StreamReader)); // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/parse.c b/src/http/request/parser/parse.c index 84e2120..075fcd0 100644 --- a/src/http/request/parser/parse.c +++ b/src/http/request/parser/parse.c @@ -33,7 +33,7 @@ httpRequestSkip(char ** data) for (; 0 != **data && ! isalpha(**data); (*data)++); } -void +size_t httpRequestParserParse(HttpRequestParser this) { char * line; @@ -111,6 +111,8 @@ httpRequestParserParse(HttpRequestParser this) break; } } + + return this->request_queue->nmsgs; } // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/read.c b/src/http/request/parser/read.c index 6201df3..521a3a2 100644 --- a/src/http/request/parser/read.c +++ b/src/http/request/parser/read.c @@ -7,10 +7,10 @@ size_t httpRequestParserRead(HttpRequestParser this, int fd) { - size_t remaining, chunks; - char buffer[1024]; + size_t remaining, chunks; + char buffer[1024]; - ssize_t size = read(fd, buffer, 1024); + size_t size = read(fd, buffer, 1024); if (0 < size) { remaining = this->buffer_used % HTTP_REQUEST_PARSER_READ_CHUNK; @@ -35,7 +35,7 @@ httpRequestParserRead(HttpRequestParser this, int fd) this->buffer_used += size; this->buffer[this->buffer_used] = 0; - httpRequestParserParse(this); + size = httpRequestParserParse(this); } return size; diff --git a/src/http/response/writer.c b/src/http/response/writer.c index e64a452..c05080d 100644 --- a/src/http/response/writer.c +++ b/src/http/response/writer.c @@ -38,8 +38,7 @@ _clone(void * _this, void * _base) } INIT_IFACE(Class, ctor, dtor, _clone); -INIT_IFACE(StreamWriter, - (fptr_streamWriterWrite)httpResponseWriterWrite); +INIT_IFACE(StreamWriter, (fptr_streamWriterWrite)httpResponseWriterWrite); CREATE_CLASS(HttpResponseWriter, NULL, IFACE(Class), IFACE(StreamWriter)); // vim: set ts=4 sw=4: diff --git a/src/http/response/writer/write.c b/src/http/response/writer/write.c index f3e9a33..8f27400 100644 --- a/src/http/response/writer/write.c +++ b/src/http/response/writer/write.c @@ -16,19 +16,21 @@ #define _PSIZE(x) (MAX((x),RESPONSE_WRITER_MAX_BUF)) #define PSIZE _PSIZE(this->nheader+message->nbody) -HttpResponse +size_t httpResponseWriterWrite(HttpResponseWriter this, int fd) { - HttpMessageQueue respq = this->response_queue; - HttpMessage message = (HttpMessage)this->cur_response; - int cont = 1; + HttpMessageQueue respq = this->response_queue; + HttpMessage message = (HttpMessage)this->cur_response; + size_t processed = (message)? 1 : 0; + int cont = 1; while (cont) { switch (this->state) { case HTTP_RESPONSE_GET: if (NULL == this->cur_response && 0 < respq->nmsgs) { - message = respq->msgs[0]; + message = respq->msgs[0]; this->cur_response = (HttpResponse)message; + processed++; memmove(respq->msgs, &(respq->msgs[1]), @@ -128,19 +130,25 @@ httpResponseWriterWrite(HttpResponseWriter this, int fd) this->pstart = 0; this->pend = 0; - if (httpMessageHasKeepAlive(message)) { - delete(&this->cur_response); - } - else { - cont = 0; + if (! httpMessageHasKeepAlive(message)) { + /** + * if the message did not have the keep-alive feature + * we don't care about further pipelined messages and + * return the to caller with a 0 indicating that the + * underlying connection should be closed. + */ + processed = 0; + cont = 0; } + delete(&this->cur_response); + this->state = HTTP_RESPONSE_GET; break; } } - return this->cur_response; + return processed; } // vim: set ts=4 sw=4: diff --git a/src/http/worker.c b/src/http/worker.c new file mode 100644 index 0000000..73798e4 --- /dev/null +++ b/src/http/worker.c @@ -0,0 +1,55 @@ +#include + +#include "class.h" +#include "http/worker.h" +#include "http/request/parser.h" +#include "http/response/writer.h" + +#include "interface/class.h" +#include "interface/stream_reader.h" +#include "interface/stream_writer.h" + +static +void +ctor(void * _this, va_list * params) +{ + HttpWorker this = _this; + + this->parser = new(HttpRequestParser); + this->writer = new(HttpResponseWriter); +} + +static +void +dtor(void * _this) +{ + HttpWorker this = _this; + + delete(&this->parser); + delete(&this->writer); +} + +static +void +_clone(void * _this, void * _base) +{ + /** + * TODO: this actually simply creates a new worker + * and ignores the base. Think about this. + */ + va_list foo; + + ctor(_this, &foo); +} + +INIT_IFACE(Class, ctor, dtor, _clone); +INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpWorkerProcess); +INIT_IFACE(StreamWriter, (fptr_streamWriterWrite)httpWorkerWrite); +CREATE_CLASS( + HttpWorker, + NULL, + IFACE(Class), + IFACE(StreamReader), + IFACE(StreamWriter)); + +// vim: set ts=4 sw=4: diff --git a/src/http/worker/process.c b/src/http/worker/process.c new file mode 100644 index 0000000..43dd667 --- /dev/null +++ b/src/http/worker/process.c @@ -0,0 +1,59 @@ +#include "class.h" +#include "interface/class.h" + +#include "http/worker.h" +#include "http/request/parser.h" + +size_t +httpWorkerProcess(HttpWorker this, int fd) +{ + size_t size; + + if (0 < (size = httpRequestParserRead(this->parser, fd))) { + int i; + HttpMessageQueue reqq = this->parser->request_queue; + HttpMessageQueue respq = this->writer->response_queue; + + for (i=0; inmsgs; i++) { + /** + * @TODO: for now simply remove request and send not found. + * Make this sane. + */ + HttpRequest request = (HttpRequest)(reqq->msgs[i]); + HttpMessage response = NULL; + + if (0 == strcmp("GET", request->method) && + 0 == strcmp("/me/", request->uri)) { + response = (HttpMessage)httpResponseMe(); + } + else if (0 == strcmp("GET", request->method) && + 0 == strcmp("/image/", request->uri)) { + response = (HttpMessage)httpResponseImage(); + } + else { + response = (HttpMessage)httpResponse404(); + } + + if (httpMessageHasKeepAlive(reqq->msgs[i])) { + httpHeaderAdd( + &(response->header), + new(HttpHeader, "Connection", "Keep-Alive")); + } + else { + httpHeaderAdd( + &(response->header), + new(HttpHeader, "Connection", "Close")); + } + + respq->msgs[(respq->nmsgs)++] = response; + response = NULL; + delete(&(reqq->msgs[i])); + } + + reqq->nmsgs = 0; + } + + return size; +} + +// vim: set ts=4 sw=4: diff --git a/src/http/worker/write.c b/src/http/worker/write.c new file mode 100644 index 0000000..387b14f --- /dev/null +++ b/src/http/worker/write.c @@ -0,0 +1,12 @@ +#include + +#include "http/worker.h" +#include "http/response/writer.h" + +size_t +httpWorkerWrite(HttpWorker this, int fd) +{ + return httpResponseWriterWrite(this->writer, fd); +} + +// vim: set ts=4 sw=4: diff --git a/src/server.c b/src/server.c index 299f15b..ece6cfc 100644 --- a/src/server.c +++ b/src/server.c @@ -20,6 +20,7 @@ ctor(void * _this, va_list * params) int flags; this->logger = va_arg(* params, Logger); + this->worker = va_arg(* params, void *); port = va_arg(* params, int); backlog = va_arg(* params, unsigned int); @@ -45,8 +46,7 @@ dtor(void * _this) for (i=0; infds; i++) { if (this->sock->handle != (this->fds)[i].fd) { delete(&(this->conns[(this->fds)[i].fd]).sock); - delete(&(this->conns[(this->fds)[i].fd]).reader); - delete(&(this->conns[(this->fds)[i].fd]).writer); + delete(&(this->conns[(this->fds)[i].fd]).worker); } } diff --git a/src/server/close_conn.c b/src/server/close_conn.c index 264aef3..9d7d756 100644 --- a/src/server/close_conn.c +++ b/src/server/close_conn.c @@ -10,8 +10,7 @@ serverCloseConn(Server this, unsigned int i) int fd = (this->fds)[i].fd; delete(&((this->conns)[fd].sock)); - delete(&((this->conns)[fd].reader)); - delete(&((this->conns)[fd].writer)); + delete(&((this->conns)[fd].worker)); (this->conns)[fd].keep_alive = 0; diff --git a/src/server/handle_accept.c b/src/server/handle_accept.c index 76fb13e..faf6804 100644 --- a/src/server/handle_accept.c +++ b/src/server/handle_accept.c @@ -12,8 +12,7 @@ serverHandleAccept(Server this) (this->conns)[acc->handle].sock = acc; //* clone reader - (this->conns)[acc->handle].reader = new(HttpRequestParser); - (this->conns)[acc->handle].writer = new(HttpResponseWriter); + (this->conns)[acc->handle].worker = clone(this->worker); (this->fds)[this->nfds].fd = acc->handle; (this->fds)[this->nfds].events = POLLIN; diff --git a/src/server/read.c b/src/server/read.c index bdb4738..199f6f2 100644 --- a/src/server/read.c +++ b/src/server/read.c @@ -5,7 +5,7 @@ serverRead(Server this, unsigned int i) int fd = (this->fds)[i].fd; int size; - if (NULL == (this->conns)[fd].reader) { + if (NULL == (this->conns)[fd].worker) { loggerLog( this->logger, LOGGER_INFO, @@ -13,7 +13,7 @@ serverRead(Server this, unsigned int i) return -1; } - switch ((size = streamReaderRead((this->conns)[fd].reader, fd))) { + switch ((size = streamReaderRead((this->conns)[fd].worker, fd))) { case 0: /* * normal close: write remaining data diff --git a/src/server/run.c b/src/server/run.c index 324a212..23f7b44 100644 --- a/src/server/run.c +++ b/src/server/run.c @@ -19,14 +19,6 @@ #include "interface/stream_writer.h" #include "interface/logger.h" -//* @TODO: to be removed -#include "http/request.h" -#include "http/request/parser.h" -#include "http/message/queue.h" -#include "http/response.h" -#include "http/response/writer.h" -//* until here - #undef MAX #define MAX(x,y) ((x) > (y) ? (x) : (y)) @@ -94,63 +86,17 @@ serverRun(Server this) */ else { nreads--; - /** - * do some other processing - * @TODO: actually this will hard assume that our stream reader - * is a http parser and it has its queue...think about more - * generalizing here. - */ - int size; - - if (0 >= (size=serverRead(this, i))) { - serverCloseConn(this, i); - } - else { - int j; - HttpMessageQueue reqq = ((HttpRequestParser) \ - (this->conns)[fd].reader)->request_queue; - HttpMessageQueue respq = ((HttpResponseWriter) \ - (this->conns)[fd].writer)->response_queue; - - for (j=0; jnmsgs; j++) { - /** - * @TODO: for now simply remove request and send not found. - * Make this sane. - */ - HttpRequest request = (HttpRequest)(reqq->msgs[j]); - HttpMessage response = NULL; - - if (0 == strcmp("GET", request->method) && - 0 == strcmp("/me/", request->uri)) { - response = (HttpMessage)httpResponseMe(); - } - else if (0 == strcmp("GET", request->method) && - 0 == strcmp("/image/", request->uri)) { - response = (HttpMessage)httpResponseImage(); - } - else { - response = (HttpMessage)httpResponse404(); - } - - if (httpMessageHasKeepAlive(reqq->msgs[j])) { - httpHeaderAdd( - &(response->header), - new(HttpHeader, "Connection", "Keep-Alive")); - } - else { - httpHeaderAdd( - &(response->header), - new(HttpHeader, "Connection", "Close")); - } - - respq->msgs[(respq->nmsgs)++] = response; - response = NULL; - delete(&(reqq->msgs[j])); - (this->fds)[i].events |= POLLOUT; - } + switch (serverRead(this, i)) { + case -1: + serverCloseConn(this, i); + break; + + case 0: + break; - reqq->nmsgs = 0; + default: + (this->fds)[i].events |= POLLOUT; } } } @@ -159,22 +105,14 @@ serverRun(Server this) * handle writes */ if (0 != ((this->fds)[i].revents & POLLOUT) && 0 < nwrites) { - HttpResponseWriter writer = - (HttpResponseWriter)(this->conns)[fd].writer; - HttpMessage message; - events--; nwrites--; - message = (HttpMessage)streamWriterWrite(writer, fd); - - if (NULL == message) { - (this->fds)[i].events &= ~POLLOUT; - } - else { - delete(&message); + if (0 >= streamWriterWrite((this->conns)[fd].worker, fd)) { serverCloseConn(this, i); } + + (this->fds)[i].events &= ~POLLOUT; } } } diff --git a/src/testserver.c b/src/testserver.c index 6e3a262..d7b3ba1 100644 --- a/src/testserver.c +++ b/src/testserver.c @@ -7,7 +7,7 @@ #include "server.h" #include "logger.h" -#include "http/request/parser.h" +#include "http/worker.h" #include "signalHandling.h" @@ -18,8 +18,9 @@ void daemonize(void); int main() { - Logger logger = new(LoggerSyslog, LOGGER_ERR); - Server server = new(Server, logger, 11212, SOMAXCONN); + Logger logger = new(LoggerSyslog, LOGGER_ERR); + HttpWorker worker = new(HttpWorker); + Server server = new(Server, logger, worker, 11212, SOMAXCONN); struct rlimit limit = {RLIM_INFINITY, RLIM_INFINITY}; setrlimit(RLIMIT_CPU, &limit); @@ -29,6 +30,7 @@ main() serverRun(server); delete(&server); + delete(&worker); delete(&logger); return 0;