summaryrefslogtreecommitdiff
path: root/deps/hiredis
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-12-04 19:01:33 +0100
committerantirez <antirez@gmail.com>2019-01-09 17:00:29 +0100
commitd5c54f0b3a908595afead2cbcd93555f2886f22f (patch)
tree21ed76fcff4d2340a094edac83925e5f69fe03de /deps/hiredis
parent809e3a44a7b53f26ba6bfe49012a88a5203fae3e (diff)
downloadredis-d5c54f0b3a908595afead2cbcd93555f2886f22f.tar.gz
RESP3: hiredis updated with recent version + some RESP3 support.
Diffstat (limited to 'deps/hiredis')
-rw-r--r--deps/hiredis/CHANGELOG.md53
-rw-r--r--deps/hiredis/Makefile24
-rw-r--r--deps/hiredis/adapters/libevent.h4
-rw-r--r--deps/hiredis/adapters/libuv.h9
-rw-r--r--deps/hiredis/appveyor.yml17
-rw-r--r--deps/hiredis/async.c67
-rw-r--r--deps/hiredis/async.h5
-rw-r--r--deps/hiredis/fmacros.h19
-rw-r--r--deps/hiredis/hiredis.c49
-rw-r--r--deps/hiredis/hiredis.h33
-rw-r--r--deps/hiredis/net.c75
-rw-r--r--deps/hiredis/net.h5
-rw-r--r--deps/hiredis/read.c176
-rw-r--r--deps/hiredis/read.h8
-rw-r--r--deps/hiredis/sds.c29
-rw-r--r--deps/hiredis/test.c156
16 files changed, 501 insertions, 228 deletions
diff --git a/deps/hiredis/CHANGELOG.md b/deps/hiredis/CHANGELOG.md
index f92bcb3c9..a7fe3ac11 100644
--- a/deps/hiredis/CHANGELOG.md
+++ b/deps/hiredis/CHANGELOG.md
@@ -1,7 +1,51 @@
### 1.0.0 (unreleased)
-**Fixes**:
+**BREAKING CHANGES**:
+
+* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
+ protocol errors. This is consistent with the RESP specification. On 32-bit
+ platforms, the upper bound is lowered to `SIZE_MAX`.
+
+* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
+ User code should compare this to `size_t` values as well. If it was used to
+ compare to other values, casting might be necessary or can be removed, if
+ casting was applied before.
+
+### 0.14.0 (2018-09-25)
+
+* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
+* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
+* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])
+* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])
+* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])
+* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])
+* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])
+* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])
+* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])
+* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])
+* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])
+* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])
+* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])
+* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])
+* Fix libevent leak (zfz [515228])
+* Clean up GCC warning (Ichito Nagata [2ec774])
+* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])
+* Solaris compilation fix (Donald Whyte [41b07d])
+* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])
+* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])
+* libuv use after free fix (Paul Scott [cbb956])
+* Properly close socket fd on reconnect attempt (WSL [64d1ec])
+* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])
+* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])
+* Update libevent (Chris Xin [386802])
+* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])
+* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])
+* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])
+* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])
+* Compatibility fix for strerror_r (Tom Lee [bb1747])
+* Properly detect integer parse/overflow errors (Justin Brewer [93421f])
+* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])
* Catch a buffer overflow when formatting the error message
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13
* Fix warnings, when compiled with -Wshadow
@@ -9,11 +53,6 @@
**BREAKING CHANGES**:
-* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
-
-User code should compare this to `size_t` values as well.
-If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
-
* Remove backwards compatibility macro's
This removes the following old function aliases, use the new name now:
@@ -94,7 +133,7 @@ The parser, standalone since v0.12.0, can now be compiled on Windows
* Add IPv6 support
-* Remove possiblity of multiple close on same fd
+* Remove possibility of multiple close on same fd
* Add ability to bind source address on connect
diff --git a/deps/hiredis/Makefile b/deps/hiredis/Makefile
index 9a4de8360..06ca99468 100644
--- a/deps/hiredis/Makefile
+++ b/deps/hiredis/Makefile
@@ -36,13 +36,13 @@ 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')
-CXX:=$(shell sh -c 'type $(CXX) >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
+CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
+CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
OPTIMIZATION?=-O3
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
DEBUG_FLAGS?= -g -ggdb
-REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) $(ARCH)
-REAL_LDFLAGS=$(LDFLAGS) $(ARCH)
+REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
+REAL_LDFLAGS=$(LDFLAGS)
DYLIBSUFFIX=so
STLIBSUFFIX=a
@@ -58,12 +58,11 @@ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
ifeq ($(uname_S),SunOS)
REAL_LDFLAGS+= -ldl -lnsl -lsocket
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
- INSTALL= cp -r
endif
ifeq ($(uname_S),Darwin)
DYLIBSUFFIX=dylib
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
- DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
+ DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
endif
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
@@ -94,7 +93,7 @@ hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME)
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
- $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) $(shell pkg-config --cflags --libs glib-2.0) -I. $< $(STLIBNAME)
+ $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME)
hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME)
@@ -161,11 +160,7 @@ clean:
dep:
$(CC) -MM *.c
-ifeq ($(uname_S),SunOS)
- INSTALL?= cp -r
-endif
-
-INSTALL?= cp -a
+INSTALL?= cp -pPR
$(PKGCONFNAME): hiredis.h
@echo "Generating $@ for pkgconfig..."
@@ -181,8 +176,9 @@ $(PKGCONFNAME): hiredis.h
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
- mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
- $(INSTALL) hiredis.h async.h read.h sds.h adapters $(INSTALL_INCLUDE_PATH)
+ mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
+ $(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)
+ $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
diff --git a/deps/hiredis/adapters/libevent.h b/deps/hiredis/adapters/libevent.h
index 273d8b2dd..7d2bef180 100644
--- a/deps/hiredis/adapters/libevent.h
+++ b/deps/hiredis/adapters/libevent.h
@@ -73,8 +73,8 @@ static void redisLibeventDelWrite(void *privdata) {
static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
- event_del(e->rev);
- event_del(e->wev);
+ event_free(e->rev);
+ event_free(e->wev);
free(e);
}
diff --git a/deps/hiredis/adapters/libuv.h b/deps/hiredis/adapters/libuv.h
index ff08c25e1..39ef7cf5e 100644
--- a/deps/hiredis/adapters/libuv.h
+++ b/deps/hiredis/adapters/libuv.h
@@ -15,15 +15,12 @@ typedef struct redisLibuvEvents {
static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+ int ev = (status ? p->events : events);
- if (status != 0) {
- return;
- }
-
- if (p->context != NULL && (events & UV_READABLE)) {
+ if (p->context != NULL && (ev & UV_READABLE)) {
redisAsyncHandleRead(p->context);
}
- if (p->context != NULL && (events & UV_WRITABLE)) {
+ if (p->context != NULL && (ev & UV_WRITABLE)) {
redisAsyncHandleWrite(p->context);
}
}
diff --git a/deps/hiredis/appveyor.yml b/deps/hiredis/appveyor.yml
index 06bbef117..819efbd58 100644
--- a/deps/hiredis/appveyor.yml
+++ b/deps/hiredis/appveyor.yml
@@ -1,24 +1,13 @@
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)
environment:
matrix:
- - CYG_ROOT: C:\cygwin64
- CYG_SETUP: setup-x86_64.exe
- CYG_MIRROR: http://cygwin.mirror.constant.com
- CYG_CACHE: C:\cygwin64\var\cache\setup
- CYG_BASH: C:\cygwin64\bin\bash
+ - CYG_BASH: C:\cygwin64\bin\bash
CC: gcc
- - CYG_ROOT: C:\cygwin
- CYG_SETUP: setup-x86.exe
- CYG_MIRROR: http://cygwin.mirror.constant.com
- CYG_CACHE: C:\cygwin\var\cache\setup
- CYG_BASH: C:\cygwin\bin\bash
+ - CYG_BASH: C:\cygwin\bin\bash
CC: gcc
TARGET: 32bit
TARGET_VARS: 32bit-vars
-# Cache Cygwin files to speed up build
-cache:
- - '%CYG_CACHE%'
clone_depth: 1
# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
@@ -27,8 +16,6 @@ init:
# Install needed build dependencies
install:
- - ps: 'Start-FileDownload "http://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP"'
- - '%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages automake,bison,gcc-core,libtool,make,gettext-devel,gettext,intltool,pkg-config,clang,llvm > NUL 2>&1'
- '%CYG_BASH% -lc "cygcheck -dc cygwin"'
build_script:
diff --git a/deps/hiredis/async.c b/deps/hiredis/async.c
index d955203f8..0cecd30d9 100644
--- a/deps/hiredis/async.c
+++ b/deps/hiredis/async.c
@@ -336,7 +336,8 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
if (ac->err == 0) {
/* For clean disconnects, there should be no pending callbacks. */
- assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
+ int ret = __redisShiftCallback(&ac->replies,NULL);
+ assert(ret == REDIS_ERR);
} else {
/* Disconnection is caused by an error, make sure that pending
* callbacks cannot call new commands. */
@@ -364,6 +365,7 @@ void redisAsyncDisconnect(redisAsyncContext *ac) {
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
redisContext *c = &(ac->c);
dict *callbacks;
+ redisCallback *cb;
dictEntry *de;
int pvariant;
char *stype;
@@ -387,16 +389,28 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
de = dictFind(callbacks,sname);
if (de != NULL) {
- memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));
+ cb = dictGetEntryVal(de);
+
+ /* If this is an subscribe reply decrease pending counter. */
+ if (strcasecmp(stype+pvariant,"subscribe") == 0) {
+ cb->pending_subs -= 1;
+ }
+
+ memcpy(dstcb,cb,sizeof(*dstcb));
/* If this is an unsubscribe message, remove it. */
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
- dictDelete(callbacks,sname);
+ if (cb->pending_subs == 0)
+ dictDelete(callbacks,sname);
/* If this was the last unsubscribe message, revert to
* non-subscribe mode. */
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
- if (reply->element[2]->integer == 0)
+
+ /* Unset subscribed flag only when no pipelined pending subscribe. */
+ if (reply->element[2]->integer == 0
+ && dictSize(ac->sub.channels) == 0
+ && dictSize(ac->sub.patterns) == 0)
c->flags &= ~REDIS_SUBSCRIBED;
}
}
@@ -410,7 +424,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
void redisProcessCallbacks(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
- redisCallback cb = {NULL, NULL, NULL};
+ redisCallback cb = {NULL, NULL, 0, NULL};
void *reply = NULL;
int status;
@@ -492,22 +506,22 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
* write event fires. When connecting was not successful, the connect callback
* is called with a REDIS_ERR status and the context is free'd. */
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
+ int completed = 0;
redisContext *c = &(ac->c);
-
- if (redisCheckSocketError(c) == REDIS_ERR) {
- /* Try again later when connect(2) is still in progress. */
- if (errno == EINPROGRESS)
- return REDIS_OK;
-
- if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);
+ if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
+ /* Error! */
+ redisCheckSocketError(c);
+ if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
__redisAsyncDisconnect(ac);
return REDIS_ERR;
+ } else if (completed == 1) {
+ /* connected! */
+ if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
+ c->flags |= REDIS_CONNECTED;
+ return REDIS_OK;
+ } else {
+ return REDIS_OK;
}
-
- /* Mark context as connected. */
- c->flags |= REDIS_CONNECTED;
- if (ac->onConnect) ac->onConnect(ac,REDIS_OK);
- return REDIS_OK;
}
/* This function should be called when the socket is readable.
@@ -583,6 +597,9 @@ static const char *nextArgument(const char *start, const char **str, size_t *len
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
redisContext *c = &(ac->c);
redisCallback cb;
+ struct dict *cbdict;
+ dictEntry *de;
+ redisCallback *existcb;
int pvariant, hasnext;
const char *cstr, *astr;
size_t clen, alen;
@@ -596,6 +613,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
/* Setup callback */
cb.fn = fn;
cb.privdata = privdata;
+ cb.pending_subs = 1;
/* Find out which command will be appended. */
p = nextArgument(cmd,&cstr,&clen);
@@ -612,9 +630,18 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
sname = sdsnewlen(astr,alen);
if (pvariant)
- ret = dictReplace(ac->sub.patterns,sname,&cb);
+ cbdict = ac->sub.patterns;
else
- ret = dictReplace(ac->sub.channels,sname,&cb);
+ cbdict = ac->sub.channels;
+
+ de = dictFind(cbdict,sname);
+
+ if (de != NULL) {
+ existcb = dictGetEntryVal(de);
+ cb.pending_subs = existcb->pending_subs + 1;
+ }
+
+ ret = dictReplace(cbdict,sname,&cb);
if (ret == 0) sdsfree(sname);
}
@@ -676,6 +703,8 @@ int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *priv
int len;
int status;
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
+ if (len < 0)
+ return REDIS_ERR;
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
sdsfree(cmd);
return status;
diff --git a/deps/hiredis/async.h b/deps/hiredis/async.h
index 59cbf469b..740555c24 100644
--- a/deps/hiredis/async.h
+++ b/deps/hiredis/async.h
@@ -45,6 +45,7 @@ typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
typedef struct redisCallback {
struct redisCallback *next; /* simple singly linked list */
redisCallbackFn *fn;
+ int pending_subs;
void *privdata;
} redisCallback;
@@ -92,6 +93,10 @@ typedef struct redisAsyncContext {
/* Regular command callbacks */
redisCallbackList replies;
+ /* Address used for connect() */
+ struct sockaddr *saddr;
+ size_t addrlen;
+
/* Subscription callbacks */
struct {
redisCallbackList invalid;
diff --git a/deps/hiredis/fmacros.h b/deps/hiredis/fmacros.h
index 9a56643df..3227faafd 100644
--- a/deps/hiredis/fmacros.h
+++ b/deps/hiredis/fmacros.h
@@ -1,25 +1,12 @@
#ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H
-#if defined(__linux__)
-#define _BSD_SOURCE
-#define _DEFAULT_SOURCE
-#endif
-
-#if defined(__CYGWIN__)
-#include <sys/cdefs.h>
-#endif
-
-#if defined(__sun__)
-#define _POSIX_C_SOURCE 200112L
-#else
-#if !(defined(__APPLE__) && defined(__MACH__)) && !(defined(__FreeBSD__))
#define _XOPEN_SOURCE 600
-#endif
-#endif
+#define _POSIX_C_SOURCE 200112L
#if defined(__APPLE__) && defined(__MACH__)
-#define _OSX
+/* Enable TCP_KEEPALIVE */
+#define _DARWIN_C_SOURCE
#endif
#endif
diff --git a/deps/hiredis/hiredis.c b/deps/hiredis/hiredis.c
index 18bdfc99c..bfbf483e1 100644
--- a/deps/hiredis/hiredis.c
+++ b/deps/hiredis/hiredis.c
@@ -84,16 +84,14 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_ARRAY:
if (r->element != NULL) {
for (j = 0; j < r->elements; j++)
- if (r->element[j] != NULL)
- freeReplyObject(r->element[j]);
+ freeReplyObject(r->element[j]);
free(r->element);
}
break;
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
- if (r->str != NULL)
- free(r->str);
+ free(r->str);
break;
}
free(r);
@@ -432,11 +430,7 @@ cleanup:
}
sdsfree(curarg);
-
- /* No need to check cmd since it is the last statement that can fail,
- * but do it anyway to be as defensive as possible. */
- if (cmd != NULL)
- free(cmd);
+ free(cmd);
return error_type;
}
@@ -581,7 +575,7 @@ void __redisSetError(redisContext *c, int type, const char *str) {
} else {
/* Only REDIS_ERR_IO may lack a description! */
assert(type == REDIS_ERR_IO);
- __redis_strerror_r(errno, c->errstr, sizeof(c->errstr));
+ strerror_r(errno, c->errstr, sizeof(c->errstr));
}
}
@@ -596,14 +590,8 @@ static redisContext *redisContextInit(void) {
if (c == NULL)
return NULL;
- c->err = 0;
- c->errstr[0] = '\0';
c->obuf = sdsempty();
c->reader = redisReaderCreate();
- c->tcp.host = NULL;
- c->tcp.source_addr = NULL;
- c->unix_sock.path = NULL;
- c->timeout = NULL;
if (c->obuf == NULL || c->reader == NULL) {
redisFree(c);
@@ -618,18 +606,14 @@ void redisFree(redisContext *c) {
return;
if (c->fd > 0)
close(c->fd);
- if (c->obuf != NULL)
- sdsfree(c->obuf);
- if (c->reader != NULL)
- redisReaderFree(c->reader);
- if (c->tcp.host)
- free(c->tcp.host);
- if (c->tcp.source_addr)
- free(c->tcp.source_addr);
- if (c->unix_sock.path)
- free(c->unix_sock.path);
- if (c->timeout)
- free(c->timeout);
+
+ sdsfree(c->obuf);
+ redisReaderFree(c->reader);
+ free(c->tcp.host);
+ free(c->tcp.source_addr);
+ free(c->unix_sock.path);
+ free(c->timeout);
+ free(c->saddr);
free(c);
}
@@ -710,6 +694,8 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
redisContext *redisConnectBindNonBlock(const char *ip, int port,
const char *source_addr) {
redisContext *c = redisContextInit();
+ if (c == NULL)
+ return NULL;
c->flags &= ~REDIS_BLOCK;
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
return c;
@@ -718,6 +704,8 @@ redisContext *redisConnectBindNonBlock(const char *ip, int port,
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
const char *source_addr) {
redisContext *c = redisContextInit();
+ if (c == NULL)
+ return NULL;
c->flags &= ~REDIS_BLOCK;
c->flags |= REDIS_REUSEADDR;
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
@@ -789,7 +777,7 @@ int redisEnableKeepAlive(redisContext *c) {
/* 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.
*
- * After this function is called, you may use redisContextReadReply to
+ * After this function is called, you may use redisGetReplyFromReader to
* see if there is a reply available. */
int redisBufferRead(redisContext *c) {
char buf[1024*16];
@@ -1007,9 +995,8 @@ void *redisvCommand(redisContext *c, const char *format, va_list ap) {
void *redisCommand(redisContext *c, const char *format, ...) {
va_list ap;
- void *reply = NULL;
va_start(ap,format);
- reply = redisvCommand(c,format,ap);
+ void *reply = redisvCommand(c,format,ap);
va_end(ap);
return reply;
}
diff --git a/deps/hiredis/hiredis.h b/deps/hiredis/hiredis.h
index 423d5e504..1b0d5e659 100644
--- a/deps/hiredis/hiredis.h
+++ b/deps/hiredis/hiredis.h
@@ -40,9 +40,9 @@
#include "sds.h" /* for sds */
#define HIREDIS_MAJOR 0
-#define HIREDIS_MINOR 13
-#define HIREDIS_PATCH 3
-#define HIREDIS_SONAME 0.13
+#define HIREDIS_MINOR 14
+#define HIREDIS_PATCH 0
+#define HIREDIS_SONAME 0.14
/* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */
@@ -80,30 +80,6 @@
* SO_REUSEADDR is being used. */
#define REDIS_CONNECT_RETRIES 10
-/* strerror_r has two completely different prototypes and behaviors
- * depending on system issues, so we need to operate on the error buffer
- * differently depending on which strerror_r we're using. */
-#ifndef _GNU_SOURCE
-/* "regular" POSIX strerror_r that does the right thing. */
-#define __redis_strerror_r(errno, buf, len) \
- do { \
- strerror_r((errno), (buf), (len)); \
- } while (0)
-#else
-/* "bad" GNU strerror_r we need to clean up after. */
-#define __redis_strerror_r(errno, buf, len) \
- do { \
- char *err_str = strerror_r((errno), (buf), (len)); \
- /* If return value _isn't_ the start of the buffer we passed in, \
- * then GNU strerror_r returned an internal static buffer and we \
- * need to copy the result into our private buffer. */ \
- if (err_str != (buf)) { \
- strncpy((buf), err_str, ((len) - 1)); \
- buf[(len)-1] = '\0'; \
- } \
- } while (0)
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -158,6 +134,9 @@ typedef struct redisContext {
char *path;
} unix_sock;
+ /* For non-blocking connect */
+ struct sockadr *saddr;
+ size_t addrlen;
} redisContext;
redisContext *redisConnect(const char *ip, int port);
diff --git a/deps/hiredis/net.c b/deps/hiredis/net.c
index 7d4120985..a4b3abc6d 100644
--- a/deps/hiredis/net.c
+++ b/deps/hiredis/net.c
@@ -65,12 +65,13 @@ static void redisContextCloseFd(redisContext *c) {
}
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
+ int errorno = errno; /* snprintf() may change errno */
char buf[128] = { 0 };
size_t len = 0;
if (prefix != NULL)
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
- __redis_strerror_r(errno, (char *)(buf + len), sizeof(buf) - len);
+ strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
__redisSetError(c,type,buf);
}
@@ -135,14 +136,13 @@ int redisKeepAlive(redisContext *c, int interval) {
val = interval;
-#ifdef _OSX
+#if defined(__APPLE__) && defined(__MACH__)
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR;
}
#else
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
- val = interval;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR;
@@ -221,8 +221,10 @@ static int redisContextWaitReady(redisContext *c, long msec) {
return REDIS_ERR;
}
- if (redisCheckSocketError(c) != REDIS_OK)
+ if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
+ redisCheckSocketError(c);
return REDIS_ERR;
+ }
return REDIS_OK;
}
@@ -232,8 +234,28 @@ static int redisContextWaitReady(redisContext *c, long msec) {
return REDIS_ERR;
}
+int redisCheckConnectDone(redisContext *c, int *completed) {
+ int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);
+ if (rc == 0) {
+ *completed = 1;
+ return REDIS_OK;
+ }
+ switch (errno) {
+ case EISCONN:
+ *completed = 1;
+ return REDIS_OK;
+ case EALREADY:
+ case EINPROGRESS:
+ case EWOULDBLOCK:
+ *completed = 0;
+ return REDIS_OK;
+ default:
+ return REDIS_ERR;
+ }
+}
+
int redisCheckSocketError(redisContext *c) {
- int err = 0;
+ int err = 0, errno_saved = errno;
socklen_t errlen = sizeof(err);
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
@@ -241,6 +263,10 @@ int redisCheckSocketError(redisContext *c) {
return REDIS_ERR;
}
+ if (err == 0) {
+ err = errno_saved;
+ }
+
if (err) {
errno = err;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
@@ -285,8 +311,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
* This is a bit ugly, but atleast it works and doesn't leak memory.
**/
if (c->tcp.host != addr) {
- if (c->tcp.host)
- free(c->tcp.host);
+ free(c->tcp.host);
c->tcp.host = strdup(addr);
}
@@ -299,8 +324,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
memcpy(c->timeout, timeout, sizeof(struct timeval));
}
} else {
- if (c->timeout)
- free(c->timeout);
+ free(c->timeout);
c->timeout = NULL;
}
@@ -356,6 +380,7 @@ addrretry:
n = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
sizeof(n)) < 0) {
+ freeaddrinfo(bservinfo);
goto error;
}
}
@@ -374,12 +399,27 @@ addrretry:
goto error;
}
}
+
+ /* For repeat connection */
+ if (c->saddr) {
+ free(c->saddr);
+ }
+ c->saddr = malloc(p->ai_addrlen);
+ memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
+ c->addrlen = p->ai_addrlen;
+
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) {
redisContextCloseFd(c);
continue;
- } else if (errno == EINPROGRESS && !blocking) {
- /* This is ok. */
+ } else if (errno == EINPROGRESS) {
+ if (blocking) {
+ goto wait_for_ready;
+ }
+ /* This is ok.
+ * Note that even when it's in blocking mode, we unset blocking
+ * for `connect()`
+ */
} else if (errno == EADDRNOTAVAIL && reuseaddr) {
if (++reuses >= REDIS_CONNECT_RETRIES) {
goto error;
@@ -388,6 +428,7 @@ addrretry:
goto addrretry;
}
} else {
+ wait_for_ready:
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
goto error;
}
@@ -411,7 +452,10 @@ addrretry:
error:
rv = REDIS_ERR;
end:
- freeaddrinfo(servinfo);
+ if(servinfo) {
+ freeaddrinfo(servinfo);
+ }
+
return rv; // Need to return REDIS_OK if alright
}
@@ -431,7 +475,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
struct sockaddr_un sa;
long timeout_msec = -1;
- if (redisCreateSocket(c,AF_LOCAL) < 0)
+ if (redisCreateSocket(c,AF_UNIX) < 0)
return REDIS_ERR;
if (redisSetBlocking(c,0) != REDIS_OK)
return REDIS_ERR;
@@ -448,15 +492,14 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
memcpy(c->timeout, timeout, sizeof(struct timeval));
}
} else {
- if (c->timeout)
- free(c->timeout);
+ free(c->timeout);
c->timeout = NULL;
}
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
return REDIS_ERR;
- sa.sun_family = AF_LOCAL;
+ sa.sun_family = AF_UNIX;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) {
diff --git a/deps/hiredis/net.h b/deps/hiredis/net.h
index 2f1a0bf85..a11594e68 100644
--- a/deps/hiredis/net.h
+++ b/deps/hiredis/net.h
@@ -37,10 +37,6 @@
#include "hiredis.h"
-#if defined(__sun)
-#define AF_LOCAL AF_UNIX
-#endif
-
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);
@@ -49,5 +45,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
const char *source_addr);
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
int redisKeepAlive(redisContext *c, int interval);
+int redisCheckConnectDone(redisContext *c, int *completed);
#endif
diff --git a/deps/hiredis/read.c b/deps/hiredis/read.c
index 50333b534..084d0b078 100644
--- a/deps/hiredis/read.c
+++ b/deps/hiredis/read.c
@@ -39,6 +39,7 @@
#include <assert.h>
#include <errno.h>
#include <ctype.h>
+#include <limits.h>
#include "read.h"
#include "sds.h"
@@ -52,11 +53,9 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
}
/* Clear input buffer on errors. */
- if (r->buf != NULL) {
- sdsfree(r->buf);
- r->buf = NULL;
- r->pos = r->len = 0;
- }
+ sdsfree(r->buf);
+ r->buf = NULL;
+ r->pos = r->len = 0;
/* Reset task stack. */
r->ridx = -1;
@@ -143,33 +142,79 @@ static char *seekNewline(char *s, size_t len) {
return NULL;
}
-/* Read a long long value starting at *s, under the assumption that it will be
- * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
-static long long readLongLong(char *s) {
- long long v = 0;
- int dec, mult = 1;
- char c;
-
- if (*s == '-') {
- mult = -1;
- s++;
- } else if (*s == '+') {
- mult = 1;
- s++;
+/* Convert a string into a long long. Returns REDIS_OK if the string could be
+ * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
+ * will be set to the parsed value when appropriate.
+ *
+ * Note that this function demands that the string strictly represents
+ * a long long: no spaces or other characters before or after the string
+ * representing the number are accepted, nor zeroes at the start if not
+ * for the string "0" representing the zero number.
+ *
+ * Because of its strictness, it is safe to use this function to check if
+ * you can convert a string into a long long, and obtain back the string
+ * from the number without any loss in the string representation. */
+static int string2ll(const char *s, size_t slen, long long *value) {
+ const char *p = s;
+ size_t plen = 0;
+ int negative = 0;
+ unsigned long long v;
+
+ if (plen == slen)
+ return REDIS_ERR;
+
+ /* Special case: first and only digit is 0. */
+ if (slen == 1 && p[0] == '0') {
+ if (value != NULL) *value = 0;
+ return REDIS_OK;
}
- while ((c = *(s++)) != '\r') {
- dec = c - '0';
- if (dec >= 0 && dec < 10) {
- v *= 10;
- v += dec;
- } else {
- /* Should not happen... */
- return -1;
- }
+ if (p[0] == '-') {
+ negative = 1;
+ p++; plen++;
+
+ /* Abort on only a negative sign. */
+ if (plen == slen)
+ return REDIS_ERR;
}
- return mult*v;
+ /* First digit should be 1-9, otherwise the string should just be 0. */
+ if (p[0] >= '1' && p[0] <= '9') {
+ v = p[0]-'0';
+ p++; plen++;
+ } else if (p[0] == '0' && slen == 1) {
+ *value = 0;
+ return REDIS_OK;
+ } else {
+ return REDIS_ERR;
+ }
+
+ while (plen < slen && p[0] >= '0' && p[0] <= '9') {
+ if (v > (ULLONG_MAX / 10)) /* Overflow. */
+ return REDIS_ERR;
+ v *= 10;
+
+ if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
+ return REDIS_ERR;
+ v += p[0]-'0';
+
+ p++; plen++;
+ }
+
+ /* Return if not all bytes were used. */
+ if (plen < slen)
+ return REDIS_ERR;
+
+ if (negative) {
+ if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
+ return REDIS_ERR;
+ if (value != NULL) *value = -v;
+ } else {
+ if (v > LLONG_MAX) /* Overflow. */
+ return REDIS_ERR;
+ if (value != NULL) *value = v;
+ }
+ return REDIS_OK;
}
static char *readLine(redisReader *r, int *_len) {
@@ -220,10 +265,17 @@ static int processLineItem(redisReader *r) {
if ((p = readLine(r,&len)) != NULL) {
if (cur->type == REDIS_REPLY_INTEGER) {
- if (r->fn && r->fn->createInteger)
- obj = r->fn->createInteger(cur,readLongLong(p));
- else
+ if (r->fn && r->fn->createInteger) {
+ long long v;
+ if (string2ll(p, len, &v) == REDIS_ERR) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Bad integer value");
+ return REDIS_ERR;
+ }
+ obj = r->fn->createInteger(cur,v);
+ } else {
obj = (void*)REDIS_REPLY_INTEGER;
+ }
} else {
/* Type will be error or status. */
if (r->fn && r->fn->createString)
@@ -250,7 +302,7 @@ static int processBulkItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
void *obj = NULL;
char *p, *s;
- long len;
+ long long len;
unsigned long bytelen;
int success = 0;
@@ -259,9 +311,20 @@ static int processBulkItem(redisReader *r) {
if (s != NULL) {
p = r->buf+r->pos;
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
- len = readLongLong(p);
- if (len < 0) {
+ if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Bad bulk string length");
+ return REDIS_ERR;
+ }
+
+ if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Bulk string length out of range");
+ return REDIS_ERR;
+ }
+
+ if (len == -1) {
/* The nil object can always be created. */
if (r->fn && r->fn->createNil)
obj = r->fn->createNil(cur);
@@ -299,12 +362,13 @@ static int processBulkItem(redisReader *r) {
return REDIS_ERR;
}
-static int processMultiBulkItem(redisReader *r) {
+/* Process the array, map and set types. */
+static int processAggregateItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
void *obj;
char *p;
- long elements;
- int root = 0;
+ long long elements;
+ int root = 0, len;
/* Set error for nested multi bulks with depth > 7 */
if (r->ridx == 8) {
@@ -313,10 +377,21 @@ static int processMultiBulkItem(redisReader *r) {
return REDIS_ERR;
}
- if ((p = readLine(r,NULL)) != NULL) {
- elements = readLongLong(p);
+ if ((p = readLine(r,&len)) != NULL) {
+ if (string2ll(p, len, &elements) == REDIS_ERR) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Bad multi-bulk length");
+ return REDIS_ERR;
+ }
+
root = (r->ridx == 0);
+ if (elements < -1 || elements > INT_MAX) {
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "Multi-bulk length out of range");
+ return REDIS_ERR;
+ }
+
if (elements == -1) {
if (r->fn && r->fn->createNil)
obj = r->fn->createNil(cur);
@@ -330,10 +405,12 @@ static int processMultiBulkItem(redisReader *r) {
moveToNextTask(r);
} else {
+ if (cur->type == REDIS_REPLY_MAP) elements *= 2;
+
if (r->fn && r->fn->createArray)
obj = r->fn->createArray(cur,elements);
else
- obj = (void*)REDIS_REPLY_ARRAY;
+ obj = (void*)(long)cur->type;
if (obj == NULL) {
__redisReaderSetErrorOOM(r);
@@ -387,6 +464,12 @@ static int processItem(redisReader *r) {
case '*':
cur->type = REDIS_REPLY_ARRAY;
break;
+ case '%':
+ cur->type = REDIS_REPLY_MAP;
+ break;
+ case '~':
+ cur->type = REDIS_REPLY_SET;
+ break;
default:
__redisReaderSetErrorProtocolByte(r,*p);
return REDIS_ERR;
@@ -406,7 +489,9 @@ static int processItem(redisReader *r) {
case REDIS_REPLY_STRING:
return processBulkItem(r);
case REDIS_REPLY_ARRAY:
- return processMultiBulkItem(r);
+ case REDIS_REPLY_MAP:
+ case REDIS_REPLY_SET:
+ return processAggregateItem(r);
default:
assert(NULL);
return REDIS_ERR; /* Avoid warning. */
@@ -416,12 +501,10 @@ static int processItem(redisReader *r) {
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
redisReader *r;
- r = calloc(sizeof(redisReader),1);
+ r = calloc(1,sizeof(redisReader));
if (r == NULL)
return NULL;
- r->err = 0;
- r->errstr[0] = '\0';
r->fn = fn;
r->buf = sdsempty();
r->maxbuf = REDIS_READER_MAX_BUF;
@@ -435,10 +518,11 @@ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
}
void redisReaderFree(redisReader *r) {
+ if (r == NULL)
+ return;
if (r->reply != NULL && r->fn && r->fn->freeObject)
r->fn->freeObject(r->reply);
- if (r->buf != NULL)
- sdsfree(r->buf);
+ sdsfree(r->buf);
free(r);
}
diff --git a/deps/hiredis/read.h b/deps/hiredis/read.h
index 2988aa453..84ee15cb6 100644
--- a/deps/hiredis/read.h
+++ b/deps/hiredis/read.h
@@ -53,6 +53,14 @@
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
+#define REDIS_REPLY_DOUBLE 7
+#define REDIS_REPLY_BOOL 8
+#define REDIS_REPLY_VERB 9
+#define REDIS_REPLY_MAP 9
+#define REDIS_REPLY_SET 10
+#define REDIS_REPLY_ATTR 11
+#define REDIS_REPLY_PUSH 12
+#define REDIS_REPLY_BIGNUM 13
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
diff --git a/deps/hiredis/sds.c b/deps/hiredis/sds.c
index 923ffd82f..44777b10c 100644
--- a/deps/hiredis/sds.c
+++ b/deps/hiredis/sds.c
@@ -219,7 +219,10 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
hdrlen = sdsHdrSize(type);
if (oldtype==type) {
newsh = s_realloc(sh, hdrlen+newlen+1);
- if (newsh == NULL) return NULL;
+ if (newsh == NULL) {
+ s_free(sh);
+ return NULL;
+ }
s = (char*)newsh+hdrlen;
} else {
/* Since the header size changes, need to move the string forward,
@@ -592,6 +595,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* Make sure there is always space for at least 1 char. */
if (sdsavail(s)==0) {
s = sdsMakeRoomFor(s,1);
+ if (s == NULL) goto fmt_error;
}
switch(*f) {
@@ -605,6 +609,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = (next == 's') ? strlen(str) : sdslen(str);
if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l);
+ if (s == NULL) goto fmt_error;
}
memcpy(s+i,str,l);
sdsinclen(s,l);
@@ -621,6 +626,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = sdsll2str(buf,num);
if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l);
+ if (s == NULL) goto fmt_error;
}
memcpy(s+i,buf,l);
sdsinclen(s,l);
@@ -638,6 +644,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = sdsull2str(buf,unum);
if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l);
+ if (s == NULL) goto fmt_error;
}
memcpy(s+i,buf,l);
sdsinclen(s,l);
@@ -662,6 +669,10 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* Add null-term */
s[i] = '\0';
return s;
+
+fmt_error:
+ va_end(ap);
+ return NULL;
}
/* Remove the part of the string from left and from right composed just of
@@ -1018,10 +1029,18 @@ sds *sdssplitargs(const char *line, int *argc) {
if (*p) p++;
}
/* add the token to the vector */
- vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
- vector[*argc] = current;
- (*argc)++;
- current = NULL;
+ {
+ char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
+ if (new_vector == NULL) {
+ s_free(vector);
+ return NULL;
+ }
+
+ vector = new_vector;
+ vector[*argc] = current;
+ (*argc)++;
+ current = NULL;
+ }
} else {
/* Even on empty input string return something not NULL. */
if (vector == NULL) vector = s_malloc(sizeof(void*));
diff --git a/deps/hiredis/test.c b/deps/hiredis/test.c
index a23d60676..79cff4308 100644
--- a/deps/hiredis/test.c
+++ b/deps/hiredis/test.c
@@ -3,7 +3,9 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
+#include <sys/socket.h>
#include <sys/time.h>
+#include <netdb.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
@@ -91,7 +93,7 @@ static int disconnect(redisContext *c, int keep_fd) {
return -1;
}
-static redisContext *connect(struct config config) {
+static redisContext *do_connect(struct config config) {
redisContext *c = NULL;
if (config.type == CONN_TCP) {
@@ -248,7 +250,7 @@ static void test_append_formatted_commands(struct config config) {
char *cmd;
int len;
- c = connect(config);
+ c = do_connect(config);
test("Append format command: ");
@@ -302,6 +304,82 @@ static void test_reply_reader(void) {
strncasecmp(reader->errstr,"No support for",14) == 0);
redisReaderFree(reader);
+ test("Correctly parses LLONG_MAX: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, ":9223372036854775807\r\n",22);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_OK &&
+ ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
+ ((redisReply*)reply)->integer == LLONG_MAX);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+ test("Set error when > LLONG_MAX: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, ":9223372036854775808\r\n",22);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_ERR &&
+ strcasecmp(reader->errstr,"Bad integer value") == 0);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+ test("Correctly parses LLONG_MIN: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_OK &&
+ ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
+ ((redisReply*)reply)->integer == LLONG_MIN);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+ test("Set error when < LLONG_MIN: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_ERR &&
+ strcasecmp(reader->errstr,"Bad integer value") == 0);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+ test("Set error when array < -1: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_ERR &&
+ strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+ test("Set error when bulk < -1: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, "$-2\r\nasdf\r\n",11);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_ERR &&
+ strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+ test("Set error when array > INT_MAX: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_ERR &&
+ strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+
+#if LLONG_MAX > SIZE_MAX
+ test("Set error when bulk > SIZE_MAX: ");
+ reader = redisReaderCreate();
+ redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
+ ret = redisReaderGetReply(reader,&reply);
+ test_cond(ret == REDIS_ERR &&
+ strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
+ freeReplyObject(reply);
+ redisReaderFree(reader);
+#endif
+
test("Works with NULL functions for reply: ");
reader = redisReaderCreate();
reader->fn = NULL;
@@ -358,18 +436,32 @@ static void test_free_null(void) {
static void test_blocking_connection_errors(void) {
redisContext *c;
-
- test("Returns error when host cannot be resolved: ");
- c = redisConnect((char*)"idontexist.test", 6379);
- test_cond(c->err == REDIS_ERR_OTHER &&
- (strcmp(c->errstr,"Name or service not known") == 0 ||
- strcmp(c->errstr,"Can't resolve: idontexist.test") == 0 ||
- strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 ||
- strcmp(c->errstr,"No address associated with hostname") == 0 ||
- strcmp(c->errstr,"Temporary failure in name resolution") == 0 ||
- strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 ||
- strcmp(c->errstr,"no address associated with name") == 0));
- redisFree(c);
+ struct addrinfo hints = {.ai_family = AF_INET};
+ struct addrinfo *ai_tmp = NULL;
+ const char *bad_domain = "idontexist.com";
+
+ int rv = getaddrinfo(bad_domain, "6379", &hints, &ai_tmp);
+ if (rv != 0) {
+ // Address does *not* exist
+ test("Returns error when host cannot be resolved: ");
+ // First see if this domain name *actually* resolves to NXDOMAIN
+ c = redisConnect("dontexist.com", 6379);
+ test_cond(
+ c->err == REDIS_ERR_OTHER &&
+ (strcmp(c->errstr, "Name or service not known") == 0 ||
+ strcmp(c->errstr, "Can't resolve: sadkfjaskfjsa.com") == 0 ||
+ strcmp(c->errstr,
+ "nodename nor servname provided, or not known") == 0 ||
+ strcmp(c->errstr, "No address associated with hostname") == 0 ||
+ strcmp(c->errstr, "Temporary failure in name resolution") == 0 ||
+ strcmp(c->errstr,
+ "hostname nor servname provided, or not known") == 0 ||
+ strcmp(c->errstr, "no address associated with name") == 0));
+ redisFree(c);
+ } else {
+ printf("Skipping NXDOMAIN test. Found evil ISP!\n");
+ freeaddrinfo(ai_tmp);
+ }
test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 1);
@@ -387,7 +479,7 @@ static void test_blocking_connection(struct config config) {
redisContext *c;
redisReply *reply;
- c = connect(config);
+ c = do_connect(config);
test("Is able to deliver commands: ");
reply = redisCommand(c,"PING");
@@ -468,7 +560,7 @@ static void test_blocking_connection_timeouts(struct config config) {
const char *cmd = "DEBUG SLEEP 3\r\n";
struct timeval tv;
- c = connect(config);
+ c = do_connect(config);
test("Successfully completes a command when the timeout is not exceeded: ");
reply = redisCommand(c,"SET foo fast");
freeReplyObject(reply);
@@ -480,7 +572,7 @@ static void test_blocking_connection_timeouts(struct config config) {
freeReplyObject(reply);
disconnect(c, 0);
- c = connect(config);
+ c = do_connect(config);
test("Does not return a reply when the command times out: ");
s = write(c->fd, cmd, strlen(cmd));
tv.tv_sec = 0;
@@ -514,7 +606,7 @@ static void test_blocking_io_errors(struct config config) {
int major, minor;
/* Connect to target given by config. */
- c = connect(config);
+ c = do_connect(config);
{
/* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:";
@@ -549,7 +641,7 @@ static void test_blocking_io_errors(struct config config) {
strcmp(c->errstr,"Server closed the connection") == 0);
redisFree(c);
- c = connect(config);
+ c = do_connect(config);
test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 };
assert(redisSetTimeout(c,tv) == REDIS_OK);
@@ -583,7 +675,7 @@ static void test_invalid_timeout_errors(struct config config) {
}
static void test_throughput(struct config config) {
- redisContext *c = connect(config);
+ redisContext *c = do_connect(config);
redisReply **replies;
int i, num;
long long t1, t2;
@@ -616,6 +708,17 @@ static void test_throughput(struct config config) {
free(replies);
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
+ replies = malloc(sizeof(redisReply*)*num);
+ t1 = usec();
+ for (i = 0; i < num; i++) {
+ replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
+ }
+ t2 = usec();
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
+ free(replies);
+ printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
+
num = 10000;
replies = malloc(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
@@ -644,6 +747,19 @@ static void test_throughput(struct config config) {
free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
+ replies = malloc(sizeof(redisReply*)*num);
+ for (i = 0; i < num; i++)
+ redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
+ t1 = usec();
+ for (i = 0; i < num; i++) {
+ assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
+ }
+ t2 = usec();
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
+ free(replies);
+ printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
+
disconnect(c, 0);
}