[hackers] [quark] Introduce state-handling and interruptiblity in serve() || Laslo Hunhold
commit 5d0221dd68c0d2b8796479d06b602be666d0f4c6
Author: Laslo Hunhold <dev_AT_frign.de>
AuthorDate: Wed Sep 16 18:18:40 2020 +0200
Commit: Laslo Hunhold <dev_AT_frign.de>
CommitDate: Wed Sep 16 18:18:40 2020 +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_AT_frign.de>
diff --git a/main.c b/main.c
index 4f7b75f..d64774b 100644
--- a/main.c
+++ b/main.c
_AT_@ -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);
_AT_@ -118,6 +150,7 @@ err:
shutdown(c->fd, SHUT_RD);
shutdown(c->fd, SHUT_WR);
close(c->fd);
+ c->state = C_VACANT;
}
static void
_AT_@ -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:
Received on Wed Sep 16 2020 - 18:22:25 CEST
This archive was generated by hypermail 2.3.0
: Wed Sep 16 2020 - 18:24:38 CEST