From 47e13329da36b76918d44d51be78ef9e5584fcc0 Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Tue, 18 Jun 2013 20:10:12 +0200 Subject: Header Line separation implemented --- picohttp.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- picohttp.h | 25 ++++++++- test/bsd_socket.c | 21 ++++++- 3 files changed, 203 insertions(+), 8 deletions(-) diff --git a/picohttp.c b/picohttp.c index e3762f1..1b2550e 100644 --- a/picohttp.c +++ b/picohttp.c @@ -1,11 +1,22 @@ #include "picohttp.h" +#ifdef HOST_DEBUG +#include +#define debug_printf(...) do{fprintf(stderr, __VA_ARGS__);}while(0) +#define debug_putc(x) do{fputc(x, stderr);}while(0) +#else +#define debug_printf(...) do{}while(0) +#define debug_putc(x) do{}while(0) +#endif + #include #include +#include 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_HOST[] = "Host"; static char const PICOHTTP_STR_SERVER[] = "Server"; static char const PICOHTTP_STR_PICOWEB[] = "picoweb/0.1"; @@ -17,12 +28,21 @@ 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_APPLICATION_[] = "application/"; +static char const PICOHTTP_STR_TEXT_[] = "text/"; +static char const PICOHTTP_STR_MULTIPART_[] = "multipart/"; + +static char const PICOHTTP_STR_FORMDATA[] = "form-data"; + static char const PICOHTTP_STR_CACHECONTROL[] = "Cache-Control"; static char const PICOHTTP_STR_DATE[] = "Date"; static char const PICOHTTP_STR_EXPECT[] = "Expect"; +static char const PICOHTTP_STR_BOUNDARY[] = " boundary="; + #if !defined(PICOHTTP_CONFIG_HAVE_LIBDJB) /* Number formating functions modified from libdjb by * Daniel J. Bernstein, packaged at http://www.fefe.de/djb/ @@ -196,7 +216,7 @@ static int16_t picohttpIoGetPercentCh( * 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. + * Implement this to improve memory footprint reduction. */ static size_t picohttpMatchURL( char const * const urlhead, @@ -481,15 +501,75 @@ static int16_t picohttpProcessHTTPVersion ( return ch; } +static void picohttpProcessContentType( + struct picohttpRequest * const req, + char const *contenttype ) +{ + if(!strncmp(contenttype, + PICOHTTP_STR_APPLICATION_, sizeof(PICOHTTP_STR_APPLICATION_)-1)) { + } + + if(!strncmp(contenttype, + PICOHTTP_STR_TEXT_, sizeof(PICOHTTP_STR_TEXT_)-1)) { + } + + if(!strncmp(contenttype, PICOHTTP_STR_MULTIPART_, + sizeof(PICOHTTP_STR_MULTIPART_)-1)) { + req->query.contenttype.type = PICOHTTP_CONTENTTYPE_MULTIPART; + contenttype += sizeof(PICOHTTP_STR_MULTIPART_)-1; + + if(!strncmp(contenttype,PICOHTTP_STR_FORMDATA, + sizeof(PICOHTTP_STR_FORMDATA)-1)) { + req->query.contenttype.subtype = + PICOHTTP_CONTENTTYPE_MULTIPART_SUBTYPE_FORM_DATA; + contenttype += sizeof(PICOHTTP_STR_FORMDATA)-1; + } + char *boundary = strstr(contenttype, PICOHTTP_STR_BOUNDARY); + if(boundary) { + /* see RFC1521 regarding maximum length of boundary */ + strncpy(req->query.multipartboundary, + boundary + sizeof(PICOHTTP_STR_BOUNDARY)-1, + PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN); + } + } +} + +static void picohttpProcessHeaderField( + struct picohttpRequest * const req, + char const * const headername, + char const * const headervalue) +{ + debug_printf("%s: %s\n", headername, headervalue); + if(!strncmp(headername, + PICOHTTP_STR_CONTENT, + sizeof(PICOHTTP_STR_CONTENT)-1)) { + /* Content Length */ + if(!strncmp(headername + sizeof(PICOHTTP_STR_CONTENT)-1, + PICOHTTP_STR__LENGTH, sizeof(PICOHTTP_STR__LENGTH)-1)) { + req->query.contentlength = atol(headervalue); + } + + /* Content Type */ + if(!strncmp(headername + sizeof(PICOHTTP_STR_CONTENT)-1, + PICOHTTP_STR__TYPE, sizeof(PICOHTTP_STR__TYPE)-1)) { + picohttpProcessContentType(req, headervalue); + } + } +} + static int16_t picohttpProcessHeaders ( struct picohttpRequest * const req, int16_t ch ) { #define PICOHTTP_HEADERNAME_MAX_LEN 32 - char headername[PICOHTTP_HEADERNAME_MAX_LEN] = {0,}; - - - /* FIXME: Add Header handling here */ + char headername[PICOHTTP_HEADERNAME_MAX_LEN+1] = {0,}; +#define PICOHTTP_HEADERVALUE_MAX_LEN 224 + char headervalue[PICOHTTP_HEADERVALUE_MAX_LEN+1] = {0,}; + char *hn; + char *hv; + +#define JUST_SKIP_HEADERS 0 +#if JUST_SKIP_HEADERS while( !picohttpIsCRLF(ch) ) { while( !picohttpIsCRLF( ch=picohttpIoSkipSpace(req->ioops, ch)) ){ if( 0 > ( ch=picohttpIoGetch(req->ioops) ) ) { @@ -505,6 +585,81 @@ static int16_t picohttpProcessHeaders ( return -PICOHTTP_STATUS_400_BAD_REQUEST; } } +#else + /* TODO: Add Header handling here */ + while( !picohttpIsCRLF(ch) ) { + /* Beginning of new header line */ + #if 0 + ch = picohttpIoGetch(req->ioops); + #endif + if( 0 < ch && !picohttpIsCRLF(ch) ){ + /* new header field OR field continuation */ + if( picohttpIsLWS(ch) ) { + /* continuation */ + /* skip space */ + if( 0 > (ch = picohttpIoSkipSpace(req->ioops, ch)) ) + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; + + /* read until EOL */ + for(; + 0 < ch && !picohttpIsCRLF(ch); + hv = (hv - headervalue) < + PICOHTTP_HEADERVALUE_MAX_LEN ? + hv+1 : 0 ) { + /* add to header field content */ + + if(hv) + *hv = ch; + + ch = picohttpIoGetch(req->ioops); + } + } else { + if( *headername && *headervalue ) + picohttpProcessHeaderField( + req, + headername, + headervalue ); + /* new header field */ + memset(headername, 0, PICOHTTP_HEADERNAME_MAX_LEN+1); + hn = headername; + + memset(headervalue, 0, PICOHTTP_HEADERVALUE_MAX_LEN+1); + hv = headervalue; + /* read until ':' or EOL */ + for(; + 0 < ch && ':' != ch && !picohttpIsCRLF(ch); + hn = (hn - headername) < + PICOHTTP_HEADERNAME_MAX_LEN ? + hn+1 : 0 ) { + /* add to header name */ + if(hn) + *hn = ch; + + ch = picohttpIoGetch(req->ioops); + } + } + } + if( 0 > ch ) { + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; + } + if( picohttpIsCRLF(ch) ) + ch = picohttpIoSkipOverCRLF(req->ioops, ch); + else + ch = picohttpIoGetch(req->ioops); + if( 0 > ch ) { + return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; + } + if( !ch ) { + return -PICOHTTP_STATUS_400_BAD_REQUEST; + } + } + if( *headername && *headervalue ) + picohttpProcessHeaderField( + req, + headername, + headervalue ); +#endif/*JUST_SKIP_HEADERS*/ + return ch; } diff --git a/picohttp.h b/picohttp.h index 041f124..831e1b0 100644 --- a/picohttp.h +++ b/picohttp.h @@ -5,6 +5,8 @@ #include #include +#define PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN 70 + #define PICOHTTP_MAJORVERSION(x) ( (x & 0x7f00) >> 8 ) #define PICOHTTP_MINORVERSION(x) ( (x & 0x007f) ) @@ -12,6 +14,21 @@ #define PICOHTTP_METHOD_HEAD 2 #define PICOHTTP_METHOD_POST 3 +#define PICOHTTP_CONTENTTYPE_APPLICATION 0 +#define PICOHTTP_CONTENTTYPE_AUDIO 1 +#define PICOHTTP_CONTENTTYPE_IMAGE 2 +#define PICOHTTP_CONTENTTYPE_MESSAGE 3 +#define PICOHTTP_CONTENTTYPE_MODEL 4 +#define PICOHTTP_CONTENTTYPE_MULTIPART 5 +#define PICOHTTP_CONTENTTYPE_TEXT 6 +#define PICOHTTP_CONTENTTYPE_VIDEO 7 + +#define PICOHTTP_CONTENTTYPE_TEXT_SUBTYPE_CSV 3 +#define PICOHTTP_CONTENTTYPE_TEXT_SUBTYPE_HTML 4 +#define PICOHTTP_CONTENTTYPE_TEXT_SUBTYPE_PLAIN 6 + +#define PICOHTTP_CONTENTTYPE_MULTIPART_SUBTYPE_FORM_DATA 4 + #define PICOHTTP_CODING_IDENTITY 0 #define PICOHTTP_CODING_COMPRESS 1 #define PICOHTTP_CODING_DEFLATE 2 @@ -92,10 +109,14 @@ struct picohttpRequest { uint8_t minor; } httpversion; struct { - char const *contenttype; + struct { + uint16_t type:4; + uint16_t subtype:12; + } contenttype; size_t contentlength; - uint8_t contentcoding; + uint8_t contentencoding; uint8_t te; + char multipartboundary[PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN+1]; } query; struct { char const *contenttype; diff --git a/test/bsd_socket.c b/test/bsd_socket.c index 02ee592..f193e49 100644 --- a/test/bsd_socket.c +++ b/test/bsd_socket.c @@ -104,7 +104,14 @@ void rhRoot(struct picohttpRequest *req) req->response.contenttype = "text/html"; - char http_test[] = "handling request /\n/test\n"; + char http_test[] = +"handling request /\n" +"/test" +"
" +"" +"" +"
" +"\n"; picohttpResponseWrite(req, sizeof(http_test)-1, http_test); } @@ -120,6 +127,17 @@ void rhTest(struct picohttpRequest *req) } } +void rhUpload(struct picohttpRequest *req) +{ + fprintf(stderr, "handling request /upload%s\n", req->urltail); + + char http_test[] = "handling request /upload"; + picohttpResponseWrite(req, sizeof(http_test)-1, http_test); + if(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, @@ -209,6 +227,7 @@ int main(int argc, char *argv[]) struct picohttpURLRoute routes[] = { {"/favicon.ico|", 0, rhFavicon, 0, PICOHTTP_METHOD_GET}, { "/test", 0, rhTest, 16, PICOHTTP_METHOD_GET }, + { "/upload|", 0, rhUpload, 0, PICOHTTP_METHOD_GET }, { "/|", 0, rhRoot, 0, PICOHTTP_METHOD_GET }, { NULL, 0, 0, 0, 0 } }; -- cgit v1.2.3 From 03292a30587f7795a88b8014fba33810c182c009 Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Tue, 18 Jun 2013 20:21:06 +0200 Subject: ... --- picohttp.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/picohttp.c b/picohttp.c index 1b2550e..6576297 100644 --- a/picohttp.c +++ b/picohttp.c @@ -568,30 +568,9 @@ static int16_t picohttpProcessHeaders ( char *hn; char *hv; -#define JUST_SKIP_HEADERS 0 -#if JUST_SKIP_HEADERS - while( !picohttpIsCRLF(ch) ) { - while( !picohttpIsCRLF( ch=picohttpIoSkipSpace(req->ioops, ch)) ){ - if( 0 > ( ch=picohttpIoGetch(req->ioops) ) ) { - return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; - } - } - - ch = picohttpIoSkipOverCRLF(req->ioops, ch); - if( 0 > ch ) { - return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR; - } - if( !ch ) { - return -PICOHTTP_STATUS_400_BAD_REQUEST; - } - } -#else /* TODO: Add Header handling here */ while( !picohttpIsCRLF(ch) ) { /* Beginning of new header line */ - #if 0 - ch = picohttpIoGetch(req->ioops); - #endif if( 0 < ch && !picohttpIsCRLF(ch) ){ /* new header field OR field continuation */ if( picohttpIsLWS(ch) ) { @@ -658,7 +637,6 @@ static int16_t picohttpProcessHeaders ( req, headername, headervalue ); -#endif/*JUST_SKIP_HEADERS*/ return ch; } -- cgit v1.2.3