diff options
author | Yorhel <git@yorhel.nl> | 2015-12-15 13:51:50 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2015-12-15 13:51:50 +0100 |
commit | fd1852ed969a21f8b752697c90da5c5ca8bf07b8 (patch) | |
tree | 775fd15f1855c7b351eaa4e5db33af6498c6f4b7 | |
parent | 11070a0f4153def1616d52d38c9f60355ec4412d (diff) |
Read FastCGI packets + some minor refactoring
-rw-r--r-- | Makefile.am | 8 | ||||
-rwxr-xr-x | init-from-git.sh | 3 | ||||
-rw-r--r-- | src/app.c | 66 | ||||
-rw-r--r-- | src/app.h | 24 | ||||
-rw-r--r-- | src/fcgy.h | 8 | ||||
-rw-r--r-- | src/fconn_fastcgi.c | 92 | ||||
-rw-r--r-- | src/fconn_fastcgi.h | 40 |
7 files changed, 212 insertions, 29 deletions
diff --git a/Makefile.am b/Makefile.am index 615ff0c..a950b47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,13 +29,17 @@ EXTRA_DIST+=\ deps/ev/ev_wrap.h # ylib -EXTRA_DIST+=deps/ylib/vec.h +EXTRA_DIST+=\ + deps/ylib/list.h\ + deps/ylib/vec.h\ + deps/ylib/yopt.h # fcgy bin_PROGRAMS=fcgy fcgy_LDADD=libdeps.a -lm -lpthread $(EV_LIBS) fcgy_SOURCES=\ src/app.c\ + src/fconn_fastcgi.c\ src/main.c\ src/util.c @@ -44,6 +48,8 @@ nodist_fcgy_SOURCES=version.c noinst_HEADERS=\ src/app.h\ src/fcgy.h\ + src/fconn_fastcgi.h\ + src/main.h\ src/util.h MOSTLYCLEANFILES+=version.c diff --git a/init-from-git.sh b/init-from-git.sh index b736e1a..029b2d3 100755 --- a/init-from-git.sh +++ b/init-from-git.sh @@ -26,11 +26,12 @@ ylib() { U=http://g.blicky.net/ylib.git/plain rm -rf ylib mkdir -p ylib\ + && wget -q $U/list.h -O ylib/list.h\ && wget -q $U/vec.h -O ylib/vec.h\ && wget -q $U/yopt.h -O ylib/yopt.h } - +rm -r deps mkdir -p deps cd deps @@ -22,22 +22,49 @@ #include "fcgy.h" +/* Time to wait before retrying a failed accept() */ +#define LISTEN_BACKOFF_TIME 3. + + +static void front_timer_cb(EV_P_ ev_timer *w, int revents) { + fcgy_front *f = w->data; + ev_io_start(EV_A_ f->listener); +} + static void front_accept_cb(EV_P_ ev_io *w, int revents) { - fprintf(stderr, "HI!\n"); + fcgy_front *f = w->data; + + int fd = accept(w->fd, NULL, NULL); + if(fd < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)) + return; + if(fd < 0) { + fprintf(stderr, "Error accept()'ing: %s.\n", strerror(errno)); + ev_io_stop(EV_A_ w); + ev_timer_set(f->backoff, LISTEN_BACKOFF_TIME, 0.); + ev_timer_start(EV_A_ f->backoff); + return; + } + + fconn_fastcgi *c = fconn_fastcgi_create(fd, fd); + hlist_prepend(f->app->fconns, c); + fprintf(stderr, "HI!\n"); } -static void front_add(fcgy_app *app, fcgy_front_type t, const char *addr, uint16_t port) { - fcgy_front *f = vec_appendp(app->fronts);; - memset(f, 0, sizeof(fcgy_front)); +static fcgy_front *front_create(fcgy_app *app, fcgy_front_type t, const char *addr, uint16_t port) { + fcgy_front *f = calloc(1, sizeof(fcgy_front)); + f->app = app; f->type = t; f->port = port; f->fd = -1; f->addr = strdup(addr); ev_init(f->listener, front_accept_cb); - f->listener->data = app; + ev_init(f->backoff, front_timer_cb); + f->listener->data = f; + f->backoff->data = f; + return f; } @@ -91,6 +118,7 @@ static void front_stop(fcgy_front *f) { unlink(f->addr); case FCGY_FRONT_TCP: ev_io_stop(EV_DEFAULT_UC_ f->listener); + ev_timer_stop(EV_DEFAULT_UC_ f->backoff); break; case FCGY_FRONT_STDIO: @@ -112,9 +140,11 @@ fcgy_app *app_create() { /* Returns 0 on success, -1 on error. * Writes a string to *err on error. */ int app_config(fcgy_app *app, fcgy_config_name name, const char *val, char *err, size_t errlen) { + fcgy_front *f; switch(name) { case FCGY_CONFIG_APP_UNIX: - front_add(app, FCGY_FRONT_UNIX, val, 0); + f = front_create(app, FCGY_FRONT_UNIX, val, 0); + hlist_prepend(app->fronts, f); return 0; default: assert(0 && "app_config() called on invalid option"); @@ -123,27 +153,29 @@ int app_config(fcgy_app *app, fcgy_config_name name, const char *val, char *err, } +/* TODO: Error reporting */ int app_bind(fcgy_app *app) { - size_t i; + fcgy_front *f; int n = 0; - for(i=0; n == 0 && i<app->fronts.n; i++) - n = front_bind(app->fronts.a+i); + for(f=app->fronts; n == 0 && f; f=f->next) + n = front_bind(f); return n; } void app_run(fcgy_app *app) { - size_t i; - for(i=0; i<app->fronts.n; i++) - front_start(app->fronts.a+i); + fcgy_front *f; + for(f=app->fronts; f; f=f->next) + front_start(f); } void app_destroy(fcgy_app *app) { - size_t i; - for(i=0; i<app->fronts.n; i++) - front_stop(app->fronts.a+i); - - free(app->fronts.a); + while(app->fronts) { + fcgy_front *n = app->fronts; + front_stop(n); + hlist_remove(app->fronts, n); + free(n); + } free(app); } @@ -23,29 +23,33 @@ #ifndef FCGY_APP_H #define FCGY_APP_H - -typedef enum { +enum fcgy_front_type { FCGY_FRONT_STDIO, FCGY_FRONT_UNIX, FCGY_FRONT_TCP -} fcgy_front_type; +}; + +/* TODO: Replace addr/port with a struct sockaddr (/union), to cause invalid + * addresses to fail at config reading time and simplify the binding logic. */ +struct fcgy_front { + fcgy_front *next, *prev; + fcgy_app *app; -typedef struct { fcgy_front_type type; uint16_t port; int fd; char *addr; ev_io listener[1]; -} fcgy_front; - + ev_timer backoff[1]; +}; -typedef struct { - vec_t(fcgy_front) fronts; /* Frontend configuration (and listen sockets) */ - vec_t(int) fconn; /* Frontend connections */ +struct fcgy_app { + fcgy_front *fronts; /* Linked list of frontends */ + fconn_fastcgi *fconns; /* Linked list of (alive) frontend connections */ /* TODO: uid / gid */ /* TODO: backends & options */ -} fcgy_app; +}; fcgy_app *app_create(); int app_config(fcgy_app *, fcgy_config_name, const char *, char *, size_t); @@ -49,12 +49,20 @@ #include <ev.h> #include <vec.h> +#include <list.h> /* version.c */ extern char *fcgy_version; +/* forward typedefs */ +typedef enum fcgy_front_type fcgy_front_type; +typedef struct fcgy_app fcgy_app; +typedef struct fcgy_front fcgy_front; +typedef struct fconn_fastcgi fconn_fastcgi; + #include "util.h" #include "main.h" #include "app.h" +#include "fconn_fastcgi.h" #endif diff --git a/src/fconn_fastcgi.c b/src/fconn_fastcgi.c new file mode 100644 index 0000000..d39c39e --- /dev/null +++ b/src/fconn_fastcgi.c @@ -0,0 +1,92 @@ +/* Copyright (c) 2015-2016 Yoran Heling + + 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 THE AUTHORS OR COPYRIGHT HOLDERS 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. +*/ + +#include "fcgy.h" + +typedef struct { + uint8_t version; + uint8_t type; + uint16_t requestId; + uint16_t contentLength; + uint8_t paddingLength; + uint8_t reserved; +} fastcgi_header; + + +/* Returns -1 if no complete packet has been received, otherwise writes to *h + * and returns the length of the first packet in the buffer. */ +static ssize_t fastcgi_parse_header(fastcgi_header *h, char *buf, size_t buflen) { + if(buflen < sizeof(fastcgi_header)) + return -1; + memcpy(h, buf, sizeof(fastcgi_header)); + /* TODO: A proper byteswap in the struct itself seems more efficient and clear */ + h->requestId = (((uint8_t)buf[2])<<8) + ((uint8_t)buf[3]); + h->contentLength = (((uint8_t)buf[4])<<8) + ((uint8_t)buf[5]); + + size_t len = sizeof(fastcgi_header) + h->contentLength + h->paddingLength; + return buflen < len ? -1 : (ssize_t)len; +} + + +static void io_read_cb(EV_P_ ev_io *w, int revents) { + fconn_fastcgi *c = w->data; + + ssize_t r = read(c->rfd, c->rbuf+c->rbuf_len, sizeof(c->rbuf)-c->rbuf_len); + if(r < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)) + return; + + if(r <= 0) { + fprintf(stderr, "Read error: %s\n", strerror(r == 0 ? ECONNRESET : errno)); + /* TODO: Destroy connection */ + return; + } + + size_t len = c->rbuf_len + r; + char *buf = c->rbuf; + while(true) { + fastcgi_header h; + r = fastcgi_parse_header(&h, buf, len); + if(r < 0) + break; + fprintf(stderr, "Got FastCGI packet: len = %4u, version = %u, type = %2u, requestId = %2u, contentLength = %4u\n", + (unsigned)r, (unsigned)h.version, (unsigned)h.type, (unsigned)h.requestId, (unsigned)h.contentLength); + /* TODO: Handle packet */ + len -= r; + buf += r; + } + memmove(c->rbuf, buf, len); + c->rbuf_len = len; +} + + +fconn_fastcgi *fconn_fastcgi_create(int rfd, int wfd) { + fconn_fastcgi *c = malloc(sizeof(fconn_fastcgi)); + + c->rfd = rfd; + c->rbuf_len = 0; + ev_io_init(c->rio, io_read_cb, c->rfd, EV_READ); + ev_io_start(EV_DEFAULT_UC_ c->rio); + c->rio->data = c; + + c->wfd = wfd; + return c; +} diff --git a/src/fconn_fastcgi.h b/src/fconn_fastcgi.h new file mode 100644 index 0000000..9fac641 --- /dev/null +++ b/src/fconn_fastcgi.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2015-2016 Yoran Heling + + 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 THE AUTHORS OR COPYRIGHT HOLDERS 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. +*/ + +#ifndef FCGY_FCONN_FASTCGI_H +#define FCGY_FCONN_FASTCGI_H + +struct fconn_fastcgi { + fconn_fastcgi *next, *prev; + int rfd, + wfd; + + /* Read buffer. Large enough to hold a single FastCGI packet. + * (should this be a dynamic buffer? Might save some memory and/or optimize read() calls) */ + size_t rbuf_len; + char rbuf[8+65535+255]; + ev_io rio[1]; +}; + +fconn_fastcgi *fconn_fastcgi_create(int, int); + +#endif |