aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de>2013-07-19 17:33:47 +0200
committerWolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de>2013-07-19 17:33:47 +0200
commit1f958fc608ea5c468c04d27e0029ae298f920134 (patch)
treec0ddd226fd86be6e087e4b207ddda604ec2c7416
parenta32672d18e889fe5020400edc591350f50f2af41 (diff)
parentd62d604877249f1eeaf920b3f149eb906b24cd16 (diff)
downloadlitheweb-1f958fc608ea5c468c04d27e0029ae298f920134.tar.gz
litheweb-1f958fc608ea5c468c04d27e0029ae298f920134.tar.bz2
merged
-rw-r--r--picohttp.c722
-rw-r--r--picohttp.h106
-rw-r--r--test/Makefile9
-rw-r--r--test/bsdsocket.c259
-rw-r--r--test/bufbsdsocket.c (renamed from test/bsd_socket.c)142
5 files changed, 1129 insertions, 109 deletions
diff --git a/picohttp.c b/picohttp.c
index 43d4652..476630e 100644
--- a/picohttp.c
+++ b/picohttp.c
@@ -1,21 +1,47 @@
#include "picohttp.h"
+#define picohttpIoWrite(ioops,size,buf) (ioops->write(size, buf, ioops->data))
+#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))
+
+#ifdef HOST_DEBUG
+#include <stdio.h>
+#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 <alloca.h>
#include <string.h>
+#include <stdlib.h>
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";
static char const PICOHTTP_STR_ACCEPT[] = "Accept";
+static char const PICOHTTP_STR_TRANSFER[] = "Transfer";
+
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__DISPOSITION[] = "-Disposition";
+
+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";
@@ -23,9 +49,21 @@ static char const PICOHTTP_STR_CONNECTION[] = "Connection";
static char const PICOHTTP_STR_CLOSE[] = "close";
static char const PICOHTTP_STR_DATE[] = "Date";
-
static char const PICOHTTP_STR_EXPECT[] = "Expect";
+static char const PICOHTTP_STR_BOUNDARY[] = " boundary=";
+static char const PICOHTTP_STR_NAME__[] = " name=\"";
+
+static char const PICOHTTP_STR_CHUNKED[] = "chunked";
+
+/* compilation unit local function forward declarations */
+static int picohttpProcessHeaders (
+ struct picohttpRequest * const req,
+ picohttpHeaderFieldCallback headerfieldcallback,
+ void * const data,
+ int ch );
+
+/* compilation unit local helper functions */
#if !defined(PICOHTTP_CONFIG_HAVE_LIBDJB)
/* Number formating functions modified from libdjb by
* Daniel J. Bernstein, packaged at http://www.fefe.de/djb/
@@ -61,7 +99,7 @@ static size_t picohttp_fmt_int(char *dest,int i) {
#define picohttp_fmt_int fmt_long
#endif
-static char const * const picohttpStatusString(int16_t code)
+static char const * const picohttpStatusString(int code)
{
switch(code) {
case 200:
@@ -83,14 +121,14 @@ static char const * const picohttpStatusString(int16_t code)
}
void picohttpStatusResponse(
- struct picohttpRequest *req, int16_t status )
+ struct picohttpRequest *req, int status )
{
req->status = status;
char const * const c = picohttpStatusString(req->status);
picohttpResponseWrite(req, strlen(c), c);
}
-static uint8_t picohttpIsCRLF(int16_t ch)
+static uint8_t picohttpIsCRLF(int ch)
{
switch(ch) {
case '\r':
@@ -100,37 +138,42 @@ static uint8_t picohttpIsCRLF(int16_t ch)
return 0;
}
-static uint8_t picohttpIsLWS(int16_t ch)
+static uint8_t picohttpIsLWS(int ch)
{
return
picohttpIsCRLF(ch) ||
' ' == ch || '\t' == ch;
}
-static int16_t picohttpIoSkipSpace (
+static int picohttpIoSkipSpace (
struct picohttpIoOps const * const ioops,
- int16_t ch)
+ int ch)
{
- for(;;ch = 0) {
- if(!ch)
+ for(;;ch = -1) {
+ if(0 > ch) {
ch = picohttpIoGetch(ioops);
- if( 0 >= ch ||
+ }
+
+ if( 0 > ch ||
( ' ' != ch && '\t' != ch ) )
break;
}
return ch;
}
-static int16_t picohttpIoSkipOverCRLF (
+static int picohttpIoSkipOverCRLF (
struct picohttpIoOps const * const ioops,
- int16_t ch)
+ int ch)
{
- for(;;ch = 0) {
- if(!ch)
+ for(;;ch = -1) {
+ if(0 > ch) {
ch = picohttpIoGetch(ioops);
+ }
+
if( ch < 0 ) {
return -1;
}
+
if( ch == '\n' ) {
break;
}
@@ -149,12 +192,29 @@ static int16_t picohttpIoSkipOverCRLF (
return ch;
}
-static int16_t picohttpIoB10ToU8 (
+static int picohttpIoB10ToU8 (
uint8_t *i,
struct picohttpIoOps const * const ioops,
- int16_t ch )
+ int ch )
+{
+ if( 0 > ch )
+ ch = picohttpIoGetch(ioops);
+
+ while( ch >= '0' && ch <= '9' ) {
+ *i *= 10;
+ *i += (ch & 0x0f);
+ ch = picohttpIoGetch(ioops);
+ }
+
+ return ch;
+}
+
+static int picohttpIoB10ToU64 (
+ uint64_t *i,
+ struct picohttpIoOps const * const ioops,
+ int ch )
{
- if( !ch )
+ if( 0 > ch )
ch = picohttpIoGetch(ioops);
while( ch >= '0' && ch <= '9' ) {
@@ -166,11 +226,11 @@ static int16_t picohttpIoB10ToU8 (
return ch;
}
-static int16_t picohttpIoGetPercentCh(
+static int picohttpIoGetPercentCh(
struct picohttpIoOps const * const ioops )
{
char ch=0;
- int16_t chr;
+ int chr;
if( 0 > (chr = picohttpIoGetch(ioops)))
return chr;
@@ -196,11 +256,168 @@ static int16_t picohttpIoGetPercentCh(
return ch;
}
+int picohttpGetch(struct picohttpRequest * const req)
+{
+ int ch;
+ /* skipping over Chunked Transfer Boundaries
+ * if Chunked Transfer Encoding is used */
+ if(req->query.transferencoding == PICOHTTP_CODING_CHUNKED ) {
+ if( !req->query.chunklength ) {
+ /* this is a new chunk;
+ * read the length and skip to after <CR><LF> */
+ if( 0 > (ch = picohttpIoGetch(req->ioops)) ) {
+ return -1;
+ }
+ uint64_t len;
+ if( 0 > (ch = picohttpIoB10ToU64(
+ &len,
+ req->ioops,
+ ch))
+ ) {
+ return ch;
+ }
+ if( 0 > (ch = picohttpIoSkipOverCRLF(req->ioops, ch)) ) {
+ return ch;
+ }
+ req->query.chunklength = len;
+ return ch;
+ }
+
+ if( req->query.chunklength <= req->received_octets ) {
+ /* If this happens the data is corrupted, or
+ * the client is nonconforming, or an attack is
+ * underway, or something entierely different,
+ * or all of that.
+ * Abort processing the query!
+ */
+ return -1;
+ }
+ }
+
+ if( 0 <= (ch = picohttpIoGetch(req->ioops)) ) {
+ memmove(req->query.prev_ch + 1, req->query.prev_ch, 4);
+ req->query.prev_ch[0] = ch;
+ req->received_octets++;
+ } else {
+ return ch;
+ }
+
+ if(req->query.transferencoding == PICOHTTP_CODING_CHUNKED ) {
+ if( !req->query.chunklength <= req->received_octets ) {
+ /* end of chunk;
+ * skip over <CR><LF>, make sure the trailing '0' is
+ * there and read the headers, err, I mean footers
+ * (whatever, the header processing code will do for
+ * chunk footers just fine).
+ */
+ if( '0' != (ch = picohttpIoGetch(req->ioops)) ) {
+ return -1;
+ }
+ if( 0 > (ch = picohttpIoGetch(req->ioops)) ) {
+ return -1;
+ }
+ if( 0 > (ch = picohttpProcessHeaders(
+ req,
+ NULL, NULL,
+ ch))
+ ) {
+ return ch;
+ }
+
+ req->received_octets =
+ req->query.chunklength = 0;
+ }
+ }
+
+ return ch;
+}
+
+int picohttpRead(struct picohttpRequest * const req, size_t len, char * const buf)
+{
+ /* skipping over Chunked Transfer Boundaries
+ * if Chunked Transfer Encoding is used */
+ if(req->query.transferencoding == PICOHTTP_CODING_CHUNKED ) {
+ if( !req->query.chunklength ) {
+ int ch;
+ /* this is a new chunk;
+ * read the length and skip to after <CR><LF> */
+ if( 0 > (ch = picohttpIoGetch(req->ioops)) ) {
+ return -1;
+ }
+ uint64_t len;
+ if( 0 > (ch = picohttpIoB10ToU64(
+ &len,
+ req->ioops,
+ ch))
+ ) {
+ return ch;
+ }
+ if( 0 > (ch = picohttpIoSkipOverCRLF(req->ioops, ch)) ) {
+ return ch;
+ }
+ req->query.chunklength = len;
+ return ch;
+ }
+
+ if( req->query.chunklength <= req->received_octets ) {
+ /* If this happens the data is corrupted, or
+ * the client is nonconforming, or an attack is
+ * underway, or something entierely different,
+ * or all of that.
+ * Abort processing the query!
+ */
+ return -1;
+ }
+ }
+
+ if( req->received_octets + len > req->query.chunklength ) {
+ len = req->query.chunklength - req->received_octets;
+ }
+
+ int r = picohttpIoRead(req->ioops, len, buf);
+ if( 5 < r ) {
+ memmove(req->query.prev_ch + r, req->query.prev_ch, 5-r);
+ memcpy(req->query.prev_ch, buf, r);
+ } else if (0 < r) {
+ memcpy(req->query.prev_ch, buf + len - 5, 5);
+ }
+
+ if(req->query.transferencoding == PICOHTTP_CODING_CHUNKED ) {
+ if( !req->query.chunklength <= req->received_octets ) {
+ int ch;
+ /* end of chunk;
+ * skip over <CR><LF>, make sure the trailing '0' is
+ * there and read the headers, err, I mean footers
+ * (whatever, the header processing code will do for
+ * chunk footers just fine).
+ */
+ if( '0' != (ch = picohttpIoGetch(req->ioops)) ) {
+ return -1;
+ }
+ if( 0 > (ch = picohttpIoGetch(req->ioops)) ) {
+ return -1;
+ }
+ if( 0 > (ch = picohttpProcessHeaders(
+ req,
+ NULL, NULL,
+ ch))
+ ) {
+ return ch;
+ }
+
+ req->received_octets =
+ req->query.chunklength = 0;
+ }
+ }
+
+ return r;
+}
+
/* 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.
+ * Implement this to improve memory footprint reduction.
*/
static size_t picohttpMatchURL(
char const * const urlhead,
@@ -236,7 +453,7 @@ static size_t picohttpMatchURL(
return j;
}
-static int8_t picohttpMatchRoute(
+static int picohttpMatchRoute(
struct picohttpRequest * const req,
struct picohttpURLRoute const * const routes )
{
@@ -253,10 +470,10 @@ static int8_t picohttpMatchRoute(
return 0;
}
-static int16_t picohttpProcessRequestMethod (
+static int picohttpProcessRequestMethod (
struct picohttpIoOps const * const ioops )
{
- int16_t method = 0;
+ int method = 0;
/* Poor man's string matching tree; trade RAM for code */
switch( picohttpIoGetch(ioops) ) {
@@ -316,10 +533,10 @@ static int16_t picohttpProcessRequestMethod (
return method;
}
-static int16_t picohttpProcessURL (
+static int picohttpProcessURL (
struct picohttpRequest * const req,
size_t const url_max_length,
- int16_t ch )
+ int ch )
{
/* copy url up to the first query component; note that this is not
* fully compliant to RFC 3986, which permits query components in each
@@ -355,9 +572,9 @@ static int16_t picohttpProcessURL (
return ch;
}
-static int16_t picohttpProcessQuery (
+static int picohttpProcessQuery (
struct picohttpRequest * const req,
- int16_t ch )
+ int ch )
{
size_t var_max_length = 0;
if(req->route->get_vars) {
@@ -423,6 +640,7 @@ static int16_t picohttpProcessQuery (
ch = picohttpIoGetch(req->ioops);
}
if( '=' == ch ) {
+ debug_printf("set variable '%s'\n", var);
} else {
}
}
@@ -433,9 +651,9 @@ static int16_t picohttpProcessQuery (
return ch;
}
-static int16_t picohttpProcessHTTPVersion (
+static int picohttpProcessHTTPVersion (
struct picohttpRequest * const req,
- int16_t ch )
+ int ch )
{
if( !picohttpIsCRLF(ch) ) {
@@ -464,7 +682,7 @@ static int16_t picohttpProcessHTTPVersion (
ch = picohttpIoB10ToU8(
&req->httpversion.minor,
req->ioops,
- 0 );
+ -1 );
if( ch < 0 ) {
return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR;
}
@@ -485,23 +703,172 @@ static int16_t picohttpProcessHTTPVersion (
return ch;
}
-static int16_t picohttpProcessHeaders (
+static int picohttpProcessContentType(
+ char const **contenttype)
+{
+ int ct = 0;
+ if(!strncmp(*contenttype,
+ PICOHTTP_STR_APPLICATION_, sizeof(PICOHTTP_STR_APPLICATION_)-1)) {
+ ct = PICOHTTP_CONTENTTYPE_APPLICATION;
+ }
+
+ if(!strncmp(*contenttype,
+ PICOHTTP_STR_TEXT_, sizeof(PICOHTTP_STR_TEXT_)-1)) {
+ ct = PICOHTTP_CONTENTTYPE_TEXT;
+ }
+
+ if(!strncmp(*contenttype, PICOHTTP_STR_MULTIPART_,
+ sizeof(PICOHTTP_STR_MULTIPART_)-1)) {
+ ct = PICOHTTP_CONTENTTYPE_MULTIPART;
+ *contenttype += sizeof(PICOHTTP_STR_MULTIPART_)-1;
+
+ if(!strncmp(*contenttype,PICOHTTP_STR_FORMDATA,
+ sizeof(PICOHTTP_STR_FORMDATA)-1)) {
+ *contenttype += sizeof(PICOHTTP_STR_FORMDATA)-1;
+
+ ct = PICOHTTP_CONTENTTYPE_MULTIPART_FORMDATA;
+ }
+ }
+
+ return ct;
+}
+
+static void picohttpProcessHeaderContentType(
struct picohttpRequest * const req,
- int16_t ch )
+ char const *contenttype )
{
-#define PICOHTTP_HEADERNAME_MAX_LEN 32
- char headername[PICOHTTP_HEADERNAME_MAX_LEN] = {0,};
+ req->query.contenttype = picohttpProcessContentType(&contenttype);
+
+ if( PICOHTTP_CONTENTTYPE_MULTIPART == (req->query.contenttype & 0xf000) ) {
+ char *boundary = strstr(contenttype, PICOHTTP_STR_BOUNDARY);
+ if(boundary) {
+ /* see RFC1521 regarding maximum length of boundary */
+ memset(req->query.multipartboundary, 0,
+ PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN+1);
+ memcpy(req->query.multipartboundary, "\r\n--", 4);
+ strncpy(req->query.multipartboundary+4,
+ boundary + sizeof(PICOHTTP_STR_BOUNDARY)-1,
+ PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN);
+ }
+ }
+}
+static void picohttpProcessHeaderField(
+ void * const data,
+ char const *headername,
+ char const *headervalue)
+{
+ struct picohttpRequest * const req = data;
+ debug_printf("%s: %s\n", headername, headervalue);
+ if(!strncmp(headername,
+ PICOHTTP_STR_CONTENT,
+ sizeof(PICOHTTP_STR_CONTENT)-1)) {
+ headername += sizeof(PICOHTTP_STR_CONTENT)-1;
+ /* Content Length */
+ if(!strncmp(headername,
+ PICOHTTP_STR__LENGTH, sizeof(PICOHTTP_STR__LENGTH)-1)) {
+ req->query.contentlength = atol(headervalue);
+ return;
+ }
- /* FIXME: Add Header handling here */
- while( !picohttpIsCRLF(ch) ) {
- while( !picohttpIsCRLF( ch=picohttpIoSkipSpace(req->ioops, ch)) ){
- if( 0 > ( ch=picohttpIoGetch(req->ioops) ) ) {
- return -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR;
+ /* Content Type */
+ if(!strncmp(headername,
+ PICOHTTP_STR__TYPE, sizeof(PICOHTTP_STR__TYPE)-1)) {
+ picohttpProcessHeaderContentType(req, headervalue);
+ return;
+ }
+ return;
+ }
+
+ if(!strncmp(headername,
+ PICOHTTP_STR_TRANSFER,
+ sizeof(PICOHTTP_STR_TRANSFER)-1)) {
+ headername += sizeof(PICOHTTP_STR_TRANSFER)-1;
+ /* Transfer Encoding */
+ if(!strncmp(headername,
+ PICOHTTP_STR__ENCODING, sizeof(PICOHTTP_STR__ENCODING)-1)) {
+ if(!strncmp(headervalue,
+ PICOHTTP_STR_CHUNKED,
+ sizeof(PICOHTTP_STR_CHUNKED)-1)) {
+ req->query.transferencoding = PICOHTTP_CODING_CHUNKED;
+ req->query.chunklength = 0;
}
+ return;
}
+ return;
+ }
+}
+
+static int picohttpProcessHeaders (
+ struct picohttpRequest * const req,
+ picohttpHeaderFieldCallback headerfieldcallback,
+ void * const data,
+ int ch )
+{
+#define PICOHTTP_HEADERNAME_MAX_LEN 32
+ 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;
+
+ /* TODO: Add Header handling here */
+ while( !picohttpIsCRLF(ch) ) {
+ /* Beginning of new header line */
+ 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 = picohttpIoSkipOverCRLF(req->ioops, ch);
+ ch = picohttpIoGetch(req->ioops);
+ }
+ } else {
+ if( *headername && *headervalue && headerfieldcallback )
+ headerfieldcallback(
+ data,
+ 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;
}
@@ -509,6 +876,12 @@ static int16_t picohttpProcessHeaders (
return -PICOHTTP_STATUS_400_BAD_REQUEST;
}
}
+ if( *headername && *headervalue && headerfieldcallback)
+ headerfieldcallback(
+ data,
+ headername,
+ headervalue );
+
return ch;
}
@@ -517,7 +890,7 @@ void picohttpProcessRequest (
struct picohttpURLRoute const * const routes )
{
- int16_t ch;
+ int ch;
struct picohttpRequest request = {0,};
size_t url_max_length = 0;
@@ -548,6 +921,7 @@ void picohttpProcessRequest (
request.httpversion.minor = 0;
request.sent.header = 0;
request.sent.octets = 0;
+ request.received_octets = 0;
request.method = picohttpProcessRequestMethod(ioops);
if( !request.method ) {
@@ -559,7 +933,7 @@ void picohttpProcessRequest (
goto http_error;
}
- if( 0 > (ch = picohttpIoSkipSpace(ioops, 0)) ) {
+ if( 0 > (ch = picohttpIoSkipSpace(ioops, -1)) ) {
ch = -PICOHTTP_STATUS_500_INTERNAL_SERVER_ERROR;
goto http_error;
}
@@ -567,7 +941,6 @@ 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;
@@ -589,9 +962,28 @@ void picohttpProcessRequest (
goto http_error;
}
- if( 0 > (ch = picohttpProcessHeaders(&request, ch)) )
+ if( 0 > (ch = picohttpProcessHeaders(
+ &request,
+ picohttpProcessHeaderField,
+ &request,
+ ch)) )
goto http_error;
+ if( '\r' == ch ) {
+ if( 0 > (ch = picohttpIoGetch(ioops)) )
+ goto http_error;
+ if( '\n' != ch ) {
+ ch = -PICOHTTP_STATUS_400_BAD_REQUEST;
+ goto http_error;
+ }
+ }
+
+ request.query.prev_ch[0] = '\n';
+ request.query.prev_ch[1] = '\r';
+ request.query.prev_ch[2] =
+ request.query.prev_ch[3] =
+ request.query.prev_ch[4] = 0;
+
request.status = PICOHTTP_STATUS_200_OK;
request.route->handler(&request);
@@ -721,3 +1113,245 @@ int picohttpResponseWrite (
return len;
}
+int picohttpMultipartGetch(
+ struct picohttpMultipart * const mp)
+{
+ int ch;
+ if( mp->finished ) {
+ return -1;
+ } else
+ if( 0 <= mp->mismatch ) {
+replay:
+ if( mp->replayhead < mp->replay ) {
+ ch = mp->req->query.multipartboundary[mp->replayhead];
+ mp->replayhead++;
+ return ch;
+ } else {
+ ch = mp->mismatch;
+ mp->mismatch = -1;
+ mp->replayhead = 0;
+ return ch;
+ }
+ } else {
+ ch = picohttpGetch(mp->req);
+
+ /* picohttp's query and header parsing is forgiving
+ * regarding line termination. <CR><LF> or just <LF>
+ * are accepted.
+ * However multipart boundaries are to start with
+ * a <CR><LF> sequence.
+ */
+
+ while( 0 <= ch ) {
+
+ if( mp->req->query.multipartboundary[mp->in_boundary] == ch ) {
+ if( 0 == mp->req->query.multipartboundary[mp->in_boundary+1] ) {
+ mp->in_boundary = 0;
+ /* matched boundary */
+ char trail[2] = {0, 0};
+ for(int i=0; i<2; i++) {
+ trail[i] = picohttpGetch(mp->req);
+ if( 0 > trail[i] )
+ return -1;
+ }
+
+ if(trail[0] == '\r' && trail[1] == '\n') {
+ mp->finished = 1;
+ }
+
+ /* TODO: Technically the last boundary is followed by a
+ * terminating <CR><LF> sequence... we should check for
+ * this as well, just for completeness
+ */
+ if(trail[0] == '-' && trail[1] == '-') {
+ mp->finished = 2;
+ }
+
+ return -1;
+ }
+ mp->in_boundary++;
+ } else {
+ if( mp->in_boundary )
+ {
+#if 1
+ if( '\r' == ch ) {
+ mp->replay = mp->in_boundary-1;
+ mp->mismatch = mp->req->query.multipartboundary[mp->replay];
+ mp->in_boundary = 1;
+ } else
+#endif
+ {
+ mp->mismatch = ch;
+ mp->replay = mp->in_boundary;
+ mp->in_boundary = 0;
+ }
+
+ goto replay;
+
+ }
+ return ch;
+ }
+ ch = picohttpGetch(mp->req);
+ }
+ }
+ return ch;
+}
+
+int picohttpMultipartRead(
+ struct picohttpMultipart * const mp,
+ size_t len,
+ char * const buf)
+{
+/* TODO: Replace this with a dedicated variant processing whole buffers?
+ * Probably a lot of code would be shared with the ...Getch variant
+ * and could be placed into a commonly used function.
+ */
+ int ch;
+ size_t i;
+ for(i = 0; i < len; i++) {
+ if( 0 > (ch = picohttpMultipartGetch(mp)) ) {
+ if( mp->finished )
+ return 0;
+ else
+ return ch;
+ }
+
+ buf[i] = ch;
+ }
+ return i;
+}
+
+static void picohttpProcessMultipartContentType(
+ struct picohttpMultipart * const mp,
+ char const *contenttype )
+{
+ mp->contenttype = picohttpProcessContentType(&contenttype);
+
+ if( PICOHTTP_CONTENTTYPE_MULTIPART == (mp->contenttype & 0xf000) ) {
+ }
+}
+
+static void picohttpProcessMultipartContentDisposition(
+ struct picohttpMultipart * const mp,
+ char const *disposition )
+{
+ char const *name = strstr(disposition, PICOHTTP_STR_NAME__);
+ if(name) {
+ name += sizeof(PICOHTTP_STR_NAME__)-1;
+ char const * const nameend = strchr(name, '"');
+ if(nameend) {
+ size_t len = nameend - name;
+ if( PICOHTTP_DISPOSITION_NAME_MAX < len )
+ len = PICOHTTP_DISPOSITION_NAME_MAX;
+
+ strncpy(mp->disposition.name, name, len);
+ }
+ }
+}
+
+static void picohttpMultipartHeaderField(
+ void * const data,
+ char const *headername,
+ char const *headervalue)
+{
+ struct picohttpMultipart * const mp = data;
+ if(!strncmp(headername,
+ PICOHTTP_STR_CONTENT,
+ sizeof(PICOHTTP_STR_CONTENT)-1)) {
+ headername += sizeof(PICOHTTP_STR_CONTENT)-1;
+ /* Content Length
+ * TODO: Is this a header actually defined for multipart bodies?
+ * Anyway, even if it's not defined, this has not negative
+ * side effects, so why care about it. Worst it can do it
+ * be usefull later.
+ */
+ if(!strncmp(headername,
+ PICOHTTP_STR__LENGTH, sizeof(PICOHTTP_STR__LENGTH)-1)) {
+ return;
+ }
+
+ /* Content Disposition */
+ if(!strncmp(headername,
+ PICOHTTP_STR__DISPOSITION, sizeof(PICOHTTP_STR__DISPOSITION)-1)) {
+ picohttpProcessMultipartContentDisposition(mp, headervalue);
+ return;
+ }
+
+ /* Content Type */
+ if(!strncmp(headername,
+ PICOHTTP_STR__TYPE, sizeof(PICOHTTP_STR__TYPE)-1)) {
+ picohttpProcessMultipartContentType(mp, headervalue);
+ return;
+ }
+ return;
+ }
+}
+
+struct picohttpMultipart picohttpMultipartStart(
+ struct picohttpRequest * const req)
+{
+ struct picohttpMultipart mp = {
+ .req = req,
+ .finished = 0,
+ .contenttype = 0,
+ .disposition = { .name = {0,} },
+ .in_boundary = 2,
+ .replayhead = 2,
+ .mismatch = -1,
+ };
+
+ return mp;
+}
+
+int picohttpMultipartNext(
+ struct picohttpMultipart * const mp)
+{
+ if( 2 == mp->finished ) {
+ return -1;
+ }
+
+ for(;;) {
+ int ch = picohttpMultipartGetch(mp);
+ if( 0 > ch ) {
+ if( 2 == mp->finished ) {
+ return -1;
+ }
+
+ if( 1 == mp->finished ) {
+ mp->finished = 0;
+
+ if( 0 > (ch = picohttpGetch(mp->req)) )
+ return ch;
+
+ if( 0 > (ch = picohttpProcessHeaders(
+ mp->req,
+ picohttpMultipartHeaderField,
+ mp,
+ ch)) )
+ return ch;
+
+ if( '\r' == ch ) {
+ if( 0 > (ch = picohttpIoGetch(mp->req->ioops)) )
+ return ch;
+ if( '\n' != ch ) {
+ return -1;
+ }
+ }
+ mp->mismatch = -1;
+ mp->in_boundary =
+ mp->replayhead = 0;
+
+ mp->req->query.prev_ch[0] = '\n';
+ mp->req->query.prev_ch[1] = '\r';
+ mp->req->query.prev_ch[2] =
+ mp->req->query.prev_ch[3] =
+ mp->req->query.prev_ch[4] = 0;
+
+ return 0;
+ }
+ }
+
+ }
+
+ return -1;
+}
diff --git a/picohttp.h b/picohttp.h
index 6041935..7f6e65a 100644
--- a/picohttp.h
+++ b/picohttp.h
@@ -5,12 +5,31 @@
#include <stddef.h>
#include <stdint.h>
-#define PICOHTTP_MAJORVERSION(x) ( (x & 0x7f00) >> 8 )
-#define PICOHTTP_MINORVERSION(x) ( (x & 0x007f) )
+/* max 70 for boundary + 4 chars for "<CR><LF>--" */
+#define PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN 74
+#define PICOHTTP_DISPOSITION_NAME_MAX 48
#define PICOHTTP_METHOD_GET 1
#define PICOHTTP_METHOD_HEAD 2
-#define PICOHTTP_METHOD_POST 3
+#define PICOHTTP_METHOD_POST 4
+
+#define PICOHTTP_CONTENTTYPE_APPLICATION 0x1000
+#define PICOHTTP_CONTENTTYPE_APPLICATION_OCTETSTREAM 0x1000
+
+#define PICOHTTP_CONTENTTYPE_AUDIO 0x2000
+#define PICOHTTP_CONTENTTYPE_IMAGE 0x3000
+#define PICOHTTP_CONTENTTYPE_MESSAGE 0x4000
+#define PICOHTTP_CONTENTTYPE_MODEL 0x5000
+
+#define PICOHTTP_CONTENTTYPE_MULTIPART 0x6000
+#define PICOHTTP_CONTENTTYPE_MULTIPART_FORMDATA 0x6004
+
+#define PICOHTTP_CONTENTTYPE_TEXT 0x7000
+#define PICOHTTP_CONTENTTYPE_TEXT_CSV 0x7003
+#define PICOHTTP_CONTENTTYPE_TEXT_HTML 0x7004
+#define PICOHTTP_CONTENTTYPE_TEXT_PLAIN 0x7006
+
+#define PICOHTTP_CONTENTTYPE_VIDEO 0x8000
#define PICOHTTP_CODING_IDENTITY 0
#define PICOHTTP_CODING_COMPRESS 1
@@ -28,20 +47,14 @@
#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 (*read)(size_t /*count*/, void* /*buf*/, void*);
+ int (*write)(size_t /*count*/, void const* /*buf*/, void*);
+ int (*getch)(void*); // returns negative value on error
+ int (*putch)(int, void*);
int (*flush)(void*);
void *data;
};
-#define picohttpIoWrite(ioops,size,buf) (ioops->write(size, buf, ioops->data))
-#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,
PICOHTTP_TYPE_INTEGER = 1,
@@ -75,8 +88,19 @@ struct picohttpURLRoute {
char const * urlhead;
struct picohttpVarSpec const * get_vars;
picohttpHandler handler;
- uint16_t max_urltail_len;
- int16_t allowed_methods;
+ unsigned int max_urltail_len;
+ int allowed_methods;
+};
+
+#define PICOHTTP_EPOCH_YEAR 1970
+
+struct picohttpDateTime {
+ unsigned int Y:7; /* EPOCH + 127 years */
+ unsigned int M:4;
+ unsigned int D:5;
+ unsigned int h:5;
+ unsigned int m:6;
+ unsigned int s:5; /* seconds / 2 */
};
#define PICOHTTP_EPOCH_YEAR 1980
@@ -96,39 +120,61 @@ struct picohttpRequest {
struct picohttpVar *get_vars;
char *url;
char *urltail;
- int16_t status;
- int16_t method;
+ int status;
+ int method;
struct {
uint8_t major;
uint8_t minor;
} httpversion;
struct {
- char const *contenttype;
+ int contenttype;
size_t contentlength;
- uint8_t contentcoding;
- uint8_t te;
+ uint8_t contentencoding;
+ uint8_t transferencoding;
+ unsigned char multipartboundary[PICOHTTP_MULTIPARTBOUNDARY_MAX_LEN+1];
+ unsigned char prev_ch[5];
+ size_t chunklength;
} query;
struct {
char const *contenttype;
char const *disposition;
struct picohttpDateTime lastmodified;
- uint16_t max_age;
+ int max_age;
size_t contentlength;
uint8_t contentencoding;
uint8_t transferencoding;
} response;
+ size_t received_octets;
struct {
size_t octets;
uint8_t header;
} sent;
};
+struct picohttpMultipart {
+ struct picohttpRequest *req;
+ uint8_t finished;
+ int contenttype;
+ struct {
+ char name[PICOHTTP_DISPOSITION_NAME_MAX+1];
+ } disposition;
+ int in_boundary;
+ int replay;
+ int replayhead;
+ int mismatch;
+};
+
+typedef void (*picohttpHeaderFieldCallback)(
+ void * const data,
+ char const *headername,
+ char const *headervalue);
+
void picohttpProcessRequest(
struct picohttpIoOps const * const ioops,
struct picohttpURLRoute const * const routes );
void picohttpStatusResponse(
- struct picohttpRequest *req, int16_t status );
+ struct picohttpRequest *req, int status );
int picohttpResponseSendHeader (
struct picohttpRequest * const req );
@@ -138,4 +184,20 @@ int picohttpResponseWrite (
size_t len,
char const *buf );
+int picohttpGetch(struct picohttpRequest * const req);
+
+struct picohttpMultipart picohttpMultipartStart(
+ struct picohttpRequest * const req);
+
+int picohttpMultipartNext(
+ struct picohttpMultipart * const mp);
+
+int picohttpMultipartGetch(
+ struct picohttpMultipart * const mp);
+
+int picohttpMultipartRead(
+ struct picohttpMultipart * const mp,
+ size_t len,
+ char * const buf);
+
#endif/*PICOHTTP_H_HEADERGUARD*/
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..e96cb0f
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,9 @@
+.PHONY: all
+
+all: bsdsocket bsdsocket_nhd
+
+bsdsocket: bsdsocket.c ../picohttp.c ../picohttp.h
+ $(CC) -std=c99 -DHOST_DEBUG -O0 -g3 -I../ -o bsdsocket ../picohttp.c bsdsocket.c
+
+bsdsocket_nhd: bsdsocket.c ../picohttp.c ../picohttp.h
+ $(CC) -std=c99 -O0 -g3 -I../ -o bsdsocket_nhd ../picohttp.c bsdsocket.c
diff --git a/test/bsdsocket.c b/test/bsdsocket.c
new file mode 100644
index 0000000..5350db9
--- /dev/null
+++ b/test/bsdsocket.c
@@ -0,0 +1,259 @@
+#define _BSD_SOURCE
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+#include "../picohttp.h"
+
+int bsdsock_read(size_t count, void *buf, void *data)
+{
+ int fd = *((int*)data);
+
+ ssize_t rb = 0;
+ ssize_t r = 0;
+ do {
+ r = read(fd, (unsigned char*)buf + rb, count-rb);
+ if( 0 < r ) {
+ rb += r;
+ continue;
+ }
+ if( !r ) {
+ break;
+ }
+
+ if( EAGAIN == errno ||
+ EWOULDBLOCK == errno ) {
+ usleep(100);
+ continue;
+ }
+ return -3 + errno;
+ } while( rb < count );
+ return rb;
+}
+
+int bsdsock_write(size_t count, void const *buf, void *data)
+{
+ int fd = *((int*)data);
+
+ ssize_t wb = 0;
+ ssize_t w = 0;
+ do {
+ w = write(fd, (unsigned char*)buf + wb, count-wb);
+ if( 0 < w ) {
+ wb += w;
+ continue;
+ }
+ if( !w ) {
+ break;
+ }
+
+ if( EAGAIN == errno ||
+ EWOULDBLOCK == errno ) {
+ usleep(100);
+ continue;
+ }
+ return -3 + errno;
+ } while( wb < count );
+ return wb;
+}
+
+int bsdsock_getch(void *data)
+{
+ unsigned char ch;
+ int err;
+ if( 1 != (err = bsdsock_read(1, &ch, data)) )
+ return err;
+ return ch;
+}
+
+int bsdsock_putch(int ch, void *data)
+{
+ char ch_ = ch;
+ return bsdsock_write(1, &ch_, data);
+}
+
+int bsdsock_flush(void* data)
+{
+ return 0;
+}
+
+int sockfd = -1;
+
+void bye(void)
+{
+ fputs("exiting\n", stderr);
+ int const one = 1;
+ /* allows for immediate reuse of address:port
+ * after program termination */
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockfd);
+}
+
+void rhRoot(struct picohttpRequest *req)
+{
+ fprintf(stderr, "handling request /%s\n", req->urltail);
+
+ req->response.contenttype = "text/html";
+
+ char http_test[] =
+"<html><head><title>handling request /</title></head><body>\n"
+"<a href=\"/test\">/test</a>\n"
+"<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"post\">\n"
+"<label for=\"name\">Name: </label><input type=\"text\" name=\"name\"></input><br/>\n"
+"<label for=\"file1\">File: </label><input type=\"file\" name=\"file1\"></input><br/>\n"
+"<label for=\"file2\">File: </label><input type=\"file\" name=\"file2\"></input><br/>\n"
+"<input type=\"submit\" value=\"Upload\"></input>\n"
+"</form>\n"
+"</body></html>\n";
+
+ 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;
+ picohttpResponseWrite(req, sizeof(http_header)-1, http_header);
+ char http_test[] = "handling request /test";
+ picohttpResponseWrite(req, sizeof(http_test)-1, http_test);
+ if(req->urltail) {
+ picohttpResponseWrite(req, strlen(req->urltail), req->urltail);
+ }
+}
+
+void rhUpload(struct picohttpRequest *req)
+{
+ fprintf(stderr, "handling request /upload%s\n", req->urltail);
+
+ if( PICOHTTP_CONTENTTYPE_MULTIPART_FORMDATA != req->query.contenttype )
+ return;
+
+ char http_test[] = "handling request /upload";
+
+ struct picohttpMultipart mp = picohttpMultipartStart(req);
+
+ chdir("/tmp/uploadtest");
+ while( !picohttpMultipartNext(&mp) ) {
+ fprintf(stderr, "\nprocessing form field \"%s\"\n", mp.disposition.name);
+ FILE *fil = fopen(mp.disposition.name, "wb");
+ if(!fil) {
+ continue;
+ }
+
+ for(int ch = picohttpMultipartGetch(&mp);
+ 0 <= ch;
+ ch = picohttpMultipartGetch(&mp) ) {
+ fputc(ch, fil);
+
+ #if HOST_DEBUG
+ fputs("\e[32m", stderr);
+ switch(ch) {
+ case '\r':
+ fputs("[CR]", stderr); break;
+ case '\n':
+ fputs("[LF]", stderr); break;
+
+ default:
+ fputc(ch, stderr);
+ }
+ fputs("\e[0m", stderr);
+ #endif/*HOST_DEBUG*/
+ }
+
+ fclose(fil);
+ if( !mp.finished ) {
+ break;
+ }
+ }
+ if( !mp.finished ) {
+ }
+
+ picohttpResponseWrite(req, sizeof(http_test)-1, http_test);
+ if(req->urltail) {
+ picohttpResponseWrite(req, strlen(req->urltail), req->urltail);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if( -1 == sockfd ) {
+ perror("socket");
+ return -1;
+ }
+#if 0
+ if( atexit(bye) ) {
+ return -1;
+ }
+#endif
+
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8000),
+ .sin_addr = 0
+ };
+
+ int const one = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ if( -1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) ) {
+ perror("bind");
+ return -1;
+ }
+
+ if( -1 == listen(sockfd, 2) ) {
+ perror("listen");
+ return -1;
+ }
+
+ for(;;) {
+ socklen_t addrlen = 0;
+ int confd = accept(sockfd, (struct sockaddr*)&addr, &addrlen);
+ if( -1 == confd ) {
+ if( EAGAIN == errno ||
+ EWOULDBLOCK == errno ) {
+ usleep(1000);
+ continue;
+ } else {
+ perror("accept");
+ return -1;
+ }
+ }
+
+ struct picohttpIoOps ioops = {
+ .read = bsdsock_read,
+ .write = bsdsock_write,
+ .getch = bsdsock_getch,
+ .putch = bsdsock_putch,
+ .flush = bsdsock_flush,
+ .data = &confd
+ };
+
+ struct picohttpURLRoute routes[] = {
+ { "/test", 0, rhTest, 16, PICOHTTP_METHOD_GET },
+ { "/upload", 0, rhUpload, 16, PICOHTTP_METHOD_POST },
+ { "/|", 0, rhRoot, 0, PICOHTTP_METHOD_GET },
+ { NULL, 0, 0, 0, 0 }
+ };
+
+ picohttpProcessRequest(&ioops, routes);
+
+ shutdown(confd, SHUT_RDWR);
+ close(confd);
+ }
+
+ return 0;
+}
+
diff --git a/test/bsd_socket.c b/test/bufbsdsocket.c
index 02ee592..22a4a99 100644
--- a/test/bsd_socket.c
+++ b/test/bufbsdsocket.c
@@ -6,81 +6,118 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
+#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <netinet/ip.h>
+#include <poll.h>
#include "../picohttp.h"
-int bsdsock_read(size_t count, char *buf, void *data)
+#define SENDBUF_LEN 256
+
+struct bufbsdsockData {
+ char * recvbuf;
+ size_t recvbuf_len;
+ size_t recvbuf_pos;
+ char sendbuf[SENDBUF_LEN];
+ size_t sendbuf_pos;
+ int fd;
+};
+
+int bufbsdsock_read(size_t count, char *buf, void *data_)
{
- int fd = *((int*)data);
-
+ struct bufbsdsockData *data = data_;
+
ssize_t rb = 0;
ssize_t r = 0;
do {
- r = read(fd, buf+rb, count-rb);
- if( 0 < r ) {
- rb += r;
- continue;
- }
- if( !r ) {
- break;
- }
+ size_t len = 0;
+
+ if( !data->recvbuf ||
+ data->recvbuf_pos >= data->recvbuf_len ) {
+ if( data->recvbuf )
+ free( data->recvbuf );
+ data->recvbuf_len = 0;
+ data->recvbuf_pos = 0;
+
+ int avail = 0;
+ do {
+ struct pollfd pfd = {
+ .fd = data->fd,
+ .events = POLLIN | POLLPRI,
+ .revents = 0
+ };
+
+ int const pret = poll(&pfd, 1, -1);
+ if( 0 >= pret ) {
+ return -1;
+ }
+
+ assert(pfd.revents & (POLLIN | POLLPRI));
+
+ if( -1 == ioctl(data->fd, FIONREAD, &avail) ) {
+ perror("ioctl(FIONREAD)");
+ return -1;
+ }
+ } while( !avail );
+
+ data->recvbuf = malloc( avail);
+
+ int r;
+ while( 0 > (r = read(data->fd, data->recvbuf, avail)) ) {
+ if( EINTR == errno )
+ continue;
+
+ if( EAGAIN == errno ||
+ EWOULDBLOCK == errno ) {
+ usleep(200);
+ continue;
+ }
- if( EAGAIN == errno ||
- EWOULDBLOCK == errno ) {
- usleep(100);
- continue;
+ return -1;
+ }
+ data->recvbuf_len += r;
}
- return -3 + errno;
+
+ len = data->recvbuf_len - data->recvbuf_pos;
+ if( len > count )
+ len = count;
+
+ rb += len;
} while( rb < count );
return rb;
}
-int bsdsock_write(size_t count, char const *buf, void *data)
+int bufbsdsock_write(size_t count, char const *buf, void *data)
{
int fd = *((int*)data);
ssize_t wb = 0;
ssize_t w = 0;
do {
- w = write(fd, buf+wb, count-wb);
- if( 0 < w ) {
- wb += w;
- continue;
- }
- if( !w ) {
- break;
- }
-
- if( EAGAIN == errno ||
- EWOULDBLOCK == errno ) {
- usleep(100);
- continue;
- }
- return -3 + errno;
} while( wb < count );
return wb;
}
-int16_t bsdsock_getch(void *data)
+int16_t bufbsdsock_getch(void *data)
{
char ch;
- if( 1 != bsdsock_read(1, &ch, data) )
+ if( 1 != bufbsdsock_read(1, &ch, data) )
return -1;
return ch;
}
-int bsdsock_putch(char ch, void *data)
+int bufbsdsock_putch(char ch, void *data)
{
- return bsdsock_write(1, &ch, data);
+ return bufbsdsock_write(1, &ch, data);
}
-int bsdsock_flush(void* data)
+int bufbsdsock_flush(void *data)
{
return 0;
}
@@ -104,7 +141,14 @@ void rhRoot(struct picohttpRequest *req)
req->response.contenttype = "text/html";
- char http_test[] = "<html><head><title>handling request /</title></head>\n<body><a href=\"/test\">/test</a></body></html>\n";
+ char http_test[] =
+"<html><head><title>handling request /</title></head><body>\n"
+"<a href=\"/test\">/test</a>"
+"<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"post\">"
+"<label for=\"file\">File: </label><input type=\"file\" name=\"file\"></input>"
+"<input type=\"submit\" value=\"Upload\"></input>"
+"</form>"
+"</body></html>\n";
picohttpResponseWrite(req, sizeof(http_test)-1, http_test);
}
@@ -120,6 +164,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,
@@ -198,17 +253,18 @@ int main(int argc, char *argv[])
}
struct picohttpIoOps ioops = {
- .read = bsdsock_read,
- .write = bsdsock_write,
- .getch = bsdsock_getch,
- .putch = bsdsock_putch,
- .flush = bsdsock_flush,
+ .read = bufbsdsock_read,
+ .write = bufbsdsock_write,
+ .getch = bufbsdsock_getch,
+ .putch = bufbsdsock_putch,
+ .flush = bufbsdsock_flush,
.data = &confd
};
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 }
};