commit a87f6625658284c10597c2835135397ae698ad6c from: Omar Polo date: Sun Jan 24 09:49:09 2021 UTC refactoring state management instead of having a flag to discern between two different behaviours in S_SENDING, split that state into S_SENDING_FILE and S_SENDING_CGI (this will also make it easier in the future to add other sending states). While there, also get rid of `goodbye' and make start_reply advance the state machine by itself. commit - a7b9bb4d24e5f0d9ae6a1d67ac2224e0aef64d4e commit + a87f6625658284c10597c2835135397ae698ad6c blob - 5388023427098827fecf83c357f898d76678514e blob + a60d7c0a55f19c7fa56a947d1abd7f3618467f89 --- gmid.h +++ gmid.h @@ -112,7 +112,8 @@ enum { S_HANDSHAKE, S_OPEN, S_INITIALIZING, - S_SENDING, + S_SENDING_FILE, + S_SENDING_CGI, S_CLOSING, }; @@ -120,11 +121,10 @@ struct client { struct tls *ctx; char req[GEMINI_URL_LEN]; struct iri iri; - int state; + int state, next; int code; const char *meta; int fd, waiting_on_child; - int child; char sbuf[1024]; /* static buffer */ void *buf, *i; /* mmap buffer */ ssize_t len, off; /* mmap/static buffer */ @@ -181,7 +181,7 @@ int check_for_cgi(char *, char*, struct pollfd*, str void mark_nonblock(int); void handle_handshake(struct pollfd*, struct client*); void handle_open_conn(struct pollfd*, struct client*); -int start_reply(struct pollfd*, struct client*, int, const char*); +void start_reply(struct pollfd*, struct client*, int, const char*); int start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*); void send_file(struct pollfd*, struct client*); void send_dir(struct pollfd*, struct client*); @@ -189,7 +189,6 @@ void cgi_poll_on_child(struct pollfd*, struct client void cgi_poll_on_client(struct pollfd*, struct client*); void handle_cgi(struct pollfd*, struct client*); void close_conn(struct pollfd*, struct client*); -void goodbye(struct pollfd*, struct client*, int, const char*); void do_accept(int, struct tls*, struct pollfd*, struct client*); void handle(struct pollfd*, struct client*); void loop(struct tls*, int, int); blob - 62fef97850b53b67c06472051910e83c05ff751d blob + 4de3a7daccf45cf8e6aa56f61b5494aed92d8aa1 --- server.c +++ server.c @@ -88,9 +88,8 @@ open_file(struct pollfd *fds, struct client *c) return 0; } c->i = c->buf; - if (!start_reply(fds, c, SUCCESS, mime(c->host, c->iri.path))) - return 0; - send_file(fds, c); + c->next = S_SENDING_FILE; + start_reply(fds, c, SUCCESS, mime(c->host, c->iri.path)); return 0; case FILE_DIRECTORY: @@ -102,7 +101,7 @@ open_file(struct pollfd *fds, struct client *c) case FILE_MISSING: if (c->host->cgi != NULL && starts_with(c->iri.path, c->host->cgi)) return check_for_cgi(c->iri.path, c->iri.query, fds, c); - goodbye(fds, c, NOT_FOUND, "not found"); + start_reply(fds, c, NOT_FOUND, "not found"); return 0; default: @@ -149,7 +148,7 @@ check_for_cgi(char *path, char *query, struct pollfd * } err: - goodbye(fds, c, NOT_FOUND, "not found"); + start_reply(fds, c, NOT_FOUND, "not found"); return 0; } @@ -207,7 +206,7 @@ handle_handshake(struct pollfd *fds, struct client *c) else strncpy(c->req, "null", sizeof(c->req)); - goodbye(fds, c, BAD_REQUEST, "Wrong host or missing SNI"); + start_reply(fds, c, BAD_REQUEST, "Wrong host or missing SNI"); } void @@ -234,20 +233,20 @@ handle_open_conn(struct pollfd *fds, struct client *c) } if (!trim_req_iri(c->req) || !parse_iri(c->req, &c->iri, &parse_err)) { - goodbye(fds, c, BAD_REQUEST, parse_err); + start_reply(fds, c, BAD_REQUEST, parse_err); return; } /* XXX: we should check that the SNI matches the requested host */ if (strcmp(c->iri.schema, "gemini") || c->iri.port_no != conf.port) { - goodbye(fds, c, PROXY_REFUSED, "won't proxy request"); + start_reply(fds, c, PROXY_REFUSED, "won't proxy request"); return; } open_file(fds, c); } -int +void start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta) { char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */ @@ -268,16 +267,28 @@ start_reply(struct pollfd *pfd, struct client *c, int assert(len < sizeof(buf)); switch (tls_write(c->ctx, buf, len)) { + case -1: + close_conn(pfd, c); + return; case TLS_WANT_POLLIN: pfd->events = POLLIN; - return 0; + return; case TLS_WANT_POLLOUT: pfd->events = POLLOUT; - return 0; - default: - log_request(c, buf, sizeof(buf)); - return 1; + return; } + + log_request(c, buf, sizeof(buf)); + + /* we don't need a body */ + if (c->code != SUCCESS) { + close_conn(pfd, c); + return; + } + + /* advance the state machine */ + c->state = c->next; + handle(pfd, c); } int @@ -317,11 +328,10 @@ start_cgi(const char *spath, const char *relpath, cons close(c->fd); if ((c->fd = recv_fd(exfd)) == -1) { - goodbye(fds, c, TEMP_FAILURE, "internal server error"); + start_reply(fds, c, TEMP_FAILURE, "internal server error"); return 0; } - c->child = 1; - c->state = S_SENDING; + c->state = S_SENDING_CGI; cgi_poll_on_child(fds, c); c->code = -1; /* handle_cgi(fds, c); */ @@ -338,7 +348,7 @@ send_file(struct pollfd *fds, struct client *c) ssize_t ret, len; /* ensure the correct state */ - c->state = S_SENDING; + c->state = S_SENDING_FILE; len = (c->buf + c->len) - c->i; @@ -381,7 +391,7 @@ send_dir(struct pollfd *fds, struct client *c) * - foo/$INDEX is a directory. */ if (c->iri.path == c->sbuf) { - goodbye(fds, c, TEMP_REDIRECT, c->sbuf); + start_reply(fds, c, TEMP_REDIRECT, c->sbuf); return; } @@ -392,7 +402,7 @@ send_dir(struct pollfd *fds, struct client *c) /* redirect to url with the trailing / */ strlcat(c->sbuf, c->iri.path, sizeof(c->sbuf)); strlcat(c->sbuf, "/", sizeof(c->sbuf)); - goodbye(fds, c, TEMP_REDIRECT, c->sbuf); + start_reply(fds, c, TEMP_REDIRECT, c->sbuf); return; } @@ -406,7 +416,7 @@ send_dir(struct pollfd *fds, struct client *c) len = strlcat(c->sbuf, index, sizeof(c->sbuf)); if (len >= sizeof(c->sbuf)) { - goodbye(fds, c, TEMP_FAILURE, "internal server error"); + start_reply(fds, c, TEMP_FAILURE, "internal server error"); return; } @@ -565,14 +575,6 @@ close_conn(struct pollfd *pfd, struct client *c) } void -goodbye(struct pollfd *fds, struct client *c, int code, const char *meta) -{ - if (!start_reply(fds, c, code, meta)) - return; - close_conn(fds, c); -} - -void do_accept(int sock, struct tls *ctx, struct pollfd *fds, struct client *clients) { int i, fd; @@ -598,8 +600,8 @@ do_accept(int sock, struct tls *ctx, struct pollfd *fd fds[i].events = POLLIN; clients[i].state = S_HANDSHAKE; + clients[i].next = S_SENDING_FILE; clients[i].fd = -1; - clients[i].child = 0; clients[i].waiting_on_child = 0; clients[i].buf = MAP_FAILED; clients[i].addr = addr; @@ -625,24 +627,15 @@ handle(struct pollfd *fds, struct client *client) break; case S_INITIALIZING: - if (!start_reply(fds, client, client->code, client->meta)) - return; + start_reply(fds, client, client->code, client->meta); + break; + + case S_SENDING_FILE: + send_file(fds, client); + break; - if (client->code != SUCCESS) { - /* we don't need a body */ - close_conn(fds, client); - return; - } - - client->state = S_SENDING; - - /* fallthrough */ - - case S_SENDING: - if (client->child) - handle_cgi(fds, client); - else - send_file(fds, client); + case S_SENDING_CGI: + handle_cgi(fds, client); break; case S_CLOSING: