summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deps/hiredis/.gitignore2
-rw-r--r--deps/hiredis/.travis.yml6
-rw-r--r--deps/hiredis/CHANGELOG.md8
-rw-r--r--deps/hiredis/Makefile66
-rw-r--r--deps/hiredis/README.md9
-rw-r--r--deps/hiredis/adapters/libuv.h121
-rw-r--r--deps/hiredis/async.c59
-rw-r--r--deps/hiredis/async.h1
-rw-r--r--deps/hiredis/examples/example-ae.c (renamed from deps/hiredis/example-ae.c)14
-rw-r--r--deps/hiredis/examples/example-libev.c (renamed from deps/hiredis/example-libev.c)7
-rw-r--r--deps/hiredis/examples/example-libevent.c (renamed from deps/hiredis/example-libevent.c)7
-rw-r--r--deps/hiredis/examples/example-libuv.c53
-rw-r--r--deps/hiredis/examples/example.c (renamed from deps/hiredis/example.c)22
-rw-r--r--deps/hiredis/fmacros.h4
-rw-r--r--deps/hiredis/hiredis.c99
-rw-r--r--deps/hiredis/hiredis.h20
-rw-r--r--deps/hiredis/net.c166
-rw-r--r--deps/hiredis/net.h12
-rw-r--r--deps/hiredis/sds.c286
-rw-r--r--deps/hiredis/sds.h1
-rw-r--r--deps/hiredis/test.c111
-rw-r--r--src/cluster.c3
-rw-r--r--src/redis.h3
-rw-r--r--src/sds.c2
-rw-r--r--src/sentinel.c4
25 files changed, 914 insertions, 172 deletions
diff --git a/deps/hiredis/.gitignore b/deps/hiredis/.gitignore
index 1a4d60d28..0c166a02e 100644
--- a/deps/hiredis/.gitignore
+++ b/deps/hiredis/.gitignore
@@ -1,5 +1,5 @@
/hiredis-test
-/hiredis-example*
+/examples/hiredis-example*
/*.o
/*.so
/*.dylib
diff --git a/deps/hiredis/.travis.yml b/deps/hiredis/.travis.yml
new file mode 100644
index 000000000..030427ff4
--- /dev/null
+++ b/deps/hiredis/.travis.yml
@@ -0,0 +1,6 @@
+language: c
+compiler:
+ - gcc
+ - clang
+
+script: make && make check
diff --git a/deps/hiredis/CHANGELOG.md b/deps/hiredis/CHANGELOG.md
index d41db8a60..268b15cd5 100644
--- a/deps/hiredis/CHANGELOG.md
+++ b/deps/hiredis/CHANGELOG.md
@@ -1,3 +1,11 @@
+### 0.11.0
+
+* Increase the maximum multi-bulk reply depth to 7.
+
+* Increase the read buffer size from 2k to 16k.
+
+* Use poll(2) instead of select(2) to support large fds (>= 1024).
+
### 0.10.1
* Makefile overhaul. Important to check out if you override one or more
diff --git a/deps/hiredis/Makefile b/deps/hiredis/Makefile
index 16b8767b1..ddcc4e4f6 100644
--- a/deps/hiredis/Makefile
+++ b/deps/hiredis/Makefile
@@ -4,11 +4,24 @@
# This file is released under the BSD license, see the COPYING file
OBJ=net.o hiredis.o sds.o async.o
-BINS=hiredis-example hiredis-test
+EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev
+TESTS=hiredis-test
LIBNAME=libhiredis
HIREDIS_MAJOR=0
-HIREDIS_MINOR=10
+HIREDIS_MINOR=11
+
+# redis-server configuration used for testing
+REDIS_PORT=56379
+REDIS_SERVER=redis-server
+define REDIS_TEST_CONFIG
+ daemonize yes
+ pidfile /tmp/hiredis-test-redis.pid
+ port $(REDIS_PORT)
+ bind 127.0.0.1
+ unixsocket /tmp/hiredis-test-redis.sock
+endef
+export REDIS_TEST_CONFIG
# Fallback to gcc when $CC is not in $PATH.
CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
@@ -41,12 +54,11 @@ ifeq ($(uname_S),Darwin)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
endif
-all: $(DYLIBNAME) $(BINS)
+all: $(DYLIBNAME)
# Deps (use make dep to generate this)
net.o: net.c fmacros.h net.h hiredis.h
async.o: async.c async.h hiredis.h sds.h dict.c dict.h
-example.o: example.c hiredis.h
hiredis.o: hiredis.c fmacros.h hiredis.h net.h sds.h
sds.o: sds.c sds.h
test.o: test.c hiredis.h
@@ -61,36 +73,44 @@ dynamic: $(DYLIBNAME)
static: $(STLIBNAME)
# Binaries:
-hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
- $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)
+hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
+ $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME)
-hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
- $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)
+hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
+ $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME)
ifndef AE_DIR
hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@false
else
-hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
- $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
+hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME)
+ $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)
endif
-hiredis-%: %.o $(STLIBNAME)
+ifndef LIBUV_DIR
+hiredis-example-libuv:
+ @echo "Please specify LIBUV_DIR (e.g. ../libuv/)"
+ @false
+else
+hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
+ $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread $(STLIBNAME)
+endif
+
+hiredis-example: examples/example.c $(STLIBNAME)
+ $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME)
+
+examples: $(EXAMPLES)
+
+hiredis-test: test.o $(STLIBNAME)
$(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
test: hiredis-test
./hiredis-test
check: hiredis-test
- echo \
- "daemonize yes\n" \
- "pidfile /tmp/hiredis-test-redis.pid\n" \
- "port 56379\n" \
- "bind 127.0.0.1\n" \
- "unixsocket /tmp/hiredis-test-redis.sock" \
- | redis-server -
- ./hiredis-test -h 127.0.0.1 -p 56379 -s /tmp/hiredis-test-redis.sock || \
+ @echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) -
+ ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -s /tmp/hiredis-test-redis.sock || \
( kill `cat /tmp/hiredis-test-redis.pid` && false )
kill `cat /tmp/hiredis-test-redis.pid`
@@ -98,17 +118,15 @@ check: hiredis-test
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
clean:
- rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
+ rm -rf $(DYLIBNAME) $(STLIBNAME) $(TESTS) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
dep:
$(CC) -MM *.c
# Installation related variables and target
PREFIX?=/usr/local
-INCLUDE_PATH?=include/hiredis
-LIBRARY_PATH?=lib
-INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
-INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
+INSTALL_INCLUDE_PATH= $(PREFIX)/include/hiredis
+INSTALL_LIBRARY_PATH= $(PREFIX)/lib
ifeq ($(uname_S),SunOS)
INSTALL?= cp -r
diff --git a/deps/hiredis/README.md b/deps/hiredis/README.md
index 62fe1067b..dba4a8c8e 100644
--- a/deps/hiredis/README.md
+++ b/deps/hiredis/README.md
@@ -1,3 +1,5 @@
+[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)
+
# HIREDIS
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.
@@ -44,7 +46,7 @@ After trying to connect to Redis using `redisConnect` you should
check the `err` field to see if establishing the connection was successful:
redisContext *c = redisConnect("127.0.0.1", 6379);
- if (c->err) {
+ if (c != NULL && c->err) {
printf("Error: %s\n", c->errstr);
// handle error
}
@@ -66,7 +68,7 @@ When you need to pass binary safe strings in a command, the `%b` specifier can b
used. Together with a pointer to the string, it requires a `size_t` length argument
of the string:
- reply = redisCommand(context, "SET foo %b", value, valuelen);
+ reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
Internally, Hiredis splits the command in different arguments and will
convert it to the protocol used to communicate with Redis.
@@ -337,6 +339,9 @@ and a reply object (as described above) via `void **reply`. The returned status
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went
wrong (either a protocol error, or an out of memory error).
+The parser limits the level of nesting for multi bulk payloads to 7. If the
+multi bulk nesting level is higher than this, the parser returns an error.
+
### Customizing replies
The function `redisReaderGetReply` creates `redisReply` and makes the function
diff --git a/deps/hiredis/adapters/libuv.h b/deps/hiredis/adapters/libuv.h
new file mode 100644
index 000000000..a1967f4fd
--- /dev/null
+++ b/deps/hiredis/adapters/libuv.h
@@ -0,0 +1,121 @@
+#ifndef __HIREDIS_LIBUV_H__
+#define __HIREDIS_LIBUV_H__
+#include <uv.h>
+#include "../hiredis.h"
+#include "../async.h"
+#include <string.h>
+
+typedef struct redisLibuvEvents {
+ redisAsyncContext* context;
+ uv_poll_t handle;
+ int events;
+} redisLibuvEvents;
+
+int redisLibuvAttach(redisAsyncContext*, uv_loop_t*);
+
+static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
+ redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+
+ if (status != 0) {
+ return;
+ }
+
+ if (events & UV_READABLE) {
+ redisAsyncHandleRead(p->context);
+ }
+ if (events & UV_WRITABLE) {
+ redisAsyncHandleWrite(p->context);
+ }
+}
+
+
+static void redisLibuvAddRead(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events |= UV_READABLE;
+
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelRead(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events &= ~UV_READABLE;
+
+ if (p->events) {
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+ } else {
+ uv_poll_stop(&p->handle);
+ }
+}
+
+
+static void redisLibuvAddWrite(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events |= UV_WRITABLE;
+
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelWrite(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events &= ~UV_WRITABLE;
+
+ if (p->events) {
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+ } else {
+ uv_poll_stop(&p->handle);
+ }
+}
+
+
+static void on_close(uv_handle_t* handle) {
+ redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+
+ free(p);
+}
+
+
+static void redisLibuvCleanup(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ uv_close((uv_handle_t*)&p->handle, on_close);
+}
+
+
+static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
+ redisContext *c = &(ac->c);
+
+ if (ac->ev.data != NULL) {
+ return REDIS_ERR;
+ }
+
+ ac->ev.addRead = redisLibuvAddRead;
+ ac->ev.delRead = redisLibuvDelRead;
+ ac->ev.addWrite = redisLibuvAddWrite;
+ ac->ev.delWrite = redisLibuvDelWrite;
+ ac->ev.cleanup = redisLibuvCleanup;
+
+ redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
+
+ if (!p) {
+ return REDIS_ERR;
+ }
+
+ memset(p, 0, sizeof(*p));
+
+ if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
+ return REDIS_ERR;
+ }
+
+ ac->ev.data = p;
+ p->handle.data = p;
+ p->context = ac;
+
+ return REDIS_OK;
+}
+#endif
diff --git a/deps/hiredis/async.c b/deps/hiredis/async.c
index f65f8694c..f7f343bef 100644
--- a/deps/hiredis/async.c
+++ b/deps/hiredis/async.c
@@ -62,7 +62,8 @@ void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
/* Functions managing dictionary of callbacks for pub/sub. */
static unsigned int callbackHash(const void *key) {
- return dictGenHashFunction((unsigned char*)key,sdslen((char*)key));
+ return dictGenHashFunction((const unsigned char *)key,
+ sdslen((const sds)key));
}
static void *callbackValDup(void *privdata, const void *src) {
@@ -76,8 +77,8 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
int l1, l2;
((void) privdata);
- l1 = sdslen((sds)key1);
- l2 = sdslen((sds)key2);
+ l1 = sdslen((const sds)key1);
+ l2 = sdslen((const sds)key2);
if (l1 != l2) return 0;
return memcmp(key1,key2,l1) == 0;
}
@@ -102,7 +103,12 @@ static dictType callbackDict = {
};
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
- redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
+ redisAsyncContext *ac;
+
+ ac = realloc(c,sizeof(redisAsyncContext));
+ if (ac == NULL)
+ return NULL;
+
c = &(ac->c);
/* The regular connect functions will always set the flag REDIS_CONNECTED.
@@ -142,15 +148,45 @@ static void __redisAsyncCopyError(redisAsyncContext *ac) {
}
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
- redisContext *c = redisConnectNonBlock(ip,port);
+ redisContext *c;
+ redisAsyncContext *ac;
+
+ c = redisConnectNonBlock(ip,port);
+ if (c == NULL)
+ return NULL;
+
+ ac = redisAsyncInitialize(c);
+ if (ac == NULL) {
+ redisFree(c);
+ return NULL;
+ }
+
+ __redisAsyncCopyError(ac);
+ return ac;
+}
+
+redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
+ const char *source_addr) {
+ redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
redisAsyncContext *ac = redisAsyncInitialize(c);
__redisAsyncCopyError(ac);
return ac;
}
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
- redisContext *c = redisConnectUnixNonBlock(path);
- redisAsyncContext *ac = redisAsyncInitialize(c);
+ redisContext *c;
+ redisAsyncContext *ac;
+
+ c = redisConnectUnixNonBlock(path);
+ if (c == NULL)
+ return NULL;
+
+ ac = redisAsyncInitialize(c);
+ if (ac == NULL) {
+ redisFree(c);
+ return NULL;
+ }
+
__redisAsyncCopyError(ac);
return ac;
}
@@ -182,6 +218,9 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
/* Copy callback from stack to heap */
cb = malloc(sizeof(*cb));
+ if (cb == NULL)
+ return REDIS_ERR_OOM;
+
if (source != NULL) {
memcpy(cb,source,sizeof(*cb));
cb->next = NULL;
@@ -360,7 +399,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
void redisProcessCallbacks(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
- redisCallback cb;
+ redisCallback cb = {NULL, NULL, NULL};
void *reply = NULL;
int status;
@@ -372,7 +411,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
__redisAsyncDisconnect(ac);
return;
}
-
+
/* If monitor mode, repush callback */
if(c->flags & REDIS_MONITORING) {
__redisPushCallback(&ac->replies,&cb);
@@ -442,7 +481,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
- if (redisCheckSocketError(c,c->fd) == REDIS_ERR) {
+ if (redisCheckSocketError(c) == REDIS_ERR) {
/* Try again later when connect(2) is still in progress. */
if (errno == EINPROGRESS)
return REDIS_OK;
diff --git a/deps/hiredis/async.h b/deps/hiredis/async.h
index 268274e8e..8a2cf1ecd 100644
--- a/deps/hiredis/async.h
+++ b/deps/hiredis/async.h
@@ -102,6 +102,7 @@ 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 *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
diff --git a/deps/hiredis/example-ae.c b/deps/hiredis/examples/example-ae.c
index 5ed34a3a6..8efa7306a 100644
--- a/deps/hiredis/example-ae.c
+++ b/deps/hiredis/examples/example-ae.c
@@ -2,9 +2,10 @@
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-#include "hiredis.h"
-#include "async.h"
-#include "adapters/ae.h"
+
+#include <hiredis.h>
+#include <async.h>
+#include <adapters/ae.h>
/* Put event loop in the global scope, so it can be explicitly stopped */
static aeEventLoop *loop;
@@ -21,17 +22,22 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
+ aeStop(loop);
return;
}
+
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
+ aeStop(loop);
return;
}
+
printf("Disconnected...\n");
+ aeStop(loop);
}
int main (int argc, char **argv) {
@@ -44,7 +50,7 @@ int main (int argc, char **argv) {
return 1;
}
- loop = aeCreateEventLoop();
+ loop = aeCreateEventLoop(64);
redisAeAttach(loop, c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
diff --git a/deps/hiredis/example-libev.c b/deps/hiredis/examples/example-libev.c
index 7894f1f48..cc8b166ec 100644
--- a/deps/hiredis/example-libev.c
+++ b/deps/hiredis/examples/example-libev.c
@@ -2,9 +2,10 @@
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-#include "hiredis.h"
-#include "async.h"
-#include "adapters/libev.h"
+
+#include <hiredis.h>
+#include <async.h>
+#include <adapters/libev.h>
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
diff --git a/deps/hiredis/example-libevent.c b/deps/hiredis/examples/example-libevent.c
index 9da8e02bf..d333c22b7 100644
--- a/deps/hiredis/example-libevent.c
+++ b/deps/hiredis/examples/example-libevent.c
@@ -2,9 +2,10 @@
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-#include "hiredis.h"
-#include "async.h"
-#include "adapters/libevent.h"
+
+#include <hiredis.h>
+#include <async.h>
+#include <adapters/libevent.h>
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
diff --git a/deps/hiredis/examples/example-libuv.c b/deps/hiredis/examples/example-libuv.c
new file mode 100644
index 000000000..a5462d410
--- /dev/null
+++ b/deps/hiredis/examples/example-libuv.c
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <hiredis.h>
+#include <async.h>
+#include <adapters/libuv.h>
+
+void getCallback(redisAsyncContext *c, void *r, void *privdata) {
+ redisReply *reply = r;
+ if (reply == NULL) return;
+ printf("argv[%s]: %s\n", (char*)privdata, reply->str);
+
+ /* Disconnect after receiving the reply to GET */
+ redisAsyncDisconnect(c);
+}
+
+void connectCallback(const redisAsyncContext *c, int status) {
+ if (status != REDIS_OK) {
+ printf("Error: %s\n", c->errstr);
+ return;
+ }
+ printf("Connected...\n");
+}
+
+void disconnectCallback(const redisAsyncContext *c, int status) {
+ if (status != REDIS_OK) {
+ printf("Error: %s\n", c->errstr);
+ return;
+ }
+ printf("Disconnected...\n");
+}
+
+int main (int argc, char **argv) {
+ signal(SIGPIPE, SIG_IGN);
+ uv_loop_t* loop = uv_default_loop();
+
+ redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
+ if (c->err) {
+ /* Let *c leak for now... */
+ printf("Error: %s\n", c->errstr);
+ return 1;
+ }
+
+ redisLibuvAttach(c,loop);
+ redisAsyncSetConnectCallback(c,connectCallback);
+ redisAsyncSetDisconnectCallback(c,disconnectCallback);
+ redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
+ redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
+ uv_run(loop, UV_RUN_DEFAULT);
+ return 0;
+}
diff --git a/deps/hiredis/example.c b/deps/hiredis/examples/example.c
index 90ff9ed5e..25226a807 100644
--- a/deps/hiredis/example.c
+++ b/deps/hiredis/examples/example.c
@@ -2,17 +2,24 @@
#include <stdlib.h>
#include <string.h>
-#include "hiredis.h"
+#include <hiredis.h>
-int main(void) {
+int main(int argc, char **argv) {
unsigned int j;
redisContext *c;
redisReply *reply;
+ const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
+ int port = (argc > 2) ? atoi(argv[2]) : 6379;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
- c = redisConnectWithTimeout((char*)"127.0.0.2", 6379, timeout);
- if (c->err) {
- printf("Connection error: %s\n", c->errstr);
+ c = redisConnectWithTimeout(hostname, port, timeout);
+ if (c == NULL || c->err) {
+ if (c) {
+ printf("Connection error: %s\n", c->errstr);
+ redisFree(c);
+ } else {
+ printf("Connection error: can't allocate redis context\n");
+ }
exit(1);
}
@@ -27,7 +34,7 @@ int main(void) {
freeReplyObject(reply);
/* Set a key using binary safe API */
- reply = redisCommand(c,"SET %b %b", "bar", 3, "hello", 5);
+ reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
printf("SET (binary API): %s\n", reply->str);
freeReplyObject(reply);
@@ -64,5 +71,8 @@ int main(void) {
}
freeReplyObject(reply);
+ /* Disconnects and frees the context */
+ redisFree(c);
+
return 0;
}
diff --git a/deps/hiredis/fmacros.h b/deps/hiredis/fmacros.h
index 96324eb49..9e5fec0ce 100644
--- a/deps/hiredis/fmacros.h
+++ b/deps/hiredis/fmacros.h
@@ -13,4 +13,8 @@
#define _XOPEN_SOURCE
#endif
+#if __APPLE__ && __MACH__
+#define _OSX
+#endif
+
#endif
diff --git a/deps/hiredis/hiredis.c b/deps/hiredis/hiredis.c
index 0b04935a1..2afee5666 100644
--- a/deps/hiredis/hiredis.c
+++ b/deps/hiredis/hiredis.c
@@ -917,7 +917,7 @@ err:
* %b represents a binary safe string
*
* When using %b you need to provide both the pointer to the string
- * and the length in bytes. Examples:
+ * and the length in bytes as a size_t. Examples:
*
* len = redisFormatCommand(target, "GET %s", mykey);
* len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
@@ -1010,58 +1010,122 @@ void redisFree(redisContext *c) {
free(c);
}
+int redisFreeKeepFd(redisContext *c) {
+ int fd = c->fd;
+ c->fd = -1;
+ redisFree(c);
+ return fd;
+}
+
/* Connect to a Redis instance. On error the field error in the returned
* context will be set to the return value of the error function.
* When no set of reply functions is given, the default set will be used. */
redisContext *redisConnect(const char *ip, int port) {
- redisContext *c = redisContextInit();
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
c->flags |= REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,NULL);
return c;
}
-redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
- redisContext *c = redisContextInit();
+redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
c->flags |= REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,&tv);
return c;
}
redisContext *redisConnectNonBlock(const char *ip, int port) {
- redisContext *c = redisContextInit();
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
c->flags &= ~REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,NULL);
return c;
}
-redisContext *redisConnectUnix(const char *path) {
+redisContext *redisConnectBindNonBlock(const char *ip, int port,
+ const char *source_addr) {
redisContext *c = redisContextInit();
+ c->flags &= ~REDIS_BLOCK;
+ redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
+ return c;
+}
+
+redisContext *redisConnectUnix(const char *path) {
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
c->flags |= REDIS_BLOCK;
redisContextConnectUnix(c,path,NULL);
return c;
}
-redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
- redisContext *c = redisContextInit();
+redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
c->flags |= REDIS_BLOCK;
redisContextConnectUnix(c,path,&tv);
return c;
}
redisContext *redisConnectUnixNonBlock(const char *path) {
- redisContext *c = redisContextInit();
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
c->flags &= ~REDIS_BLOCK;
redisContextConnectUnix(c,path,NULL);
return c;
}
+redisContext *redisConnectFd(int fd) {
+ redisContext *c;
+
+ c = redisContextInit();
+ if (c == NULL)
+ return NULL;
+
+ c->fd = fd;
+ c->flags |= REDIS_BLOCK | REDIS_CONNECTED;
+ return c;
+}
+
/* Set read/write timeout on a blocking socket. */
-int redisSetTimeout(redisContext *c, struct timeval tv) {
+int redisSetTimeout(redisContext *c, const struct timeval tv) {
if (c->flags & REDIS_BLOCK)
return redisContextSetTimeout(c,tv);
return REDIS_ERR;
}
+/* Enable connection KeepAlive. */
+int redisEnableKeepAlive(redisContext *c) {
+ if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)
+ return REDIS_ERR;
+ return REDIS_OK;
+}
+
/* Use this function to handle a read event on the descriptor. It will try
* and read some bytes from the socket and feed them to the reply parser.
*
@@ -1077,7 +1141,7 @@ int redisBufferRead(redisContext *c) {
nread = read(c->fd,buf,sizeof(buf));
if (nread == -1) {
- if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
+ if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
/* Try again later */
} else {
__redisSetError(c,REDIS_ERR_IO,NULL);
@@ -1114,7 +1178,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 && !(c->flags & REDIS_BLOCK)) {
+ if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
/* Try again later */
} else {
__redisSetError(c,REDIS_ERR_IO,NULL);
@@ -1180,7 +1244,7 @@ int redisGetReply(redisContext *c, void **reply) {
* is used, you need to call redisGetReply yourself to retrieve
* the reply (or replies in pub/sub).
*/
-int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
+int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
sds newbuf;
newbuf = sdscatlen(c->obuf,cmd,len);
@@ -1193,6 +1257,15 @@ int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
return REDIS_OK;
}
+int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {
+
+ if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {
+ return REDIS_ERR;
+ }
+
+ return REDIS_OK;
+}
+
int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
char *cmd;
int len;
diff --git a/deps/hiredis/hiredis.h b/deps/hiredis/hiredis.h
index b922831e3..7700f4b89 100644
--- a/deps/hiredis/hiredis.h
+++ b/deps/hiredis/hiredis.h
@@ -36,8 +36,8 @@
#include <sys/time.h> /* for struct timeval */
#define HIREDIS_MAJOR 0
-#define HIREDIS_MINOR 10
-#define HIREDIS_PATCH 1
+#define HIREDIS_MINOR 11
+#define HIREDIS_PATCH 0
#define REDIS_ERR -1
#define REDIS_OK 0
@@ -88,6 +88,8 @@
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
+#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -171,13 +173,17 @@ typedef struct redisContext {
} redisContext;
redisContext *redisConnect(const char *ip, int port);
-redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
+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 *redisConnectUnix(const char *path);
-redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
+redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
-int redisSetTimeout(redisContext *c, struct timeval tv);
+redisContext *redisConnectFd(int fd);
+int redisSetTimeout(redisContext *c, const struct timeval tv);
+int redisEnableKeepAlive(redisContext *c);
void redisFree(redisContext *c);
+int redisFreeKeepFd(redisContext *c);
int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done);
@@ -188,6 +194,10 @@ int redisBufferWrite(redisContext *c, int *done);
int redisGetReply(redisContext *c, void **reply);
int redisGetReplyFromReader(redisContext *c, void **reply);
+/* Write a formatted command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
+
/* Write a command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
diff --git a/deps/hiredis/net.c b/deps/hiredis/net.c
index b10eee2e5..9fe80bba7 100644
--- a/deps/hiredis/net.c
+++ b/deps/hiredis/net.c
@@ -54,8 +54,15 @@
/* Defined in hiredis.c */
void __redisSetError(redisContext *c, int type, const char *str);
+static void redisContextCloseFd(redisContext *c) {
+ if (c && c->fd >= 0) {
+ close(c->fd);
+ c->fd = -1;
+ }
+}
+
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
- char buf[128];
+ char buf[128] = { 0 };
size_t len = 0;
if (prefix != NULL)
@@ -64,11 +71,11 @@ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *pref
__redisSetError(c,type,buf);
}
-static int redisSetReuseAddr(redisContext *c, int fd) {
+static int redisSetReuseAddr(redisContext *c) {
int on = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+ if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
- close(fd);
+ redisContextCloseFd(c);
return REDIS_ERR;
}
return REDIS_OK;
@@ -80,23 +87,24 @@ static int redisCreateSocket(redisContext *c, int type) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
return REDIS_ERR;
}
+ c->fd = s;
if (type == AF_INET) {
- if (redisSetReuseAddr(c,s) == REDIS_ERR) {
+ if (redisSetReuseAddr(c) == REDIS_ERR) {
return REDIS_ERR;
}
}
- return s;
+ return REDIS_OK;
}
-static int redisSetBlocking(redisContext *c, int fd, int blocking) {
+static int redisSetBlocking(redisContext *c, int blocking) {
int flags;
/* Set the socket nonblocking.
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */
- if ((flags = fcntl(fd, F_GETFL)) == -1) {
+ if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
- close(fd);
+ redisContextCloseFd(c);
return REDIS_ERR;
}
@@ -105,19 +113,61 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
else
flags |= O_NONBLOCK;
- if (fcntl(fd, F_SETFL, flags) == -1) {
+ if (fcntl(c->fd, F_SETFL, flags) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
- close(fd);
+ redisContextCloseFd(c);
+ return REDIS_ERR;
+ }
+ return REDIS_OK;
+}
+
+int redisKeepAlive(redisContext *c, int interval) {
+ int val = 1;
+ int fd = c->fd;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
+ return REDIS_ERR;
+ }
+
+ val = interval;
+
+#ifdef _OSX
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
+ return REDIS_ERR;
+ }
+#else
+#ifndef __sun
+ val = interval;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
+ return REDIS_ERR;
+ }
+
+ val = interval/3;
+ if (val == 0) val = 1;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
+ return REDIS_ERR;
+ }
+
+ val = 3;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR;
}
+#endif
+#endif
+
return REDIS_OK;
}
-static int redisSetTcpNoDelay(redisContext *c, int fd) {
+static int redisSetTcpNoDelay(redisContext *c) {
int yes = 1;
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
- close(fd);
+ redisContextCloseFd(c);
return REDIS_ERR;
}
return REDIS_OK;
@@ -125,18 +175,19 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
-static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
+static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) {
struct pollfd wfd[1];
long msec;
msec = -1;
- wfd[0].fd = fd;
+ wfd[0].fd = c->fd;
wfd[0].events = POLLOUT;
/* Only use timeout when not NULL. */
if (timeout != NULL) {
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
- close(fd);
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
+ redisContextCloseFd(c);
return REDIS_ERR;
}
@@ -152,47 +203,45 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
if ((res = poll(wfd, 1, msec)) == -1) {
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
- close(fd);
+ redisContextCloseFd(c);
return REDIS_ERR;
} else if (res == 0) {
errno = ETIMEDOUT;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
- close(fd);
+ redisContextCloseFd(c);
return REDIS_ERR;
}
- if (redisCheckSocketError(c, fd) != REDIS_OK)
+ if (redisCheckSocketError(c) != REDIS_OK)
return REDIS_ERR;
return REDIS_OK;
}
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
- close(fd);
+ redisContextCloseFd(c);
return REDIS_ERR;
}
-int redisCheckSocketError(redisContext *c, int fd) {
+int redisCheckSocketError(redisContext *c) {
int err = 0;
socklen_t errlen = sizeof(err);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
- close(fd);
return REDIS_ERR;
}
if (err) {
errno = err;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
- close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
-int redisContextSetTimeout(redisContext *c, struct timeval tv) {
+int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
return REDIS_ERR;
@@ -204,10 +253,12 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
return REDIS_OK;
}
-int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
+static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout,
+ const char *source_addr) {
int s, rv;
char _port[6]; /* strlen("65535"); */
- struct addrinfo hints, *servinfo, *p;
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
int blocking = (c->flags & REDIS_BLOCK);
snprintf(_port, 6, "%d", port);
@@ -231,25 +282,47 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
continue;
- if (redisSetBlocking(c,s,0) != REDIS_OK)
+ c->fd = s;
+ if (redisSetBlocking(c,0) != REDIS_OK)
goto error;
+ if (source_addr) {
+ int bound = 0;
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
+ if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) {
+ char buf[128];
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
+ goto error;
+ }
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
+ bound = 1;
+ break;
+ }
+ }
+ if (!bound) {
+ char buf[128];
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
+ goto error;
+ }
+ }
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) {
- close(s);
+ redisContextCloseFd(c);
continue;
} else if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
- if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
+ if (redisContextWaitReady(c,timeout) != REDIS_OK)
goto error;
}
}
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
goto error;
- if (redisSetTcpNoDelay(c,s) != REDIS_OK)
+ if (redisSetTcpNoDelay(c) != REDIS_OK)
goto error;
- c->fd = s;
c->flags |= REDIS_CONNECTED;
rv = REDIS_OK;
goto end;
@@ -268,32 +341,41 @@ end:
return rv; // Need to return REDIS_OK if alright
}
-int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
- int s;
+int redisContextConnectTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout) {
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
+}
+
+int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout,
+ const char *source_addr) {
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
+}
+
+int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_un sa;
- if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
+ if (redisCreateSocket(c,AF_LOCAL) < 0)
return REDIS_ERR;
- if (redisSetBlocking(c,s,0) != REDIS_OK)
+ if (redisSetBlocking(c,0) != REDIS_OK)
return REDIS_ERR;
sa.sun_family = AF_LOCAL;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
- if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
+ if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
- if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
+ if (redisContextWaitReady(c,timeout) != REDIS_OK)
return REDIS_ERR;
}
}
/* Reset socket to be blocking after connect(2). */
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
return REDIS_ERR;
- c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK;
}
diff --git a/deps/hiredis/net.h b/deps/hiredis/net.h
index eb8a0a1cf..5e742f577 100644
--- a/deps/hiredis/net.h
+++ b/deps/hiredis/net.h
@@ -39,9 +39,13 @@
#define AF_LOCAL AF_UNIX
#endif
-int redisCheckSocketError(redisContext *c, int fd);
-int redisContextSetTimeout(redisContext *c, struct timeval tv);
-int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
-int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
+int redisCheckSocketError(redisContext *c);
+int redisContextSetTimeout(redisContext *c, const struct timeval tv);
+int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
+int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
+ const struct timeval *timeout,
+ const char *source_addr);
+int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
+int redisKeepAlive(redisContext *c, int interval);
#endif
diff --git a/deps/hiredis/sds.c b/deps/hiredis/sds.c
index d66c1d730..47b9823ea 100644
--- a/deps/hiredis/sds.c
+++ b/deps/hiredis/sds.c
@@ -289,27 +289,118 @@ sds sdscpy(sds s, const char *t) {
return sdscpylen(s, t, strlen(t));
}
+/* Helper for sdscatlonglong() doing the actual number -> string
+ * conversion. 's' must point to a string with room for at least
+ * SDS_LLSTR_SIZE bytes.
+ *
+ * The function returns the lenght of the null-terminated string
+ * representation stored at 's'. */
+#define SDS_LLSTR_SIZE 21
+int sdsll2str(char *s, long long value) {
+ char *p, aux;
+ unsigned long long v;
+ size_t l;
+
+ /* Generate the string representation, this method produces
+ * an reversed string. */
+ v = (value < 0) ? -value : value;
+ p = s;
+ do {
+ *p++ = '0'+(v%10);
+ v /= 10;
+ } while(v);
+ if (value < 0) *p++ = '-';
+
+ /* Compute length and add null term. */
+ l = p-s;
+ *p = '\0';
+
+ /* Reverse the string. */
+ p--;
+ while(s < p) {
+ aux = *s;
+ *s = *p;
+ *p = aux;
+ s++;
+ p--;
+ }
+ return l;
+}
+
+/* Identical sdsll2str(), but for unsigned long long type. */
+int sdsull2str(char *s, unsigned long long v) {
+ char *p, aux;
+ size_t l;
+
+ /* Generate the string representation, this method produces
+ * an reversed string. */
+ p = s;
+ do {
+ *p++ = '0'+(v%10);
+ v /= 10;
+ } while(v);
+
+ /* Compute length and add null term. */
+ l = p-s;
+ *p = '\0';
+
+ /* Reverse the string. */
+ p--;
+ while(s < p) {
+ aux = *s;
+ *s = *p;
+ *p = aux;
+ s++;
+ p--;
+ }
+ return l;
+}
+
+/* Create an sds string from a long long value. It is much faster than:
+ *
+ * sdscatprintf(sdsempty(),"%lld\n", value);
+ */
+sds sdsfromlonglong(long long value) {
+ char buf[SDS_LLSTR_SIZE];
+ int len = sdsll2str(buf,value);
+
+ return sdsnewlen(buf,len);
+}
+
/* Like sdscatpritf() but gets va_list instead of being variadic. */
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy;
- char *buf, *t;
- size_t buflen = 16;
+ char staticbuf[1024], *buf = staticbuf, *t;
+ size_t buflen = strlen(fmt)*2;
- while(1) {
+ /* We try to start using a static buffer for speed.
+ * If not possible we revert to heap allocation. */
+ if (buflen > sizeof(staticbuf)) {
buf = zmalloc(buflen);
if (buf == NULL) return NULL;
+ } else {
+ buflen = sizeof(staticbuf);
+ }
+
+ /* Try with buffers two times bigger every time we fail to
+ * fit the string in the current buffer size. */
+ while(1) {
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
if (buf[buflen-2] != '\0') {
- zfree(buf);
+ if (buf != staticbuf) zfree(buf);
buflen *= 2;
+ buf = zmalloc(buflen);
+ if (buf == NULL) return NULL;
continue;
}
break;
}
+
+ /* Finally concat the obtained string to the SDS string and return it. */
t = sdscat(s, buf);
- zfree(buf);
+ if (buf != staticbuf) zfree(buf);
return t;
}
@@ -338,6 +429,122 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
return t;
}
+/* This function is similar to sdscatprintf, but much faster as it does
+ * not rely on sprintf() family functions implemented by the libc that
+ * are often very slow. Moreover directly handling the sds string as
+ * new data is concatenated provides a performance improvement.
+ *
+ * However this function only handles an incompatible subset of printf-alike
+ * format specifiers:
+ *
+ * %s - C String
+ * %S - SDS string
+ * %i - signed int
+ * %I - 64 bit signed integer (long long, int64_t)
+ * %u - unsigned int
+ * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
+ * %% - Verbatim "%" character.
+ */
+sds sdscatfmt(sds s, char const *fmt, ...) {
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
+ size_t initlen = sdslen(s);
+ const char *f = fmt;
+ int i;
+ va_list ap;
+
+ va_start(ap,fmt);
+ f = fmt; /* Next format specifier byte to process. */
+ i = initlen; /* Position of the next byte to write to dest str. */
+ while(*f) {
+ char next, *str;
+ int l;
+ long long num;
+ unsigned long long unum;
+
+ /* Make sure there is always space for at least 1 char. */
+ if (sh->free == 0) {
+ s = sdsMakeRoomFor(s,1);
+ sh = (void*) (s-(sizeof(struct sdshdr)));
+ }
+
+ switch(*f) {
+ case '%':
+ next = *(f+1);
+ f++;
+ switch(next) {
+ case 's':
+ case 'S':
+ str = va_arg(ap,char*);
+ l = (next == 's') ? strlen(str) : sdslen(str);
+ if (sh->free < l) {
+ s = sdsMakeRoomFor(s,l);
+ sh = (void*) (s-(sizeof(struct sdshdr)));
+ }
+ memcpy(s+i,str,l);
+ sh->len += l;
+ sh->free -= l;
+ i += l;
+ break;
+ case 'i':
+ case 'I':
+ if (next == 'i')
+ num = va_arg(ap,int);
+ else
+ num = va_arg(ap,long long);
+ {
+ char buf[SDS_LLSTR_SIZE];
+ l = sdsll2str(buf,num);
+ if (sh->free < l) {
+ s = sdsMakeRoomFor(s,l);
+ sh = (void*) (s-(sizeof(struct sdshdr)));
+ }
+ memcpy(s+i,buf,l);
+ sh->len += l;
+ sh->free -= l;
+ i += l;
+ }
+ break;
+ case 'u':
+ case 'U':
+ if (next == 'u')
+ unum = va_arg(ap,unsigned int);
+ else
+ unum = va_arg(ap,unsigned long long);
+ {
+ char buf[SDS_LLSTR_SIZE];
+ l = sdsull2str(buf,unum);
+ if (sh->free < l) {
+ s = sdsMakeRoomFor(s,l);
+ sh = (void*) (s-(sizeof(struct sdshdr)));
+ }
+ memcpy(s+i,buf,l);
+ sh->len += l;
+ sh->free -= l;
+ i += l;
+ }
+ break;
+ default: /* Handle %% and generally %<unknown>. */
+ s[i++] = next;
+ sh->len += 1;
+ sh->free -= 1;
+ break;
+ }
+ break;
+ default:
+ s[i++] = *f;
+ sh->len += 1;
+ sh->free -= 1;
+ break;
+ }
+ f++;
+ }
+ va_end(ap);
+
+ /* Add null-term */
+ s[i] = '\0';
+ return s;
+}
+
/* Remove the part of the string from left and from right composed just of
* contiguous characters found in 'cset', that is a null terminted C string.
*
@@ -383,7 +590,7 @@ sds sdstrim(sds s, const char *cset) {
* Example:
*
* s = sdsnew("Hello World");
- * sdstrim(s,1,-1); => "ello Worl"
+ * sdsrange(s,1,-1); => "ello World"
*/
void sdsrange(sds s, int start, int end) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
@@ -525,25 +732,6 @@ void sdsfreesplitres(sds *tokens, int count) {
zfree(tokens);
}
-/* Create an sds string from a long long value. It is much faster than:
- *
- * sdscatprintf(sdsempty(),"%lld\n", value);
- */
-sds sdsfromlonglong(long long value) {
- char buf[32], *p;
- unsigned long long v;
-
- v = (value < 0) ? -value : value;
- p = buf+31; /* point to the last character */
- do {
- *p-- = '0'+(v%10);
- v /= 10;
- } while(v);
- if (value < 0) *p-- = '-';
- p++;
- return sdsnewlen(p,32-(p-buf));
-}
-
/* Append to the sds string "s" an escaped string representation where
* all the non-printable characters (tested with isprint()) are turned into
* escapes in the form "\n\r\a...." or "\x<hex-number>".
@@ -582,7 +770,7 @@ int is_hex_digit(char c) {
(c >= 'A' && c <= 'F');
}
-/* Helper function for sdssplitargs() that converts an hex digit into an
+/* Helper function for sdssplitargs() that converts a hex digit into an
* integer from 0 to 15 */
int hex_digit_to_int(char c) {
switch(c) {
@@ -774,6 +962,7 @@ sds sdsjoin(char **argv, int argc, char *sep) {
#ifdef SDS_TEST_MAIN
#include <stdio.h>
#include "testhelp.h"
+#include "limits.h"
int main(void) {
{
@@ -804,39 +993,61 @@ int main(void) {
sdsfree(x);
x = sdscatprintf(sdsempty(),"%d",123);
test_cond("sdscatprintf() seems working in the base case",
- sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
+ sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
+
+ sdsfree(x);
+ x = sdsnew("--");
+ x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
+ test_cond("sdscatfmt() seems working in the base case",
+ sdslen(x) == 60 &&
+ memcmp(x,"--Hello Hi! World -9223372036854775808,"
+ "9223372036854775807--",60) == 0)
sdsfree(x);
- x = sdstrim(sdsnew("xxciaoyyy"),"xy");
+ x = sdsnew("--");
+ x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
+ test_cond("sdscatfmt() seems working with unsigned numbers",
+ sdslen(x) == 35 &&
+ memcmp(x,"--4294967295,18446744073709551615--",35) == 0)
+
+ sdsfree(x);
+ x = sdsnew("xxciaoyyy");
+ sdstrim(x,"xy");
test_cond("sdstrim() correctly trims characters",
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
- y = sdsrange(sdsdup(x),1,1);
+ y = sdsdup(x);
+ sdsrange(y,1,1);
test_cond("sdsrange(...,1,1)",
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
sdsfree(y);
- y = sdsrange(sdsdup(x),1,-1);
+ y = sdsdup(x);
+ sdsrange(y,1,-1);
test_cond("sdsrange(...,1,-1)",
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
sdsfree(y);
- y = sdsrange(sdsdup(x),-2,-1);
+ y = sdsdup(x);
+ sdsrange(y,-2,-1);
test_cond("sdsrange(...,-2,-1)",
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
sdsfree(y);
- y = sdsrange(sdsdup(x),2,1);
+ y = sdsdup(x);
+ sdsrange(y,2,1);
test_cond("sdsrange(...,2,1)",
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
sdsfree(y);
- y = sdsrange(sdsdup(x),1,100);
+ y = sdsdup(x);
+ sdsrange(y,1,100);
test_cond("sdsrange(...,1,100)",
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
sdsfree(y);
- y = sdsrange(sdsdup(x),100,100);
+ y = sdsdup(x);
+ sdsrange(y,100,100);
test_cond("sdsrange(...,100,100)",
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
@@ -858,6 +1069,13 @@ int main(void) {
y = sdsnew("bar");
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
+ sdsfree(y);
+ sdsfree(x);
+ x = sdsnewlen("\a\n\0foo\r",7);
+ y = sdscatrepr(sdsempty(),x,sdslen(x));
+ test_cond("sdscatrepr(...data...)",
+ memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
+
{
int oldfree;
diff --git a/deps/hiredis/sds.h b/deps/hiredis/sds.h
index 615c751cd..9a604021c 100644
--- a/deps/hiredis/sds.h
+++ b/deps/hiredis/sds.h
@@ -76,6 +76,7 @@ sds sdscatprintf(sds s, const char *fmt, ...)
sds sdscatprintf(sds s, const char *fmt, ...);
#endif
+sds sdscatfmt(sds s, char const *fmt, ...);
sds sdstrim(sds s, const char *cset);
void sdsrange(sds s, int start, int end);
void sdsupdatelen(sds s);
diff --git a/deps/hiredis/test.c b/deps/hiredis/test.c
index 5945b6552..713cc06c5 100644
--- a/deps/hiredis/test.c
+++ b/deps/hiredis/test.c
@@ -8,12 +8,14 @@
#include <unistd.h>
#include <signal.h>
#include <errno.h>
+#include <limits.h>
#include "hiredis.h"
enum connection_type {
CONN_TCP,
- CONN_UNIX
+ CONN_UNIX,
+ CONN_FD
};
struct config {
@@ -22,6 +24,7 @@ struct config {
struct {
const char *host;
int port;
+ struct timeval timeout;
} tcp;
struct {
@@ -62,7 +65,7 @@ static redisContext *select_database(redisContext *c) {
return c;
}
-static void disconnect(redisContext *c) {
+static int disconnect(redisContext *c, int keep_fd) {
redisReply *reply;
/* Make sure we're on DB 9. */
@@ -73,8 +76,11 @@ static void disconnect(redisContext *c) {
assert(reply != NULL);
freeReplyObject(reply);
- /* Free the context as well. */
+ /* Free the context as well, but keep the fd if requested. */
+ if (keep_fd)
+ return redisFreeKeepFd(c);
redisFree(c);
+ return -1;
}
static redisContext *connect(struct config config) {
@@ -84,11 +90,22 @@ static redisContext *connect(struct config config) {
c = redisConnect(config.tcp.host, config.tcp.port);
} else if (config.type == CONN_UNIX) {
c = redisConnectUnix(config.unix.path);
+ } else if (config.type == CONN_FD) {
+ /* Create a dummy connection just to get an fd to inherit */
+ redisContext *dummy_ctx = redisConnectUnix(config.unix.path);
+ if (dummy_ctx) {
+ int fd = disconnect(dummy_ctx, 1);
+ printf("Connecting to inherited fd %d\n", fd);
+ c = redisConnectFd(fd);
+ }
} else {
assert(NULL);
}
- if (c->err) {
+ if (c == NULL) {
+ printf("Connection error: can't allocate redis context\n");
+ exit(1);
+ } else if (c->err) {
printf("Connection error: %s\n", c->errstr);
exit(1);
}
@@ -125,13 +142,13 @@ static void test_format_commands(void) {
free(cmd);
test("Format command with %%b string interpolation: ");
- len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
+ len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%b and an empty string: ");
- len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);
+ len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
@@ -177,7 +194,7 @@ static void test_format_commands(void) {
FLOAT_WIDTH_TEST(double);
test("Format command with invalid printf format: ");
- len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",3);
+ len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3);
test_cond(len == -1);
const char *argv[3];
@@ -200,10 +217,33 @@ static void test_format_commands(void) {
free(cmd);
}
+static void test_append_formatted_commands(struct config config) {
+ redisContext *c;
+ redisReply *reply;
+ char *cmd;
+ int len;
+
+ c = connect(config);
+
+ test("Append format command: ");
+
+ len = redisFormatCommand(&cmd, "SET foo bar");
+
+ test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK);
+
+ assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
+
+ free(cmd);
+ freeReplyObject(reply);
+
+ disconnect(c, 0);
+}
+
static void test_reply_reader(void) {
redisReader *reader;
void *reply;
int ret;
+ int i;
test("Error handling in reply parser: ");
reader = redisReaderCreate();
@@ -225,12 +265,13 @@ static void test_reply_reader(void) {
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
- test("Set error on nested multi bulks with depth > 2: ");
+ test("Set error on nested multi bulks with depth > 7: ");
reader = redisReaderCreate();
- redisReaderFeed(reader,(char*)"*1\r\n",4);
- redisReaderFeed(reader,(char*)"*1\r\n",4);
- redisReaderFeed(reader,(char*)"*1\r\n",4);
- redisReaderFeed(reader,(char*)"*1\r\n",4);
+
+ for (i = 0; i < 9; i++) {
+ redisReaderFeed(reader,(char*)"*1\r\n",4);
+ }
+
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strncasecmp(reader->errstr,"No support for",14) == 0);
@@ -284,7 +325,10 @@ static void test_blocking_connection_errors(void) {
c = redisConnect((char*)"idontexist.local", 6379);
test_cond(c->err == REDIS_ERR_OTHER &&
(strcmp(c->errstr,"Name or service not known") == 0 ||
- strcmp(c->errstr,"Can't resolve: idontexist.local") == 0));
+ strcmp(c->errstr,"Can't resolve: idontexist.local") == 0 ||
+ strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 ||
+ strcmp(c->errstr,"No address associated with hostname") == 0 ||
+ strcmp(c->errstr,"no address associated with name") == 0));
redisFree(c);
test("Returns error when the port is not open: ");
@@ -326,7 +370,7 @@ static void test_blocking_connection(struct config config) {
freeReplyObject(reply);
test("%%b String interpolation works: ");
- reply = redisCommand(c,"SET %b %b","foo",3,"hello\x00world",11);
+ reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11);
freeReplyObject(reply);
reply = redisCommand(c,"GET foo");
test_cond(reply->type == REDIS_REPLY_STRING &&
@@ -374,7 +418,7 @@ static void test_blocking_connection(struct config config) {
strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply);
- disconnect(c);
+ disconnect(c, 0);
}
static void test_blocking_io_errors(struct config config) {
@@ -428,6 +472,30 @@ static void test_blocking_io_errors(struct config config) {
redisFree(c);
}
+static void test_invalid_timeout_errors(struct config config) {
+ redisContext *c;
+
+ test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: ");
+
+ config.tcp.timeout.tv_sec = 0;
+ config.tcp.timeout.tv_usec = 10000001;
+
+ c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
+
+ test_cond(c->err == REDIS_ERR_IO);
+
+ test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
+
+ config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;
+ config.tcp.timeout.tv_usec = 0;
+
+ c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
+
+ test_cond(c->err == REDIS_ERR_IO);
+
+ redisFree(c);
+}
+
static void test_throughput(struct config config) {
redisContext *c = connect(config);
redisReply **replies;
@@ -490,7 +558,7 @@ static void test_throughput(struct config config) {
free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
- disconnect(c);
+ disconnect(c, 0);
}
// static long __test_callback_flags = 0;
@@ -603,6 +671,7 @@ int main(int argc, char **argv) {
}
};
int throughput = 1;
+ int test_inherit_fd = 1;
/* Ignore broken pipe signal (for I/O error tests). */
signal(SIGPIPE, SIG_IGN);
@@ -621,6 +690,8 @@ int main(int argc, char **argv) {
cfg.unix.path = argv[0];
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
throughput = 0;
+ } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
+ test_inherit_fd = 0;
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
exit(1);
@@ -636,6 +707,8 @@ int main(int argc, char **argv) {
cfg.type = CONN_TCP;
test_blocking_connection(cfg);
test_blocking_io_errors(cfg);
+ test_invalid_timeout_errors(cfg);
+ test_append_formatted_commands(cfg);
if (throughput) test_throughput(cfg);
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
@@ -644,6 +717,12 @@ int main(int argc, char **argv) {
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
+ if (test_inherit_fd) {
+ printf("\nTesting against inherited fd (%s):\n", cfg.unix.path);
+ cfg.type = CONN_FD;
+ test_blocking_connection(cfg);
+ }
+
if (fails) {
printf("*** %d TESTS FAILED ***\n", fails);
return 1;
diff --git a/src/cluster.c b/src/cluster.c
index 4ed559376..20da6d519 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -2794,8 +2794,7 @@ void clusterCron(void) {
clusterLink *link;
fd = anetTcpNonBlockBindConnect(server.neterr, node->ip,
- node->port+REDIS_CLUSTER_PORT_INCR,
- server.bindaddr_count ? server.bindaddr[0] : NULL);
+ node->port+REDIS_CLUSTER_PORT_INCR, REDIS_BIND_ADDR);
if (fd == -1) {
redisLog(REDIS_DEBUG, "Unable to connect to "
"Cluster Node [%s]:%d -> %s", node->ip,
diff --git a/src/redis.h b/src/redis.h
index 02def27d7..b39206f93 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -367,6 +367,9 @@
#define REDIS_NOTIFY_EVICTED (1<<9) /* e */
#define REDIS_NOTIFY_ALL (REDIS_NOTIFY_GENERIC | REDIS_NOTIFY_STRING | REDIS_NOTIFY_LIST | REDIS_NOTIFY_SET | REDIS_NOTIFY_HASH | REDIS_NOTIFY_ZSET | REDIS_NOTIFY_EXPIRED | REDIS_NOTIFY_EVICTED) /* A */
+/* Get the first bind addr or NULL */
+#define REDIS_BIND_ADDR (server.bindaddr_count ? server.bindaddr[0] : NULL)
+
/* Using the following macro you can run code inside serverCron() with the
* specified period, specified in milliseconds.
* The actual resolution depends on server.hz. */
diff --git a/src/sds.c b/src/sds.c
index b64ffb13a..47b9823ea 100644
--- a/src/sds.c
+++ b/src/sds.c
@@ -457,7 +457,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
i = initlen; /* Position of the next byte to write to dest str. */
while(*f) {
char next, *str;
- size_t l;
+ int l;
long long num;
unsigned long long unum;
diff --git a/src/sentinel.c b/src/sentinel.c
index 7f81443bb..f94ec9f26 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -1676,7 +1676,7 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
/* Commands connection. */
if (ri->cc == NULL) {
- ri->cc = redisAsyncConnect(ri->addr->ip,ri->addr->port);
+ ri->cc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,REDIS_BIND_ADDR);
if (ri->cc->err) {
sentinelEvent(REDIS_DEBUG,"-cmd-link-reconnection",ri,"%@ #%s",
ri->cc->errstr);
@@ -1698,7 +1698,7 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
}
/* Pub / Sub */
if ((ri->flags & (SRI_MASTER|SRI_SLAVE)) && ri->pc == NULL) {
- ri->pc = redisAsyncConnect(ri->addr->ip,ri->addr->port);
+ ri->pc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,REDIS_BIND_ADDR);
if (ri->pc->err) {
sentinelEvent(REDIS_DEBUG,"-pubsub-link-reconnection",ri,"%@ #%s",
ri->pc->errstr);