summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2015-12-20 11:26:49 +0100
committerYorhel <git@yorhel.nl>2015-12-20 11:29:32 +0100
commit36f4c73123e70e437628f424a1c47877278ab335 (patch)
tree5779967ef417d379551b8fa5dc079fc8e2c6a946
parenta82d9fee21b78045b2c8143ea0bb1c64954e71a3 (diff)
Request management + param passing + some fastcgi reader fixes
-rw-r--r--Makefile.am2
-rw-r--r--src/fastcgi.c37
-rw-r--r--src/fastcgi.h36
-rw-r--r--src/fcgy.h2
-rw-r--r--src/fconn_fastcgi.c97
-rw-r--r--src/fconn_fastcgi.h9
-rw-r--r--src/req.c52
-rw-r--r--src/req.h57
-rw-r--r--test/fastcgi_param_parse.c2
9 files changed, 268 insertions, 26 deletions
diff --git a/Makefile.am b/Makefile.am
index 47ce969..c2a038e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,6 +42,7 @@ fcgy_SOURCES=\
src/fastcgi.c\
src/fconn_fastcgi.c\
src/main.c\
+ src/req.c\
src/util.c
nodist_fcgy_SOURCES=version.c
@@ -52,6 +53,7 @@ noinst_HEADERS=\
src/fcgy.h\
src/fconn_fastcgi.h\
src/main.h\
+ src/req.h\
src/util.h
MOSTLYCLEANFILES+=version.c
diff --git a/src/fastcgi.c b/src/fastcgi.c
index 44247ef..1fb352b 100644
--- a/src/fastcgi.c
+++ b/src/fastcgi.c
@@ -23,17 +23,17 @@
#include "fcgy.h"
-/* Returns -1 if no complete packet has been received, otherwise writes to *h
- * and returns the length of the first packet in the buffer. */
+/* Returns -1 if no complete record has been received, otherwise writes to *h
+ * and returns the length of the first record in the buffer. */
static ssize_t parse_header(fastcgi_header *h, char *buf, size_t buflen) {
- if(buflen < sizeof(fastcgi_header))
+ if(buflen < FCGI_HEADER_LEN)
return -1;
- memcpy(h, buf, sizeof(fastcgi_header));
+ memcpy(h, buf, FCGI_HEADER_LEN);
/* 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;
+ size_t len = FCGI_HEADER_LEN + h->contentLength + h->paddingLength;
return buflen < len ? -1 : (ssize_t)len;
}
@@ -41,22 +41,30 @@ static ssize_t parse_header(fastcgi_header *h, char *buf, size_t buflen) {
static void reader_consume(fastcgi_reader *r) {
size_t len = r->buf_len;
char *buf = r->buf;
- while(true) {
+
+ r->processing = true;
+
+ while(r->cb) {
fastcgi_header h;
ssize_t n = parse_header(&h, buf, len);
if(n < 0)
break;
- r->cb(r, n, h, buf);
+ r->cb(r, n, h, buf+FCGI_HEADER_LEN);
len -= n;
buf += n;
}
memmove(r->buf, buf, len);
r->buf_len = len;
+
+ r->processing = false;
+ if(!r->cb)
+ fastcgi_reader_destroy(r);
}
static void reader_io_cb(EV_P_ ev_io *w, int revents) {
fastcgi_reader *r = w->data;
+ assert(r->cb != NULL);
ssize_t n = read(r->fd, r->buf+r->buf_len, sizeof(r->buf)-r->buf_len);
if(n < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN))
@@ -75,13 +83,24 @@ static void reader_io_cb(EV_P_ ev_io *w, int revents) {
}
-void fastcgi_reader_init(fastcgi_reader *r, int fd, fastcgi_reader_cb cb, void *data) {
+fastcgi_reader *fastcgi_reader_create(int fd, fastcgi_reader_cb cb, void *data) {
+ fastcgi_reader *r = malloc(sizeof(fastcgi_reader));
r->fd = fd;
r->buf_len = 0;
r->data = data;
r->cb = cb;
ev_io_init(r->io, reader_io_cb, r->fd, EV_READ);
r->io->data = r;
+ return r;
+}
+
+
+void fastcgi_reader_destroy(fastcgi_reader *r) {
+ r->cb = NULL;
+ if(r->processing)
+ return;
+ ev_io_stop(EV_DEFAULT_UC_ r->io);
+ free(r);
}
@@ -134,6 +153,7 @@ ssize_t fastcgi_param_parse(fastcgi_param_parser *p, const char *buf, size_t len
case 2: /* Name data */
if(c == 0 || c == '=') {
free(p->buf);
+ p->buf = NULL;
return -1;
}
p->buf[p->off++] = c;
@@ -152,6 +172,7 @@ ssize_t fastcgi_param_parse(fastcgi_param_parser *p, const char *buf, size_t len
case 3: /* Value data */
if(c == 0) {
free(p->buf);
+ p->buf = NULL;
return -1;
}
p->buf[p->off++] = c;
diff --git a/src/fastcgi.h b/src/fastcgi.h
index b3c1c3e..c9028cc 100644
--- a/src/fastcgi.h
+++ b/src/fastcgi.h
@@ -23,6 +23,22 @@
#ifndef FCGY_FASTCGI_H
#define FCGY_FASTCGI_H
+
+#define FCGI_BEGIN_REQUEST 1
+#define FCGI_ABORT_REQUEST 2
+#define FCGI_END_REQUEST 3
+#define FCGI_PARAMS 4
+#define FCGI_STDIN 5
+#define FCGI_STDOUT 6
+#define FCGI_STDERR 7
+#define FCGI_DATA 8
+#define FCGI_GET_VALUES 9
+#define FCGI_GET_VALUES_RESULT 10
+#define FCGI_UNKNOWN_TYPE 11
+#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
+
+#define FCGI_HEADER_LEN 8
+
typedef struct {
uint8_t version;
uint8_t type;
@@ -34,29 +50,33 @@ typedef struct {
typedef struct fastcgi_reader fastcgi_reader;
-/* Called when a complete FastCGI packet has arrived. Arguments:
- * ssize_t -> -1 on error (see errno), otherwise total length of the packet.
- * char * -> packet buffer, including header.
+/* Called when a complete FastCGI record has arrived. Arguments:
+ * ssize_t -> -1 on error (see errno), otherwise total length of the record.
+ * (XXX: Is the total length useful?)
+ * char * -> buffer containing the record contents (after the header)
*/
typedef void(*fastcgi_reader_cb)(fastcgi_reader *, ssize_t, fastcgi_header, const char *);
struct fastcgi_reader {
int fd;
+ bool processing; /* Currently processing the buffer */
+ ev_io io[1];
+ fastcgi_reader_cb cb; /* Set to NULL when _destroy()'ed while processing */
+ void *data;
size_t buf_len;
- /* Read buffer. Large enough to hold a single FastCGI packet.
+ /* Read buffer. Large enough to hold a single FastCGI record.
* (should this be a dynamic buffer? Might save some memory and/or optimize read() calls) */
char buf[8+65535+255];
- ev_io io[1];
- fastcgi_reader_cb cb;
- void *data;
};
-void fastcgi_reader_init(fastcgi_reader *, int, fastcgi_reader_cb, void *);
+fastcgi_reader *fastcgi_reader_create(int, fastcgi_reader_cb, void *);
+void fastcgi_reader_destroy(fastcgi_reader *);
static inline void fastcgi_reader_start(fastcgi_reader *r) { ev_io_start(EV_DEFAULT_UC_ r->io); }
static inline void fastcgi_reader_stop (fastcgi_reader *r) { ev_io_stop (EV_DEFAULT_UC_ r->io); }
+
typedef struct {
uint32_t state;
uint32_t namelen;
diff --git a/src/fcgy.h b/src/fcgy.h
index b3175eb..dcacb1c 100644
--- a/src/fcgy.h
+++ b/src/fcgy.h
@@ -58,6 +58,7 @@ extern char *fcgy_version;
typedef enum fcgy_front_type fcgy_front_type;
typedef struct fcgy_app fcgy_app;
typedef struct fcgy_front fcgy_front;
+typedef struct fcgy_req fcgy_req;
typedef struct fconn_fastcgi fconn_fastcgi;
#include "util.h"
@@ -65,5 +66,6 @@ typedef struct fconn_fastcgi fconn_fastcgi;
#include "main.h"
#include "app.h"
#include "fconn_fastcgi.h"
+#include "req.h"
#endif
diff --git a/src/fconn_fastcgi.c b/src/fconn_fastcgi.c
index bba353e..1d21603 100644
--- a/src/fconn_fastcgi.c
+++ b/src/fconn_fastcgi.c
@@ -22,36 +22,117 @@
#include "fcgy.h"
+/* Comparison for use with the c->reqs vector */
+#define req_cmp(c, val) (((int)(c)->reqs.a[i]->fconn_id) - ((int)val))
-void read_cb(fastcgi_reader *r, ssize_t len, fastcgi_header h, const char *buf) {
+
+static void handle_begin_request(fconn_fastcgi *c, uint16_t id, size_t len, const char *buf) {
+ if(id == 0 || len < 8 || *buf != 0 || buf[1] < 1 || buf[1] > 3) {
+ fprintf(stderr, "Invalid BEGIN_REQUEST record\n");
+ fconn_fastcgi_destroy(c);
+ return;
+ }
+
+ size_t n;
+ vec_search_insert(c->reqs, n, req_cmp(c, id));
+ if(n < c->reqs.n && c->reqs.a[n]->fconn_id == id) {
+ fprintf(stderr, "BEGIN_REQUEST record for existing request ID\n");
+ fconn_fastcgi_destroy(c);
+ return;
+ }
+ *vec_insert_orderp(c->reqs, n) = req_create(c, id);
+}
+
+
+static void handle_params(fconn_fastcgi *c, fcgy_req *req, size_t len, const char *buf) {
+ if(!len) /* End of param list. Should signal this to fcgy_req? */
+ return;
+
+ ssize_t n;
+ while(len > 0) {
+ n = fastcgi_param_parse(c->pp, buf, len);
+ if(n < 0) {
+ fprintf(stderr, "Invalid parameter\n");
+ fconn_fastcgi_destroy(c);
+ return;
+ }
+ if(n == 0)
+ break;
+
+ req_set_param(req, c->pp->namelen, c->pp->valuelen, c->pp->buf);
+ c->pp->buf = NULL; /* To avoid a double-free in fconn_fastcgi_destroy() */
+ len -= n;
+ buf += n;
+ }
+}
+
+
+static fcgy_req *get_request(fconn_fastcgi *c, uint16_t id) {
+ vec_search(c->reqs, req_cmp(c, id), return c->reqs.a[i]);
+ return NULL;
+}
+
+
+static void read_cb(fastcgi_reader *r, ssize_t len, fastcgi_header h, const char *buf) {
fconn_fastcgi *c = r->data;
if(len < 0) {
- fconn_fastcgi_destroy(c);
fprintf(stderr, "Read error: %s\n", strerror(errno));
+ fconn_fastcgi_destroy(c);
return;
}
- fprintf(stderr, "Got FastCGI packet: len = %4u, version = %u, type = %2u, requestId = %2u, contentLength = %4u\n",
+ fprintf(stderr, "Got FastCGI record: len = %4u, version = %u, type = %2u, requestId = %2u, contentLength = %4u\n",
(unsigned)len, (unsigned)h.version, (unsigned)h.type, (unsigned)h.requestId, (unsigned)h.contentLength);
+
+ /* TODO: Handle management records here */
+
+ if(h.type == FCGI_BEGIN_REQUEST) {
+ handle_begin_request(c, h.requestId, h.contentLength, buf);
+ return;
+ }
+
+ fcgy_req *req = get_request(c, h.requestId);
+ if(!req) {
+ fprintf(stderr, "Received record for unknown request id (%u)\n", (unsigned)h.requestId);
+ fconn_fastcgi_destroy(c);
+ return;
+ }
+
+ if(h.type == FCGI_PARAMS)
+ handle_params(c, req, h.contentLength, buf);
+
+ /* TODO: Handle other request records here */
}
void fconn_fastcgi_create(fcgy_app *app, int rfd, int wfd) {
- fconn_fastcgi *c = malloc(sizeof(fconn_fastcgi));
+ fconn_fastcgi *c = calloc(1, sizeof(fconn_fastcgi));
c->app = app;
+ c->rfd = rfd;
+ c->wfd = wfd;
- fastcgi_reader_init(c->rd, rfd, read_cb, c);
+ c->rd = fastcgi_reader_create(c->rfd, read_cb, c);
fastcgi_reader_start(c->rd);
- c->wfd = wfd;
-
hlist_prepend(app->fconns, c);
}
void fconn_fastcgi_destroy(fconn_fastcgi *c) {
- fastcgi_reader_stop(c->rd);
+ fastcgi_reader_destroy(c->rd);
hlist_remove(c->app->fconns, c);
+
+ free(c->pp->buf);
+
+ size_t i;
+ for(i=0; i<c->reqs.n; i++)
+ req_unset_front(c->reqs.a[i]);
+ free(c->reqs.a);
+
+ close(c->wfd);
+ if(c->wfd != c->rfd)
+ close(c->rfd);
+
free(c);
}
diff --git a/src/fconn_fastcgi.h b/src/fconn_fastcgi.h
index 8a313af..ea049ae 100644
--- a/src/fconn_fastcgi.h
+++ b/src/fconn_fastcgi.h
@@ -26,9 +26,14 @@
struct fconn_fastcgi {
fconn_fastcgi *next, *prev;
fcgy_app *app;
- int wfd;
+ int wfd,
+ rfd;
- fastcgi_reader rd[1];
+ fastcgi_reader *rd;
+ fastcgi_param_parser pp[1];
+
+ /* List of active requests for this connection. Ordered by fconn_id. */
+ vec_t(fcgy_req *) reqs;
};
diff --git a/src/req.c b/src/req.c
new file mode 100644
index 0000000..12a0267
--- /dev/null
+++ b/src/req.c
@@ -0,0 +1,52 @@
+/* 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"
+
+fcgy_req *req_create(fconn_fastcgi *fconn, uint16_t id) {
+ fcgy_req *r = calloc(1, sizeof(fcgy_req));
+ r->front = fconn;
+ r->fconn_id = id;
+ return r;
+}
+
+void req_unset_front(fcgy_req *r) {
+ r->front = NULL;
+
+ /* No backends implemented yet. The free() needs to be deferred until both
+ * front and back are NULL */
+ assert(!r->back);
+
+ size_t i;
+ for(i=0; i<r->params.n; i++)
+ free(r->params.a[i].buf);
+ free(r->params.a);
+ free(r);
+}
+
+void req_set_param(fcgy_req *r, uint32_t namelen, uint32_t valuelen, char *buf) {
+ fcgy_req_param *p = vec_appendp(r->params);
+ p->namelen = namelen;
+ p->valuelen = valuelen;
+ p->buf = buf;
+ fprintf(stderr, "Parameter: %s=%s\n", buf, buf+namelen+1);
+}
diff --git a/src/req.h b/src/req.h
new file mode 100644
index 0000000..34cc127
--- /dev/null
+++ b/src/req.h
@@ -0,0 +1,57 @@
+/* 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_REQ_H
+#define FCGY_REQ_H
+
+
+typedef struct {
+ uint32_t namelen,
+ valuelen;
+ char *buf;
+} fcgy_req_param;
+
+
+/* The front and back pointers also serve as a ref count. If both are NULL, the
+ * request object is not referenced anywhere and may be freed. */
+struct fcgy_req {
+ fconn_fastcgi *front;
+ uint16_t fconn_id;
+
+ void *back;
+
+ /* Order is unspecified for now */
+ vec_t(fcgy_req_param) params;
+};
+
+
+fcgy_req *req_create(fconn_fastcgi *, uint16_t);
+
+/* Lost connection with frontend */
+void req_unset_front(fcgy_req *);
+
+/* Set a parameter. Buffer follows the same format as given by
+ * fastcgi_param_parse(). Ownership of the buffer is passed to the fcgy_req
+ * object and will be free()'d. */
+void req_set_param(fcgy_req *, uint32_t, uint32_t, char *);
+
+#endif
diff --git a/test/fastcgi_param_parse.c b/test/fastcgi_param_parse.c
index 1f702c2..a121d25 100644
--- a/test/fastcgi_param_parse.c
+++ b/test/fastcgi_param_parse.c
@@ -49,6 +49,7 @@
#define F(in) do {\
fastcgi_param_parser p = {0};\
assert(fastcgi_param_parse(&p, in, sizeof(in)-1) == -1);\
+ assert(p.buf == NULL);\
p.state = 0;\
p.off = 0;\
size_t off = 0;\
@@ -57,6 +58,7 @@
if((n = fastcgi_param_parse(&p, in+off, 1)) != 0)\
break;\
assert(n == -1);\
+ assert(p.buf == NULL);\
} while(0)
#define x127 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"