diff --git a/include/http/request/parser.h b/include/http/parser.h similarity index 60% rename from include/http/request/parser.h rename to include/http/parser.h index e16b7a2..43a4bb1 100644 --- a/include/http/request/parser.h +++ b/include/http/parser.h @@ -21,11 +21,11 @@ * along with this program. If not, see . */ -#ifndef __HTTP_REQUEST_PARSER_H__ -#define __HTTP_REQUEST_PARSER_H__ +#ifndef __HTTP_PARSER_H__ +#define __HTTP_PARSER_H__ #include "class.h" -#include "http/request.h" +#include "http/message.h" #include "http/message/queue.h" #include "cbuf.h" @@ -38,36 +38,33 @@ #endif -typedef enum e_HttpRequestState { - HTTP_REQUEST_GARBAGE=0, - HTTP_REQUEST_START, - HTTP_REQUEST_REQUEST_LINE_DONE, - HTTP_REQUEST_HEADERS_DONE, - HTTP_REQUEST_DONE -} HttpRequestState; +typedef enum e_HttpMessageState { + HTTP_MESSAGE_GARBAGE=0, + HTTP_MESSAGE_START, + HTTP_MESSAGE_INTRO_DONE, + HTTP_MESSAGE_HEADERS_DONE, + HTTP_MESSAGE_DONE +} HttpMessageState; -CLASS(HttpRequestParser) { +CLASS(HttpParser) { Cbuf buffer; void * ourLock; char * incomplete; size_t isize; - HttpMessageQueue request_queue; - HttpRequest cur_request; + HttpMessageQueue queue; + HttpMessage current; - HttpRequestState state; + HttpMessageState state; }; -ssize_t httpRequestParserParse(HttpRequestParser, int); -void httpRequestParserGetBody(HttpRequestParser); +ssize_t httpParserParse(void *, int); +void httpParserHeader(HttpParser, const char *, const char *); +void httpParserNewMessage(HttpParser, const char *, const char * lend); +size_t httpParserBody(HttpParser, const char *, size_t); -void httpRequestParserGetRequestLine( - HttpRequest, const char *, const char *); -void httpRequestParserGetHeader( - HttpRequest, const char *, const char *); - -#endif // __HTTP_REQUEST_PARSER_H__ +#endif // __HTTP_PARSER_H__ // vim: set ts=4 sw=4: diff --git a/include/http/worker.h b/include/http/worker.h index 08bf8dc..73dc9a4 100644 --- a/include/http/worker.h +++ b/include/http/worker.h @@ -27,7 +27,7 @@ #include #include "class.h" -#include "http/request/parser.h" +#include "http/parser.h" #include "http/response/writer.h" #include "cbuf.h" @@ -49,13 +49,10 @@ CLASS(HttpWorker) { Cbuf pbuf; Cbuf wbuf; - HttpRequestParser parser; + HttpParser parser; HttpResponseWriter writer; }; -ssize_t httpWorkerProcess(HttpWorker, int); -ssize_t httpWorkerWrite(HttpWorker, int); - #endif // __HTTP_WORKER_H__ // vim: set ts=4 sw=4: diff --git a/include/utils/http.h b/include/utils/http.h new file mode 100644 index 0000000..38d4511 --- /dev/null +++ b/include/utils/http.h @@ -0,0 +1,16 @@ +#ifndef __UTILS_HTTP_H__ +#define __UTILS_HTTP_H__ + +#include + +#include "http/message.h" + +char isHttpVersion(const char *, size_t); +HttpMessage httpGetMessage( + const char *, size_t, + const char *, size_t, + const char *, size_t); + +#endif // __UTILS_HTTP_H__ + +// vim: set ts=4 sw=4: diff --git a/src/Makefile.am b/src/Makefile.am index 4fbfc6a..49cbb74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,24 +14,38 @@ CB = cbuf.c cbuf/read.c cbuf/write.c \ cbuf/inc_read.c cbuf/inc_write.c cbuf/is_empty.c cbuf/memchr.c \ cbuf/skip_non_alpha.c cbuf/is_locked.c cbuf/lock.c cbuf/release.c \ cbuf/empty.c -MSG = http/message.c http/message/queue.c http/message/has_keep_alive.c \ - http/message/header_size_get.c http/message/header_to_string.c \ - http/message/get_version.c http/message/has_valid_version.c -REQ = http/request.c http/request/has_valid_method.c +MSG = http/message.c \ + http/message/has_keep_alive.c \ + http/message/header_size_get.c \ + http/message/header_to_string.c \ + http/message/get_version.c \ + http/message/has_valid_version.c +MSGQ = http/message/queue.c +REQ = http/request.c \ + http/request/has_valid_method.c RESP = http/response.c \ http/response/304.c \ http/response/404.c \ http/response/asset.c \ http/response/me.c -WORKER = http/worker.c http/worker/process.c http/worker/write.c \ - http/worker/get_asset.c http/worker/add_common_header.c +PARSER = http/parser.c \ + http/parser/parse.c \ + http/parser/new_message.c \ + http/parser/header.c \ + http/parser/body.c +WORKER = http/worker.c \ + http/worker/process.c \ + http/worker/write.c \ + http/worker/get_asset.c \ + http/worker/add_common_header.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/to_string.c -PARSER = http/request/parser.c http/request/parser/get_header.c \ - http/request/parser/parse.c http/request/parser/get_request_line.c \ - http/request/parser/get_body.c -UTILS = utils/hash.c utils/memory.c utils/daemonize.c utils/signalHandling.c +UTILS = utils/hash.c \ + utils/memory.c \ + utils/http.c \ + utils/daemonize.c \ + utils/signalHandling.c AM_CFLAGS = -Wall -I ../include/ @@ -41,6 +55,6 @@ bin_PROGRAMS = testserver testserver_SOURCES = testserver.c \ $(IFACE) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \ $(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) $(CB) \ - $(UTILS) + $(UTILS) $(MSGQ) testserver_CFLAGS = -Wall -I ../include/ testserver_LDFLAGS = -lrt diff --git a/src/http/message/has_keep_alive.c b/src/http/message/has_keep_alive.c index 652cf5d..e92e575 100644 --- a/src/http/message/has_keep_alive.c +++ b/src/http/message/has_keep_alive.c @@ -28,6 +28,13 @@ #include "http/request.h" #include "http/header.h" +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif char httpMessageHasKeepAlive(HttpMessage message) @@ -35,7 +42,6 @@ httpMessageHasKeepAlive(HttpMessage message) HttpHeader header; size_t size; char * value; - char * keep_alive = "keep-alive"; header = httpHeaderGet( &(message->header), @@ -46,21 +52,10 @@ httpMessageHasKeepAlive(HttpMessage message) return 0; } - if ((sizeof("keep-alive")-1) != (header->nvalue)[0]) { - return 0; - } - size = (header->nvalue)[0]; value = (header->value)[0]; - for (; 0 < size && tolower(*value) == *keep_alive; - size--, value++, keep_alive++); - - if (0 == size) { - return 1; - } - - return 0; + return (0 == strncasecmp("keep-alive", value, size))? 1 : 0; } // vim: set ts=4 sw=4: diff --git a/src/http/message/has_valid_version.c b/src/http/message/has_valid_version.c index 6fed4ab..1195995 100644 --- a/src/http/message/has_valid_version.c +++ b/src/http/message/has_valid_version.c @@ -23,6 +23,7 @@ #include #include "http/message.h" +#include "utils/http.h" int httpMessageHasValidVersion(HttpMessage this) @@ -30,18 +31,12 @@ httpMessageHasValidVersion(HttpMessage this) int major; int minor; - if (NULL == this->version) - return 0; - - if (8 > strlen(this->version)) + if (! isHttpVersion(this->version, strlen(this->version))) return 0; if (0 > httpMessageGetVersion(this, &major, &minor)) return 0; - if (0 != memcmp("HTTP/", this->version, sizeof("HTTP/")-1)) - return 0; - if (1 != major) return 0; diff --git a/src/http/request/parser.c b/src/http/parser.c similarity index 68% rename from src/http/request/parser.c rename to src/http/parser.c index a3b57b3..d294a67 100644 --- a/src/http/request/parser.c +++ b/src/http/parser.c @@ -28,9 +28,10 @@ #include "interface/class.h" #include "interface/stream_reader.h" -#include "http/request/parser.h" +#include "http/parser.h" #include "http/message/queue.h" #include "http/request.h" +#include "http/response.h" #include "cbuf.h" #include "utils/memory.h" @@ -38,33 +39,38 @@ static int -requestParserCtor(void * _this, va_list * params) +httpParserCtor(void * _this, va_list * params) { - HttpRequestParser this = _this; + HttpParser this = _this; - this->buffer = va_arg(* params, Cbuf); - this->request_queue = new(HttpMessageQueue); + this->buffer = va_arg(* params, Cbuf); + + if (NULL == this->buffer) { + return -1; + } + + this->queue = new(HttpMessageQueue); return 0; } static void -requestParserDtor(void * _this) +httpParserDtor(void * _this) { - HttpRequestParser this = _this; + HttpParser this = _this; - delete(this->request_queue); + delete(this->queue); if (TRUE == this->ourLock) cbufRelease(this->buffer); FREE(this->incomplete); - delete(this->cur_request); + delete(this->current); } -INIT_IFACE(Class, requestParserCtor, requestParserDtor, NULL); -INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpRequestParserParse); -CREATE_CLASS(HttpRequestParser, NULL, IFACE(Class), IFACE(StreamReader)); +INIT_IFACE(Class, httpParserCtor, httpParserDtor, NULL); +INIT_IFACE(StreamReader, httpParserParse); +CREATE_CLASS(HttpParser, NULL, IFACE(Class), IFACE(StreamReader)); // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/get_header.c b/src/http/parser/body.c similarity index 63% rename from src/http/request/parser/get_header.c rename to src/http/parser/body.c index eadb079..8557217 100644 --- a/src/http/request/parser/get_header.c +++ b/src/http/parser/body.c @@ -20,31 +20,31 @@ * along with this program. If not, see . */ -#include +#include #include -#include "class.h" -#include "interface/class.h" #include "http/header.h" #include "http/message.h" -#include "http/request/parser.h" -#include "ringbuffer.h" - -void -httpRequestParserGetHeader( - HttpMessage message, - const char * line, - const char * line_end) +#include "http/parser.h" +#include "cbuf.h" + +#define MAX(a,b) (((a) > (b))? (a) : (b)) + +size_t +httpParserBody(HttpParser this, const char * buf, size_t nbuf) { - const char * name = line; - char * value = memchr(line, ':', line_end - line); - size_t nname = (value++) - name; + size_t len = 0; + HttpMessage current = this->current; + + if (current->dbody < current->nbody) { + len = MAX(current->nbody - current->dbody, nbuf); + + memcpy(current->body, buf, len); - for (; *value == ' ' && *value != 0; value++); + current->dbody += len; + } - httpHeaderAdd( - &(message->header), - new(HttpHeader, name, nname, value, line_end - value)); + return len; } // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/get_body.c b/src/http/parser/header.c similarity index 54% rename from src/http/request/parser/get_body.c rename to src/http/parser/header.c index c5758a1..d6b48cf 100644 --- a/src/http/request/parser/get_body.c +++ b/src/http/parser/header.c @@ -21,14 +21,14 @@ */ #include +#include +#include +#include "class.h" +#include "interface/class.h" #include "http/header.h" +#include "http/parser.h" #include "http/message.h" -#include "http/request/parser.h" -#include "cbuf.h" - -#define MAX(a,b) (((a) > (b))? (a) : (b)) - #define MAX(x,y) ((x) > (y) ? (x) : (y)) @@ -37,41 +37,38 @@ * @TODO: not final...input buffer handling not final */ void -httpRequestParserGetBody(HttpRequestParser this) +httpParserHeader( + HttpParser this, + const char * line, + const char * lend) { - HttpMessage message = (HttpMessage)(this->cur_request); - size_t len; + const char * name = line; + char * value = memchr(line, ':', lend - line); + size_t nname = (value++) - name; + HttpMessage current = this->current; - if (0 == message->nbody) { - HttpHeader clen = httpHeaderGet( - &(message->header), - "Content-Length", - sizeof("Content-Length")-1); - - if (NULL == clen) { - this->state = HTTP_REQUEST_DONE; - return; - } - else { - message->type = HTTP_MESSAGE_BUFFERED; - message->nbody = atoi((clen->value)[0]); - if (0 < message->nbody) { - message->body = malloc(message->nbody); - } - message->dbody = 0; - } + if (NULL == value) { + return; } this->buffer->bused -= len; - if (message->dbody < message->nbody) { - len = MAX( - message->nbody - message->dbody, - this->buffer->bused); + for (; *value == ' ' && value < lend; value++); - memcpy(message->body, cbufGetData(this->buffer, len), len); + if (value == lend) { + return; + } - message->dbody += len; + if (0 == strncasecmp("content-length", name, nname-1)) { + current->nbody = strtoul(value, NULL, 10); + if (0 < this->current->nbody) { + current->body = malloc(current->nbody); + } + current->dbody = 0; } + + httpHeaderAdd( + &(current->header), + new(HttpHeader, name, nname, value, lend - value)); } // vim: set ts=4 sw=4: diff --git a/src/http/parser/new_message.c b/src/http/parser/new_message.c new file mode 100644 index 0000000..5c392fc --- /dev/null +++ b/src/http/parser/new_message.c @@ -0,0 +1,34 @@ +#include "http/parser.h" + +#include "utils/http.h" + +void +httpParserNewMessage( + HttpParser this, + const char * line, + const char * lend) +{ + const char * part1, * part2, * part3; + size_t len1, len2, len3; + + part1 = line; + part2 = memchr(line, ' ', lend - line); + + if (NULL == part2) return; + + len1 = part2 - part1; + for (; *part2 == ' ' && *part2 != 0; part2++); + + part3 = memchr(part2, ' ', lend - part2); + + if (NULL == part3) return; + + len2 = part3 - part2; + for (; *part3 == ' ' && *part3 != 0; part3++); + + len3 = lend - part3; + + this->current = httpGetMessage(part1, len1, part2, len2, part3, len3); +} + +// vim: set ts=4 sw=4: diff --git a/src/http/request/parser/parse.c b/src/http/parser/parse.c similarity index 63% rename from src/http/request/parser/parse.c rename to src/http/parser/parse.c index 0ccfb0a..9962ea4 100644 --- a/src/http/request/parser/parse.c +++ b/src/http/parser/parse.c @@ -22,19 +22,19 @@ #include -#include "http/request.h" -#include "http/message.h" -#include "http/request/parser.h" +#include "http/parser.h" #include "interface/class.h" +#include "interface/http_intro.h" #include "cbuf.h" ssize_t -httpRequestParserParse(HttpRequestParser this, int fd) +httpParserParse(void * _this, int fd) { - int cont = 1; - ssize_t read; - char * line; - char * line_end; + HttpParser this = _this; + int cont = 1; + ssize_t read; + char * line; + char * line_end; if (cbufIsLocked(this->buffer)) { if (FALSE == this->ourLock) @@ -57,11 +57,10 @@ httpRequestParserParse(HttpRequestParser this, int fd) while (cont) { switch(this->state) { - case HTTP_REQUEST_GARBAGE: + case HTTP_MESSAGE_GARBAGE: cbufSkipNonAlpha(this->buffer); if (! cbufIsEmpty(this->buffer)) { - this->cur_request = new(HttpRequest); - this->state = HTTP_REQUEST_START; + this->state = HTTP_MESSAGE_START; } else { cbufRelease(this->buffer); @@ -71,7 +70,7 @@ httpRequestParserParse(HttpRequestParser this, int fd) break; - case HTTP_REQUEST_START: + case HTTP_MESSAGE_START: if (NULL == (line = cbufGetLine(this->buffer, &line_end))) { if (! cbufIsEmpty(this->buffer)) { this->isize = this->buffer->bused; @@ -86,22 +85,17 @@ httpRequestParserParse(HttpRequestParser this, int fd) break; } - httpRequestParserGetRequestLine(this->cur_request, line, line_end); - if (! httpRequestHasValidMethod(this->cur_request)) { - cbufRelease(this->buffer); - this->ourLock = FALSE; - return -1; - } - if (! httpMessageHasValidVersion((HttpMessage)this->cur_request)) { + httpParserNewMessage(this, line, line_end); + if (NULL == this->current) { cbufRelease(this->buffer); this->ourLock = FALSE; return -1; } - this->state = HTTP_REQUEST_REQUEST_LINE_DONE; + this->state = HTTP_MESSAGE_INTRO_DONE; break; - case HTTP_REQUEST_REQUEST_LINE_DONE: + case HTTP_MESSAGE_INTRO_DONE: if (NULL == (line = cbufGetLine(this->buffer, &line_end))) { if (! cbufIsEmpty(this->buffer)) { this->isize = this->buffer->bused; @@ -116,46 +110,45 @@ httpRequestParserParse(HttpRequestParser this, int fd) break; } - if (0 == line_end - this->buffer->buffer - this->buffer->bstart) { - this->buffer->bstart += 2; - if (this->buffer->bstart >= this->buffer->bsize) { - this->buffer->bstart -= this->buffer->bsize; - } - this->buffer->bused -= 2; - - this->state = HTTP_REQUEST_HEADERS_DONE; + if (0 == strlen(line)) { + this->state = HTTP_MESSAGE_HEADERS_DONE; break; } - httpRequestParserGetHeader(this->cur_request, line, line_end); + httpParserHeader(this->current, line, line_end); break; - case HTTP_REQUEST_HEADERS_DONE: + case HTTP_MESSAGE_HEADERS_DONE: { - HttpMessage message = (HttpMessage)this->cur_request; + cbufIncRead( + this->buffer, + httpParserBody( + this, + cbufGetRead(this->buffer), + this->buffer->bused)); - httpRequestParserGetBody(this); if (cbufIsEmpty(this->buffer)) { cbufRelease(this->buffer); this->ourLock = FALSE; } - if (message->dbody == message->nbody) { - this->state = HTTP_REQUEST_DONE; + if (this->current->dbody == this->current->nbody) { + this->state = HTTP_MESSAGE_DONE; } } break; - case HTTP_REQUEST_DONE: - this->request_queue->msgs[(this->request_queue->nmsgs)++] = - (HttpMessage)this->cur_request; - - this->cur_request = NULL; + case HTTP_MESSAGE_DONE: + /** + * enqueue current request + */ + this->queue->msgs[(this->queue->nmsgs)++] = this->current; + this->current = NULL; /** * prepare for next request */ - this->state = HTTP_REQUEST_GARBAGE; + this->state = HTTP_MESSAGE_GARBAGE; break; @@ -164,7 +157,7 @@ httpRequestParserParse(HttpRequestParser this, int fd) } } - return ret; + return this->queue->nmsgs; } // vim: set ts=4 sw=4: diff --git a/src/http/request.c b/src/http/request.c index 12a7960..4299a27 100644 --- a/src/http/request.c +++ b/src/http/request.c @@ -37,10 +37,25 @@ static int httpRequestCtor(void * _this, va_list * params) { - /** - * the parent is not called by intention. All infomations - * are read from the request stream. - */ + HttpRequest this = _this; + char * method, * uri; + size_t mlen, ulen; + + method = va_arg(* params, char *); + mlen = va_arg(* params, size_t); + uri = va_arg(* params, char *); + ulen = va_arg(* params, size_t); + + PARENTCALL(_this, Class, ctor, params); + + this->method = malloc(mlen + 1); + this->method[mlen] = 0; + memcpy(this->method, method, mlen); + + this->uri = malloc(ulen + 1); + this->uri[ulen] = 0; + memcpy(this->uri, uri, ulen); + return 0; } diff --git a/src/http/worker.c b/src/http/worker.c index 62c21b2..5e84d55 100644 --- a/src/http/worker.c +++ b/src/http/worker.c @@ -6,7 +6,7 @@ #include "class.h" #include "http/worker.h" -#include "http/request/parser.h" +#include "http/parser.h" #include "http/response/writer.h" #include "interface/class.h" @@ -34,7 +34,7 @@ httpWorkerCtor(void * _this, va_list * params) sprintf(cbuf_id, "%s_%s", "writer", id); this->wbuf = new(Cbuf, cbuf_id, RESPONSE_WRITER_MAX_BUF); - this->parser = new(HttpRequestParser, this->pbuf); + this->parser = new(HttpParser, this->pbuf); this->writer = new(HttpResponseWriter, this->wbuf); return 0; @@ -67,13 +67,16 @@ _clone(void * _this, void * _base) this->pbuf = NULL; this->wbuf = NULL; - this->parser = new(HttpRequestParser, base->pbuf); + this->parser = new(HttpParser, base->pbuf); this->writer = new(HttpResponseWriter, base->wbuf); } +ssize_t httpWorkerProcess(void *, int); +ssize_t httpWorkerWrite(void *, int); + INIT_IFACE(Class, httpWorkerCtor, httpWorkerDtor, _clone); -INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpWorkerProcess); -INIT_IFACE(StreamWriter, (fptr_streamWriterWrite)httpWorkerWrite); +INIT_IFACE(StreamReader, httpWorkerProcess); +INIT_IFACE(StreamWriter, httpWorkerWrite); CREATE_CLASS( HttpWorker, NULL, diff --git a/src/http/worker/process.c b/src/http/worker/process.c index ee5353f..b244e2a 100644 --- a/src/http/worker/process.c +++ b/src/http/worker/process.c @@ -29,10 +29,7 @@ #include "http/message.h" #include "http/request.h" #include "http/message/queue.h" -#include "http/request/parser.h" -#include "http/header.h" -#include "http/request.h" -#include "http/message.h" +#include "http/parser.h" HttpMessage httpWorkerGetAsset(HttpRequest, const char *, const char *, size_t); void httpWorkerAddCommonHeader(HttpMessage, HttpMessage); @@ -45,9 +42,9 @@ httpWorkerProcess(HttpWorker this, int fd) char buffer[200]; ssize_t size; - if (0 < (size = httpRequestParserParse(this->parser, fd))) { + if (0 < (size = httpParserParse(this->parser, fd))) { int i; - HttpMessageQueue reqq = this->parser->request_queue; + HttpMessageQueue reqq = this->parser->queue; HttpMessageQueue respq = this->writer->response_queue; for (i=0; inmsgs; i++) { diff --git a/src/utils/http.c b/src/utils/http.c new file mode 100644 index 0000000..ee57227 --- /dev/null +++ b/src/utils/http.c @@ -0,0 +1,57 @@ +#include +#include +#include + +#include "http/message.h" +#include "http/request.h" +#include "http/response.h" + +#include "interface/class.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +char +isHttpVersion(const char * str, size_t len) +{ + if (NULL == str) + return FALSE; + + if (8 > len) + return FALSE; + + if (0 != memcmp("HTTP/", str, sizeof("HTTP/")-1)) + return FALSE; + + return TRUE; +} + +HttpMessage +httpGetMessage( + const char * part1, size_t len1, + const char * part2, size_t len2, + const char * part3, size_t len3) +{ + if (isHttpVersion(part1, len1)) { + return new(HttpResponse, + part1, len1, + strtoul(part2, NULL, 10), + part3, len3); + } + + if (isHttpVersion(part3, len3)) { + return new(HttpRequest, + part1, len1, + part2, len2, + part3, len3); + } + + return NULL; +} + +// vim: set ts=4 sw=4: