diff --git a/ChangeLog b/ChangeLog index 7bd00df..e383ade 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,14 @@ +2012-02-18 20:12:27 +0100 Georg Hopp + + * lots of changes but primarily change the request parser to use a ringbuffer. The ringbuffer is implemented using the shared memory trick. (HEAD, master) + 2012-02-15 12:30:33 +0100 Georg Hopp - * some more cleanups in the server code. Removing not needed header includes (HEAD, master) + * some more cleanups in the server code. Removing not needed header includes (origin/master, origin/HEAD) 2012-02-15 12:17:39 +0100 Georg Hopp - * Merge branch 'master' of 192.168.100.2:/var/lib/git/server (origin/master, origin/HEAD) + * Merge branch 'master' of 192.168.100.2:/var/lib/git/server 2012-02-15 12:17:00 +0100 Georg Hopp diff --git a/doc/file-upload.txt b/doc/file-upload.txt new file mode 100644 index 0000000..e96b4e0 --- /dev/null +++ b/doc/file-upload.txt @@ -0,0 +1,87 @@ +POST / HTTP/1.1 +Host: localhost:11213 +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:8.0) Gecko/20100101 Firefox/8.0 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Connection: keep-alive +Referer: http://localhost:11213/ +Content-Type: multipart/form-data; boundary=---------------------------2713599294882465121951036798 +Content-Length: 2652 + +-----------------------------2713599294882465121951036798 +Content-Disposition: form-data; name="submitter" + +fooo +-----------------------------2713599294882465121951036798 +Content-Disposition: form-data; name="pics"; filename="bar.c" +Content-Type: text/x-csrc + +#include + +#define _B64(chr, ref) (chr)==_##ref?__##ref +#define B64(_) \ + ( _B64(_,A):_B64(_,B):_B64(_,C):_B64(_,D):_B64(_,E):_B64(_,F):_B64(_,G): \ + _B64(_,H):_B64(_,I):_B64(_,J):_B64(_,K):_B64(_,L):_B64(_,M):_B64(_,N): \ + _B64(_,O):_B64(_,P):_B64(_,Q):_B64(_,R):_B64(_,S):_B64(_,T):_B64(_,U): \ + _B64(_,V):_B64(_,W):_B64(_,X):_B64(_,Y):_B64(_,Z):_B64(_,a):_B64(_,b): \ + _B64(_,c):_B64(_,d):_B64(_,e):_B64(_,f):_B64(_,g):_B64(_,h):_B64(_,i): \ + _B64(_,j):_B64(_,k):_B64(_,l):_B64(_,m):_B64(_,n):_B64(_,o):_B64(_,p): \ + _B64(_,q):_B64(_,r):_B64(_,s):_B64(_,t):_B64(_,u):_B64(_,v):_B64(_,w): \ + _B64(_,x):_B64(_,y):_B64(_,z):_B64(_,0):_B64(_,1):_B64(_,2):_B64(_,3): \ + _B64(_,4):_B64(_,5):_B64(_,6):_B64(_,7):_B64(_,8):_B64(_,9): \ + _B64(_,PLUS):_B64(_,SLASH):-1 ) + + +#define B64_8(_) \ + B64(_),B64(_+1),B64(_+2),B64(_+3),B64(_+4),B64(_+5),B64(_+6),B64(_+7) + +#define B64_64(_) \ + B64_8(_),B64_8(_+8), B64_8(_+16),B64_8(_+24), \ + B64_8(_+32),B64_8(_+40),B64_8(_+48),B64_8(_+56) + +#define IS_BASE64(ch) ( \ + UCHAR_IN_RANGE((unsigned char)(ch)) \ + && 0 <= b64[(unsigned char)(ch)] ) + + +static const char b64str[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +enum b64vals { + __A=0,__B,__C,__D,__E,__F,__G,__H,__I,__J,__K,__L,__M,__N,__O,__P,__Q,__R, + __S,__T,__U,__V,__W,__X,__Y,__Z,__a,__b,__c,__d,__e,__f,__g,__h,__i,__j, + __k,__l,__m,__n,__o,__p,__q,__r,__s,__t,__u,__v,__w,__x,__y,__z,__0,__1, + __2,__3,__4,__5,__6,__7,__8,__9,__PLUS,__SLASH +}; + +enum b64chars { + _A='A',_B='B',_C='C',_D='D',_E='E',_F='F',_G='G',_H='H',_I='I',_J='J',_K='K', + _L='L',_M='M',_N='N',_O='O',_P='P',_Q='Q',_R='R',_S='S',_T='T',_U='U',_V='V', + _W='W',_X='X',_Y='Y',_Z='Z',_a='a',_b='b',_c='c',_d='d',_e='e',_f='f',_g='g', + _h='h',_i='i',_j='j',_k='k',_l='l',_m='m',_n='n',_o='o',_p='p',_q='q',_r='r', + _s='s',_t='t',_u='u',_v='v',_w='w',_x='x',_y='y',_z='z',_0='0',_1='1',_2='2', + _3='3',_4='4',_5='5',_6='6',_7='7',_8='8',_9='9',_PLUS='+',_SLASH='/' +}; + +#define __46B(chr) __##chr +inline +char +__B64(int val) +{ + return b64str[val]; +} + +int main() +{ + int i=12; + char a='k'; + + //printf("i => %c | a => %d\n", __B64(12), __46B('k')); + printf("i => %c | a => %d\n", __B64(i), __46B(a)); +} + +// vim: set ts=4 sw=4: + +-----------------------------2713599294882465121951036798-- diff --git a/doc/file-upload2.txt b/doc/file-upload2.txt new file mode 100644 index 0000000..571eb61 --- /dev/null +++ b/doc/file-upload2.txt @@ -0,0 +1,75 @@ +-----------------------------2713599294882465121951036798 +Content-Disposition: form-data; name="submitter" + +fooo +-----------------------------2713599294882465121951036798 +Content-Disposition: form-data; name="pics"; filename="bar.c" +Content-Type: text/x-csrc + +#include + +#define _B64(chr, ref) (chr)==_##ref?__##ref +#define B64(_) \ + ( _B64(_,A):_B64(_,B):_B64(_,C):_B64(_,D):_B64(_,E):_B64(_,F):_B64(_,G): \ + _B64(_,H):_B64(_,I):_B64(_,J):_B64(_,K):_B64(_,L):_B64(_,M):_B64(_,N): \ + _B64(_,O):_B64(_,P):_B64(_,Q):_B64(_,R):_B64(_,S):_B64(_,T):_B64(_,U): \ + _B64(_,V):_B64(_,W):_B64(_,X):_B64(_,Y):_B64(_,Z):_B64(_,a):_B64(_,b): \ + _B64(_,c):_B64(_,d):_B64(_,e):_B64(_,f):_B64(_,g):_B64(_,h):_B64(_,i): \ + _B64(_,j):_B64(_,k):_B64(_,l):_B64(_,m):_B64(_,n):_B64(_,o):_B64(_,p): \ + _B64(_,q):_B64(_,r):_B64(_,s):_B64(_,t):_B64(_,u):_B64(_,v):_B64(_,w): \ + _B64(_,x):_B64(_,y):_B64(_,z):_B64(_,0):_B64(_,1):_B64(_,2):_B64(_,3): \ + _B64(_,4):_B64(_,5):_B64(_,6):_B64(_,7):_B64(_,8):_B64(_,9): \ + _B64(_,PLUS):_B64(_,SLASH):-1 ) + + +#define B64_8(_) \ + B64(_),B64(_+1),B64(_+2),B64(_+3),B64(_+4),B64(_+5),B64(_+6),B64(_+7) + +#define B64_64(_) \ + B64_8(_),B64_8(_+8), B64_8(_+16),B64_8(_+24), \ + B64_8(_+32),B64_8(_+40),B64_8(_+48),B64_8(_+56) + +#define IS_BASE64(ch) ( \ + UCHAR_IN_RANGE((unsigned char)(ch)) \ + && 0 <= b64[(unsigned char)(ch)] ) + + +static const char b64str[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +enum b64vals { + __A=0,__B,__C,__D,__E,__F,__G,__H,__I,__J,__K,__L,__M,__N,__O,__P,__Q,__R, + __S,__T,__U,__V,__W,__X,__Y,__Z,__a,__b,__c,__d,__e,__f,__g,__h,__i,__j, + __k,__l,__m,__n,__o,__p,__q,__r,__s,__t,__u,__v,__w,__x,__y,__z,__0,__1, + __2,__3,__4,__5,__6,__7,__8,__9,__PLUS,__SLASH +}; + +enum b64chars { + _A='A',_B='B',_C='C',_D='D',_E='E',_F='F',_G='G',_H='H',_I='I',_J='J',_K='K', + _L='L',_M='M',_N='N',_O='O',_P='P',_Q='Q',_R='R',_S='S',_T='T',_U='U',_V='V', + _W='W',_X='X',_Y='Y',_Z='Z',_a='a',_b='b',_c='c',_d='d',_e='e',_f='f',_g='g', + _h='h',_i='i',_j='j',_k='k',_l='l',_m='m',_n='n',_o='o',_p='p',_q='q',_r='r', + _s='s',_t='t',_u='u',_v='v',_w='w',_x='x',_y='y',_z='z',_0='0',_1='1',_2='2', + _3='3',_4='4',_5='5',_6='6',_7='7',_8='8',_9='9',_PLUS='+',_SLASH='/' +}; + +#define __46B(chr) __##chr +inline +char +__B64(int val) +{ + return b64str[val]; +} + +int main() +{ + int i=12; + char a='k'; + + //printf("i => %c | a => %d\n", __B64(12), __46B('k')); + printf("i => %c | a => %d\n", __B64(i), __46B(a)); +} + +// vim: set ts=4 sw=4: + +-----------------------------2713599294882465121951036798-- diff --git a/include/http/request/parser.h b/include/http/request/parser.h index 8f4f67b..110390a 100644 --- a/include/http/request/parser.h +++ b/include/http/request/parser.h @@ -4,11 +4,21 @@ #include "class.h" #include "http/request.h" #include "http/message/queue.h" +#include "ringbuffer.h" -#define HTTP_REQUEST_PARSER_READ_CHUNK 1024 +#define HTTP_REQUEST_PARSER_MAX_BUF 131072 -#define REMAINS(pars) \ - ((pars)->buffer_used - ((pars)->cur_data - (pars)->buffer)) +/** + * limits to stop invalid requests from killing + * the server. + * If any of these limits is reached the server + * will send an error message and kill the connection + * immediate. + * + * The given limits include any trailing \r\n + */ +#define HTTP_REQUEST_LINE_MAX 8192 +#define HTTP_REQUEST_HEADER_LINE_MAX 2048 typedef enum e_HttpRequestState { @@ -21,11 +31,7 @@ typedef enum e_HttpRequestState { CLASS(HttpRequestParser) { - char * buffer; - char * cur_data; - - size_t buffer_used; - size_t buffer_size; + Ringbuffer buffer; HttpMessageQueue request_queue; HttpRequest cur_request; @@ -34,11 +40,11 @@ CLASS(HttpRequestParser) { }; ssize_t httpRequestParserRead(HttpRequestParser, int); -ssize_t httpRequestParserParse(HttpRequestParser); -void httpRequestParserGetBody(HttpRequestParser); +ssize_t httpRequestParserParse(HttpRequestParser, int); -void httpRequestParserGetRequestLine(HttpRequest, char *); -void httpRequestParserGetHeader(HttpRequest, char *); +ssize_t httpRequestParserGetRequestLine(HttpRequestParser, char *); +ssize_t httpRequestParserGetHeader(HttpRequestParser, char *); +void httpRequestParserGetBody(HttpRequestParser); #endif /* __HTTP_REQUEST_PARSER_H__ */ diff --git a/include/http/worker.h b/include/http/worker.h index 2f23884..cc44256 100644 --- a/include/http/worker.h +++ b/include/http/worker.h @@ -8,6 +8,9 @@ #include "http/response/writer.h" CLASS(HttpWorker) { + char * remoteAddr; + int handle; + HttpRequestParser parser; HttpResponseWriter writer; }; diff --git a/include/ringbuffer.h b/include/ringbuffer.h new file mode 100644 index 0000000..e6e62da --- /dev/null +++ b/include/ringbuffer.h @@ -0,0 +1,37 @@ +/** + * my implementation of a ringbuffer. + * It maps a shared memory object twice directly following + * thus make it possible to read and write from any + * position within the buffer without the nasty wrap + * calculations. + * This is achived because the same memory region is mapped + * at the two addresses. + */ +#ifndef __RINGBUFFER_H__ +#define __RINGBUFFER_H__ + +#include + +#include "class.h" + +#define ERBOVRFL 100 + + +CLASS(Ringbuffer) { + char * shm_name; // shared memory identifier + + char * buffer; + char * mirror; + + size_t bsize; + size_t bused; + size_t bstart; + size_t bend; +}; + +ssize_t rbRead(Ringbuffer, int fd); +ssize_t rbWrite(Ringbuffer, int fd); + +#endif // __RINGBUFFER_H__ + +// vim: set ts=4 sw=4: diff --git a/include/socket.h b/include/socket.h index e5b116d..ee6a698 100644 --- a/include/socket.h +++ b/include/socket.h @@ -15,7 +15,7 @@ CLASS(Sock) { void socketConnect(Sock this, const char * addr); void socketListen(Sock this, int backlog); -Sock socketAccept(Sock this, char remoteAddr[16]); +Sock socketAccept(Sock this, char (*remoteAddr)[]); #endif // __SOCKET_H__ diff --git a/src/Makefile.am b/src/Makefile.am index ac86379..f2905a1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ IFACE = interface/class.c interface/stream_reader.c interface/logger.c \ interface/stream_writer.c interface/http_intro.c \ interface/subject.c interface/observer.c CLASS = class.c interface.c +RB = ringbuffer.c ringbuffer/rb_read.c SOCKET = socket.c socket/accept.c socket/connect.c socket/listen.c SERVER = server.c server/run.c server/close_conn.c LOGGER = logger.c logger/stderr.c logger/syslog.c @@ -21,7 +22,7 @@ HEADER = http/header.c http/header/get.c http/header/add.c \ http/header/size_get.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/read.c http/request/parser/get_body.c + http/request/parser/get_body.c AM_CFLAGS = -Wall -I ../include/ @@ -30,6 +31,7 @@ bin_PROGRAMS = testserver testserver_SOURCES = testserver.c \ $(IFACE) $(CLASS) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \ - $(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) \ + $(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) $(RB) \ signalHandling.c daemonize.c testserver_CFLAGS = -Wall -I ../include/ +testserver_LDFLAGS = -lrt diff --git a/src/http/request/parser.c b/src/http/request/parser.c index 60c9b7f..d33e864 100644 --- a/src/http/request/parser.c +++ b/src/http/request/parser.c @@ -10,18 +10,18 @@ #include "http/request/parser.h" #include "http/message/queue.h" #include "http/request.h" +#include "ringbuffer.h" static void ctor(void * _this, va_list * params) { - HttpRequestParser this = _this; + HttpRequestParser this = _this; + char * shm_name = va_arg(*params, char*); + this->buffer = new(Ringbuffer, shm_name, HTTP_REQUEST_LINE_MAX); this->request_queue = new(HttpMessageQueue); - - this->buffer = malloc(HTTP_REQUEST_PARSER_READ_CHUNK); - this->buffer[0] = 0; } static @@ -30,36 +30,16 @@ dtor(void * _this) { HttpRequestParser this = _this; - free(this->buffer); delete(&(this->request_queue)); + delete(&(this->buffer)); if (NULL != this->cur_request) delete(&(this->cur_request)); } -static -void -_clone(void * _this, void * _base) -{ - HttpRequestParser this = _this; - HttpRequestParser base = _base; - size_t chunks; - - /** - * every parser has its own queue... - */ - this->request_queue = new(HttpMessageQueue); - this->buffer_used = base->buffer_used; - - chunks = this->buffer_used / HTTP_REQUEST_PARSER_READ_CHUNK; - chunks++; - - this->buffer = malloc(chunks * HTTP_REQUEST_PARSER_READ_CHUNK); - memcpy(this->buffer, base->buffer, this->buffer_used); -} -INIT_IFACE(Class, ctor, dtor, _clone); -INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpRequestParserRead); +INIT_IFACE(Class, ctor, dtor, NULL); +INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpRequestParserParse); CREATE_CLASS(HttpRequestParser, NULL, IFACE(Class), IFACE(StreamReader)); // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/get_body.c b/src/http/request/parser/get_body.c index 71d5b31..4c00f8d 100644 --- a/src/http/request/parser/get_body.c +++ b/src/http/request/parser/get_body.c @@ -4,33 +4,50 @@ #include "http/message.h" #include "http/request/parser.h" +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + + +/** + * @TODO: not final...input buffer handling not final + */ void httpRequestParserGetBody(HttpRequestParser this) { HttpMessage message = (HttpMessage)(this->cur_request); - char * nbody; + char * str_nbody; + int nbody; + int len; + + str_nbody = httpHeaderGet( + &(message->header), + "Content-Length"); + + if (NULL == str_nbody) { + this->state = HTTP_REQUEST_DONE; + return -1; + } + + nbody = atoi(str_nbody); if (0 == message->nbody) { - nbody = httpHeaderGet( - &(message->header), - "Content-Length"); - - if (NULL == nbody) { - this->state = HTTP_REQUEST_DONE; - return; - } - else { - message->type = HTTP_MESSAGE_BUFFERED; - message->nbody = atoi(nbody); - } + message->type = HTTP_MESSAGE_BUFFERED; + if (0 < nbody) + message->body = malloc(nbody); + } + + len = MAX(nbody - message->nbody, this->buffer->bused); + memcpy(message->body + message->nbody, + this->buffer->buffer + this->buffer->bstart, + len); + + message->nbody += len; + this->buffer->bstart += len; + if (this->buffer->bstart >= this->buffer->bsize) { + this->buffer->bstart -= this->buffer->bsize; } + this->buffer->bused -= len; - if (REMAINS(this) >= message->nbody) { - message->body = calloc(1, message->nbody + 1); - memcpy(message->body, - this->cur_data, - message->nbody); - this->cur_data += message->nbody; + if (message->nbody == nbody) { this->state = HTTP_REQUEST_DONE; } } diff --git a/src/http/request/parser/get_header.c b/src/http/request/parser/get_header.c index af437ff..7c8b013 100644 --- a/src/http/request/parser/get_header.c +++ b/src/http/request/parser/get_header.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -6,17 +8,32 @@ #include "interface/class.h" #include "http/header.h" #include "http/message.h" +#include "http/request/parser.h" +#include "ringbuffer.h" -void -httpRequestParserGetHeader(HttpMessage request, char * line) +ssize_t +httpRequestParserGetHeader(HttpRequestParser this, char * cr) { - char * name = line; - char * value = strchr(line, ':'); + HttpMessage message = (HttpMessage)this->cur_request; + char * value; + char * name = this->buffer->buffer + this->buffer->bstart; + size_t len = cr - name; + value = memchr( + this->buffer->buffer + this->buffer->bstart, + ':', len); + + if (NULL == value) { + return -1; + } + + *cr = 0; *(value++) = 0; - for (; *value == ' ' && *value != 0; value++); + while(' ' == *value) value++; + + httpHeaderAdd(&(message->header), new(HttpHeader, name, value)); - httpHeaderAdd(&(request->header), new(HttpHeader, name, value)); + return 1; //* @TODO: return something useful here } // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/get_request_line.c b/src/http/request/parser/get_request_line.c index a8ab861..5b556fd 100644 --- a/src/http/request/parser/get_request_line.c +++ b/src/http/request/parser/get_request_line.c @@ -1,31 +1,95 @@ #include #include +#include "http/message.h" #include "http/request.h" +#include "http/request/parser.h" +#include "ringbuffer.h" +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MIN_SIZE(x,y) (MAX(strlen((x)), (y))) -void -httpRequestParserGetRequestLine(HttpRequest request, char * line) +enum e_method { + OPTIONS=0, + GET, + HEAD, + POST, + PUT, + DELETE, + TRACE, + CONNECT +}; + +#define N_METHODS 8 + +static +const +char * method[N_METHODS] = { + "OPTIONS", + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "TRACE", + "CONNECT" +}; + +ssize_t +httpRequestParserGetRequestLine(HttpRequestParser this, char * cr) { + HttpRequest request = this->cur_request; HttpMessage message = (HttpMessage)request; - char * method, * uri, * version; + char * space1, * space2; + size_t len = cr - this->buffer->buffer - this->buffer->bstart; + int i; + + space1 = memchr( + this->buffer->buffer + this->buffer->bstart, + ' ', len); + + if (NULL == space1) { + return -1; + } + + len = cr - space1; + space2 = memchr(space1 + 1, ' ', len); + + if (NULL == space2) { + return -1; + } + + len = space1 - this->buffer->buffer - this->buffer->bstart; + request->method = calloc(1, len + 1); + memcpy(request->method, + this->buffer->buffer + this->buffer->bstart, + len); + + for (i= 0; i< N_METHODS; i++) { + if (0 == memcmp(method[i], request->method, MIN_SIZE(method[i], len))) { + break; + } + } + + if (i == N_METHODS) { + return -1; + } - method = line; + len = space2 - space1 - 1; + request->uri = calloc(1, len + 1); + memcpy(request->uri, space1 + 1, len); - uri = strchr(line, ' '); - *uri++ = 0; - for (; *uri == ' ' && *uri != 0; uri++); + len = cr - space2 - 1; + message->version = calloc(1, len + 1); + memcpy(message->version, space2 + 1, len); - version = strchr(uri, ' '); - *version++ = 0; - for (; *version == ' ' && *version != 0; version++); + if (len+1 != sizeof("HTTP/1.1") || + 0 != memcmp("HTTP/1.", message->version, len-1)) { + return -1; + } - request->method = malloc(strlen(method) + 1); - strcpy(request->method, method); - request->uri = malloc(strlen(uri) + 1); - strcpy(request->uri, uri); - message->version = malloc(strlen(version) + 1); - strcpy(message->version, method); + return 1; //* @TODO: return something useful here } // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/parse.c b/src/http/request/parser/parse.c index ff86a23..c507cd9 100644 --- a/src/http/request/parser/parse.c +++ b/src/http/request/parser/parse.c @@ -3,112 +3,147 @@ #include #include +#include "http/request.h" +#include "http/message.h" #include "http/request/parser.h" #include "interface/class.h" +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif static inline char * -httpRequestParserGetLine(char ** data) +getLine(HttpRequestParser this) { - char * line_end = strstr(*data, "\r\n"); - char * ret = *data; + char * cr = memchr( + this->buffer->buffer + this->buffer->bstart, + '\r', + this->buffer->bused); - if (NULL == line_end) { - return NULL; - } + char * nl = (NULL == cr)? NULL : cr + 1; - *line_end = 0; - *data = line_end + 2; + if (NULL != cr && NULL != nl && '\n' == *nl) { + *cr = 0; + return cr; + } - return ret; + return NULL; } static inline -void -httpRequestSkip(char ** data) +char +httpRequestSkip(HttpRequestParser this) { - for (; 0 != **data && ! isalpha(**data); (*data)++); + while (this->buffer->bused > 0 && + ! isalpha(this->buffer->buffer[this->buffer->bstart])) { + this->buffer->bstart = (this->buffer->bstart >= this->buffer->bsize)? + 0 : this->buffer->bstart + 1; + this->buffer->bused--; + } + + return (isalpha(this->buffer->buffer[this->buffer->bstart]))? TRUE : FALSE; } ssize_t -httpRequestParserParse(HttpRequestParser this) +httpRequestParserParse(HttpRequestParser this, int fd) { - char * line; int cont = 1; - ssize_t ret = this->request_queue->nmsgs; + ssize_t ret; + + if (0 > (ret = rbRead(this->buffer, fd))) { + cont = 0; + } while (cont) { switch(this->state) { - case HTTP_REQUEST_GARBAGE: - this->cur_data = this->buffer; // initialize static pointer - httpRequestSkip(&(this->cur_data)); - this->cur_request = new(HttpRequest); + char * line_end; + size_t len; - this->state = HTTP_REQUEST_START; + case HTTP_REQUEST_GARBAGE: + if (httpRequestSkip(this)) { + this->cur_request = new(HttpRequest); + this->state = HTTP_REQUEST_START; + } + else { + cont = 0; + } break; case HTTP_REQUEST_START: - if (NULL == (line = httpRequestParserGetLine(&(this->cur_data)))) { + if (NULL == (line_end = getLine(this))) { cont = 0; break; } - httpRequestParserGetRequestLine(this->cur_request, line); + if (0 > httpRequestParserGetRequestLine(this, line_end)) { + ret = -1; + cont = 0; + break; + } + + len = line_end - this->buffer->buffer - this->buffer->bstart + 2; + this->buffer->bstart += len; + if (this->buffer->bstart >= this->buffer->bsize) { + this->buffer->bstart -= this->buffer->bsize; + } + this->buffer->bused -= len; this->state = HTTP_REQUEST_REQUEST_LINE_DONE; break; case HTTP_REQUEST_REQUEST_LINE_DONE: - if (NULL == (line = httpRequestParserGetLine(&(this->cur_data)))) { + if (NULL == (line_end = getLine(this))) { cont = 0; break; } - if (0 == strlen(line)) { + 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; break; } - httpRequestParserGetHeader(this->cur_request, line); + httpRequestParserGetHeader(this, line_end); + + len = line_end - this->buffer->buffer - this->buffer->bstart + 2; + this->buffer->bstart += len; + if (this->buffer->bstart >= this->buffer->bsize) { + this->buffer->bstart -= this->buffer->bsize; + } + this->buffer->bused -= len; + break; case HTTP_REQUEST_HEADERS_DONE: + /** + * allocate memory according to content-length. + * If content length is to large reject request. + * + * @FUTURE check for multipart mime and handle it + * with temporary file. + */ httpRequestParserGetBody(this); break; case HTTP_REQUEST_DONE: - /** - * enqueue current request - */ this->request_queue->msgs[(this->request_queue->nmsgs)++] = (HttpMessage)this->cur_request; - if (! httpMessageHasKeepAlive((HttpMessage)this->cur_request)) { - ret = -2; - } - + ret = this->request_queue->nmsgs; this->cur_request = NULL; - /** - * remove processed stuff from input buffer. - */ - memmove(this->buffer, this->cur_data, REMAINS(this)); - - this->buffer_used -= this->cur_data - this->buffer; - - /** - * dont continue loop if input buffer is empty - */ - if (0 == this->buffer_used) { - cont = 0; - } - - /** - * prepare for next request - */ this->state = HTTP_REQUEST_GARBAGE; break; @@ -118,7 +153,7 @@ httpRequestParserParse(HttpRequestParser this) } } - return this->request_queue->nmsgs; + return ret; } // vim: set ts=4 sw=4: diff --git a/src/http/request/parser/read.c b/src/http/request/parser/read.c index 1a1d493..57987df 100644 --- a/src/http/request/parser/read.c +++ b/src/http/request/parser/read.c @@ -3,46 +3,40 @@ #include "http/request/parser.h" +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define _BSIZE(x) (MAX((x),RESPONSE_WRITER_MAX_BUF)) +#define BSIZE _BSIZE(this->nheader+message->nbody) + +/** + * @deprecated + */ ssize_t httpRequestParserRead(HttpRequestParser this, int fd) { - size_t remaining, chunks; - char buffer[1024]; + size_t rsize; + ssize_t temp; - ssize_t size = read(fd, buffer, 1024); + this->bend = (this->bsize == this->bend)? + 0 : this->bend; - if (0 < size) { - remaining = this->buffer_used % HTTP_REQUEST_PARSER_READ_CHUNK; - remaining = HTTP_REQUEST_PARSER_READ_CHUNK - remaining; - chunks = this->buffer_used / HTTP_REQUEST_PARSER_READ_CHUNK; + rsize = (this->bstart <= this->bend)? + this->bsize - this->bend : + this->bstart - 1; + if (0 >= (temp = read(fd, &(this->buffer[this->bend]), rsize))) { /** - * because a division always rounds down - * chunks holds exactly the currently allocated chunks if - * remaining equals 0 but there is no space left. - * Else chunks holds the actually allocated amount of chunks - * minus 1. - * For this reason chunks always has to be increased by 1. + * this means either we had an rsize of 0 what indicates that + * the buffer ran full without any processing took place or + * the connection was terminated in some way. In both cases + * we want to terminate the connection. */ - chunks++; - - if (size >= remaining) { - this->buffer = - realloc(this->buffer, chunks * HTTP_REQUEST_PARSER_READ_CHUNK); - } - - memcpy(this->buffer + this->buffer_used, buffer, size); - this->buffer_used += size; - this->buffer[this->buffer_used] = 0; - - size = httpRequestParserParse(this); - } - else { - size = (0 == size)? -2 : size; + return (0 == temp)? -2 : -1; } - return size; + this->bend += temp; + + return temp; } // vim: set ts=4 sw=4: diff --git a/src/http/response/404.c b/src/http/response/404.c index f59df29..edb0ab1 100644 --- a/src/http/response/404.c +++ b/src/http/response/404.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "class.h" #include "interface/class.h" @@ -23,8 +22,6 @@ HttpResponse httpResponse404() { - time_t t; - struct tm * tmp; char buffer[200]; HttpResponse response; HttpMessage message; @@ -46,12 +43,6 @@ httpResponse404() httpHeaderAdd(&(message->header), new(HttpHeader, "Content-Length", buffer)); - t = time(NULL); - tmp = localtime(&t); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); - httpHeaderAdd(&(message->header), - new(HttpHeader, "Date", buffer)); - return response; } diff --git a/src/http/response/image.c b/src/http/response/image.c index 9d2b1d8..0400202 100644 --- a/src/http/response/image.c +++ b/src/http/response/image.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -16,8 +15,6 @@ HttpResponse httpResponseImage() { - time_t t; - struct tm * tmp; char buffer[200]; struct stat st; HttpResponse response; @@ -40,12 +37,6 @@ httpResponseImage() httpHeaderAdd(&(message->header), new(HttpHeader, "Content-Length", buffer)); - t = time(NULL); - tmp = localtime(&t); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); - httpHeaderAdd(&(message->header), - new(HttpHeader, "Date", buffer)); - return response; } diff --git a/src/http/response/me.c b/src/http/response/me.c index 109008a..2c0836a 100644 --- a/src/http/response/me.c +++ b/src/http/response/me.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "class.h" #include "interface/class.h" @@ -23,8 +22,6 @@ HttpResponse httpResponseMe() { - time_t t; - struct tm * tmp; char buffer[200]; HttpResponse response; HttpMessage message; @@ -46,12 +43,6 @@ httpResponseMe() httpHeaderAdd(&(message->header), new(HttpHeader, "Content-Length", buffer)); - t = time(NULL); - tmp = localtime(&t); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); - httpHeaderAdd(&(message->header), - new(HttpHeader, "Date", buffer)); - return response; } diff --git a/src/http/response/writer/write.c b/src/http/response/writer/write.c index eddbf3a..b185c3d 100644 --- a/src/http/response/writer/write.c +++ b/src/http/response/writer/write.c @@ -21,7 +21,7 @@ httpResponseWriterWrite(HttpResponseWriter this, int fd) { HttpMessageQueue respq = this->response_queue; HttpMessage message = (HttpMessage)this->cur_response; - ssize_t processed = (message)? 1 : 0; + ssize_t processed = (message)? 1 : 0; int cont = 1; while (cont) { @@ -74,7 +74,6 @@ httpResponseWriterWrite(HttpResponseWriter this, int fd) &(this->pipe[this->pend]), &(message->body[this->nbuffer]), temp); - break; case HTTP_MESSAGE_PIPED: diff --git a/src/http/worker.c b/src/http/worker.c index 73798e4..4e7a347 100644 --- a/src/http/worker.c +++ b/src/http/worker.c @@ -1,4 +1,6 @@ +#include #include +#include #include "class.h" #include "http/worker.h" @@ -9,13 +11,19 @@ #include "interface/stream_reader.h" #include "interface/stream_writer.h" +#define SHMN "/worker_" static void ctor(void * _this, va_list * params) { HttpWorker this = _this; + char id[sizeof(SHMN) + 15 + 5]; - this->parser = new(HttpRequestParser); + this->remoteAddr = va_arg(*params, char *); + this->handle = va_arg(*params, int); + sprintf(id, SHMN "%s_%05d", this->remoteAddr, this->handle); + + this->parser = new(HttpRequestParser, id); this->writer = new(HttpResponseWriter); } @@ -29,20 +37,7 @@ dtor(void * _this) 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(Class, ctor, dtor, NULL); INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpWorkerProcess); INIT_IFACE(StreamWriter, (fptr_streamWriterWrite)httpWorkerWrite); CREATE_CLASS( diff --git a/src/http/worker/process.c b/src/http/worker/process.c index 949b4d5..ebc4bcc 100644 --- a/src/http/worker/process.c +++ b/src/http/worker/process.c @@ -1,15 +1,23 @@ +#include + #include "class.h" #include "interface/class.h" #include "http/worker.h" #include "http/request/parser.h" +#include "http/header.h" +#include "http/request.h" +#include "http/message.h" ssize_t httpWorkerProcess(HttpWorker this, int fd) { - ssize_t size; + time_t t; + struct tm * tmp; + char buffer[200]; + ssize_t size; - if (0 < (size = httpRequestParserRead(this->parser, fd))) { + if (0 < (size = httpRequestParserParse(this->parser, fd))) { int i; HttpMessageQueue reqq = this->parser->request_queue; HttpMessageQueue respq = this->writer->response_queue; @@ -19,8 +27,8 @@ httpWorkerProcess(HttpWorker this, int fd) * @TODO: for now simply remove request and send not found. * Make this sane. */ - HttpRequest request = (HttpRequest)(reqq->msgs[i]); - HttpMessage response = NULL; + HttpRequest request = (HttpRequest)(reqq->msgs[i]); + HttpMessage response = NULL; if (0 == strcmp("GET", request->method) && 0 == strcmp("/me/", request->uri)) { @@ -45,12 +53,19 @@ httpWorkerProcess(HttpWorker this, int fd) new(HttpHeader, "Connection", "Close")); } + t = time(NULL); + tmp = localtime(&t); + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp); + httpHeaderAdd(&(response->header), + new(HttpHeader, "Date", buffer)); + respq->msgs[(respq->nmsgs)++] = response; response = NULL; delete(&(reqq->msgs[i])); } reqq->nmsgs = 0; + size = respq->nmsgs; } return size; diff --git a/src/rbtest.c b/src/rbtest.c new file mode 100644 index 0000000..08060a8 --- /dev/null +++ b/src/rbtest.c @@ -0,0 +1,21 @@ +#include + +#include "ringbuffer.h" +#include "interface/class.h" + +int +main() +{ + Ringbuffer buf = new(Ringbuffer, "/rbtest", 1); + + strcpy(buf->buffer + buf->bsize - 5, "Dies ist ein foobar test"); + + printf("%s\n", buf->buffer); + printf("%s\n", buf->buffer + buf->bsize - 5); + + delete(&buf); + + return 0; +} + +// vim: set ts=4 sw=4: diff --git a/src/ringbuffer.c b/src/ringbuffer.c new file mode 100644 index 0000000..fd4bcb0 --- /dev/null +++ b/src/ringbuffer.c @@ -0,0 +1,117 @@ +#define _POSIX_SOURCE +#define _POSIX_C_SOURCE 200112L + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "class.h" +#include "interface/class.h" + +#include "ringbuffer.h" + +#define PAGES(size, psize) ((size)/(psize)+(0 == (size)%(psize))?0:1) + + +static void dtor(void*); + +static +void +ctor(void * _this, va_list * params) +{ + Ringbuffer this = _this; + char state = 0; + char * shm_name = va_arg(*params, char*); + long psize = sysconf(_SC_PAGESIZE); + size_t size; + int shm; + void * shm_addr; + + this->shm_name = malloc(strlen(shm_name) + 1); + strcpy(this->shm_name, shm_name); + + /** + * align size at page boundary. + * increase as neccessary + */ + size = va_arg(*params, size_t); + size = (0 <= size)? 1 : size; + this->bsize = psize * PAGES(size, psize); + + while (0 == state) { + shm = shm_open(this->shm_name, O_RDWR|O_CREAT|O_EXCL, S_IRWXU); + if (-1 == shm) { + break; + } + else { + ftruncate(shm, this->bsize); + } + + shm_addr = mmap (0, this->bsize<<1, + PROT_READ|PROT_WRITE, MAP_SHARED, shm, 0); + if (shm_addr == MAP_FAILED) + break; + + munmap(shm_addr, this->bsize<<1); + + this->buffer = mmap (shm_addr, this->bsize, + PROT_READ|PROT_WRITE, MAP_SHARED, shm, 0); + if (this->buffer == MAP_FAILED) { + shm_addr = NULL; + break; + } + + this->mirror = mmap (shm_addr + this->bsize, this->bsize, + PROT_READ|PROT_WRITE, MAP_SHARED, shm, 0); + if (this->mirror != shm_addr + this->bsize) { + shm_addr = NULL; + break; + } + + state = 1; + } + + if (-1 != shm) { + shm_unlink(this->shm_name); + close(shm); + } + + if (1 != state) { + if (shm_addr) { + munmap(shm_addr, this->bsize<<1); + } + + dtor(this); + } +} + +static +void +dtor(void * _this) +{ + Ringbuffer this = _this; + + if (NULL != this->shm_name) { + free(this->shm_name); + } + + if (this->buffer) { + munmap(this->buffer, this->bsize); + this->buffer = NULL; + } + + if (this->mirror) { + munmap(this->mirror, this->bsize); + this->mirror = NULL; + } +} + +INIT_IFACE(Class, ctor, dtor, NULL); +CREATE_CLASS(Ringbuffer, NULL, IFACE(Class)); + +// vim: set ts=4 sw=4: diff --git a/src/ringbuffer/rb_read.c b/src/ringbuffer/rb_read.c new file mode 100644 index 0000000..4359963 --- /dev/null +++ b/src/ringbuffer/rb_read.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "ringbuffer.h" + +ssize_t +rbRead(Ringbuffer this, int fd) +{ + ssize_t rrsize = 0; + size_t rsize = this->bsize - this->bused; + + if (0 == rsize) { + errno = ERBOVRFL; + return -1; + } + + rrsize = read(fd, this->buffer + this->bend, rsize); + + switch (rrsize) { + case 0: + rsize = -2; + + case -1: + break; + + default: + this->bend += rrsize; + this->bused += rrsize; + + if (this->bend >= this->bsize) { + this->bend -= this->bsize; + } + + break; + } + + return rrsize; +} + +// vim: set ts=4 sw=4: diff --git a/src/server/handle_accept.c b/src/server/handle_accept.c index 259fa24..12c28b1 100644 --- a/src/server/handle_accept.c +++ b/src/server/handle_accept.c @@ -1,20 +1,22 @@ #include +#include "http/worker.h" + static int serverHandleAccept(Server this) { - char remoteAddr[16] = ""; + char remoteAddr[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; Sock acc; - acc = socketAccept(this->sock, remoteAddr); + acc = socketAccept(this->sock, &remoteAddr); if (-1 != acc->handle) { //* save the socket handle (this->conns)[acc->handle].sock = acc; //* clone reader - (this->conns)[acc->handle].worker = clone(this->worker); + (this->conns)[acc->handle].worker = new(HttpWorker, remoteAddr, acc->handle); (this->fds)[this->nfds].fd = acc->handle; (this->fds)[this->nfds].events = POLLIN; diff --git a/src/socket/accept.c b/src/socket/accept.c index 0e2d0a0..ec428d5 100644 --- a/src/socket/accept.c +++ b/src/socket/accept.c @@ -6,7 +6,7 @@ #include "interface/logger.h" Sock -socketAccept(Sock this, char remoteAddr[16]) +socketAccept(Sock this, char (*remoteAddr)[]) { Sock sock; /* Socket for client */ unsigned int len; /* Length of client address data structure */ @@ -33,8 +33,9 @@ socketAccept(Sock this, char remoteAddr[16]) loggerLog(this->log, LOGGER_WARNING, "error accepting connection: %s", strerror(errno)); } else { + memcpy(*remoteAddr, inet_ntoa((sock->addr).sin_addr), 15); loggerLog(this->log, LOGGER_INFO, - "handling client %s\n", inet_ntoa((sock->addr).sin_addr)); + "handling client %s\n", *remoteAddr); } return sock; diff --git a/src/testserver.c b/src/testserver.c index d7b3ba1..2bc6c8b 100644 --- a/src/testserver.c +++ b/src/testserver.c @@ -19,7 +19,7 @@ int main() { Logger logger = new(LoggerSyslog, LOGGER_ERR); - HttpWorker worker = new(HttpWorker); + HttpWorker worker = new(HttpWorker, "", 0); Server server = new(Server, logger, worker, 11212, SOMAXCONN); struct rlimit limit = {RLIM_INFINITY, RLIM_INFINITY};