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 /ex.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 'ex.c')
-rw-r--r-- | ex.c | 117 |
1 files changed, 117 insertions, 0 deletions
@@ -16,6 +16,8 @@ #include "gmid.h" +#include <sys/un.h> + #include <err.h> #include <errno.h> @@ -28,10 +30,12 @@ #include <string.h> static void handle_imsg_cgi_req(struct imsgbuf*, struct imsg*, size_t); +static void handle_imsg_fcgi_req(struct imsgbuf*, struct imsg*, size_t); static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); static void handle_dispatch_imsg(int, short, void*); static imsg_handlerfn *handlers[] = { + [IMSG_FCGI_REQ] = handle_imsg_fcgi_req, [IMSG_CGI_REQ] = handle_imsg_cgi_req, [IMSG_QUIT] = handle_imsg_quit, }; @@ -294,6 +298,119 @@ handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) imsg_flush(ibuf); } +static int +fcgi_open_prog(struct fcgi *f) +{ + int s[2]; + pid_t p; + + /* XXX! */ + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, s) == -1) + err(1, "socketpair"); + + switch (p = fork()) { + case -1: + err(1, "fork"); + case 0: + close(s[0]); + if (dup2(s[1], 0) == -1) + err(1, "dup2"); + execl(f->prog, f->prog, NULL); + err(1, "execl %s", f->prog); + default: + close(s[1]); + return s[0]; + } +} + +static int +fcgi_open_sock(struct fcgi *f) +{ + struct sockaddr_un addr; + int fd; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + log_err(NULL, "socket: %s", strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, f->path, sizeof(addr.sun_path)); + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + log_warn(NULL, "failed to connect to %s: %s", f->path, + strerror(errno)); + close(fd); + return -1; + } + + return fd; +} + +static int +fcgi_open_conn(struct fcgi *f) +{ + struct addrinfo hints, *servinfo, *p; + int r, sock; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + if ((r = getaddrinfo(f->path, f->port, &hints, &servinfo)) != 0) { + log_warn(NULL, "getaddrinfo %s:%s: %s", f->path, f->port, + gai_strerror(r)); + return -1; + } + + for (p = servinfo; p != NULL; p = p->ai_next) { + sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sock == -1) + continue; + if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) { + close(sock); + continue; + } + break; + } + + if (p == NULL) { + log_warn(NULL, "couldn't connect to %s:%s", f->path, f->port); + sock = -1; + } + + freeaddrinfo(servinfo); + return sock; +} + +static void +handle_imsg_fcgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) +{ + struct fcgi *f; + int id, fd; + + if (datalen != sizeof(id)) + abort(); + memcpy(&id, imsg->data, datalen); + + if (id > FCGI_MAX || (fcgi[id].path == NULL && fcgi[id].prog == NULL)) + abort(); + + f = &fcgi[id]; + if (f->prog != NULL) + fd = fcgi_open_prog(f); + else if (f->port != NULL) + fd = fcgi_open_conn(f); + else + fd = fcgi_open_sock(f); + + imsg_compose(ibuf, IMSG_FCGI_FD, id, 0, fd, NULL, 0); + imsg_flush(ibuf); +} + static void handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) { |