commit 4d3a6c5297015285f88a56cc47f6a53c372b1506
parent 5d0221dd68c0d2b8796479d06b602be666d0f4c6
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(-)
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));
/*
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
+}
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 */