The bare minimum has been implemented, it is currently unused. It allows
the server to maintain a stateful connection with the client. Also,
keep-alive connections are more efficient than successive
request/response pairs of connections.
---
 http.c | 21 +++++++++++++++++++--
 http.h |  9 +++++++++
 main.c | 23 +++++++++++++++++++++--
 3 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/http.c b/http.c
index f1e15a4..5b8848e 100644
--- a/http.c
+++ b/http.c
@@ -17,6 +17,7 @@
 #include <unistd.h>
 
 #include "config.h"
+#include "data.h"
 #include "http.h"
 #include "util.h"
 
@@ -57,6 +58,11 @@ const char *res_field_str[] = {
        [RES_CONTENT_TYPE]   = "Content-Type",
 };
 
+const char *lifetime_str[] = {
+       [LT_CLOSE]      = "close",
+       [LT_KEEP_ALIVE] = "keep-alive",
+};
+
 enum status
 http_prepare_header_buf(const struct response *res, struct buffer *buf)
 {
@@ -75,8 +81,9 @@ http_prepare_header_buf(const struct response *res, struct 
buffer *buf)
        if (buffer_appendf(buf,
                           "HTTP/1.1 %d %s\r\n"
                           "Date: %s\r\n"
-                          "Connection: close\r\n",
-                          res->status, status_str[res->status], tstmp)) {
+                          "Connection: %s\r\n",
+                          res->status, status_str[res->status], tstmp,
+                          lifetime_str[res->lifetime])) {
                goto err;
        }
 
@@ -839,6 +846,9 @@ void
 http_prepare_error_response(const struct request *req,
                             struct response *res, enum status s)
 {
+       struct buffer buf;
+       size_t progress;
+
        /* used later */
        (void)req;
 
@@ -861,4 +871,11 @@ http_prepare_error_response(const struct request *req,
                        res->status = S_INTERNAL_SERVER_ERROR;
                }
        }
+
+       if (data_prepare_error_buf(res, &buf, &progress)
+           || esnprintf(res->field[RES_CONTENT_LENGTH],
+                     sizeof(res->field[RES_CONTENT_LENGTH]),
+                     "%zu", buf.len)) {
+               s = S_INTERNAL_SERVER_ERROR;
+       }
 }
diff --git a/http.h b/http.h
index bfaa807..0e2367b 100644
--- a/http.h
+++ b/http.h
@@ -69,9 +69,17 @@ enum res_type {
        NUM_RES_TYPES,
 };
 
+enum lifetime {
+       LT_CLOSE,
+       LT_KEEP_ALIVE,
+};
+
+extern const char *lifetime_str[];
+
 struct response {
        enum res_type type;
        enum status status;
+       enum lifetime lifetime;
        char field[NUM_RES_FIELDS][FIELD_MAX];
        char uri[PATH_MAX];
        char path[PATH_MAX];
@@ -83,6 +91,7 @@ struct response {
 
 enum conn_state {
        C_VACANT,
+       C_START,
        C_RECV_HEADER,
        C_SEND_HEADER,
        C_SEND_BODY,
diff --git a/main.c b/main.c
index d64774b..8917138 100644
--- a/main.c
+++ b/main.c
@@ -60,9 +60,14 @@ serve(struct connection *c, const struct server *srv)
 
        switch (c->state) {
        case C_VACANT:
+               /* we were passed a "fresh" connection, reset all state */
+
+               c->state = C_START;
+               /* fallthrough */
+       case C_START:
                /*
-                * we were passed a "fresh" connection which should now
-                * try to receive the header, reset buf beforehand
+                * we start handling a request, so we first must try to
+                * receive the header, reset buf beforehand
                 */
                memset(&c->buf, 0, sizeof(c->buf));
 
@@ -146,6 +151,20 @@ response:
 err:
        logmsg(c);
 
+       /* don't cleanup if we keep the connection alive */
+       if (c->res.lifetime == LT_KEEP_ALIVE) {
+               /*
+                * if the length is unspecified, a keep-alive connection will
+                * wait timeout: kill the connection to avoid it
+                */
+               if (c->res.field[RES_CONTENT_LENGTH][0] == '\0') {
+                       c->res.status = S_INTERNAL_SERVER_ERROR;
+               } else {
+                       c->state = C_START;
+                       return;
+               }
+       }
+
        /* clean up and finish */
        shutdown(c->fd, SHUT_RD);
        shutdown(c->fd, SHUT_WR);
-- 
2.29.0


Reply via email to