summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike <mike@okcupid.com>2014-08-14 16:24:59 -0400
committerMatt Stancliff <matt@genges.com>2015-01-05 16:39:30 -0500
commit7c4d2557c4bbc637514392cb725719790b50f677 (patch)
tree7203ac816e360be67253f6f7175580e90b8d1caa
parenta1bc89b23621df61437619eec849a6a8bcc30a03 (diff)
Add support for SO_REUSEADDR
[This introduces some new API functions.] * Adds new flag to the connection context indicating SO_REUSEADDR should be set. * Adds max number of retries constant for when connect() hits EADDRNOTAVAIL. * Adds new function, redisAsyncConnectBindWithReuse(), letting clients enable this functionality. [Removed trailing whitespace in new header lines.] Closes #264
-rw-r--r--async.c8
-rw-r--r--async.h2
-rw-r--r--hiredis.c9
-rw-r--r--hiredis.h12
-rw-r--r--net.c20
5 files changed, 49 insertions, 2 deletions
diff --git a/async.c b/async.c
index f236544..8b50de2 100644
--- a/async.c
+++ b/async.c
@@ -173,6 +173,14 @@ redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
return ac;
}
+redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
+ const char *source_addr) {
+ redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr);
+ redisAsyncContext *ac = redisAsyncInitialize(c);
+ __redisAsyncCopyError(ac);
+ return ac;
+}
+
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
redisContext *c;
redisAsyncContext *ac;
diff --git a/async.h b/async.h
index 67c75ff..59cbf46 100644
--- a/async.h
+++ b/async.h
@@ -103,6 +103,8 @@ typedef struct redisAsyncContext {
/* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
+redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
+ const char *source_addr);
redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
diff --git a/hiredis.c b/hiredis.c
index 4dfd863..be67658 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -1123,6 +1123,15 @@ redisContext *redisConnectBindNonBlock(const char *ip, int port,
return c;
}
+redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
+ const char *source_addr) {
+ redisContext *c = redisContextInit();
+ c->flags &= ~REDIS_BLOCK;
+ c->flags |= REDIS_REUSEADDR;
+ redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
+ return c;
+}
+
redisContext *redisConnectUnix(const char *path) {
redisContext *c;
diff --git a/hiredis.h b/hiredis.h
index fa3a89d..8e8aa03 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -80,6 +80,9 @@
/* Flag that is set when monitor mode is active */
#define REDIS_MONITORING 0x40
+/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
+#define REDIS_REUSEADDR 0x80
+
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
@@ -91,6 +94,10 @@
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
+/* number of times we retry to connect in the case of EADDRNOTAVAIL and
+ * SO_REUSEADDR is being used. */
+#define REDIS_CONNECT_RETRIES 10
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -177,7 +184,10 @@ typedef struct redisContext {
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);
-redisContext *redisConnectBindNonBlock(const char *ip, int port, const char *source_addr);
+redisContext *redisConnectBindNonBlock(const char *ip, int port,
+ const char *source_addr);
+redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
+ const char *source_addr);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
diff --git a/net.c b/net.c
index bdb84ce..0bea81e 100644
--- a/net.c
+++ b/net.c
@@ -256,10 +256,12 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
const struct timeval *timeout,
const char *source_addr) {
- int s, rv;
+ int s, rv, n;
char _port[6]; /* strlen("65535"); */
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
int blocking = (c->flags & REDIS_BLOCK);
+ int reuseaddr = (c->flags & REDIS_REUSEADDR);
+ int reuses = 0;
snprintf(_port, 6, "%d", port);
memset(&hints,0,sizeof(hints));
@@ -279,6 +281,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
}
}
for (p = servinfo; p != NULL; p = p->ai_next) {
+addrretry:
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
continue;
@@ -294,6 +297,15 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
__redisSetError(c,REDIS_ERR_OTHER,buf);
goto error;
}
+
+ if (reuseaddr) {
+ n = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
+ sizeof(n)) < 0) {
+ goto error;
+ }
+ }
+
for (b = bservinfo; b != NULL; b = b->ai_next) {
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
bound = 1;
@@ -314,6 +326,12 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
continue;
} else if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
+ } else if (errno == EADDRNOTAVAIL && reuseaddr) {
+ if (++reuses >= REDIS_CONNECT_RETRIES) {
+ goto error;
+ } else {
+ goto addrretry;
+ }
} else {
if (redisContextWaitReady(c,timeout) != REDIS_OK)
goto error;