Introduction
Introduction Statistics Contact Development Disclaimer Help
Refactor http_send_response() into http_prepare_response() - quark - quark web …
git clone git://git.suckless.org/quark
Log
Files
Refs
LICENSE
---
commit 58d0f44e0395fe37b3575da35992b3d3e7f262d7
parent a5163d08135b81b271b1d7ba36b24e342b57961f
Author: Laslo Hunhold <[email protected]>
Date: Sat, 22 Aug 2020 23:20:00 +0200
Refactor http_send_response() into http_prepare_response()
The function http_send_response() did too much. It not only took
the request fields and built them together into a response, it
delegated too little and many functions were "hacked" into it, for
instance shady directory-changes for vhosts and hand-construction
of response structs.
The preparations for a rework were already made in previous commits,
including a tighter focus on the response-struct itself. Instead of
doing everything locally in the http_send_response() function, the
new http_prepare_response() only really takes the request-struct and
builds a response-struct. The response-struct is expanded such that
it's possible to do the data-sending simply with the response-struct
itself and not any other magic parameters that just drop out of the
function.
Another matter are the http_send_status()-calls. Because the
aforementioned function is so central, this refactoring has included
many areas. Instead of calling http_send_status() in every error-case,
which makes little sense now given we first delegate everything through
a response struct, errors are just sent as a return value and caught
centrally (in serve() in main.c), which centralizes the error handling
a bit.
It might look a bit strange now and it might not be clear in which
direction this is going, but subsequent commits will hopefully give
clarity in this regard.
Signed-off-by: Laslo Hunhold <[email protected]>
Diffstat:
M http.c | 261 ++++++++++++++++++++---------…
M http.h | 11 +++++------
M main.c | 16 ++++++++++++----
M resp.c | 202 +++++++----------------------…
M resp.h | 5 ++---
M util.c | 60 +++++++++++++++++++++++++++++…
M util.h | 2 ++
7 files changed, 294 insertions(+), 263 deletions(-)
---
diff --git a/http.c b/http.c
@@ -61,7 +61,7 @@ const char *res_field_str[] = {
enum status
http_send_header(int fd, const struct response *res)
{
- char t[FIELD_MAX];
+ char t[FIELD_MAX], esc[PATH_MAX];
size_t i;
if (timestamp(t, sizeof(t), time(NULL))) {
@@ -89,6 +89,18 @@ 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;
+ }
+ }
+
return res->status;
}
@@ -536,93 +548,90 @@ parse_range(const char *str, size_t size, size_t *lower, …
#define RELPATH(x) ((!*(x) || !strcmp(x, "/")) ? "." : ((x) + 1))
enum status
-http_send_response(int fd, const struct request *req, const struct server *s)
+http_prepare_response(const struct request *req, struct response *res,
+ const struct server *s)
{
enum status returnstatus;
struct in6_addr addr;
- struct response res = { 0 };
struct stat st;
struct tm tm = { 0 };
+ struct vhost *vhost;
size_t len, i;
- size_t lower, upper;
int hasport, ipv6host;
- static char realtarget[PATH_MAX], tmptarget[PATH_MAX];
+ static char realuri[PATH_MAX], tmpuri[PATH_MAX];
char *p, *mime;
- const char *vhostmatch, *targethost;
+ const char *targethost;
+
+ /* empty all response fields */
+ memset(res, 0, sizeof(*res));
- /* make a working copy of the target */
- memcpy(realtarget, req->target, sizeof(realtarget));
+ /* make a working copy of the URI and normalize it */
+ memcpy(realuri, req->target, sizeof(realuri));
+ if (normabspath(realuri)) {
+ return S_BAD_REQUEST;
+ }
/* match vhost */
- vhostmatch = NULL;
+ vhost = NULL;
if (s->vhost) {
for (i = 0; i < s->vhost_len; i++) {
- /* switch to vhost directory if there is a match */
- if (!regexec(&(s->vhost[i].re), req->field[REQ_HOST], …
- NULL, 0)) {
- if (chdir(s->vhost[i].dir) < 0) {
- return http_send_status(fd, (errno == …
- S_FORBIDDEN : …
- }
- vhostmatch = s->vhost[i].chost;
+ if (!regexec(&(s->vhost[i].re), req->field[REQ_HOST],
+ 0, NULL, 0)) {
+ /* we have a matching vhost */
+ vhost = &(s->vhost[i]);
break;
}
}
if (i == s->vhost_len) {
- return http_send_status(fd, S_NOT_FOUND);
+ return S_NOT_FOUND;
}
- /* if we have a vhost prefix, prepend it to the target */
- if (s->vhost[i].prefix) {
- if (esnprintf(tmptarget, sizeof(tmptarget), "%s%s",
- s->vhost[i].prefix, realtarget)) {
- return http_send_status(fd, S_REQUEST_TOO_LARG…
- }
- memcpy(realtarget, tmptarget, sizeof(realtarget));
+ /* if we have a vhost prefix, prepend it to the URI */
+ if (vhost->prefix &&
+ prepend(realuri, LEN(realuri), vhost->prefix)) {
+ return S_REQUEST_TOO_LARGE;
}
}
/* apply target prefix mapping */
for (i = 0; i < s->map_len; i++) {
len = strlen(s->map[i].from);
- if (!strncmp(realtarget, s->map[i].from, len)) {
+ if (!strncmp(realuri, s->map[i].from, len)) {
/* match canonical host if vhosts are enabled and
* the mapping specifies a canonical host */
if (s->vhost && s->map[i].chost &&
- strcmp(s->map[i].chost, vhostmatch)) {
+ strcmp(s->map[i].chost, vhost->chost)) {
continue;
}
/* swap out target prefix */
- if (esnprintf(tmptarget, sizeof(tmptarget), "%s%s",
- s->map[i].to, realtarget + len)) {
- return http_send_status(fd, S_REQUEST_TOO_LARG…
+ memmove(realuri, realuri + len, strlen(realuri) + 1);
+ if (prepend(realuri, LEN(realuri), s->map[i].to)) {
+ return S_REQUEST_TOO_LARGE;
}
- memcpy(realtarget, tmptarget, sizeof(realtarget));
break;
}
}
- /* normalize target */
- if (normabspath(realtarget)) {
- return http_send_status(fd, S_BAD_REQUEST);
+ /* normalize URI again, in case we introduced dirt */
+ if (normabspath(realuri)) {
+ return S_BAD_REQUEST;
}
- /* stat the target */
- if (stat(RELPATH(realtarget), &st) < 0) {
- return http_send_status(fd, (errno == EACCES) ?
- S_FORBIDDEN : S_NOT_FOUND);
+ /* stat the relative path derived from the URI */
+ if (stat(RELPATH(realuri), &st) < 0) {
+ return (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND;
}
if (S_ISDIR(st.st_mode)) {
- /* add / to target if not present */
- len = strlen(realtarget);
- if (len >= PATH_MAX - 2) {
- return http_send_status(fd, S_REQUEST_TOO_LARGE);
+ /* append '/' to URI if not present */
+ len = strlen(realuri);
+ if (len + 1 + 1 > PATH_MAX) {
+ return S_REQUEST_TOO_LARGE;
}
- if (len && realtarget[len - 1] != '/') {
- realtarget[len] = '/';
- realtarget[len + 1] = '\0';
+ if (len > 0 && realuri[len - 1] != '/') {
+ realuri[len] = '/';
+ realuri[len + 1] = '\0';
}
}
@@ -630,24 +639,27 @@ http_send_response(int fd, const struct request *req, con…
* reject hidden target, except if it is a well-known URI
* according to RFC 8615
*/
- if (strstr(realtarget, "/.") && strncmp(realtarget,
+ if (strstr(realuri, "/.") && strncmp(realuri,
"/.well-known/", sizeof("/.well-known/") - 1)) {
- return http_send_status(fd, S_FORBIDDEN);
+ return S_FORBIDDEN;
}
- /* redirect if targets differ, host is non-canonical or we prefixed */
- if (strcmp(req->target, realtarget) || (s->vhost && vhostmatch &&
- strcmp(req->field[REQ_HOST], vhostmatch))) {
- res.status = S_MOVED_PERMANENTLY;
+ /*
+ * redirect if the original URI and the "real" URI differ or if
+ * the requested host is non-canonical
+ */
+ if (strcmp(req->target, realuri) || (s->vhost && vhost &&
+ strcmp(req->field[REQ_HOST], vhost->chost))) {
+ res->status = S_MOVED_PERMANENTLY;
- /* encode realtarget */
- encode(realtarget, tmptarget);
+ /* encode realuri */
+ encode(realuri, tmpuri);
/* determine target location */
if (s->vhost) {
/* absolute redirection URL */
- targethost = req->field[REQ_HOST][0] ? vhostmatch ?
- vhostmatch : req->field[REQ_HOST] : s->ho…
+ targethost = req->field[REQ_HOST][0] ? vhost->chost ?
+ vhost->chost : req->field[REQ_HOST] : s->…
s->host : "localhost";
/* do we need to add a port to the Location? */
@@ -658,53 +670,74 @@ http_send_response(int fd, const struct request *req, con…
* honor that later when we fill the "Location"-field …
if ((ipv6host = inet_pton(AF_INET6, targethost,
&addr)) < 0) {
- return http_send_status(fd,
- S_INTERNAL_SERVER_ERRO…
+ return S_INTERNAL_SERVER_ERROR;
}
/* write location to response struct */
- if (esnprintf(res.field[RES_LOCATION],
- sizeof(res.field[RES_LOCATION]),
+ if (esnprintf(res->field[RES_LOCATION],
+ sizeof(res->field[RES_LOCATION]),
"//%s%s%s%s%s%s",
ipv6host ? "[" : "",
targethost,
ipv6host ? "]" : "", hasport ? ":" : "",
- hasport ? s->port : "", tmptarget)) {
- return http_send_status(fd, S_REQUEST_TOO_LARG…
+ hasport ? s->port : "", tmpuri)) {
+ return S_REQUEST_TOO_LARGE;
}
} else {
- /* write relative redirection URL to response struct */
- if (esnprintf(res.field[RES_LOCATION],
- sizeof(res.field[RES_LOCATION]),
- tmptarget)) {
- return http_send_status(fd, S_REQUEST_TOO_LARG…
+ /* write relative redirection URI to response struct */
+ if (esnprintf(res->field[RES_LOCATION],
+ sizeof(res->field[RES_LOCATION]),
+ "%s", tmpuri)) {
+ return S_REQUEST_TOO_LARGE;
}
}
- return http_send_header(fd, &res);
+ return 0;
+ } else {
+ /*
+ * the URI is well-formed, we can now write the URI into
+ * the response-URI and corresponding relative path
+ * (optionally including the vhost servedir as a prefix)
+ * into the actual response-path
+ */
+ if (esnprintf(res->uri, sizeof(res->uri), "%s", req->target)) {
+ return S_REQUEST_TOO_LARGE;
+ }
+ if (esnprintf(res->path, sizeof(res->path), "%s%s",
+ vhost ? vhost->dir : "", RELPATH(req->target))) {
+ return S_REQUEST_TOO_LARGE;
+ }
}
if (S_ISDIR(st.st_mode)) {
- /* append docindex to target */
- if (esnprintf(realtarget, sizeof(realtarget), "%s%s",
+ /*
+ * check if the directory index exists by appending it to
+ * the URI
+ */
+ if (esnprintf(tmpuri, sizeof(tmpuri), "%s%s",
req->target, s->docindex)) {
- return http_send_status(fd, S_REQUEST_TOO_LARGE);
+ return S_REQUEST_TOO_LARGE;
}
/* stat the docindex, which must be a regular file */
- if (stat(RELPATH(realtarget), &st) < 0 || !S_ISREG(st.st_mode)…
+ if (stat(RELPATH(tmpuri), &st) < 0 || !S_ISREG(st.st_mode)) {
if (s->listdirs) {
- /* remove index suffix and serve dir */
- realtarget[strlen(realtarget) -
- strlen(s->docindex)] = '\0';
- return resp_dir(fd, RELPATH(realtarget), req);
+ /* serve directory listing */
+ res->type = RESTYPE_DIRLISTING;
+ res->status = (access(res->path, R_OK)) ?
+ S_FORBIDDEN : S_OK;
+
+ if (esnprintf(res->field[RES_CONTENT_TYPE],
+ sizeof(res->field[RES_CONTENT_TY…
+ "%s", "text/html; charset=utf-8"…
+ return S_INTERNAL_SERVER_ERROR;
+ }
+
+ return 0;
} else {
/* reject */
- if (!S_ISREG(st.st_mode) || errno == EACCES) {
- return http_send_status(fd, S_FORBIDDE…
- } else {
- return http_send_status(fd, S_NOT_FOUN…
- }
+ return (!S_ISREG(st.st_mode) || errno == EACCE…
+ S_FORBIDDEN : S_NOT_FOUND;
}
}
}
@@ -714,39 +747,39 @@ http_send_response(int fd, const struct request *req, con…
/* parse field */
if (!strptime(req->field[REQ_IF_MODIFIED_SINCE],
"%a, %d %b %Y %T GMT", &tm)) {
- return http_send_status(fd, S_BAD_REQUEST);
+ return S_BAD_REQUEST;
}
/* compare with last modification date of the file */
if (difftime(st.st_mtim.tv_sec, timegm(&tm)) <= 0) {
- res.status = S_NOT_MODIFIED;
- return http_send_header(fd, &res);
+ res->status = S_NOT_MODIFIED;
+ return 0;
}
}
/* range */
- if ((returnstatus = parse_range(req->field[REQ_RANGE],
- st.st_size, &lower, &upper))) {
+ if ((returnstatus = parse_range(req->field[REQ_RANGE], st.st_size,
+ &(res->file.lower),
+ &(res->file.upper)))) {
if (returnstatus == S_RANGE_NOT_SATISFIABLE) {
- res.status = S_RANGE_NOT_SATISFIABLE;
+ res->status = S_RANGE_NOT_SATISFIABLE;
- if (esnprintf(res.field[RES_CONTENT_RANGE],
- sizeof(res.field[RES_CONTENT_RANGE]),
+ if (esnprintf(res->field[RES_CONTENT_RANGE],
+ sizeof(res->field[RES_CONTENT_RANGE]),
"bytes */%zu", st.st_size)) {
- return http_send_status(fd,
- S_INTERNAL_SERVER_ERRO…
+ return S_INTERNAL_SERVER_ERROR;
}
- return http_send_header(fd, &res);
+ return 0;
} else {
- return http_send_status(fd, returnstatus);
+ return returnstatus;
}
}
/* mime */
mime = "application/octet-stream";
- if ((p = strrchr(realtarget, '.'))) {
- for (i = 0; i < sizeof(mimes) / sizeof(*mimes); i++) {
+ if ((p = strrchr(realuri, '.'))) {
+ for (i = 0; i < LEN(mimes); i++) {
if (!strcmp(mimes[i].ext, p + 1)) {
mime = mimes[i].type;
break;
@@ -754,5 +787,43 @@ http_send_response(int fd, const struct request *req, cons…
}
}
- return resp_file(fd, RELPATH(realtarget), req, &st, mime, lower, upper…
+ /* fill response struct */
+ res->type = RESTYPE_FILE;
+
+ /* check if file is readable */
+ res->status = (access(res->path, R_OK)) ? S_FORBIDDEN :
+ (req->field[REQ_RANGE][0] != '\0') ?
+ S_PARTIAL_CONTENT : S_OK;
+
+ if (esnprintf(res->field[RES_ACCEPT_RANGES],
+ sizeof(res->field[RES_ACCEPT_RANGES]),
+ "%s", "bytes")) {
+ return S_INTERNAL_SERVER_ERROR;
+ }
+
+ if (esnprintf(res->field[RES_CONTENT_LENGTH],
+ sizeof(res->field[RES_CONTENT_LENGTH]),
+ "%zu", res->file.upper - res->file.lower + 1)) {
+ return S_INTERNAL_SERVER_ERROR;
+ }
+ if (req->field[REQ_RANGE][0] != '\0') {
+ if (esnprintf(res->field[RES_CONTENT_RANGE],
+ sizeof(res->field[RES_CONTENT_RANGE]),
+ "bytes %zd-%zd/%zu", res->file.lower,
+ res->file.upper, st.st_size)) {
+ return S_INTERNAL_SERVER_ERROR;
+ }
+ }
+ if (esnprintf(res->field[RES_CONTENT_TYPE],
+ sizeof(res->field[RES_CONTENT_TYPE]),
+ "%s", mime)) {
+ return S_INTERNAL_SERVER_ERROR;
+ }
+ if (timestamp(res->field[RES_LAST_MODIFIED],
+ sizeof(res->field[RES_LAST_MODIFIED]),
+ st.st_mtim.tv_sec)) {
+ return S_INTERNAL_SERVER_ERROR;
+ }
+
+ return 0;
}
diff --git a/http.h b/http.h
@@ -3,7 +3,6 @@
#define HTTP_H
#include <limits.h>
-#include <sys/stat.h>
#include "util.h"
@@ -67,8 +66,9 @@ enum res_field {
extern const char *res_field_str[];
enum res_type {
+ RESTYPE_ERROR,
RESTYPE_FILE,
- RESTYPE_DIR,
+ RESTYPE_DIRLISTING,
NUM_RES_TYPES,
};
@@ -76,10 +76,9 @@ struct response {
enum res_type type;
enum status status;
char field[NUM_RES_FIELDS][FIELD_MAX];
+ char uri[PATH_MAX];
char path[PATH_MAX];
- struct stat st;
struct {
- char *mime;
size_t lower;
size_t upper;
} file;
@@ -106,7 +105,7 @@ enum status http_send_header(int, const struct response *);
enum status http_send_status(int, enum status);
enum status http_recv_header(int, char *, size_t, size_t *);
enum status http_parse_header(const char *, struct request *);
-enum status http_send_response(int fd, const struct request *,
- const struct server *);
+enum status http_prepare_response(const struct request *, struct response *,
+ const struct server *);
#endif /* HTTP_H */
diff --git a/main.c b/main.c
@@ -16,6 +16,7 @@
#include <time.h>
#include <unistd.h>
+#include "resp.h"
#include "http.h"
#include "sock.h"
#include "util.h"
@@ -37,12 +38,19 @@ serve(int infd, const struct sockaddr_storage *in_sa, const…
}
/* handle request */
- if ((status = http_recv_header(c.fd, c.header, LEN(c.header), &c.off))…
- status = http_send_status(c.fd, status);
- } else if ((status = http_parse_header(c.header, &c.req))) {
+ if ((status = http_recv_header(c.fd, c.header, LEN(c.header), &c.off))…
+ (status = http_parse_header(c.header, &c.req)) ||
+ (status = http_prepare_response(&c.req, &c.res, s))) {
status = http_send_status(c.fd, status);
} else {
- status = http_send_response(c.fd, &c.req, s);
+ status = http_send_header(c.fd, &c.res);
+
+ /* send data */
+ if (c.res.type == RESTYPE_FILE) {
+ resp_file(c.fd, &c.res);
+ } else if (c.res.type == RESTYPE_DIRLISTING) {
+ resp_dir(c.fd, &c.res);
+ }
}
/* write output to log */
diff --git a/resp.c b/resp.c
@@ -38,202 +38,94 @@ 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
-resp_dir(int fd, const char *name, const struct request *req)
+resp_dir(int fd, const struct response *res)
{
- enum status sendstatus;
+ enum status ret;
struct dirent **e;
- struct response res = {
- .status = S_OK,
- .field[RES_CONTENT_TYPE] = "text/html; charset=utf-8",
- };
size_t i;
int dirlen;
char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
/* read directory */
- if ((dirlen = scandir(name, &e, NULL, compareent)) < 0) {
- return http_send_status(fd, S_FORBIDDEN);
+ if ((dirlen = scandir(res->path, &e, NULL, compareent)) < 0) {
+ return S_FORBIDDEN;
}
- /* send header as late as possible */
- if ((sendstatus = http_send_header(fd, &res)) != res.status) {
- res.status = sendstatus;
- goto cleanup;
- }
-
- if (req->method == M_GET) {
- /* listing header */
- html_escape(name, 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) {
- res.status = S_REQUEST_TIMEOUT;
- goto cleanup;
- }
-
- /* listing */
- for (i = 0; i < (size_t)dirlen; i++) {
- /* skip hidden files, "." and ".." */
- if (e[i]->d_name[0] == '.') {
- continue;
- }
-
- /* entry line */
- html_escape(e[i]->d_name, esc, sizeof(esc));
- if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>…
- esc,
- (e[i]->d_type == DT_DIR) ? "/" : "",
- esc,
- suffix(e[i]->d_type)) < 0) {
- res.status = S_REQUEST_TIMEOUT;
- goto cleanup;
- }
+ /* listing */
+ for (i = 0; i < (size_t)dirlen; i++) {
+ /* skip hidden files, "." and ".." */
+ if (e[i]->d_name[0] == '.') {
+ continue;
}
- /* listing footer */
- if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
- res.status = S_REQUEST_TIMEOUT;
+ /* entry line */
+ html_escape(e[i]->d_name, esc, sizeof(esc));
+ if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
+ esc,
+ (e[i]->d_type == DT_DIR) ? "/" : "",
+ esc,
+ suffix(e[i]->d_type)) < 0) {
+ ret = S_REQUEST_TIMEOUT;
goto cleanup;
}
}
+ /* listing footer */
+ if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
+ ret = S_REQUEST_TIMEOUT;
+ goto cleanup;
+ }
+
cleanup:
while (dirlen--) {
free(e[dirlen]);
}
free(e);
- return res.status;
+ return ret;
}
enum status
-resp_file(int fd, const char *name, const struct request *req,
- const struct stat *st, const char *mime, size_t lower,
- size_t upper)
+resp_file(int fd, const struct response *res)
{
FILE *fp;
- enum status sendstatus;
- struct response res = {
- .status = (req->field[REQ_RANGE][0] != '\0') ?
- S_PARTIAL_CONTENT : S_OK,
- .field[RES_ACCEPT_RANGES] = "bytes",
- };
+ enum status ret = 0;
ssize_t bread, bwritten;
size_t remaining;
static char buf[BUFSIZ], *p;
/* open file */
- if (!(fp = fopen(name, "r"))) {
- res.status = http_send_status(fd, S_FORBIDDEN);
+ if (!(fp = fopen(res->path, "r"))) {
+ ret = S_FORBIDDEN;
goto cleanup;
}
/* seek to lower bound */
- if (fseek(fp, lower, SEEK_SET)) {
- res.status = http_send_status(fd, S_INTERNAL_SERVER_ERROR);
+ if (fseek(fp, res->file.lower, SEEK_SET)) {
+ ret = S_INTERNAL_SERVER_ERROR;
goto cleanup;
}
- /* build header */
- if (esnprintf(res.field[RES_CONTENT_LENGTH],
- sizeof(res.field[RES_CONTENT_LENGTH]),
- "%zu", upper - lower + 1)) {
- return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
- }
- if (req->field[REQ_RANGE][0] != '\0') {
- if (esnprintf(res.field[RES_CONTENT_RANGE],
- sizeof(res.field[RES_CONTENT_RANGE]),
- "bytes %zd-%zd/%zu", lower, upper,
- st->st_size)) {
- return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
- }
- }
- if (esnprintf(res.field[RES_CONTENT_TYPE],
- sizeof(res.field[RES_CONTENT_TYPE]),
- "%s", mime)) {
- return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
- }
- if (timestamp(res.field[RES_LAST_MODIFIED],
- sizeof(res.field[RES_LAST_MODIFIED]),
- st->st_mtim.tv_sec)) {
- return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
- }
-
- /* send header as late as possible */
- if ((sendstatus = http_send_header(fd, &res)) != res.status) {
- res.status = sendstatus;
- goto cleanup;
- }
+ /* write data until upper bound is hit */
+ remaining = res->file.upper - res->file.lower + 1;
- if (req->method == M_GET) {
- /* write data until upper bound is hit */
- remaining = upper - lower + 1;
-
- while ((bread = fread(buf, 1, MIN(sizeof(buf),
- remaining), fp))) {
- if (bread < 0) {
- res.status = S_INTERNAL_SERVER_ERROR;
+ while ((bread = fread(buf, 1, MIN(sizeof(buf),
+ remaining), fp))) {
+ if (bread < 0) {
+ ret = S_INTERNAL_SERVER_ERROR;
+ goto cleanup;
+ }
+ remaining -= bread;
+ p = buf;
+ while (bread > 0) {
+ bwritten = write(fd, p, bread);
+ if (bwritten <= 0) {
+ ret = S_REQUEST_TIMEOUT;
goto cleanup;
}
- remaining -= bread;
- p = buf;
- while (bread > 0) {
- bwritten = write(fd, p, bread);
- if (bwritten <= 0) {
- res.status = S_REQUEST_TIMEOUT;
- goto cleanup;
- }
- bread -= bwritten;
- p += bwritten;
- }
+ bread -= bwritten;
+ p += bwritten;
}
}
cleanup:
@@ -241,5 +133,5 @@ cleanup:
fclose(fp);
}
- return res.status;
+ return ret;
}
diff --git a/resp.h b/resp.h
@@ -7,8 +7,7 @@
#include "http.h"
-enum status resp_dir(int, const char *, const struct request *);
-enum status resp_file(int, const char *, const struct request *,
- const struct stat *, const char *, size_t, size_t);
+enum status resp_dir(int, const struct response *);
+enum status resp_file(int, const struct response *);
#endif /* RESP_H */
diff --git a/util.c b/util.c
@@ -108,6 +108,66 @@ esnprintf(char *str, size_t size, const char *fmt, ...)
return (ret < 0 || (size_t)ret >= size);
}
+int
+prepend(char *str, size_t size, const char *prefix)
+{
+ size_t len = strlen(str), prefixlen = strlen(prefix);
+
+ if (len + prefixlen + 1 > size) {
+ return 1;
+ }
+
+ memmove(str + prefixlen, str, len + 1);
+ memcpy(str, prefix, prefixlen);
+
+ 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
@@ -51,6 +51,8 @@ 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 **);
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.