Prepare http_send_buf() http_recv_header() for blocking I/O - quark - quark web server
 (HTM) git clone git://git.suckless.org/quark
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 4d3a6c5297015285f88a56cc47f6a53c372b1506
 (DIR) parent 5d0221dd68c0d2b8796479d06b602be666d0f4c6
 (HTM) Author: Laslo Hunhold <dev@frign.de>
       Date:   Sun,  1 Nov 2020 00:10:54 +0100
       
       Prepare http_send_buf() http_recv_header() for blocking I/O
       
       Signed-off-by: Laslo Hunhold <dev@frign.de>
       
       Diffstat:
         M http.c                              |      27 +++++++++++++++++++++++----
         A queue.c                             |     177 +++++++++++++++++++++++++++++++
         A queue.h                             |      32 +++++++++++++++++++++++++++++++
       
       3 files changed, 232 insertions(+), 4 deletions(-)
       ---
 (DIR) diff --git a/http.c b/http.c
       @@ -109,7 +109,17 @@ http_send_buf(int fd, struct buffer *buf)
        
                while (buf->len > 0) {
                        if ((r = write(fd, buf->data, buf->len)) <= 0) {
       -                        return S_REQUEST_TIMEOUT;
       +                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
       +                                /*
       +                                 * socket is blocking, return normally.
       +                                 * given the buffer still contains data,
       +                                 * this indicates to the caller that we
       +                                 * have been interrupted.
       +                                 */
       +                                return 0;
       +                        } else {
       +                                return S_REQUEST_TIMEOUT;
       +                        }
                        }
                        memmove(buf->data, buf->data + r, buf->len - r);
                        buf->len -= r;
       @@ -145,8 +155,17 @@ http_recv_header(int fd, struct buffer *buf, int *done)
                while (1) {
                        if ((r = read(fd, buf->data + buf->len,
                                      sizeof(buf->data) - buf->len)) <= 0) {
       -                        s = S_REQUEST_TIMEOUT;
       -                        goto err;
       +                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
       +                                /*
       +                                 * socket is drained, return normally,
       +                                 * but set done to zero
       +                                 */
       +                                *done = 0;
       +                                return 0;
       +                        } else {
       +                                s = S_REQUEST_TIMEOUT;
       +                                goto err;
       +                        }
                        }
                        buf->len += r;
        
       @@ -181,7 +200,7 @@ http_parse_header(const char *h, struct request *req)
                const char *p, *q;
                char *m, *n;
        
       -        /* empty all fields */
       +        /* empty the request struct */
                memset(req, 0, sizeof(*req));
        
                /*
 (DIR) diff --git a/queue.c b/queue.c
       @@ -0,0 +1,177 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <stddef.h>
       +
       +#ifdef __linux__
       +        #include <sys/epoll.h>
       +#else
       +        #include <sys/types.h>
       +        #include <sys/event.h>
       +        #include <sys/time.h>
       +#endif
       +
       +#include "queue.h"
       +#include "util.h"
       +
       +int
       +queue_create(void)
       +{
       +        int qfd;
       +
       +        #ifdef __linux__
       +                if ((qfd = epoll_create1(0)) < 0) {
       +                        warn("epoll_create1:");
       +                }
       +        #else
       +
       +        #endif
       +
       +        return qfd;
       +}
       +
       +int
       +queue_add_fd(int qfd, int fd, enum queue_event_type t, int shared,
       +             const void *data)
       +{
       +        #ifdef __linux__
       +                struct epoll_event e;
       +
       +                /* set event flag */
       +                if (shared) {
       +                        /*
       +                         * if the fd is shared, "exclusive" is the only
       +                         * way to avoid spurious wakeups and "blocking"
       +                         * accept()'s.
       +                         */
       +                        e.events = EPOLLEXCLUSIVE;
       +                } else {
       +                        /*
       +                         * if we have the fd for ourselves (i.e. only
       +                         * within the thread), we want to be
       +                         * edge-triggered, as our logic makes sure
       +                         * that the buffers are drained when we return
       +                         * to epoll_wait()
       +                         */
       +                        e.events = EPOLLET;
       +                }
       +
       +                switch (t) {
       +                case QUEUE_EVENT_IN:
       +                        e.events |= EPOLLIN;
       +                        break;
       +                case QUEUE_EVENT_OUT:
       +                        e.events |= EPOLLOUT;
       +                        break;
       +                }
       +
       +                /* set data */
       +                if (data == NULL) {
       +                        /* the data is the fd itself */
       +                        e.data.fd = fd;
       +                } else {
       +                        /* set data pointer */
       +                        e.data.ptr = (void *)data;
       +                }
       +
       +                /* register fd in the interest list */
       +                if (epoll_ctl(qfd, EPOLL_CTL_ADD, fd, &e) < 0) {
       +                        warn("epoll_ctl:");
       +                        return 1;
       +                }
       +        #else
       +
       +        #endif
       +
       +        return 0;
       +}
       +
       +int
       +queue_mod_fd(int qfd, int fd, enum queue_event_type t, const void *data)
       +{
       +        #ifdef __linux__
       +                struct epoll_event e;
       +
       +                /* set event flag */
       +                e.events = EPOLLET;
       +                switch (t) {
       +                case QUEUE_EVENT_IN:
       +                        e.events |= EPOLLIN;
       +                        break;
       +                case QUEUE_EVENT_OUT:
       +                        e.events |= EPOLLOUT;
       +                        break;
       +                }
       +
       +                /* set data */
       +                if (data == NULL) {
       +                        /* the data is the fd itself */
       +                        e.data.fd = fd;
       +                } else {
       +                        /* set data pointer */
       +                        e.data.ptr = (void *)data;
       +                }
       +
       +                /* register fd in the interest list */
       +                if (epoll_ctl(qfd, EPOLL_CTL_MOD, fd, &e) < 0) {
       +                        warn("epoll_ctl:");
       +                        return 1;
       +                }
       +        #else
       +
       +        #endif
       +
       +        return 0;
       +}
       +
       +int
       +queue_rem_fd(int qfd, int fd)
       +{
       +        #ifdef __linux__
       +                struct epoll_event e;
       +
       +                if (epoll_ctl(qfd, EPOLL_CTL_DEL, fd, &e) < 0) {
       +                        warn("epoll_ctl:");
       +                        return 1;
       +                }
       +        #else
       +
       +        #endif
       +
       +        return 0;
       +}
       +
       +ssize_t
       +queue_wait(int qfd, queue_event *e, size_t elen)
       +{
       +        ssize_t nready;
       +
       +        #ifdef __linux__
       +                if ((nready = epoll_wait(qfd, e, elen, -1)) < 0) {
       +                        warn("epoll_wait:");
       +                        return -1;
       +                }
       +        #else
       +
       +        #endif
       +
       +        return nready;
       +}
       +
       +int
       +queue_event_get_fd(const queue_event *e)
       +{
       +        #ifdef __linux__
       +                return e->data.fd;
       +        #else
       +
       +        #endif
       +}
       +
       +void *
       +queue_event_get_ptr(const queue_event *e)
       +{
       +        #ifdef __linux__
       +                return e->data.ptr;
       +        #else
       +
       +        #endif
       +}
 (DIR) diff --git a/queue.h b/queue.h
       @@ -0,0 +1,32 @@
       +#ifndef QUEUE_H
       +#define QUEUE_H
       +
       +#include <stddef.h>
       +
       +#ifdef __linux__
       +        #include <sys/epoll.h>
       +
       +        typedef struct epoll_event queue_event;
       +#else
       +        #include <sys/types.h>
       +        #include <sys/event.h>
       +        #include <sys/time.h>
       +
       +        typedef struct kevent queue_event;
       +#endif
       +
       +enum queue_event_type {
       +        QUEUE_EVENT_IN,
       +        QUEUE_EVENT_OUT,
       +};
       +
       +int queue_create(void);
       +int queue_add_fd(int, int, enum queue_event_type, int, const void *);
       +int queue_mod_fd(int, int, enum queue_event_type, const void *);
       +int queue_rem_fd(int, int);
       +ssize_t queue_wait(int, queue_event *, size_t);
       +
       +int queue_event_get_fd(const queue_event *);
       +void *queue_event_get_ptr(const queue_event *);
       +
       +#endif /* QUEUE_H */