Add http_send_body() and data_send_error() and refactor - quark - quark web ser… | |
git clone git://git.suckless.org/quark | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit a36b901d404f4d4268384a379fd040898f78b1b3 | |
parent db127723c67534d5693fc033f19c855a403d1447 | |
Author: Laslo Hunhold <[email protected]> | |
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 <[email protected]> | |
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, | |
+ "<!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 r… | |
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 … | |
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 **); |