diff options
author | Omar Polo <op@omarpolo.com> | 2020-11-06 16:16:42 +0100 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2020-11-06 17:09:14 +0100 |
commit | aff8d1901084bbfd81f4a6335dbed70a02b930fb (patch) | |
tree | 34f7a6036b917466d44e9c5714fce5c7aaebb114 | |
parent | 60ba426e7e5da49c017f306be78446032cdaf1cf (diff) |
handle CGI concurrently
don’t stop-the-world-until-cgi-end, but rather poll on the script, so
we can handle other requests in the meantime.
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | gmid.c | 95 |
2 files changed, 81 insertions, 17 deletions
@@ -3,6 +3,9 @@ * gmid.c (url_after_proto): ensure that the requested protocol is “gemini” and not something else that’s long 6 bytes. + * gmid.c (loop): added support for cgi scripts (can handle multiple + concurrently) + 2020-11-06 Omar Polo <op@venera> * gmid.1: added option to log to a file @@ -68,10 +68,11 @@ struct client { int state; int code; const char *meta; - int fd; + int fd, waiting_on_child; pid_t child; - void *buf, *i; - ssize_t len, off; + char sbuf[1024]; /* static buffer */ + void *buf, *i; /* mmap buffer */ + ssize_t len, off; /* mmap/static buffer */ int af; struct in_addr addr; }; @@ -122,6 +123,8 @@ const char *path_ext(const char*); const char *mime(const char*); int open_file(char*, char*, struct pollfd*, struct client*); void start_cgi(const char*, const char*, struct pollfd*, struct client*); +void cgi_setpoll_on_child(struct pollfd*, struct client*); +void cgi_setpoll_on_client(struct pollfd*, struct client*); void handle_cgi(struct pollfd*, struct client*); void send_file(char*, char*, struct pollfd*, struct client*); void send_dir(char*, struct pollfd*, struct client*); @@ -417,6 +420,8 @@ start_cgi(const char *path, const char *query, close(c->fd); c->fd = p[0]; c->child = pid; + mark_nonblock(c->fd); + c->state = S_SENDING; handle_cgi(fds, c); return; } @@ -430,32 +435,81 @@ err: childerr: dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE); close(p[1]); + + /* don't call atexit stuff */ _exit(1); } void +cgi_setpoll_on_child(struct pollfd *fds, struct client *c) +{ + int fd; + + if (c->waiting_on_child) + return; + c->waiting_on_child = 1; + + fds->events = POLLIN; + + fd = fds->fd; + fds->fd = c->fd; + c->fd = fd; +} + +void +cgi_setpoll_on_client(struct pollfd *fds, struct client *c) +{ + int fd; + + if (!c->waiting_on_child) + return; + c->waiting_on_child = 0; + + fd = fds->fd; + fds->fd = c->fd; + c->fd = fd; +} + +void handle_cgi(struct pollfd *fds, struct client *c) { - char buf[1024]; - ssize_t r, todo; + ssize_t r; + + /* ensure c->fd is the child and fds->fd the client */ + cgi_setpoll_on_client(fds, c); while (1) { - r = read(c->fd, buf, sizeof(buf)); - if (r == -1 || r == 0) - break; - todo = r; - while (todo > 0) { - switch (r = tls_write(c->ctx, buf, todo)) { + if (c->len == 0) { + if ((r = read(c->fd, c->sbuf, sizeof(c->sbuf))) == 0) + goto end; + if (r == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + cgi_setpoll_on_child(fds, c); + return; + } + goto end; + } + c->len = r; + c->off = 0; + } + + while (c->len > 0) { + switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) { case -1: goto end; case TLS_WANT_POLLOUT: + fds->events = POLLOUT; + return; + case TLS_WANT_POLLIN: - /* evil! */ - continue; + fds->events = POLLIN; + return; default: - todo -= r; + c->off += r; + c->len -= r; + break; } } } @@ -592,7 +646,10 @@ handle(struct pollfd *fds, struct client *client) /* fallthrough */ case S_SENDING: - send_file(NULL, NULL, fds, client); + if (client->child != -1) + handle_cgi(fds, client); + else + send_file(NULL, NULL, fds, client); break; case S_CLOSING: @@ -769,8 +826,12 @@ loop(struct tls *ctx, int sock) err(1, "bad fd %d", fds[i].fd); if (fds[i].revents & POLLHUP) { - goodbye(&fds[i], &clients[i]); - continue; + /* fds[i] may be the fd of the stdin + * of a cgi script that has exited. */ + if (!clients[i].waiting_on_child) { + goodbye(&fds[i], &clients[i]); + continue; + } } todo--; |