quark

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

commit c1b242e405d40067c282e8116d21c6f2641e4eee
parent 6d2fe7f29e12190d9be062852cf3f21b7f695369
Author: Laslo Hunhold <dev@frign.de>
Date:   Sat, 22 Aug 2020 09:24:57 +0200

Add connection struct

This struct contains the request and response structs, represents a state
and has some utility-buffers.

Signed-off-by: Laslo Hunhold <dev@frign.de>

Diffstat:
Mhttp.c | 84++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mhttp.h | 40++++++++++++++++++++++++++++++++++++----
Mmain.c | 18++++++++----------
3 files changed, 86 insertions(+), 56 deletions(-)

diff --git a/http.c b/http.c @@ -144,7 +144,7 @@ decode(const char src[PATH_MAX], char dest[PATH_MAX]) } int -http_get_request(struct request *req) +http_get_request(int fd, struct request *req) { struct in6_addr addr; size_t hlen, i, mlen; @@ -158,8 +158,8 @@ http_get_request(struct request *req) * receive header */ for (hlen = 0; ;) { - if ((off = read(req->fd, h + hlen, sizeof(h) - hlen)) < 0) { - return http_send_status(req->fd, S_REQUEST_TIMEOUT); + if ((off = read(fd, h + hlen, sizeof(h) - hlen)) < 0) { + return http_send_status(fd, S_REQUEST_TIMEOUT); } else if (off == 0) { break; } @@ -168,13 +168,13 @@ http_get_request(struct request *req) break; } if (hlen == sizeof(h)) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } } /* remove terminating empty line */ if (hlen < 2) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } hlen -= 2; @@ -194,12 +194,12 @@ http_get_request(struct request *req) } } if (i == NUM_REQ_METHODS) { - return http_send_status(req->fd, S_METHOD_NOT_ALLOWED); + return http_send_status(fd, S_METHOD_NOT_ALLOWED); } /* a single space must follow the method */ if (h[mlen] != ' ') { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } /* basis for next step */ @@ -207,11 +207,11 @@ http_get_request(struct request *req) /* TARGET */ if (!(q = strchr(p, ' '))) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } *q = '\0'; if (q - p + 1 > PATH_MAX) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } memcpy(req->target, p, q - p + 1); decode(req->target, req->target); @@ -221,18 +221,18 @@ http_get_request(struct request *req) /* HTTP-VERSION */ if (strncmp(p, "HTTP/", sizeof("HTTP/") - 1)) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } p += sizeof("HTTP/") - 1; if (strncmp(p, "1.0", sizeof("1.0") - 1) && strncmp(p, "1.1", sizeof("1.1") - 1)) { - return http_send_status(req->fd, S_VERSION_NOT_SUPPORTED); + return http_send_status(fd, S_VERSION_NOT_SUPPORTED); } p += sizeof("1.*") - 1; /* check terminator */ if (strncmp(p, "\r\n", sizeof("\r\n") - 1)) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } /* basis for next step */ @@ -253,7 +253,7 @@ http_get_request(struct request *req) if (i == NUM_REQ_FIELDS) { /* unmatched field, skip this line */ if (!(q = strstr(p, "\r\n"))) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } p = q + (sizeof("\r\n") - 1); continue; @@ -263,7 +263,7 @@ http_get_request(struct request *req) /* a single colon must follow the field name */ if (*p != ':') { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } /* skip whitespace */ @@ -272,11 +272,11 @@ http_get_request(struct request *req) /* extract field content */ if (!(q = strstr(p, "\r\n"))) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } *q = '\0'; if (q - p + 1 > FIELD_MAX) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } memcpy(req->field[i], p, q - p + 1); @@ -296,7 +296,7 @@ http_get_request(struct request *req) if (p && (!q || p > q)) { /* port suffix must not be empty */ if (*(p + 1) == '\0') { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } *p = '\0'; } @@ -305,7 +305,7 @@ http_get_request(struct request *req) if (q) { /* brackets must be on the outside */ if (req->field[REQ_HOST][0] != '[' || *(q + 1) != '\0') { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } /* remove the right bracket */ @@ -314,7 +314,7 @@ http_get_request(struct request *req) /* validate the contained IPv6 address */ if (inet_pton(AF_INET6, p, &addr) != 1) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } /* copy it into the host field */ @@ -528,7 +528,7 @@ parse_range(const char *str, size_t size, size_t *lower, size_t *upper) #define RELPATH(x) ((!*(x) || !strcmp(x, "/")) ? "." : ((x) + 1)) enum status -http_send_response(const struct request *req, const struct server *s) +http_send_response(int fd, const struct request *req, const struct server *s) { enum status returnstatus; struct in6_addr addr; @@ -553,7 +553,7 @@ http_send_response(const struct request *req, const struct server *s) if (!regexec(&(s->vhost[i].re), req->field[REQ_HOST], 0, NULL, 0)) { if (chdir(s->vhost[i].dir) < 0) { - return http_send_status(req->fd, (errno == EACCES) ? + return http_send_status(fd, (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND); } vhostmatch = s->vhost[i].chost; @@ -561,14 +561,14 @@ http_send_response(const struct request *req, const struct server *s) } } if (i == s->vhost_len) { - return http_send_status(req->fd, S_NOT_FOUND); + return http_send_status(fd, 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(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } memcpy(realtarget, tmptarget, sizeof(realtarget)); } @@ -588,7 +588,7 @@ http_send_response(const struct request *req, const struct server *s) /* swap out target prefix */ if (esnprintf(tmptarget, sizeof(tmptarget), "%s%s", s->map[i].to, realtarget + len)) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } memcpy(realtarget, tmptarget, sizeof(realtarget)); break; @@ -597,12 +597,12 @@ http_send_response(const struct request *req, const struct server *s) /* normalize target */ if (normabspath(realtarget)) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, S_BAD_REQUEST); } /* stat the target */ if (stat(RELPATH(realtarget), &st) < 0) { - return http_send_status(req->fd, (errno == EACCES) ? + return http_send_status(fd, (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND); } @@ -610,7 +610,7 @@ http_send_response(const struct request *req, const struct server *s) /* add / to target if not present */ len = strlen(realtarget); if (len >= PATH_MAX - 2) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } if (len && realtarget[len - 1] != '/') { realtarget[len] = '/'; @@ -624,7 +624,7 @@ http_send_response(const struct request *req, const struct server *s) */ if (strstr(realtarget, "/.") && strncmp(realtarget, "/.well-known/", sizeof("/.well-known/") - 1)) { - return http_send_status(req->fd, S_FORBIDDEN); + return http_send_status(fd, S_FORBIDDEN); } /* redirect if targets differ, host is non-canonical or we prefixed */ @@ -650,7 +650,7 @@ http_send_response(const struct request *req, const struct server *s) * honor that later when we fill the "Location"-field */ if ((ipv6host = inet_pton(AF_INET6, targethost, &addr)) < 0) { - return http_send_status(req->fd, + return http_send_status(fd, S_INTERNAL_SERVER_ERROR); } @@ -662,25 +662,25 @@ http_send_response(const struct request *req, const struct server *s) targethost, ipv6host ? "]" : "", hasport ? ":" : "", hasport ? s->port : "", tmptarget)) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, 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(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } } - return http_send_header(req->fd, &res); + return http_send_header(fd, &res); } if (S_ISDIR(st.st_mode)) { /* append docindex to target */ if (esnprintf(realtarget, sizeof(realtarget), "%s%s", req->target, s->docindex)) { - return http_send_status(req->fd, S_REQUEST_TOO_LARGE); + return http_send_status(fd, S_REQUEST_TOO_LARGE); } /* stat the docindex, which must be a regular file */ @@ -689,13 +689,13 @@ http_send_response(const struct request *req, const struct server *s) /* remove index suffix and serve dir */ realtarget[strlen(realtarget) - strlen(s->docindex)] = '\0'; - return resp_dir(req->fd, RELPATH(realtarget), req); + return resp_dir(fd, RELPATH(realtarget), req); } else { /* reject */ if (!S_ISREG(st.st_mode) || errno == EACCES) { - return http_send_status(req->fd, S_FORBIDDEN); + return http_send_status(fd, S_FORBIDDEN); } else { - return http_send_status(req->fd, S_NOT_FOUND); + return http_send_status(fd, S_NOT_FOUND); } } } @@ -706,13 +706,13 @@ http_send_response(const struct request *req, const struct server *s) /* parse field */ if (!strptime(req->field[REQ_IF_MODIFIED_SINCE], "%a, %d %b %Y %T GMT", &tm)) { - return http_send_status(req->fd, S_BAD_REQUEST); + return http_send_status(fd, 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(req->fd, &res); + return http_send_header(fd, &res); } } @@ -725,13 +725,13 @@ http_send_response(const struct request *req, const struct server *s) if (esnprintf(res.field[RES_CONTENT_RANGE], sizeof(res.field[RES_CONTENT_RANGE]), "bytes */%zu", st.st_size)) { - return http_send_status(req->fd, + return http_send_status(fd, S_INTERNAL_SERVER_ERROR); } - return http_send_header(req->fd, &res); + return http_send_header(fd, &res); } else { - return http_send_status(req->fd, returnstatus); + return http_send_status(fd, returnstatus); } } @@ -746,5 +746,5 @@ http_send_response(const struct request *req, const struct server *s) } } - return resp_file(req->fd, RELPATH(realtarget), req, &st, mime, lower, upper); + return resp_file(fd, RELPATH(realtarget), req, &st, mime, lower, upper); } diff --git a/http.h b/http.h @@ -3,6 +3,7 @@ #define HTTP_H #include <limits.h> +#include <sys/stat.h> #include "util.h" @@ -27,8 +28,8 @@ enum req_method { extern const char *req_method_str[]; struct request { - int fd; - char header[HEADER_MAX]; + char header[HEADER_MAX]; /* deprecated */ + enum req_method method; char target[PATH_MAX]; char field[NUM_REQ_FIELDS][FIELD_MAX]; @@ -65,15 +66,46 @@ enum res_field { extern const char *res_field_str[]; +enum res_type { + RESTYPE_FILE, + RESTYPE_DIR, + NUM_RES_TYPES, +}; + struct response { + enum res_type type; enum status status; char field[NUM_RES_FIELDS][FIELD_MAX]; + char path[PATH_MAX]; + struct stat st; + struct { + char *mime; + size_t lower; + size_t upper; + } file; +}; + +enum conn_state { + C_VACANT, + C_RECV_HEADER, + C_SEND_HEADER, + C_SEND_DATA, + NUM_CONN_STATES, +}; + +struct connection { + enum conn_state state; + int fd; + char header[HEADER_MAX]; /* general req/res-header buffer */ + size_t off; /* general offset (header/file/dir) */ + struct request req; + struct response res; }; enum status http_send_header(int, const struct response *); enum status http_send_status(int, enum status); -int http_get_request(struct request *); -enum status http_send_response(const struct request *, +int http_get_request(int fd, struct request *); +enum status http_send_response(int fd, const struct request *, const struct server *); #endif /* HTTP_H */ diff --git a/main.c b/main.c @@ -25,22 +25,20 @@ static char *udsname; static void serve(int infd, const struct sockaddr_storage *in_sa, const struct server *s) { - struct request req; + struct connection c = { .fd = infd }; time_t t; enum status status; char inaddr[INET6_ADDRSTRLEN /* > INET_ADDRSTRLEN */]; char tstmp[21]; /* set connection timeout */ - if (sock_set_timeout(infd, 30)) { + if (sock_set_timeout(c.fd, 30)) { goto cleanup; } /* handle request */ - req.fd = infd; - - if (!(status = http_get_request(&req))) { - status = http_send_response(&req, s); + if (!(status = http_get_request(c.fd, &c.req))) { + status = http_send_response(c.fd, &c.req, s); } /* write output to log */ @@ -54,12 +52,12 @@ serve(int infd, const struct sockaddr_storage *in_sa, const struct server *s) goto cleanup; } printf("%s\t%s\t%d\t%s\t%s\n", tstmp, inaddr, status, - req.field[REQ_HOST], req.target); + c.req.field[REQ_HOST], c.req.target); cleanup: /* clean up and finish */ - shutdown(infd, SHUT_RD); - shutdown(infd, SHUT_WR); - close(infd); + shutdown(c.fd, SHUT_RD); + shutdown(c.fd, SHUT_WR); + close(c.fd); } static void