Use dprintf() instead of snprintf()+sendbuffer()

The aim was to write quark without any mallocs. This was successful, but
proved to be a bit ugly looking at how we construct data to be sent.
Before this change, we had static buffers in each function that needed
them and filled them up, possibly risking overflow.
After that, we sent them off using our own function sendbuffer(), which
in itself represented a buffering mechanism.
Using dprintf, which is POSIX 2008, we can send things off directly,
with no need for sendbuffer() or buffers for these things.
This way we can factor out sendbuffer(), dropping a few more LOCs.

Thanks Hiltjo for the suggestion!
This commit is contained in:
Laslo Hunhold 2017-06-21 07:41:52 +02:00
parent 6347e2ec3e
commit f4db83f68c

96
quark.c
View file

@ -160,28 +160,12 @@ encode(char src[PATH_MAX], char dest[PATH_MAX])
return 0; return 0;
} }
static int
sendbuffer(int fd, char *buf, size_t buflen) {
size_t written;
ssize_t off;
for (written = 0; buflen > 0; written += off, buflen -= off) {
if ((off = write(fd, buf + written, buflen)) < 0) {
return 1;
}
}
return 0;
}
static enum status static enum status
sendstatus(int fd, enum status s) sendstatus(int fd, enum status s)
{ {
static char res[4096], t[TIMESTAMP_LEN]; static char t[TIMESTAMP_LEN];
size_t len;
/* assemble error response */ if (dprintf(fd,
len = snprintf(res, sizeof(res),
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Connection: close\r\n" "Connection: close\r\n"
@ -193,9 +177,11 @@ sendstatus(int fd, enum status s)
"\t\t<h1>%d %s</h1>\n\t</body>\n</html>", "\t\t<h1>%d %s</h1>\n\t</body>\n</html>",
s, status_str[s], timestamp(0, t), s, status_str[s], timestamp(0, t),
(s == S_METHOD_NOT_ALLOWED) ? "Allow: HEAD, GET\r\n" : "", (s == S_METHOD_NOT_ALLOWED) ? "Allow: HEAD, GET\r\n" : "",
s, status_str[s], s, status_str[s]); s, status_str[s], s, status_str[s]) < 0) {
return S_REQUEST_TIMEOUT;
}
return sendbuffer(fd, res, len) ? S_REQUEST_TIMEOUT : s; return s;
} }
static int static int
@ -347,9 +333,9 @@ static enum status
senddir(int fd, char *name, struct request *r) senddir(int fd, char *name, struct request *r)
{ {
struct dirent **e; struct dirent **e;
size_t len, i; size_t i;
int dirlen; int dirlen;
static char resheader[HEADER_MAX], buf[BUFSIZ], t[TIMESTAMP_LEN]; static char t[TIMESTAMP_LEN];
/* read directory */ /* read directory */
if ((dirlen = scandir(name, &e, NULL, alphasort)) < 0) { if ((dirlen = scandir(name, &e, NULL, alphasort)) < 0) {
@ -357,28 +343,23 @@ senddir(int fd, char *name, struct request *r)
} }
/* send header as late as possible */ /* send header as late as possible */
len = snprintf(resheader, sizeof(resheader), if (dprintf(fd,
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Connection: close\r\n" "Connection: close\r\n"
"Content-Type: text/html\r\n" "Content-Type: text/html\r\n"
"\r\n", "\r\n",
S_OK, status_str[S_OK], timestamp(0, t)); S_OK, status_str[S_OK], timestamp(0, t)) < 0) {
if (sendbuffer(fd, resheader, len)) {
return S_REQUEST_TIMEOUT; return S_REQUEST_TIMEOUT;
} }
if (r->method == M_GET) { if (r->method == M_GET) {
/* listing header */ /* listing header */
if ((len = snprintf(buf, sizeof(buf), if (dprintf(fd,
"<!DOCTYPE html>\n<html>\n\t<head>" "<!DOCTYPE html>\n<html>\n\t<head>"
"<title>Index of %s</title></head>\n" "<title>Index of %s</title></head>\n"
"\t<body>\n\t\t<a href=\"..\">..</a>", "\t<body>\n\t\t<a href=\"..\">..</a>",
name)) >= sizeof(buf)) { name) < 0) {
return S_INTERNAL_SERVER_ERROR;
}
if (sendbuffer(fd, buf, len)) {
return S_REQUEST_TIMEOUT; return S_REQUEST_TIMEOUT;
} }
@ -390,20 +371,14 @@ senddir(int fd, char *name, struct request *r)
} }
/* entry line */ /* entry line */
if ((len = snprintf(buf, sizeof(buf), if (dprintf(fd, "<br />\n\t\t<a href=\"%s\">%s</a>",
"<br />\n\t\t<a href=\"%s\">%s</a>", e[i]->d_name, e[i]->d_name) < 0) {
e[i]->d_name, e[i]->d_name))
>= sizeof(buf)) {
return S_INTERNAL_SERVER_ERROR;
}
if (sendbuffer(fd, buf, len)) {
return S_REQUEST_TIMEOUT; return S_REQUEST_TIMEOUT;
} }
} }
/* listing footer */ /* listing footer */
if (sendbuffer(fd, "\n\t</body>\n</html>", if (dprintf(fd, "\n\t</body>\n</html>") < 0) {
sizeof("\n\t</body>\n</html>") - 1)) {
return S_REQUEST_TIMEOUT; return S_REQUEST_TIMEOUT;
} }
} }
@ -417,12 +392,10 @@ sendfile(int fd, char *name, struct request *r, struct stat *st, char *mime,
{ {
FILE *fp; FILE *fp;
enum status s; enum status s;
size_t len;
ssize_t bread, bwritten; ssize_t bread, bwritten;
off_t remaining; off_t remaining;
int range; int range;
static char resheader[HEADER_MAX], buf[BUFSIZ], *p, t1[TIMESTAMP_LEN], static char buf[BUFSIZ], *p, t1[TIMESTAMP_LEN], t2[TIMESTAMP_LEN];
t2[TIMESTAMP_LEN];
/* open file */ /* open file */
if (!(fp = fopen(name, "r"))) { if (!(fp = fopen(name, "r"))) {
@ -438,7 +411,7 @@ sendfile(int fd, char *name, struct request *r, struct stat *st, char *mime,
range = r->field[REQ_RANGE][0]; range = r->field[REQ_RANGE][0];
s = range ? S_PARTIAL_CONTENT : S_OK; s = range ? S_PARTIAL_CONTENT : S_OK;
len = snprintf(resheader, sizeof(resheader), if (dprintf(fd,
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Connection: close\r\n" "Connection: close\r\n"
@ -446,15 +419,16 @@ sendfile(int fd, char *name, struct request *r, struct stat *st, char *mime,
"Content-Type: %s\r\n" "Content-Type: %s\r\n"
"Content-Length: %zu\r\n", "Content-Length: %zu\r\n",
s, status_str[s], timestamp(0, t1), s, status_str[s], timestamp(0, t1),
timestamp(st->st_mtim.tv_sec, t2), mime, upper - lower); timestamp(st->st_mtim.tv_sec, t2), mime, upper - lower) < 0) {
if (range) { return S_REQUEST_TIMEOUT;
len += snprintf(resheader + len, sizeof(resheader) - len,
"Content-Range: bytes %zu-%zu/%zu\r\n",
lower, upper - 1, st->st_size);
} }
len += snprintf(resheader + len, sizeof(resheader) - len, "\r\n"); if (range) {
if (dprintf(fd, "Content-Range: bytes %zu-%zu/%zu\r\n",
if (sendbuffer(fd, resheader, len)) { lower, upper - 1, st->st_size) < 0) {
return S_REQUEST_TIMEOUT;
}
}
if (dprintf(fd, "\r\n") < 0) {
return S_REQUEST_TIMEOUT; return S_REQUEST_TIMEOUT;
} }
@ -489,8 +463,7 @@ sendresponse(int fd, struct request *r)
struct tm tm; struct tm tm;
size_t len, i; size_t len, i;
off_t lower, upper; off_t lower, upper;
static char realtarget[PATH_MAX], resheader[HEADER_MAX], static char realtarget[PATH_MAX], tmptarget[PATH_MAX], t[TIMESTAMP_LEN];
tmptarget[PATH_MAX], t[TIMESTAMP_LEN];
char *p, *q, *mime; char *p, *q, *mime;
/* check method */ /* check method */
@ -530,7 +503,7 @@ sendresponse(int fd, struct request *r)
/* encode realtarget */ /* encode realtarget */
encode(realtarget, tmptarget); encode(realtarget, tmptarget);
len = snprintf(resheader, sizeof(resheader), if (dprintf(fd,
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Connection: close\r\n" "Connection: close\r\n"
@ -538,9 +511,11 @@ sendresponse(int fd, struct request *r)
"\r\n", "\r\n",
S_MOVED_PERMANENTLY, S_MOVED_PERMANENTLY,
status_str[S_MOVED_PERMANENTLY], timestamp(0, t), status_str[S_MOVED_PERMANENTLY], timestamp(0, t),
tmptarget); tmptarget) < 0) {
return sendbuffer(fd, resheader, len) ? S_REQUEST_TIMEOUT : return S_REQUEST_TIMEOUT;
S_MOVED_PERMANENTLY; }
return S_MOVED_PERMANENTLY;
} }
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
@ -577,14 +552,13 @@ sendresponse(int fd, struct request *r)
/* compare with last modification date of the file */ /* compare with last modification date of the file */
if (difftime(st.st_mtim.tv_sec, mktime(&tm)) <= 0) { if (difftime(st.st_mtim.tv_sec, mktime(&tm)) <= 0) {
len = snprintf(resheader, sizeof(resheader), if (dprintf(fd,
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Connection: close\r\n" "Connection: close\r\n"
"\r\n", "\r\n",
S_NOT_MODIFIED, status_str[S_NOT_MODIFIED], S_NOT_MODIFIED, status_str[S_NOT_MODIFIED],
timestamp(0, t)); timestamp(0, t)) < 0) {
if (sendbuffer(fd, resheader, len)) {
return S_REQUEST_TIMEOUT; return S_REQUEST_TIMEOUT;
} }
} }