From 5ff179fad970eb0a878a27511d6c90419bac2cdf Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Tue, 12 Mar 2013 20:13:53 +0100 Subject: Tue Mar 12 20:13:53 CET 2013 --- picohttp.c | 296 +++++++++++++++++++++++++++++++++--------------------- picohttp.h | 15 ++- test/bsd_socket.c | 56 +++++++++-- 3 files changed, 238 insertions(+), 129 deletions(-) diff --git a/picohttp.c b/picohttp.c index 06b7e20..a7f0066 100644 --- a/picohttp.c +++ b/picohttp.c @@ -6,37 +6,65 @@ #include #include -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; } diff --git a/picohttp.h b/picohttp.h index 47c8e6c..093cabc 100644 --- a/picohttp.h +++ b/picohttp.h @@ -18,11 +18,21 @@ #define PICOHTTP_CODING_GZIP 4 #define PICOHTTP_CODING_CHUNKED 8 +#define PICOHTTP_STATUS_200_OK 200 +#define PICOHTTP_STATUS_400_BAD_REQUEST 400 +#define PICOHTTP_STATUS_404_NOT_FOUND 404 +#define PICOHTTP_STATUS_405_METHOD_NOT_ALLOWED 405 +#define PICOHTTP_STATUS_414_REQUEST_URI_TOO_LONG 414 +#define PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR 500 +#define PICOHTTP_STATUS_501_NOT_IMPLEMENTED 501 +#define PICOHTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED 505 + struct picohttpIoOps { int (*read)(size_t /*count*/, char* /*buf*/, void*); int (*write)(size_t /*count*/, char const* /*buf*/, void*); int16_t (*getch)(void*); // returns -1 on error int (*putch)(char, void*); + int (*flush)(void*); void *data; }; @@ -30,6 +40,7 @@ struct picohttpIoOps { #define picohttpIoRead(ioops,size,buf) (ioops->read(size, buf, ioops->data)) #define picohttpIoGetch(ioops) (ioops->getch(ioops->data)) #define picohttpIoPutch(ioops,c) (ioops->putch(c, ioops->data)) +#define picohttpIoFlush(ioops) (ioops->flush(ioops->data)) enum picohttpVarType { PICOHTTP_TYPE_UNDEFINED = 0, @@ -85,7 +96,7 @@ struct picohttpRequest { size_t contentlength; uint8_t contentcoding; uint8_t te; - } queryheader; + } query; struct { char const *contenttype; char const *date; @@ -94,7 +105,7 @@ struct picohttpRequest { size_t contentlength; uint8_t contentencoding; uint8_t transferencoding; - } responseheader; + } response; struct { size_t octets; uint8_t header; diff --git a/test/bsd_socket.c b/test/bsd_socket.c index d4bd5a2..39f601d 100644 --- a/test/bsd_socket.c +++ b/test/bsd_socket.c @@ -80,6 +80,11 @@ int bsdsock_putch(char ch, void *data) return bsdsock_write(1, &ch, data); } +int bsdsock_flush(void* data) +{ + return 0; +} + int sockfd = -1; void bye(void) @@ -97,29 +102,56 @@ void rhRoot(struct picohttpRequest *req) { fprintf(stderr, "handling request /%s\n", req->urltail); - char http_header[] = "HTTP/x.x 200 OK\r\nServer: picoweb\r\nContent-Type: text/html\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); + req->response.contenttype = "text/html"; + char http_test[] = "handling request /\n/test\n"; - picohttpIoWrite(req->ioops, sizeof(http_test)-1, http_test); + picohttpResponseWrite(req, sizeof(http_test)-1, http_test); } void rhTest(struct picohttpRequest *req) { fprintf(stderr, "handling request /test%s\n", req->urltail); - char http_header[] = "HTTP/x.x 200 OK\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); + char http_test[] = "handling request /test"; - picohttpIoWrite(req->ioops, sizeof(http_test)-1, http_test); + picohttpResponseWrite(req, sizeof(http_test)-1, http_test); if(req->urltail) { - picohttpIoWrite(req->ioops, strlen(req->urltail), req->urltail); + picohttpResponseWrite(req, strlen(req->urltail), req->urltail); } } +static uint8_t const favicon_ico[] = { +0x00,0x00,0x01,0x00,0x01,0x00,0x10,0x10,0x10,0x00,0x01,0x00,0x04,0x00,0x28,0x01, +0x00,0x00,0x16,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x20,0x00, +0x00,0x00,0x01,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x09, +0x06,0x00,0x21,0x23,0x22,0x00,0x48,0x4a,0x48,0x00,0x70,0x73,0x71,0x00,0x8d,0x90, +0x8e,0x00,0xb4,0xb7,0xb5,0x00,0xd8,0xdc,0xda,0x00,0xfc,0xff,0xfd,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x77,0x77, +0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x76,0x06,0x76,0x06,0x77,0x77,0x77, +0x77,0x74,0x04,0x74,0x04,0x77,0x77,0x77,0x77,0x72,0x42,0x72,0x42,0x77,0x77,0x77, +0x77,0x71,0x70,0x70,0x71,0x77,0x70,0x77,0x77,0x52,0x72,0x32,0x72,0x57,0x70,0x77, +0x77,0x34,0x74,0x04,0x74,0x37,0x70,0x77,0x77,0x16,0x76,0x06,0x76,0x17,0x70,0x40, +0x03,0x77,0x77,0x77,0x77,0x77,0x70,0x26,0x62,0x37,0x77,0x77,0x77,0x77,0x70,0x67, +0x76,0x17,0x77,0x77,0x77,0x77,0x70,0x77,0x77,0x07,0x77,0x77,0x77,0x77,0x70,0x67, +0x76,0x17,0x77,0x77,0x77,0x77,0x70,0x26,0x62,0x37,0x77,0x77,0x77,0x77,0x70,0x40, +0x03,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +void rhFavicon(struct picohttpRequest *req) +{ + fprintf(stderr, "handling request /favicon.ico\n"); + + req->response.contenttype = "image/vnd.microsoft.icon"; + req->response.contentlength = sizeof(favicon_ico); + picohttpResponseWrite(req, sizeof(favicon_ico), favicon_ico); +} + int main(int argc, char *argv[]) { sockfd = socket(AF_INET, SOCK_STREAM, 0); @@ -170,10 +202,12 @@ int main(int argc, char *argv[]) .write = bsdsock_write, .getch = bsdsock_getch, .putch = bsdsock_putch, + .flush = bsdsock_flush, .data = &confd }; struct picohttpURLRoute routes[] = { + {"/favicon.ico|", 0, rhFavicon, 0, PICOHTTP_METHOD_GET}, { "/test", 0, rhTest, 16, PICOHTTP_METHOD_GET }, { "/|", 0, rhRoot, 0, PICOHTTP_METHOD_GET }, { NULL, 0, 0, 0, 0 } -- cgit v1.2.3 From ccb6ae48b135913588affdd82e11a11054a14a80 Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Tue, 12 Mar 2013 22:07:37 +0100 Subject: Tue Mar 12 22:07:37 CET 2013 --- picohttp.c | 5 ----- test/bsd_socket.c | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/picohttp.c b/picohttp.c index a7f0066..0420d94 100644 --- a/picohttp.c +++ b/picohttp.c @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -488,10 +487,7 @@ static int16_t picohttpProcessHeaders ( /* FIXME: Add Header handling here */ while( !picohttpIsCRLF(ch) ) { - fprintf(stderr, "\n>>> 0x%02x ", (int)ch, stderr); - while( !picohttpIsCRLF( ch=picohttpIoSkipSpace(req->ioops, ch)) ){ - fputc(ch, stderr); if( 0 > ( ch=picohttpIoGetch(req->ioops) ) ) { return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } @@ -505,7 +501,6 @@ static int16_t picohttpProcessHeaders ( return -PICOHTTP_STATUS_400_BAD_REQUEST; } } - fputc('\n', stderr); return ch; } diff --git a/test/bsd_socket.c b/test/bsd_socket.c index 39f601d..02ee592 100644 --- a/test/bsd_socket.c +++ b/test/bsd_socket.c @@ -147,7 +147,7 @@ void rhFavicon(struct picohttpRequest *req) { fprintf(stderr, "handling request /favicon.ico\n"); - req->response.contenttype = "image/vnd.microsoft.icon"; + req->response.contenttype = "image/x-icon"; req->response.contentlength = sizeof(favicon_ico); picohttpResponseWrite(req, sizeof(favicon_ico), favicon_ico); } -- cgit v1.2.3 From b46e67be631d5d007390c29ba89a79b02d0d984b Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Wed, 13 Mar 2013 22:22:27 +0100 Subject: Wed Mar 13 22:22:27 CET 2013 --- picohttp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picohttp.h b/picohttp.h index 093cabc..fba8dfa 100644 --- a/picohttp.h +++ b/picohttp.h @@ -40,7 +40,7 @@ struct picohttpIoOps { #define picohttpIoRead(ioops,size,buf) (ioops->read(size, buf, ioops->data)) #define picohttpIoGetch(ioops) (ioops->getch(ioops->data)) #define picohttpIoPutch(ioops,c) (ioops->putch(c, ioops->data)) -#define picohttpIoFlush(ioops) (ioops->flush(ioops->data)) +#define picohttpIoFlush(ioops) (ioops->flush(ioops->data)) enum picohttpVarType { PICOHTTP_TYPE_UNDEFINED = 0, -- cgit v1.2.3 From f713f27297f04981054937f295a895edaab594cb Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Wed, 13 Mar 2013 22:24:28 +0100 Subject: Wed Mar 13 22:24:28 CET 2013 --- picohttp.c | 37 ++++++++++++++++++++++++++----------- picohttp.h | 9 ++++++--- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/picohttp.c b/picohttp.c index 0420d94..28c0180 100644 --- a/picohttp.c +++ b/picohttp.c @@ -2,8 +2,7 @@ #include #include -#include -#include +/* #include */ static char const PICOHTTP_STR_CRLF[] = "\r\n"; static char const PICOHTTP_STR_CLSP[] = ": "; @@ -80,7 +79,7 @@ static char const * const picohttpStatusString(int16_t code) return "..."; } -static void picohttpStatusResponse( +void picohttpStatusResponse( struct picohttpRequest *req, int16_t status ) { req->status = status; @@ -167,7 +166,7 @@ static int16_t picohttpIoB10ToU8 ( static int16_t picohttpIoGetPercentCh( struct picohttpIoOps const * const ioops ) { - char ch; + char ch=0; int16_t chr; if( 0 > (chr = picohttpIoGetch(ioops))) return chr; @@ -255,6 +254,7 @@ static int16_t picohttpProcessRequestMethod ( struct picohttpIoOps const * const ioops ) { int16_t method = 0; + /* Poor man's string matching tree; trade RAM for code */ switch( picohttpIoGetch(ioops) ) { case 'H': switch( picohttpIoGetch(ioops) ) { @@ -325,7 +325,7 @@ static int16_t picohttpProcessURL ( * component. */ /* Deliberately discarding const qualifier! */ - for(char *urliter = (char*)req->url ;; urliter++) { + for(char *urliter = req->url ;; urliter++) { if( ch < 0 ) { return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; } @@ -343,11 +343,10 @@ static int16_t picohttpProcessURL ( return -PICOHTTP_STATUS_400_BAD_REQUEST; } - if( urliter - req->url >= url_max_length ) { + if( (urliter - req->url) >= url_max_length ) { return -PICOHTTP_STATUS_414_REQUEST_URI_TOO_LONG; } *urliter = ch; - ch = picohttpIoGetch(req->ioops); } return ch; @@ -367,7 +366,11 @@ static int16_t picohttpProcessQuery ( } } } +#ifdef PICOWEB_CONFIG_USE_C99VARARRAY + char var[var_max_length+1]; +#else char *var = alloca(var_max_length+1); +#endif while('?' == ch || '&' == ch) { memset(var, 0, var_max_length+1); @@ -431,6 +434,7 @@ static int16_t picohttpProcessHTTPVersion ( struct picohttpRequest * const req, int16_t ch ) { + if( !picohttpIsCRLF(ch) ) { for(uint8_t i = 0; i < 5; i++) { if(PICOHTTP_STR_HTTP_[i] != (char)ch ) { @@ -485,6 +489,7 @@ static int16_t picohttpProcessHeaders ( #define PICOHTTP_HEADERNAME_MAX_LEN 32 char headername[PICOHTTP_HEADERNAME_MAX_LEN] = {0,}; + /* FIXME: Add Header handling here */ while( !picohttpIsCRLF(ch) ) { while( !picohttpIsCRLF( ch=picohttpIoSkipSpace(req->ioops, ch)) ){ @@ -508,11 +513,12 @@ void picohttpProcessRequest ( struct picohttpIoOps const * const ioops, struct picohttpURLRoute const * const routes ) { - char *url; + int16_t ch; struct picohttpRequest request = {0,}; - size_t url_max_length = 0; + size_t url_max_length = 0; +#if 1 for(size_t i = 0; routes[i].urlhead; i++) { size_t url_length = strlen(routes[i].urlhead) + @@ -521,7 +527,14 @@ void picohttpProcessRequest ( if(url_length > url_max_length) url_max_length = url_length; } - url = alloca(url_max_length+1); +#else + url_max_length = 512; +#endif +#ifdef PICOWEB_CONFIG_USE_C99VARARRAY + char url[url_max_length+1]; +#else + char *url = alloca(url_max_length+1); +#endif memset(url, 0, url_max_length+1); request.url = url; @@ -551,6 +564,7 @@ void picohttpProcessRequest ( if( 0 > (ch = picohttpProcessURL(&request, url_max_length, ch)) ) goto http_error; + if( !picohttpMatchRoute(&request, routes) || !request.route ) { ch = -PICOHTTP_STATUS_404_NOT_FOUND; goto http_error; @@ -582,6 +596,7 @@ void picohttpProcessRequest ( return; http_error: + picohttpStatusResponse(&request, -ch); picohttpIoFlush(request.ioops); } @@ -620,7 +635,7 @@ int picohttpResponseSendHeaders ( tmp[p] = ' '; p++; p += picohttp_fmt_uint(tmp+p, req->status); tmp[p] = ' '; p++; - assert(p < sizeof(tmp)-1); + /* assert(p < sizeof(tmp)-1); */ #endif /* HTTP status line */ c = picohttpStatusString(req->status); diff --git a/picohttp.h b/picohttp.h index 093cabc..66317b1 100644 --- a/picohttp.h +++ b/picohttp.h @@ -2,7 +2,7 @@ #ifndef PICOHTTP_H_HEADERGUARD #define PICOHTTP_H_HEADERGUARD -#include +#include #include #define PICOHTTP_MAJORVERSION(x) ( (x & 0x7f00) >> 8 ) @@ -83,8 +83,8 @@ struct picohttpRequest { struct picohttpIoOps const * ioops; struct picohttpURLRoute const * route; struct picohttpVar *get_vars; - char const *url; - char const *urltail; + char *url; + char *urltail; int16_t status; int16_t method; struct { @@ -116,6 +116,9 @@ void picohttpProcessRequest( struct picohttpIoOps const * const ioops, struct picohttpURLRoute const * const routes ); +void picohttpStatusResponse( + struct picohttpRequest *req, int16_t status ); + int picohttpResponseSendHeader ( struct picohttpRequest * const req ); -- cgit v1.2.3 From 695008f9d18220e9d9c2558129ffcd4db15d2a26 Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Wed, 13 Mar 2013 22:24:59 +0100 Subject: Wed Mar 13 22:24:59 CET 2013 --- picohttp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/picohttp.c b/picohttp.c index 28c0180..e3762f1 100644 --- a/picohttp.c +++ b/picohttp.c @@ -2,7 +2,6 @@ #include #include -/* #include */ static char const PICOHTTP_STR_CRLF[] = "\r\n"; static char const PICOHTTP_STR_CLSP[] = ": "; -- cgit v1.2.3