commit 4d3a6c5297015285f88a56cc47f6a53c372b1506
Author:     Laslo Hunhold <d...@frign.de>
AuthorDate: Sun Nov 1 00:10:54 2020 +0100
Commit:     Laslo Hunhold <d...@frign.de>
CommitDate: Sun Nov 1 00:10:54 2020 +0100

    Prepare http_send_buf() http_recv_header() for blocking I/O
    
    Signed-off-by: Laslo Hunhold <d...@frign.de>

diff --git a/http.c b/http.c
index f1e15a4..d43ceaf 100644
--- 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));
 
        /*
diff --git a/queue.c b/queue.c
new file mode 100644
index 0000000..a3b8e45
--- /dev/null
+++ 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
+}
diff --git a/queue.h b/queue.h
new file mode 100644
index 0000000..06ab167
--- /dev/null
+++ 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 */

Reply via email to