Add connection struct - quark - quark web server
 (HTM) git clone git://git.suckless.org/quark
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit c1b242e405d40067c282e8116d21c6f2641e4eee
 (DIR) parent 6d2fe7f29e12190d9be062852cf3f21b7f695369
 (HTM) 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:
         M http.c                              |      84 ++++++++++++++++----------------
         M http.h                              |      40 ++++++++++++++++++++++++++++----
         M main.c                              |      18 ++++++++----------
       
       3 files changed, 86 insertions(+), 56 deletions(-)
       ---
 (DIR) 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);
        }
 (DIR) 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 */
 (DIR) 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