quark

quark web server
git clone git://git.suckless.org/quark
Log | Files | Refs | LICENSE

commit 5d0221dd68c0d2b8796479d06b602be666d0f4c6
parent e67eddd517d3f20a24ee14e1eab92806ce5bb20c
Author: Laslo Hunhold <dev@frign.de>
Date:   Wed, 16 Sep 2020 18:18:40 +0200

Introduce state-handling and interruptiblity in serve()

Granted, the changes won't make sense right now, but this allows the
functions http_recv_header() and http_send_buf() to be interrupted
(e.g. if a read or write blocks). In the main loop, we currently just
have a do-while-loop, which is not even necessary because the sockets
are blocking and the aforementioned two functions will always complete
their task, unless there's an error.

This change also adapts the code to the new http_recv_header() prototype
with the done-int-pointer.

Signed-off-by: Laslo Hunhold <dev@frign.de>

Diffstat:
Mmain.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
1 file changed, 78 insertions(+), 43 deletions(-)

diff --git a/main.c b/main.c @@ -51,65 +51,97 @@ static void serve(struct connection *c, const struct server *srv) { enum status s; + int done; /* set connection timeout */ if (sock_set_timeout(c->fd, 30)) { warn("sock_set_timeout: Failed"); } - /* read header */ - memset(&c->buf, 0, sizeof(c->buf)); - if ((s = http_recv_header(c->fd, &c->buf))) { - http_prepare_error_response(&c->req, &c->res, s); - goto response; - } - - /* parse header */ - if ((s = http_parse_header(c->buf.data, &c->req))) { - http_prepare_error_response(&c->req, &c->res, s); - goto response; - } + switch (c->state) { + case C_VACANT: + /* + * we were passed a "fresh" connection which should now + * try to receive the header, reset buf beforehand + */ + memset(&c->buf, 0, sizeof(c->buf)); + + c->state = C_RECV_HEADER; + /* fallthrough */ + case C_RECV_HEADER: + /* receive header */ + done = 0; + if ((s = http_recv_header(c->fd, &c->buf, &done))) { + http_prepare_error_response(&c->req, &c->res, s); + goto response; + } + if (!done) { + /* not done yet */ + return; + } - /* prepare response struct */ - http_prepare_response(&c->req, &c->res, srv); + /* parse header */ + if ((s = http_parse_header(c->buf.data, &c->req))) { + http_prepare_error_response(&c->req, &c->res, s); + goto response; + } + /* prepare response struct */ + http_prepare_response(&c->req, &c->res, srv); response: - /* generate response header */ - if ((s = http_prepare_header_buf(&c->res, &c->buf))) { - http_prepare_error_response(&c->req, &c->res, s); + /* generate response header */ if ((s = http_prepare_header_buf(&c->res, &c->buf))) { - /* couldn't generate the header, we failed for good */ - c->res.status = s; - goto err; - } - } - - /* send header */ - if ((s = http_send_buf(c->fd, &c->buf))) { - c->res.status = s; - goto err; - } - - /* send body */ - if (c->req.method == M_GET) { - for (;;) { - /* fill buffer with body data */ - if ((s = data_fct[c->res.type](&c->res, &c->buf, - &c->progress))) { + http_prepare_error_response(&c->req, &c->res, s); + if ((s = http_prepare_header_buf(&c->res, &c->buf))) { + /* couldn't generate the header, we failed for good */ c->res.status = s; goto err; } + } - /* if done, exit loop */ - if (c->buf.len == 0) { - break; - } + c->state = C_SEND_HEADER; + /* fallthrough */ + case C_SEND_HEADER: + if ((s = http_send_buf(c->fd, &c->buf))) { + c->res.status = s; + goto err; + } + if (c->buf.len > 0) { + /* not done yet */ + return; + } - /* send buffer */ - if ((s = http_send_buf(c->fd, &c->buf))) { - c->res.status = s; + c->state = C_SEND_BODY; + /* fallthrough */ + case C_SEND_BODY: + if (c->req.method == M_GET) { + if (c->buf.len == 0) { + /* fill buffer with body data */ + if ((s = data_fct[c->res.type](&c->res, &c->buf, + &c->progress))) { + /* too late to do any real error handling */ + c->res.status = s; + goto err; + } + + /* if the buffer remains empty, we are done */ + if (c->buf.len == 0) { + break; + } + } else { + /* send buffer */ + if ((s = http_send_buf(c->fd, &c->buf))) { + /* too late to do any real error handling */ + c->res.status = s; + goto err; + } } + return; } + break; + default: + warn("serve: invalid connection state"); + return; } err: logmsg(c); @@ -118,6 +150,7 @@ err: shutdown(c->fd, SHUT_RD); shutdown(c->fd, SHUT_WR); close(c->fd); + c->state = C_VACANT; } static void @@ -427,7 +460,9 @@ main(int argc, char *argv[]) /* fork and handle */ switch (fork()) { case 0: - serve(&c, &srv); + do { + serve(&c, &srv); + } while (c.state != C_VACANT); exit(0); break; case -1: