diff options
author | Omar Polo <op@omarpolo.com> | 2021-05-09 18:23:36 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2021-05-09 18:23:36 +0000 |
commit | 8ad1c570242cd93f0802931621b49b2510b338e7 (patch) | |
tree | 361394003bca869780ace3a3391ff13b2439a6e2 /server.c | |
parent | 50310aff335912edde625a5cde3729e34783fd7c (diff) |
fastcgi: a first implementation
Not production-ready yet, but it's a start.
This adds a third ``backend'' for gmid: until now there it served
local files or CGI scripts, now FastCGI applications too.
FastCGI is meant to be an improvement over CGI: instead of exec'ing a
script for every request, it allows to open a single connection to an
``application'' and send the requests/receive the responses over that
socket using a simple binary protocol.
At the moment gmid supports three different methods of opening a
fastcgi connection:
- local unix sockets, with: fastcgi "/path/to/sock"
- network sockets, with: fastcgi tcp "host" [port]
port defaults to 9000 and can be either a string or a number
- subprocess, with: fastcgi spawn "/path/to/program"
the fastcgi protocol is done over the executed program stdin
of these, the last is only for testing and may be removed in the
future.
P.S.: the fastcgi rule is per-location of course :)
Diffstat (limited to 'server.c')
-rw-r--r-- | server.c | 117 |
1 files changed, 109 insertions, 8 deletions
@@ -26,7 +26,8 @@ #include <limits.h> #include <string.h> -static struct client clients[MAX_USERS]; +struct client clients[MAX_USERS]; + static struct tls *ctx; static struct event e4, e6, imsgev, siginfo, sigusr2; @@ -48,7 +49,6 @@ static void fmt_sbuf(const char*, struct client*, const char*); static int apply_block_return(struct client*); static int apply_require_ca(struct client*); static void handle_open_conn(int, short, void*); -static void start_reply(struct client*, int, const char*); static void handle_start_reply(int, short, void*); static size_t host_nth(struct vhost*); static void start_cgi(const char*, const char*, struct client*); @@ -60,16 +60,16 @@ static int read_next_dir_entry(struct client*); static void send_directory_listing(int, short, void*); static void handle_cgi_reply(int, short, void*); static void handle_copy(int, short, void*); -static void close_conn(int, short, void*); static void do_accept(int, short, void*); -struct client *client_by_id(int); static void handle_imsg_cgi_res(struct imsgbuf*, struct imsg*, size_t); +static void handle_imsg_fcgi_fd(struct imsgbuf*, struct imsg*, size_t); static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); static void handle_siginfo(int, short, void*); static imsg_handlerfn *handlers[] = { [IMSG_QUIT] = handle_imsg_quit, [IMSG_CGI_RES] = handle_imsg_cgi_res, + [IMSG_FCGI_FD] = handle_imsg_fcgi_fd, }; static inline int @@ -203,6 +203,25 @@ vhost_block_return(struct vhost *v, const char *path, int *code, const char **fm } int +vhost_fastcgi(struct vhost *v, const char *path) +{ + struct location *loc; + + if (v == NULL || path == NULL) + return -1; + + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { + if (loc->fcgi != -1) + if (matches(loc->match, path)) + return loc->fcgi; + } + + loc = TAILQ_FIRST(&v->locations); + return loc->fcgi; +} + +int vhost_dirfd(struct vhost *v, const char *path) { struct location *loc; @@ -556,6 +575,37 @@ apply_block_return(struct client *c) return 1; } +/* 1 if matching `fcgi' (and apply it), 0 otherwise */ +static int +apply_fastcgi(struct client *c) +{ + int id; + struct fcgi *f; + + if ((id = vhost_fastcgi(c->host, c->iri.path)) == -1) + return 0; + + switch ((f = &fcgi[id])->s) { + case FCGI_OFF: + f->s = FCGI_INFLIGHT; + log_info(c, "opening fastcgi connection for (%s,%s,%s)", + f->path, f->port, f->prog); + imsg_compose(&exibuf, IMSG_FCGI_REQ, 0, 0, -1, + &id, sizeof(id)); + imsg_flush(&exibuf); + /* fallthrough */ + case FCGI_INFLIGHT: + c->fcgi = id; + break; + case FCGI_READY: + c->fcgi = id; + send_fcgi_req(f, c); + break; + } + + return 1; +} + /* 1 if matching `require client ca' fails (and apply it), 0 otherwise */ static int apply_require_ca(struct client *c) @@ -627,6 +677,9 @@ handle_open_conn(int fd, short ev, void *d) if (apply_block_return(c)) return; + if (apply_fastcgi(c)) + return; + if (c->host->entrypoint != NULL) { start_cgi(c->host->entrypoint, c->iri.path, c); return; @@ -635,7 +688,7 @@ handle_open_conn(int fd, short ev, void *d) open_file(c); } -static void +void start_reply(struct client *c, int code, const char *meta) { c->code = code; @@ -1039,10 +1092,11 @@ end: close_conn(c->fd, ev, d); } -static void +void close_conn(int fd, short ev, void *d) { - struct client *c = d; + struct client *c = d; + struct mbuf *mbuf; switch (tls_close(c->ctx)) { case TLS_WANT_POLLIN: @@ -1055,6 +1109,11 @@ close_conn(int fd, short ev, void *d) connected_clients--; + while ((mbuf = TAILQ_FIRST(&c->mbufhead)) != NULL) { + TAILQ_REMOVE(&c->mbufhead, mbuf, mbufs); + free(mbuf); + } + tls_free(c->ctx); c->ctx = NULL; @@ -1101,6 +1160,7 @@ do_accept(int sock, short et, void *d) c->pfd = -1; c->dir = NULL; c->addr = addr; + c->fcgi = -1; yield_read(fd, c, &handle_handshake); connected_clients++; @@ -1111,7 +1171,7 @@ do_accept(int sock, short et, void *d) close(fd); } -struct client * +static struct client * client_by_id(int id) { if ((size_t)id > sizeof(clients)/sizeof(clients[0])) @@ -1119,6 +1179,14 @@ client_by_id(int id) return &clients[id]; } +struct client * +try_client_by_id(int id) +{ + if ((size_t)id > sizeof(clients)/sizeof(clients[0])) + return NULL; + return &clients[id]; +} + static void handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) { @@ -1133,6 +1201,39 @@ handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) } static void +handle_imsg_fcgi_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) +{ + struct client *c; + struct fcgi *f; + int i, id; + + id = imsg->hdr.peerid; + f = &fcgi[id]; + + if ((f->fd = imsg->fd) != -1) { + event_set(&f->e, imsg->fd, EV_READ | EV_PERSIST, &handle_fcgi, + &fcgi[id]); + event_add(&f->e, NULL); + } else { + f->s = FCGI_OFF; + } + + for (i = 0; i < MAX_USERS; ++i) { + c = &clients[i]; + if (c->fd == -1) + continue; + if (c->fcgi != id) + continue; + + if (f->fd == -1) { + c->fcgi = -1; + start_reply(c, TEMP_FAILURE, "internal server error"); + } else + send_fcgi_req(f, c); + } +} + +static void handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) { (void)imsg; |