quark

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

commit a36b901d404f4d4268384a379fd040898f78b1b3
parent db127723c67534d5693fc033f19c855a403d1447
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:
Mdata.c | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdata.h | 1+
Mhttp.c | 43+++++++++++++++++++++----------------------
Mhttp.h | 6+++++-
Mmain.c | 10++--------
Mutil.c | 45---------------------------------------------
Mutil.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[] = { + { '&', "&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; 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 <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; +} 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[] = { - { '&', "&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 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 **);