aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de>2013-06-18 20:21:17 +0200
committerWolfgang Draxinger <Wolfgang.Draxinger@physik.uni-muenchen.de>2013-06-18 20:21:17 +0200
commit3293d9eeed04c67669c06bc745ea9ee05012cb7f (patch)
tree7ce85af8f494668992c3ec540ebbde2aed4869e0
parent03292a30587f7795a88b8014fba33810c182c009 (diff)
parent3fe9937a5505528b0a2ac17875b9117c8c17c3ff (diff)
downloadlitheweb-3293d9eeed04c67669c06bc745ea9ee05012cb7f.tar.gz
litheweb-3293d9eeed04c67669c06bc745ea9ee05012cb7f.tar.bz2
Merge branch 'master' of mhzoctdev.maiman:/srv/git/mhzoct/picoweb
-rw-r--r--coroutine.h181
-rw-r--r--picohttp.c12
-rw-r--r--picohttp.h26
-rw-r--r--test/bsdsocket.c190
-rw-r--r--test/bufbsdsocket.c (renamed from test/bsd_socket.c)111
5 files changed, 482 insertions, 38 deletions
diff --git a/coroutine.h b/coroutine.h
new file mode 100644
index 0000000..d54a718
--- /dev/null
+++ b/coroutine.h
@@ -0,0 +1,181 @@
+/* coroutine.h
+ *
+ * Coroutine mechanics, implemented on top of standard ANSI C. See
+ * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for
+ * a full discussion of the theory behind this.
+ *
+ * To use these macros to define a coroutine, you need to write a
+ * function that looks something like this.
+ *
+ * [Simple version using static variables (scr macros)]
+ * int ascending (void) {
+ * static int i;
+ *
+ * scrBegin;
+ * for (i=0; i<10; i++) {
+ * scrReturn(i);
+ * }
+ * scrFinish(-1);
+ * }
+ *
+ * [Re-entrant version using an explicit context structure (ccr macros)]
+ * int ascending (ccrContParam) {
+ * ccrBeginContext;
+ * int i;
+ * ccrEndContext(foo);
+ *
+ * ccrBegin(foo);
+ * for (foo->i=0; foo->i<10; foo->i++) {
+ * ccrReturn(foo->i);
+ * }
+ * ccrFinish(-1);
+ * }
+ *
+ * In the static version, you need only surround the function body
+ * with `scrBegin' and `scrFinish', and then you can do `scrReturn'
+ * within the function and on the next call control will resume
+ * just after the scrReturn statement. Any local variables you need
+ * to be persistent across an `scrReturn' must be declared static.
+ *
+ * In the re-entrant version, you need to declare your persistent
+ * variables between `ccrBeginContext' and `ccrEndContext'. These
+ * will be members of a structure whose name you specify in the
+ * parameter to `ccrEndContext'.
+ *
+ * The re-entrant macros will malloc() the state structure on first
+ * call, and free() it when `ccrFinish' is reached. If you want to
+ * abort in the middle, you can use `ccrStop' to free the state
+ * structure immediately (equivalent to an explicit return() in a
+ * caller-type routine).
+ *
+ * A coroutine returning void type may call `ccrReturnV',
+ * `ccrFinishV' and `ccrStopV', or `scrReturnV', to avoid having to
+ * specify an empty parameter to the ordinary return macros.
+ *
+ * Ground rules:
+ * - never put `ccrReturn' or `scrReturn' within an explicit `switch'.
+ * - never put two `ccrReturn' or `scrReturn' statements on the same
+ * source line.
+ *
+ * The caller of a static coroutine calls it just as if it were an
+ * ordinary function:
+ *
+ * void main(void) {
+ * int i;
+ * do {
+ * i = ascending();
+ * printf("got number %d\n", i);
+ * } while (i != -1);
+ * }
+ *
+ * The caller of a re-entrant coroutine must provide a context
+ * variable:
+ *
+ * void main(void) {
+ * ccrContext z = 0;
+ * do {
+ * printf("got number %d\n", ascending (&z));
+ * } while (z);
+ * }
+ *
+ * Note that the context variable is set back to zero when the
+ * coroutine terminates (by crStop, or by control reaching
+ * crFinish). This can make the re-entrant coroutines more useful
+ * than the static ones, because you can tell when they have
+ * finished.
+ *
+ * If you need to dispose of a crContext when it is non-zero (that
+ * is, if you want to stop calling a coroutine without suffering a
+ * memory leak), the caller should call `ccrAbort(ctx)' where `ctx'
+ * is the context variable.
+ *
+ * This mechanism could have been better implemented using GNU C
+ * and its ability to store pointers to labels, but sadly this is
+ * not part of the ANSI C standard and so the mechanism is done by
+ * case statements instead. That's why you can't put a crReturn()
+ * inside a switch() statement.
+ */
+
+/*
+ * coroutine.h is copyright 1995,2000 Simon Tatham.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * $Id$
+ */
+
+#ifndef COROUTINE_H
+#define COROUTINE_H
+
+#include <stdlib.h>
+
+/*
+ * `scr' macros for static coroutines.
+ */
+
+#define scrBegin static int scrLine = 0; switch(scrLine) { case 0:;
+#define scrFinish(z) } return (z)
+#define scrFinishV } return
+
+#define scrReturn(z) \
+ do {\
+ scrLine=__LINE__;\
+ return (z); case __LINE__:;\
+ } while (0)
+#define scrReturnV \
+ do {\
+ scrLine=__LINE__;\
+ return; case __LINE__:;\
+ } while (0)
+
+/*
+ * `ccr' macros for re-entrant coroutines.
+ */
+
+#define ccrContParam void **ccrParam
+
+#define ccrBeginContext struct ccrContextTag { int ccrLine
+#define ccrEndContext(x) } *x = (struct ccrContextTag *)*ccrParam
+
+#define ccrBegin(x) if(!x) {x= *ccrParam=malloc(sizeof(*x)); x->ccrLine=0;}\
+ if (x) switch(x->ccrLine) { case 0:;
+#define ccrFinish(z) } free(*ccrParam); *ccrParam=0; return (z)
+#define ccrFinishV } free(*ccrParam); *ccrParam=0; return
+
+#define ccrReturn(z) \
+ do {\
+ ((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\
+ return (z); case __LINE__:;\
+ } while (0)
+#define ccrReturnV \
+ do {\
+ ((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\
+ return; case __LINE__:;\
+ } while (0)
+
+#define ccrStop(z) do{ free(*ccrParam); *ccrParam=0; return (z); }while(0)
+#define ccrStopV do{ free(*ccrParam); *ccrParam=0; return; }while(0)
+
+#define ccrContext void *
+#define ccrAbort(ctx) do { free (ctx); ctx = 0; } while (0)
+
+#endif /* COROUTINE_H */
diff --git a/picohttp.c b/picohttp.c
index 6576297..487a870 100644
--- a/picohttp.c
+++ b/picohttp.c
@@ -37,6 +37,9 @@ static char const PICOHTTP_STR_FORMDATA[] = "form-data";
static char const PICOHTTP_STR_CACHECONTROL[] = "Cache-Control";
+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";
@@ -73,6 +76,7 @@ static size_t picohttp_fmt_int(char *dest,int i) {
return picohttp_fmt_uint(dest, i);
}
#else
+#include <djb/byte/fmt.h>
#define picohttp_fmt_uint fmt_ulong
#define picohttp_fmt_int fmt_long
#endif
@@ -783,6 +787,13 @@ int picohttpResponseSendHeaders (
0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) )
return e;
+ /* Connection header -- for now this is "Connection: close" */
+ if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CONNECTION)) ||
+ 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CLSP)) ||
+ 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CLOSE)) ||
+ 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)) ||
@@ -793,6 +804,7 @@ int picohttpResponseSendHeaders (
0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CRLF)) )
return e;
+ /* Content-Length header */
if( req->response.contentlength ){
p = picohttp_fmt_uint(tmp, req->response.contentlength);
if( 0 > (e = picohttpIO_WRITE_STATIC_STR(PICOHTTP_STR_CONTENT)) ||
diff --git a/picohttp.h b/picohttp.h
index 831e1b0..ced2ef5 100644
--- a/picohttp.h
+++ b/picohttp.h
@@ -96,6 +96,17 @@ struct picohttpURLRoute {
int16_t 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 */
+};
+
struct picohttpRequest {
struct picohttpIoOps const * ioops;
struct picohttpURLRoute const * route;
@@ -120,9 +131,9 @@ struct picohttpRequest {
} query;
struct {
char const *contenttype;
- char const *date;
- char const *cachecontrol;
char const *disposition;
+ struct picohttpDateTime lastmodified;
+ uint16_t max_age;
size_t contentlength;
uint8_t contentencoding;
uint8_t transferencoding;
@@ -148,4 +159,15 @@ int picohttpResponseWrite (
size_t len,
char const *buf );
+uint16_t picohttpGetch(
+ struct picohttpRequest * const req,
+ struct picohttpChunkTransfer * const ct);
+
+int picohttpMultipartNext(
+ struct picohttpRequest * const req,
+ struct picohttpMultiPart * const mp);
+
+uint16_t picohttpMultipartGetch(
+ struct picohttpMultiPart * const mp);
+
#endif/*PICOHTTP_H_HEADERGUARD*/
diff --git a/test/bsdsocket.c b/test/bsdsocket.c
new file mode 100644
index 0000000..d4bd5a2
--- /dev/null
+++ b/test/bsdsocket.c
@@ -0,0 +1,190 @@
+#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, char *buf, void *data)
+{
+ int fd = *((int*)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;
+ }
+
+ if( EAGAIN == errno ||
+ EWOULDBLOCK == errno ) {
+ usleep(100);
+ continue;
+ }
+ return -3 + errno;
+ } while( rb < count );
+ return rb;
+}
+
+int bsdsock_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)
+{
+ char ch;
+ if( 1 != bsdsock_read(1, &ch, data) )
+ return -1;
+ return ch;
+}
+
+int bsdsock_putch(char ch, void *data)
+{
+ return bsdsock_write(1, &ch, data);
+}
+
+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);
+
+ 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);
+ char http_test[] = "<html><head><title>handling request /</title></head>\n<body><a href=\"/test\">/test</a></body></html>\n";
+
+ picohttpIoWrite(req->ioops, 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);
+ if(req->urltail) {
+ picohttpIoWrite(req->ioops, 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,
+ .data = &confd
+ };
+
+ struct picohttpURLRoute routes[] = {
+ { "/test", 0, rhTest, 16, PICOHTTP_METHOD_GET },
+ { "/|", 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 f193e49..596bc3d 100644
--- a/test/bsd_socket.c
+++ b/test/bufbsdsocket.c
@@ -15,69 +15,108 @@
#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(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 bufbsdsock_flush(void *data)
+{
+ return 0;
}
int bsdsock_flush(void* data)