Add http_send_body() and data_send_error() and refactor - quark - quark web server
 (HTM) git clone git://git.suckless.org/quark
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit a36b901d404f4d4268384a379fd040898f78b1b3
 (DIR) parent db127723c67534d5693fc033f19c855a403d1447
 (HTM) Author: Laslo Hunhold <dev@frign.de>
       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 <dev@frign.de>
       
       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(-)
       ---
 (DIR) 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[] = {
       +                { '&',  "&amp;"  },
       +                { '<',  "&lt;"   },
       +                { '>',  "&gt;"   },
       +                { '"',  "&quot;" },
       +                { '\'', "&#x27;" },
       +        };
       +        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,
       +                    "<!DOCTYPE html>\n<html>\n\t<head>"
       +                    "<title>Index of %s</title></head>\n"
       +                    "\t<body>\n\t\t<a href=\"..\">..</a>",
       +                    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,
       +                    "<!DOCTYPE html>\n<html>\n\t<head>\n"
       +                    "\t\t<title>%d %s</title>\n\t</head>\n\t<body>\n"
       +                    "\t\t<h1>%d %s</h1>\n\t</body>\n</html>\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;
 (DIR) 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 */
 (DIR) diff --git 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,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,
       -                            "<!DOCTYPE html>\n<html>\n\t<head>"
       -                            "<title>Index of %s</title></head>\n"
       -                            "\t<body>\n\t\t<a href=\"..\">..</a>",
       -                            esc) < 0) {
       -                        return S_REQUEST_TIMEOUT;
       -                }
       -        } else if (res->type == RESTYPE_ERROR) {
       -                if (dprintf(fd,
       -                            "<!DOCTYPE html>\n<html>\n\t<head>\n"
       -                            "\t\t<title>%d %s</title>\n\t</head>\n\t<body>\n"
       -                            "\t\t<h1>%d %s</h1>\n\t</body>\n</html>\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;
       +}
 (DIR) 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 */
 (DIR) 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 */
 (DIR) 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[] = {
       -                { '&',  "&amp;"  },
       -                { '<',  "&lt;"   },
       -                { '>',  "&gt;"   },
       -                { '"',  "&quot;" },
       -                { '\'', "&#x27;" },
       -        };
       -        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
 (DIR) 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 **);