diff options
author | Wolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de> | 2013-03-12 20:13:53 +0100 |
---|---|---|
committer | Wolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de> | 2013-03-12 20:13:53 +0100 |
commit | 5ff179fad970eb0a878a27511d6c90419bac2cdf (patch) | |
tree | 1d639f92585d55831353115ef607dd899dc9930e /picohttp.c | |
parent | 62d44c3e46f63c891b18d05be5fbfa3c4a57feb5 (diff) | |
download | litheweb-5ff179fad970eb0a878a27511d6c90419bac2cdf.tar.gz litheweb-5ff179fad970eb0a878a27511d6c90419bac2cdf.tar.bz2 |
Tue Mar 12 20:13:53 CET 2013
Diffstat (limited to 'picohttp.c')
-rw-r--r-- | picohttp.c | 296 |
1 files changed, 180 insertions, 116 deletions
@@ -6,37 +6,65 @@ #include <stdlib.h> #include <assert.h> -static char const * const PICOHTTP_STR_HTTP_ = "HTTP/"; -static char const * const PICOHTTP_STR_CRLF = "\r\n"; -static char const * const PICOHTTP_STR_SERVER = "Server: "; -static char const * const PICOHTTP_STR_PICOWEB = "picoweb/0.1"; - -/* Number formating functions taken from Fefe's libowfat library - * http://www.fefe.de/libowfat */ -static size_t fmt_uint(char *dest, unsigned int i) +static char const PICOHTTP_STR_CRLF[] = "\r\n"; +static char const PICOHTTP_STR_CLSP[] = ": "; +static char const PICOHTTP_STR_HTTP_[] = "HTTP/"; +static char const PICOHTTP_STR_SERVER[] = "Server"; +static char const PICOHTTP_STR_PICOWEB[] = "picoweb/0.1"; + +static char const PICOHTTP_STR_ACCEPT[] = "Accept"; +static char const PICOHTTP_STR__ENCODING[] = "-Encoding"; + +static char const PICOHTTP_STR_CONTENT[] = "Content"; +static char const PICOHTTP_STR__TYPE[] = "-Type"; +static char const PICOHTTP_STR__LENGTH[] = "-Length"; +static char const PICOHTTP_STR__CODING[] = "-Coding"; + +static char const PICOHTTP_STR_CACHECONTROL[] = "Cache-Control"; + +static char const PICOHTTP_STR_DATE[] = "Date"; + +static char const PICOHTTP_STR_EXPECT[] = "Expect"; + +#if !defined(PICOHTTP_CONFIG_HAVE_LIBDJB) +/* Number formating functions modified from libdjb by + * Daniel J. Bernstein, packaged at http://www.fefe.de/djb/ + */ +static size_t picohttp_fmt_uint(char *dest, unsigned int i) { - register unsigned long len,tmp,len2; + register unsigned int len, tmp, len2; /* first count the number of bytes needed */ - for (len=1, tmp=i; tmp>9; ++len) + for(len = 1, tmp = i; + tmp > 9; + ++len ) tmp/=10; + if( dest ) - for(tmp=i, dest+=len, len2=len+1; --len2; tmp/=10) + for(tmp = i, dest += len, len2 = len+1; + --len2; + tmp /= 10 ) *--dest = ( tmp % 10 ) + '0'; return len; } -static size_t fmt_int(char *dest,int i) { +static size_t picohttp_fmt_int(char *dest,int i) { if( i < 0 ) { if( dest ) *dest++='-'; - return fmt_uint(dest,-i)+1; + return picohttp_fmt_uint(dest, -i) + 1; } - return fmt_uint(dest, i); + return picohttp_fmt_uint(dest, i); } +#else +#define picohttp_fmt_uint fmt_ulong +#define picohttp_fmt_int fmt_long +#endif static char const * const picohttpStatusString(int16_t code) { switch(code) { + case 200: + return "OK"; case 400: return "Bad Request"; case 404: @@ -53,54 +81,12 @@ static char const * const picohttpStatusString(int16_t code) return "..."; } -static void picohttpStatus400BadRequest( - struct picohttpRequest *req ) +static void picohttpStatusResponse( + struct picohttpRequest *req, int16_t status ) { - fputs("400\n", stderr); -} - -static void picohttpStatus404NotFound( - struct picohttpRequest *req ) -{ - char http_header[] = "HTTP/x.x 404 Not Found\r\nServer: picoweb\r\nContent-Type: text/text\r\n\r\n"; - http_header[5] = '0'+req->httpversion.major; - http_header[7] = '0'+req->httpversion.minor; - picohttpIoWrite(req->ioops, sizeof(http_header)-1, http_header); - picohttpIoWrite(req->ioops, sizeof(http_header)-1, http_header); -} - -static void picohttpStatus405MethodNotAllowed( - struct picohttpRequest *req ) -{ - fputs("405\n", stderr); -} - -static void picohttpStatus414RequestURITooLong( - struct picohttpRequest *req ) -{ - char http_header[] = "HTTP/x.x 414 URI Too Long\r\nServer: picoweb\r\nContent-Type: text/text\r\n\r\n"; - http_header[5] = '0'+req->httpversion.major; - http_header[7] = '0'+req->httpversion.minor; - picohttpIoWrite(req->ioops, sizeof(http_header)-1, http_header); - picohttpIoWrite(req->ioops, sizeof(http_header)-1, http_header); -} - -static void picohttpStatus500InternalServerError( - struct picohttpRequest *req ) -{ - fputs("500\n", stderr); -} - -static void picohttpStatus501NotImplemented( - struct picohttpRequest *req ) -{ - fputs("501\n", stderr); -} - -static void picohttpStatus505HTTPVersionNotSupported( - struct picohttpRequest *req ) -{ - fputs("505\n", stderr); + req->status = status; + char const * const c = picohttpStatusString(req->status); + picohttpResponseWrite(req, strlen(c), c); } static uint8_t picohttpIsCRLF(int16_t ch) @@ -209,6 +195,12 @@ static int16_t picohttpIoGetPercentCh( return ch; } +/* TODO: + * It is possible to do in-place pattern matching on the route definition + * array, without first reading in the URL and then processing it here. + * + * Implement this to imporove memory footprint reduction. + */ static size_t picohttpMatchURL( char const * const urlhead, char const * const url ) @@ -329,14 +321,14 @@ static int16_t picohttpProcessURL ( { /* copy url up to the first query component; note that this is not * fully compliant to RFC 3986, which permits query components in each - * path component (i.e. between '/'-es). + * path component (i.e. between '/'-s). * picohttp terminates the path once it encounters the first query * component. */ /* Deliberately discarding const qualifier! */ for(char *urliter = (char*)req->url ;; urliter++) { if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } if( '?' == ch || picohttpIsLWS(ch) ) { @@ -345,15 +337,15 @@ static int16_t picohttpProcessURL ( if( '%' == ch ) { ch = picohttpIoGetPercentCh(req->ioops); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } } if( !ch ) { - return -400; + return -PICOHTTP_STATUS_400_BAD_REQUEST; } if( urliter - req->url >= url_max_length ) { - return -414; + return -PICOHTTP_STATUS_414_REQUEST_URI_TOO_LONG; } *urliter = ch; @@ -383,7 +375,7 @@ static int16_t picohttpProcessQuery ( ch = picohttpIoGetch(req->ioops); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } if( '&' == ch ) continue; @@ -391,7 +383,7 @@ static int16_t picohttpProcessQuery ( for(char *variter = var ;; variter++) { if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } if( '=' == ch || '#' == ch || @@ -402,11 +394,11 @@ static int16_t picohttpProcessQuery ( if( '%' == ch ) { ch = picohttpIoGetPercentCh(req->ioops); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } } if( !ch ) { - return -400; + return -PICOHTTP_STATUS_400_BAD_REQUEST; } if( variter - var >= var_max_length ) { @@ -415,7 +407,7 @@ static int16_t picohttpProcessQuery ( do { ch = picohttpIoGetch(req->ioops); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } } while(!( '&' == ch || picohttpIsLWS(ch) )); @@ -430,7 +422,7 @@ static int16_t picohttpProcessQuery ( } } if( 0 > (ch = picohttpIoSkipSpace(req->ioops, ch)) ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } return ch; @@ -444,9 +436,9 @@ static int16_t picohttpProcessHTTPVersion ( for(uint8_t i = 0; i < 5; i++) { if(PICOHTTP_STR_HTTP_[i] != (char)ch ) { if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } - return -400; + return -PICOHTTP_STATUS_400_BAD_REQUEST; } ch = picohttpIoGetch(req->ioops); } @@ -458,30 +450,30 @@ static int16_t picohttpProcessHTTPVersion ( req->ioops, ch ); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } if( ch != '.' ) { - return -400; + return -PICOHTTP_STATUS_400_BAD_REQUEST; } ch = picohttpIoB10ToU8( &req->httpversion.minor, req->ioops, 0 ); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } ch = picohttpIoSkipSpace(req->ioops, ch); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } } ch = picohttpIoSkipOverCRLF(req->ioops, ch); if( ch < 0 ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } if( !ch ) { - return -400; + return -PICOHTTP_STATUS_400_BAD_REQUEST; } return ch; @@ -501,16 +493,16 @@ static int16_t picohttpProcessHeaders ( while( !picohttpIsCRLF( ch=picohttpIoSkipSpace(req->ioops, ch)) ){ fputc(ch, stderr); if( 0 > ( ch=picohttpIoGetch(req->ioops) ) ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } } ch = picohttpIoSkipOverCRLF(req->ioops, ch); if( 0 > ch ) { - return -500; + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } if( !ch ) { - return -400; + return -PICOHTTP_STATUS_400_BAD_REQUEST; } } fputc('\n', stderr); @@ -522,6 +514,7 @@ void picohttpProcessRequest ( struct picohttpURLRoute const * const routes ) { char *url; + int16_t ch; struct picohttpRequest request = {0,}; size_t url_max_length = 0; @@ -532,7 +525,6 @@ void picohttpProcessRequest ( if(url_length > url_max_length) url_max_length = url_length; - } url = alloca(url_max_length+1); memset(url, 0, url_max_length+1); @@ -543,33 +535,34 @@ void picohttpProcessRequest ( request.method = 0; request.httpversion.major = 1; request.httpversion.minor = 0; + request.sent.header = 0; + request.sent.octets = 0; request.method = picohttpProcessRequestMethod(ioops); if( !request.method ) { - picohttpStatus501NotImplemented(&request); - return; + ch = -PICOHTTP_STATUS_501_NOT_IMPLEMENTED; + goto http_error; } if( 0 > request.method ) { - picohttpStatus500InternalServerError(&request); - return; + ch = -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; + goto http_error; } - int16_t ch; if( 0 > (ch = picohttpIoSkipSpace(ioops, 0)) ) { - picohttpStatus500InternalServerError(&request); - return; + ch = -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; + goto http_error; } if( 0 > (ch = picohttpProcessURL(&request, url_max_length, ch)) ) goto http_error; if( !picohttpMatchRoute(&request, routes) || !request.route ) { - picohttpStatus404NotFound(&request); - return; + ch = -PICOHTTP_STATUS_404_NOT_FOUND; + goto http_error; } if( !(request.route->allowed_methods & request.method) ) { - picohttpStatus405MethodNotAllowed(&request); - return; + ch = -PICOHTTP_STATUS_405_METHOD_NOT_ALLOWED; + goto http_error; } if( 0 > (ch = picohttpProcessQuery(&request, ch)) ) @@ -580,51 +573,100 @@ void picohttpProcessRequest ( if( request.httpversion.major > 1 || request.httpversion.minor > 1 ) { - picohttpStatus505HTTPVersionNotSupported(&request); - return; + ch = -PICOHTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; + goto http_error; } if( 0 > (ch = picohttpProcessHeaders(&request, ch)) ) goto http_error; + request.status = PICOHTTP_STATUS_200_OK; request.route->handler(&request); + + picohttpIoFlush(request.ioops); return; http_error: - switch(-ch) { - case 400: picohttpStatus400BadRequest(&request); break; - case 404: picohttpStatus404NotFound(&request); break; - case 405: picohttpStatus405MethodNotAllowed(&request); break; - case 500: picohttpStatus500InternalServerError(&request); break; - } + picohttpStatusResponse(&request, -ch); + picohttpIoFlush(request.ioops); } -int picohttpResponseSendHeader ( +int picohttpResponseSendHeaders ( struct picohttpRequest * const req ) { +#define picohttpIO_WRITE_STATIC_STR(x) \ + (picohttpIoWrite(req->ioops, sizeof(x)-1, x)) + char tmp[16] = {0,}; char const *c; + int e; if(req->sent.header) return 0; - picohttpIoWrite( - req->ioops, - sizeof(PICOHTTP_STR_HTTP_)-1, - PICOHTTP_STR_HTTP_); + if(!req->response.contenttype) { + req->response.contenttype = "text/plain"; + } + +#if defined(PICOHTTP_CONFIG_USE_SNPRINTF) + snprintf(tmp, sizeof(tmp)-1, "%s%d.%d %d ", + PICOHTTP_STR_HTTP_, + req->httpversion.major, + req->httpversion.minor, + req->status); +#else + if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_HTTP_)) ) + return e; size_t p = 0; - p += fmt_uint(tmp+p, req->httpversion.major); + p += picohttp_fmt_uint(tmp+p, req->httpversion.major); tmp[p] = '.'; p++; - p += fmt_uint(tmp+p, req->httpversion.minor); + p += picohttp_fmt_uint(tmp+p, req->httpversion.minor); tmp[p] = ' '; p++; - p += fmt_uint(tmp+p, req->status); + p += picohttp_fmt_uint(tmp+p, req->status); tmp[p] = ' '; p++; - assert(p<15); - picohttpIoWrite(req->ioops, strlen(tmp), tmp); - + assert(p < sizeof(tmp)-1); +#endif + /* HTTP status line */ c = picohttpStatusString(req->status); - picohttpIoWrite(req->ioops, strlen(c), c); + if( 0 > (e = picohttpIoWrite(req->ioops, strlen(tmp), tmp)) || + 0 > (e = picohttpIoWrite(req->ioops, strlen(c), c)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) ) + return e; + + /* Server header */ + if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_SERVER)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CLSP)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_PICOWEB)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) ) + return e; + + /* Content-Type header */ + if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CONTENT)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR__TYPE)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CLSP)) || + 0 > (e = picohttpIoWrite( + req->ioops, strlen(req->response.contenttype), + req->response.contenttype)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) ) + return e; + + if( req->response.contentlength ){ + p = picohttp_fmt_uint(tmp, req->response.contentlength); + if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CONTENT)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR__LENGTH)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CLSP)) || + 0 > (e = picohttpIoWrite(req->ioops, p, tmp)) || + 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) ) + return e; + } + + if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) ) + return e; + + return req->sent.header = 1; + +#undef picohttpIO_WRITE_STATIC_STR } int picohttpResponseWrite ( @@ -632,7 +674,29 @@ int picohttpResponseWrite ( size_t len, char const *buf ) { + int e; + if( !req->sent.header ) - picohttpResponseSendHeader(req); + picohttpResponseSendHeaders(req); + + if( req->response.contentlength > 0 ) { + if(req->sent.octets >= req->response.contentlength) + return -1; + + if(req->sent.octets + len < req->sent.octets) /* int overflow */ + return -2; + + if(req->sent.octets + len >= req->response.contentlength) + len = req->response.contentlength - req->sent.octets; + } + + if( PICOHTTP_METHOD_HEAD == req->method ) + return 0; + + if( 0 > (e = picohttpIoWrite(req->ioops, len, buf)) ) + return e; + + req->sent.octets += len; + return len; } |