Refactor range-parsing into a separate function - quark - quark web server
 (HTM) git clone git://git.suckless.org/quark
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 26c593ade1949608a4d5a8a1b70eb6b11d8dc316
 (DIR) parent 5a7994bc61ce868521fc996e46006b61d07d23c4
 (HTM) Author: Laslo Hunhold <dev@frign.de>
       Date:   Tue,  4 Aug 2020 16:31:08 +0200
       
       Refactor range-parsing into a separate function
       
       The method http_send_response() is already long enough and this
       separation of concerns both helps shorten it a bit, improves
       readability and reduces the chance of programming errors.
       
       Signed-off-by: Laslo Hunhold <dev@frign.de>
       
       Diffstat:
         M http.c                              |     203 +++++++++++++++++--------------
       
       1 file changed, 110 insertions(+), 93 deletions(-)
       ---
 (DIR) diff --git a/http.c b/http.c
       @@ -342,6 +342,109 @@ squash:
                return 0;
        }
        
       +static enum status
       +parse_range(char *s, off_t size, off_t *lower, off_t *upper)
       +{
       +        char *p, *q;
       +        const char *err;
       +
       +        /* default to the complete range */
       +        *lower = 0;
       +        *upper = size - 1;
       +
       +        /* done if no range-string is given */
       +        if (s == NULL || *s == '\0') {
       +                return 0;
       +        }
       +
       +        /* skip opening statement */
       +        if (strncmp(s, "bytes=", sizeof("bytes=") - 1)) {
       +                return S_BAD_REQUEST;
       +        }
       +        p = s + (sizeof("bytes=") - 1);
       +
       +        /* find hyphen and replace with \0 */
       +        if (!(q = strchr(p, '-'))) {
       +                return S_BAD_REQUEST;
       +        }
       +        *(q++) = '\0';
       +
       +        /*
       +         *  byte-range=first\0last...
       +         *             ^      ^
       +         *             |      |
       +         *             p      q
       +         */
       +
       +        /*
       +         * make sure we only have a single range, and not a comma
       +         * separated list, which we will refuse to accept out of spite
       +         * towards this horrible part of the spec
       +         */
       +        if (strchr(q, ',')) {
       +                return S_RANGE_NOT_SATISFIABLE;
       +        }
       +
       +        if (p[0] != '\0') {
       +                /*
       +                 * Range has format "first-last" or "first-",
       +                 * i.e. return bytes 'first' to 'last' (or the
       +                 * last byte if 'last' is not given),
       +                 * inclusively, and byte-numbering beginning at 0
       +                 */
       +                *lower = strtonum(p, 0, LLONG_MAX, &err);
       +                if (!err) {
       +                        if (q[0] != '\0') {
       +                                *upper = strtonum(q, 0, LLONG_MAX, &err);
       +                        } else {
       +                                *upper = size - 1;
       +                        }
       +                }
       +                if (err) {
       +                        /* one of the strtonum()'s failed */
       +                        return S_BAD_REQUEST;
       +                }
       +
       +                /* check ranges */
       +                if (*lower > *upper || *lower >= size) {
       +                        return S_RANGE_NOT_SATISFIABLE;
       +                }
       +
       +                /* adjust upper limit to be at most the last byte */
       +                *upper = MIN(*upper, size - 1);
       +        } else {
       +                /*
       +                 * Range has format "-num", i.e. return the 'num'
       +                 * last bytes
       +                 */
       +
       +                /*
       +                 * use upper as a temporary storage for 'num',
       +                 * as we know 'upper' is size - 1
       +                 */
       +                *upper = strtonum(q, 0, LLONG_MAX, &err);
       +                if (err) {
       +                        return S_BAD_REQUEST;
       +                }
       +
       +                /* determine lower */
       +                if (*upper > size) {
       +                        /* more bytes requested than we have */
       +                        *lower = 0;
       +                } else {
       +                        *lower = size - *upper;
       +                }
       +
       +                /* set upper to the correct value */
       +                *upper = size - 1;
       +        }
       +
       +        /* reset \0 to the hyphen */
       +        *(q - 1) = '-';
       +
       +        return 0;
       +}
       +
        #undef RELPATH
        #define RELPATH(x) ((!*(x) || !strcmp(x, "/")) ? "." : ((x) + 1))
        
       @@ -355,8 +458,8 @@ http_send_response(int fd, struct request *r)
                off_t lower, upper;
                int hasport, ipv6host;
                static char realtarget[PATH_MAX], tmptarget[PATH_MAX], t[TIMESTAMP_LEN];
       -        char *p, *q, *mime;
       -        const char *vhostmatch, *targethost, *err;
       +        char *p, *mime;
       +        const char *vhostmatch, *targethost;
        
                /* make a working copy of the target */
                memcpy(realtarget, r->target, sizeof(realtarget));
       @@ -546,96 +649,8 @@ http_send_response(int fd, struct request *r)
                }
        
                /* range */
       -        lower = 0;
       -        upper = st.st_size - 1;
       -        if (r->field[REQ_RANGE][0]) {
       -                /* parse field */
       -                p = r->field[REQ_RANGE];
       -                err = NULL;
       -
       -                if (strncmp(p, "bytes=", sizeof("bytes=") - 1)) {
       -                        return http_send_status(fd, S_BAD_REQUEST);
       -                }
       -                p += sizeof("bytes=") - 1;
       -
       -                if (!(q = strchr(p, '-'))) {
       -                        return http_send_status(fd, S_BAD_REQUEST);
       -                }
       -                *(q++) = '\0';
       -
       -                /*
       -                 *  byte-range=first\0last...
       -                 *             ^      ^
       -                 *             |      |
       -                 *             p      q
       -                 */
       -
       -                /*
       -                 * make sure we only have a single range,
       -                 * and not a comma separated list, which we
       -                 * will refuse to accept out of spite towards
       -                 * this horrible part of the spec
       -                 */
       -                if (strchr(q, ',')) {
       -                        goto not_satisfiable;
       -                }
       -
       -                if (p[0] != '\0') {
       -                        /*
       -                         * Range has format "first-last" or "first-",
       -                         * i.e. return bytes 'first' to 'last' (or the
       -                         * last byte if 'last' is not given),
       -                         * inclusively, and byte-numbering beginning at 0
       -                         */
       -                        lower = strtonum(p, 0, LLONG_MAX, &err);
       -                        if (!err) {
       -                                if (q[0] != '\0') {
       -                                        upper = strtonum(q, 0, LLONG_MAX,
       -                                                         &err);
       -                                } else {
       -                                        upper = st.st_size - 1;
       -                                }
       -                        }
       -                        if (err) {
       -                                /* one of the strtonum()'s failed */
       -                                return http_send_status(fd, S_BAD_REQUEST);
       -                        }
       -
       -                        /* check ranges */
       -                        if (lower > upper || lower >= st.st_size) {
       -                                goto not_satisfiable;
       -                        }
       -
       -                        /* adjust upper limit to be at most the last byte */
       -                        upper = MIN(upper, st.st_size - 1);
       -                } else {
       -                        /*
       -                         * Range has format "-num", i.e. return the 'num'
       -                         * last bytes
       -                         */
       -
       -                        /*
       -                         * use upper as a temporary storage for 'num',
       -                         * as we know 'upper' is st.st_size - 1
       -                         */
       -                        upper = strtonum(q, 0, LLONG_MAX, &err);
       -                        if (err) {
       -                                return http_send_status(fd, S_BAD_REQUEST);
       -                        }
       -
       -                        /* determine lower */
       -                        if (upper > st.st_size) {
       -                                /* more bytes requested than we have */
       -                                lower = 0;
       -                        } else {
       -                                lower = st.st_size - upper;
       -                        }
       -
       -                        /* set upper to the correct value */
       -                        upper = st.st_size - 1;
       -                }
       -                goto satisfiable;
       -not_satisfiable:
       +        switch (parse_range(r->field[REQ_RANGE], st.st_size, &lower, &upper)) {
       +        case S_RANGE_NOT_SATISFIABLE:
                        if (dprintf(fd,
                                    "HTTP/1.1 %d %s\r\n"
                                    "Date: %s\r\n"
       @@ -649,7 +664,9 @@ not_satisfiable:
                                return S_REQUEST_TIMEOUT;
                        }
                        return S_RANGE_NOT_SATISFIABLE;
       -satisfiable:
       +        case S_BAD_REQUEST:
       +                return http_send_status(fd, S_BAD_REQUEST);
       +        default:
                        ;
                }