Add http_send_body() and data_send_error() and refactor - quark - quark web server
git clone git://git.suckless.org/quark
Log
Files
Refs
LICENSE
---
commit a36b901d404f4d4268384a379fd040898f78b1b3
parent db127723c67534d5693fc033f19c855a403d1447
Author: Laslo Hunhold 
Date:   Sat, 29 Aug 2020 00:42:54 +0200

Add http_send_body() and data_send_error() and refactor

This turns the data-functions into the only functions "allowed"
to send body-data (called with http_send_body()). The previous (hacky)
approach of doing this in http_send_header() is not only out of place,
it's an easy source of bugs given, for instance, the sending of body
data is not expected with HEAD-requests.

Given html_escape() is now only used in data.c, we move it there from
util.c and make it a static method again.

Signed-off-by: Laslo Hunhold 

Diffstat:
  M data.c                              |      73 ++++++++++++++++++++++++++++++-
  M data.h                              |       1 +
  M http.c                              |      43 +++++++++++++++----------------
  M http.h                              |       6 +++++-
  M main.c                              |      10 ++--------
  M util.c                              |      45 -------------------------------
  M util.h                              |       1 -

7 files changed, 101 insertions(+), 78 deletions(-)
---
diff --git a/data.c b/data.c
@@ -38,10 +38,55 @@ suffix(int t)
         return "";
 }
 
+static void
+html_escape(const char *src, char *dst, size_t dst_siz)
+{
+        const struct {
+                char c;
+                char *s;
+        } escape[] = {
+                { '&',  "&"  },
+                { '<',  "<"   },
+                { '>',  ">"   },
+                { '"',  """ },
+                { '\'', "'" },
+        };
+        size_t i, j, k, esclen;
+
+        for (i = 0, j = 0; src[i] != '\0'; i++) {
+                for (k = 0; k < LEN(escape); k++) {
+                        if (src[i] == escape[k].c) {
+                                break;
+                        }
+                }
+                if (k == LEN(escape)) {
+                        /* no escape char at src[i] */
+                        if (j == dst_siz - 1) {
+                                /* silent truncation */
+                                break;
+                        } else {
+                                dst[j++] = src[i];
+                        }
+                } else {
+                        /* escape char at src[i] */
+                        esclen = strlen(escape[k].s);
+
+                        if (j >= dst_siz - esclen) {
+                                /* silent truncation */
+                                break;
+                        } else {
+                                memcpy(&dst[j], escape[k].s, esclen);
+                                j += esclen;
+                        }
+                }
+        }
+        dst[j] = '\0';
+}
+
 enum status
 data_send_dirlisting(int fd, const struct response *res)
 {
-        enum status ret;
+        enum status ret = 0;
         struct dirent **e;
         size_t i;
         int dirlen;
@@ -52,6 +97,17 @@ data_send_dirlisting(int fd, const struct response *res)
                 return S_FORBIDDEN;
         }
 
+        /* listing header (we use esc because sizeof(esc) >= PATH_MAX) */
+        html_escape(res->uri, esc, MIN(PATH_MAX, sizeof(esc)));
+        if (dprintf(fd,
+                    "\n\n\t"
+                    "Index of %s\n"
+                    "\t\n\t\t..",
+                    esc) < 0) {
+                ret = S_REQUEST_TIMEOUT;
+                goto cleanup;
+        }
+
         /* listing */
         for (i = 0; i < (size_t)dirlen; i++) {
                 /* skip hidden files, "." and ".." */
@@ -87,6 +143,21 @@ cleanup:
 }
 
 enum status
+data_send_error(int fd, const struct response *res)
+{
+        if (dprintf(fd,
+                    "\n\n\t\n"
+                    "\t\t%d %s\n\t\n\t\n"
+                    "\t\t

%d %s

\n\t\n\n", + res->status, status_str[res->status], + res->status, status_str[res->status]) < 0) { + return S_REQUEST_TIMEOUT; + } + + return 0; +} + +enum status data_send_file(int fd, const struct response *res) { FILE *fp;
diff --git a/data.h b/data.h
@@ -5,6 +5,7 @@
 #include "http.h"
 
 enum status data_send_dirlisting(int, const struct response *);
+enum status data_send_error(int, const struct response *);
 enum status data_send_file(int, const struct response *);
 
 #endif /* DATA_H */
diff --git a/http.c b/http.c
@@ -17,6 +17,7 @@
 #include 
 
 #include "config.h"
+#include "data.h"
 #include "http.h"
 #include "util.h"
 
@@ -57,10 +58,16 @@ const char *res_field_str[] = {
         [RES_CONTENT_TYPE]   = "Content-Type",
 };
 
+enum status (* const body_fct[])(int, const struct response *) = {
+        [RESTYPE_ERROR]      = data_send_error,
+        [RESTYPE_FILE]       = data_send_file,
+        [RESTYPE_DIRLISTING] = data_send_dirlisting,
+};
+
 enum status
 http_send_header(int fd, const struct response *res)
 {
-        char t[FIELD_MAX], esc[PATH_MAX];
+        char t[FIELD_MAX];
         size_t i;
 
         if (timestamp(t, sizeof(t), time(NULL))) {
@@ -88,27 +95,6 @@ http_send_header(int fd, const struct response *res)
                 return S_REQUEST_TIMEOUT;
         }
 
-        /* listing header */
-        if (res->type == RESTYPE_DIRLISTING) {
-                html_escape(res->uri, esc, sizeof(esc));
-                if (dprintf(fd,
-                            "\n\n\t"
-                            "Index of %s\n"
-                            "\t\n\t\t..",
-                            esc) < 0) {
-                        return S_REQUEST_TIMEOUT;
-                }
-        } else if (res->type == RESTYPE_ERROR) {
-                if (dprintf(fd,
-                            "\n\n\t\n"
-                            "\t\t%d %s\n\t\n\t\n"
-                            "\t\t

%d %s

\n\t\n\n", - res->status, status_str[res->status], - res->status, status_str[res->status]) < 0) { - return S_REQUEST_TIMEOUT; - } - } - return 0; } @@ -854,3 +840,16 @@ http_prepare_error_response(const struct request *req, } } } + +enum status +http_send_body(int fd, const struct response *res, + const struct request *req) +{ + enum status s; + + if (req->method == M_GET && (s = body_fct[res->type](fd, res))) { + return s; + } + + return 0; +}
diff --git a/http.h b/http.h
@@ -82,11 +82,13 @@ struct response {
         } file;
 };
 
+extern enum status (* const body_fct[])(int, const struct response *);
+
 enum conn_state {
         C_VACANT,
         C_RECV_HEADER,
         C_SEND_HEADER,
-        C_SEND_DATA,
+        C_SEND_BODY,
         NUM_CONN_STATES,
 };
 
@@ -107,5 +109,7 @@ void http_prepare_response(const struct request *, struct response *,
                            const struct server *);
 void http_prepare_error_response(const struct request *,
                                  struct response *, enum status);
+enum status http_send_body(int, const struct response *,
+                           const struct request *);
 
 #endif /* HTTP_H */
diff --git a/main.c b/main.c
@@ -45,15 +45,9 @@ serve(int infd, const struct sockaddr_storage *in_sa, const struct server *srv)
                 http_prepare_response(&c.req, &c.res, srv);
         }
 
-        if ((s = http_send_header(c.fd, &c.res))) {
+        if ((s = http_send_header(c.fd, &c.res)) ||
+            (s = http_send_body(c.fd, &c.res, &c.req))) {
                 c.res.status = s;
-        } else {
-                /* send data */
-                if (c.res.type == RESTYPE_FILE) {
-                        data_send_file(c.fd, &c.res);
-                } else if (c.res.type == RESTYPE_DIRLISTING) {
-                        data_send_dirlisting(c.fd, &c.res);
-                }
         }
 
         /* write output to log */
diff --git a/util.c b/util.c
@@ -123,51 +123,6 @@ prepend(char *str, size_t size, const char *prefix)
         return 0;
 }
 
-void
-html_escape(const char *src, char *dst, size_t dst_siz)
-{
-        const struct {
-                char c;
-                char *s;
-        } escape[] = {
-                { '&',  "&"  },
-                { '<',  "<"   },
-                { '>',  ">"   },
-                { '"',  """ },
-                { '\'', "'" },
-        };
-        size_t i, j, k, esclen;
-
-        for (i = 0, j = 0; src[i] != '\0'; i++) {
-                for (k = 0; k < LEN(escape); k++) {
-                        if (src[i] == escape[k].c) {
-                                break;
-                        }
-                }
-                if (k == LEN(escape)) {
-                        /* no escape char at src[i] */
-                        if (j == dst_siz - 1) {
-                                /* silent truncation */
-                                break;
-                        } else {
-                                dst[j++] = src[i];
-                        }
-                } else {
-                        /* escape char at src[i] */
-                        esclen = strlen(escape[k].s);
-
-                        if (j >= dst_siz - esclen) {
-                                /* silent truncation */
-                                break;
-                        } else {
-                                memcpy(&dst[j], escape[k].s, esclen);
-                                j += esclen;
-                        }
-                }
-        }
-        dst[j] = '\0';
-}
-
 #define        INVALID  1
 #define        TOOSMALL 2
 #define        TOOLARGE 3
diff --git a/util.h b/util.h
@@ -52,7 +52,6 @@ void eunveil(const char *, const char *);
 int timestamp(char *, size_t, time_t);
 int esnprintf(char *, size_t, const char *, ...);
 int prepend(char *, size_t, const char *);
-void html_escape(const char *, char *, size_t);
 
 void *reallocarray(void *, size_t, size_t);
 long long strtonum(const char *, long long, long long, const char **);