summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2015-12-15 13:51:50 +0100
committerYorhel <git@yorhel.nl>2015-12-15 13:51:50 +0100
commitfd1852ed969a21f8b752697c90da5c5ca8bf07b8 (patch)
tree775fd15f1855c7b351eaa4e5db33af6498c6f4b7
parent11070a0f4153def1616d52d38c9f60355ec4412d (diff)
Read FastCGI packets + some minor refactoring
-rw-r--r--Makefile.am8
-rwxr-xr-xinit-from-git.sh3
-rw-r--r--src/app.c66
-rw-r--r--src/app.h24
-rw-r--r--src/fcgy.h8
-rw-r--r--src/fconn_fastcgi.c92
-rw-r--r--src/fconn_fastcgi.h40
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
diff --git a/src/app.c b/src/app.c
index a48823e..f81a6e4 100644
--- a/src/app.c
+++ b/src/app.c
@@ -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);
}
diff --git a/src/app.h b/src/app.h
index cae85bc..84a0018 100644
--- a/src/app.h
+++ b/src/app.h
@@ -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);
diff --git a/src/fcgy.h b/src/fcgy.h
index dd9ad8d..3b9e946 100644
--- a/src/fcgy.h
+++ b/src/fcgy.h
@@ -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