Prepare http_send_buf() http_recv_header() for blocking I/O - quark - quark web… | |
git clone git://git.suckless.org/quark | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit 4d3a6c5297015285f88a56cc47f6a53c372b1506 | |
parent 5d0221dd68c0d2b8796479d06b602be666d0f4c6 | |
Author: Laslo Hunhold <[email protected]> | |
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 <[email protected]> | |
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 */ |