summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Dominik Braun <PromyLOPh@lavabit.com>2009-05-15 16:36:15 +0200
committerLars-Dominik Braun <PromyLOPh@lavabit.com>2009-05-15 16:36:15 +0200
commit1a6b21e0c79e6abe20708b6ce7a8bd37b8b6c188 (patch)
treecfababfac2868d5d421ab003951d5429e31504d8
parent363bb5d195e0c74238af947c009e5efcb89d0cab (diff)
downloadpianobar-1a6b21e0c79e6abe20708b6ce7a8bd37b8b6c188.tar.gz
pianobar-1a6b21e0c79e6abe20708b6ce7a8bd37b8b6c188.tar.bz2
pianobar-1a6b21e0c79e6abe20708b6ce7a8bd37b8b6c188.zip
waitress: Add timeouts
read() and write() may time out now. Implemented using non-blocking sockets and poll (). Default timeout: 30 seconds.
-rw-r--r--libwaitress/src/main.c125
-rw-r--r--libwaitress/src/waitress.h3
2 files changed, 109 insertions, 19 deletions
diff --git a/libwaitress/src/main.c b/libwaitress/src/main.c
index f9ec460..43721f0 100644
--- a/libwaitress/src/main.c
+++ b/libwaitress/src/main.c
@@ -29,6 +29,8 @@ THE SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
+#include <fcntl.h>
+#include <poll.h>
#include "config.h"
#include "waitress.h"
@@ -41,6 +43,7 @@ typedef struct {
inline void WaitressInit (WaitressHandle_t *waith) {
memset (waith, 0, sizeof (*waith));
+ waith->socktimeout = 30000;
}
inline void WaitressFree (WaitressHandle_t *waith) {
@@ -145,11 +148,21 @@ char WaitressSplitUrl (const char *url, char *retHost, size_t retHostSize,
return 1;
}
+/* Parse url and set host, port, path
+ * @param Waitress handle
+ * @param url: protocol://host:port/path
+ */
inline char WaitressSetUrl (WaitressHandle_t *waith, const char *url) {
return WaitressSplitUrl (url, waith->host, sizeof (waith->host),
waith->port, sizeof (waith->port), waith->path, sizeof (waith->path));
}
+/* Set host, port, path
+ * @param Waitress handle
+ * @param host
+ * @param port (getaddrinfo () needs a string...)
+ * @param path, including leading /
+ */
inline void WaitressSetHPP (WaitressHandle_t *waith, const char *host,
const char *port, const char *path) {
strncpy (waith->host, host, sizeof (waith->host)-1);
@@ -198,14 +211,72 @@ WaitressReturn_t WaitressFetchBuf (WaitressHandle_t *waith, char *buf,
return WaitressFetchCall (waith);
}
+/* write () wrapper with poll () timeout
+ * @param socket fd
+ * @param write buffer
+ * @param write count bytes
+ * @param reuse existing pollfd structure
+ * @param timeout (microseconds)
+ * @return WAITRESS_RET_OK, WAITRESS_RET_TIMEOUT or WAITRESS_RET_ERR
+ */
+WaitressReturn_t WaitressPollWrite (int sockfd, const char *buf, size_t count,
+ struct pollfd *sockpoll, int timeout) {
+ int pollres = -1;
+
+ sockpoll->events = POLLOUT;
+ pollres = poll (sockpoll, 1, timeout);
+ if (pollres == 0) {
+ return WAITRESS_RET_TIMEOUT;
+ } else if (pollres == -1) {
+ return WAITRESS_RET_ERR;
+ }
+ if (write (sockfd, buf, count) == -1) {
+ return WAITRESS_RET_ERR;
+ }
+ return WAITRESS_RET_OK;
+}
+
+/* read () wrapper with poll () timeout
+ * @param socket fd
+ * @param write to this buf, not NULL terminated
+ * @param buffer size
+ * @param reuse existing pollfd struct
+ * @param timeout (in microseconds)
+ * @param read () return value/written bytes
+ * @return WAITRESS_RET_OK, WAITRESS_RET_TIMEOUT, WAITRESS_RET_ERR
+ */
+WaitressReturn_t WaitressPollRead (int sockfd, char *buf, size_t count,
+ struct pollfd *sockpoll, int timeout, ssize_t *retSize) {
+ int pollres = -1;
+
+ sockpoll->events = POLLIN;
+ pollres = poll (sockpoll, 1, timeout);
+ if (pollres == 0) {
+ return WAITRESS_RET_TIMEOUT;
+ } else if (pollres == -1) {
+ return WAITRESS_RET_ERR;
+ }
+ if ((*retSize = read (sockfd, buf, count)) == -1) {
+ return WAITRESS_RET_ERR;
+ }
+ return WAITRESS_RET_OK;
+}
+
+/* FIXME: compiler macros are ugly... */
#define CLOSE_RET(ret) close (sockfd); return ret;
+#define WRITE_RET(buf, count) \
+ if ((wRet = WaitressPollWrite (sockfd, buf, count, \
+ &sockpoll, waith->socktimeout)) != WAITRESS_RET_OK) { \
+ CLOSE_RET (wRet); \
+ }
+#define READ_RET(buf, count, size) \
+ if ((wRet = WaitressPollRead (sockfd, buf, count, \
+ &sockpoll, waith->socktimeout, size)) != WAITRESS_RET_OK) { \
+ CLOSE_RET (wRet); \
+ }
/* Receive data from host and call *callback ()
* @param waitress handle
- * @param host
- * @param port
- * @param absolute path
- * @param callback function
* @return WaitressReturn_t
*/
WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
@@ -216,6 +287,8 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
char writeBuf[2*1024];
ssize_t recvSize = 0;
WaitressReturn_t wRet = WAITRESS_RET_OK;
+ struct pollfd sockpoll;
+ int pollres;
/* initialize */
waith->contentLength = 0;
@@ -240,13 +313,27 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
freeaddrinfo (res);
return WAITRESS_RET_SOCK_ERR;
}
+ sockpoll.fd = sockfd;
- if (connect (sockfd, res->ai_addr, res->ai_addrlen) == -1) {
- freeaddrinfo (res);
- return WAITRESS_RET_CONNECT_REFUSED;
- }
+ fcntl (sockfd, F_SETFL, O_NONBLOCK);
+
+ /* non-blocking connect will return immediately */
+ connect (sockfd, res->ai_addr, res->ai_addrlen);
+ sockpoll.events = POLLOUT;
+ pollres = poll (&sockpoll, 1, waith->socktimeout);
freeaddrinfo (res);
+ if (pollres == 0) {
+ return WAITRESS_RET_TIMEOUT;
+ } else if (pollres == -1) {
+ return WAITRESS_RET_ERR;
+ }
+ /* check connect () return value */
+ socklen_t pollresSize = sizeof (pollres);
+ getsockopt (sockfd, SOL_SOCKET, SO_ERROR, &pollres, &pollresSize);
+ if (pollres != 0) {
+ return WAITRESS_RET_CONNECT_REFUSED;
+ }
/* send request */
if (WaitressProxyEnabled (waith)) {
@@ -260,33 +347,33 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
(waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"),
waith->path);
}
- write (sockfd, writeBuf, strlen (writeBuf));
+ WRITE_RET (writeBuf, strlen (writeBuf));
snprintf (writeBuf, sizeof (writeBuf),
"Host: %s\r\nUser-Agent: " PACKAGE "\r\n", waith->host);
- write (sockfd, writeBuf, strlen (writeBuf));
+ WRITE_RET (writeBuf, strlen (writeBuf));
if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) {
snprintf (writeBuf, sizeof (writeBuf), "Content-Length: %u\r\n",
strlen (waith->postData));
- write (sockfd, writeBuf, strlen (writeBuf));
+ WRITE_RET (writeBuf, strlen (writeBuf));
}
if (waith->extraHeaders != NULL) {
- write (sockfd, waith->extraHeaders, strlen (waith->extraHeaders));
+ WRITE_RET (waith->extraHeaders, strlen (waith->extraHeaders));
}
- write (sockfd, "\r\n", 2);
+ WRITE_RET ("\r\n", 2);
if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) {
- write (sockfd, waith->postData, strlen (waith->postData));
+ WRITE_RET (waith->postData, strlen (waith->postData));
}
/* throw "HTTP/1.1 " (length 9) away... */
- read (sockfd, recvBuf, 9);
+ READ_RET (recvBuf, 9, &recvSize);
/* parse status code */
- read (sockfd, statusBuf, sizeof (statusBuf));
+ READ_RET (statusBuf, sizeof (statusBuf), &recvSize);
switch (statusBuf[0]) {
case '2':
if (statusBuf[1] == '0' &&
@@ -331,7 +418,7 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
/* read and parse header, max sizeof(recvBuf) */
char *bufPos = recvBuf, *hdrMark = NULL, *hdrBegin = recvBuf;
- recvSize = read (sockfd, recvBuf, sizeof (recvBuf));
+ READ_RET (recvBuf, sizeof (recvBuf), &recvSize);
while (bufPos - recvBuf < recvSize) {
/* mark header name end */
@@ -376,7 +463,7 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
/* receive content */
do {
- recvSize = read (sockfd, recvBuf, sizeof (recvBuf));
+ READ_RET (recvBuf, sizeof (recvBuf), &recvSize);
if (recvSize > 0) {
waith->contentReceived += recvSize;
if (!waith->callback (recvBuf, recvSize, waith->data)) {
@@ -395,4 +482,6 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
}
#undef CLOSE_RET
+#undef WRITE_RET
+#undef READ_RET
diff --git a/libwaitress/src/waitress.h b/libwaitress/src/waitress.h
index cc999b3..b05d786 100644
--- a/libwaitress/src/waitress.h
+++ b/libwaitress/src/waitress.h
@@ -48,13 +48,14 @@ typedef struct {
/* extra data handed over to callback function */
void *data;
char (*callback) (void *, size_t, void *);
+ int socktimeout;
} WaitressHandle_t;
typedef enum {WAITRESS_RET_ERR = 0, WAITRESS_RET_OK, WAITRESS_RET_STATUS_UNKNOWN,
WAITRESS_RET_NOTFOUND, WAITRESS_RET_FORBIDDEN, WAITRESS_RET_CONNECT_REFUSED,
WAITRESS_RET_SOCK_ERR, WAITRESS_RET_GETADDR_ERR,
WAITRESS_RET_CB_ABORT, WAITRESS_RET_HDR_OVERFLOW,
- WAITRESS_RET_PARTIAL_FILE} WaitressReturn_t;
+ WAITRESS_RET_PARTIAL_FILE, WAITRESS_RET_TIMEOUT} WaitressReturn_t;
void WaitressInit (WaitressHandle_t *);
void WaitressFree (WaitressHandle_t *);