diff --git a/hiredis.c b/hiredis.c index 970b45a..ac6dc67 100644 --- a/hiredis.c +++ b/hiredis.c @@ -851,6 +851,13 @@ redisContext *redisConnectUnixNonBlock(const char *path) { return c; } +/* Set read/write timeout on a blocking socket. */ +int redisSetTimeout(redisContext *c, struct timeval tv) { + if (c->flags & REDIS_BLOCK) + return redisContextSetTimeout(c,tv); + return REDIS_ERR; +} + /* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader * was already initialized and the function set could not be re-set. * Return REDIS_OK when they could be set. */ @@ -878,7 +885,7 @@ int redisBufferRead(redisContext *c) { char buf[2048]; int nread = read(c->fd,buf,sizeof(buf)); if (nread == -1) { - if (errno == EAGAIN) { + if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) { /* Try again later */ } else { __redisSetError(c,REDIS_ERR_IO,NULL); @@ -909,7 +916,7 @@ int redisBufferWrite(redisContext *c, int *done) { if (sdslen(c->obuf) > 0) { nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); if (nwritten == -1) { - if (errno == EAGAIN) { + if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) { /* Try again later */ } else { __redisSetError(c,REDIS_ERR_IO,NULL); diff --git a/hiredis.h b/hiredis.h index 8c26a8f..8fdca4a 100644 --- a/hiredis.h +++ b/hiredis.h @@ -33,6 +33,7 @@ #define __HIREDIS_H #include /* for size_t */ #include /* for va_list */ +#include /* for struct timeval */ #define HIREDIS_MAJOR 0 #define HIREDIS_MINOR 9 @@ -146,6 +147,7 @@ redisContext *redisConnect(const char *ip, int port); redisContext *redisConnectNonBlock(const char *ip, int port); redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnixNonBlock(const char *path); +int redisSetTimeout(redisContext *c, struct timeval tv); int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn); void redisFree(redisContext *c); int redisBufferRead(redisContext *c); diff --git a/net.c b/net.c index d4a92ab..75cbe54 100644 --- a/net.c +++ b/net.c @@ -98,6 +98,20 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) { return REDIS_OK; } +int redisContextSetTimeout(redisContext *c, struct timeval tv) { + if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { + __redisSetError(c,REDIS_ERR_IO, + sdscatprintf(sdsempty(), "setsockopt(SO_RCVTIMEO): %s", strerror(errno))); + return REDIS_ERR; + } + if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { + __redisSetError(c,REDIS_ERR_IO, + sdscatprintf(sdsempty(), "setsockopt(SO_SNDTIMEO): %s", strerror(errno))); + return REDIS_ERR; + } + return REDIS_OK; +} + int redisContextConnectTcp(redisContext *c, const char *addr, int port) { int s; int blocking = (c->flags & REDIS_BLOCK); diff --git a/net.h b/net.h index 00ae32e..9d15fd2 100644 --- a/net.h +++ b/net.h @@ -39,6 +39,7 @@ #define AF_LOCAL AF_UNIX #endif +int redisContextSetTimeout(redisContext *c, struct timeval tv); int redisContextConnectTcp(redisContext *c, const char *addr, int port); int redisContextConnectUnix(redisContext *c, const char *path); diff --git a/test.c b/test.c index ed355a7..9ab0354 100644 --- a/test.c +++ b/test.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "hiredis.h" @@ -246,9 +247,17 @@ static void test_blocking_connection() { * conditions, the error will be set to EOF. */ assert(c->err == REDIS_ERR_EOF && strcmp(c->errstr,"Server closed the connection") == 0); - - /* Clean up context and reconnect again */ redisFree(c); + + __connect(&c); + test("Returns I/O error on socket timeout: "); + struct timeval tv = { 0, 1000 }; + assert(redisSetTimeout(c,tv) == REDIS_OK); + test_cond(redisGetReply(c,(void**)&reply) == REDIS_ERR && + c->err == REDIS_ERR_IO && errno == EAGAIN); + redisFree(c); + + /* Context should be connected */ __connect(&c); }