The hiredis lib shipped with Redis was updated to latest version.

This commit is contained in:
antirez 2012-02-19 20:26:36 +01:00
parent cb598cdd59
commit b66e5add82
22 changed files with 1264 additions and 737 deletions

16
deps/hiredis/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,16 @@
### 0.10.1
* Makefile overhaul. Important to check out if you override one or more
variables using environment variables or via arguments to the "make" tool.
* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements
being created by the default reply object functions.
* Issue #43: Don't crash in an asynchronous context when Redis returns an error
reply after the connection has been made (this happens when the maximum
number of connections is reached).
### 0.10.0
* See commit log.

31
deps/hiredis/COPYING vendored
View File

@ -1,10 +1,29 @@
Copyright (c) 2006-2009, Salvatore Sanfilippo Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions of source code must retain the above copyright notice,
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. this list of conditions and the following disclaimer.
* Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

165
deps/hiredis/Makefile vendored
View File

@ -1,115 +1,148 @@
# Hiredis Makefile # Hiredis Makefile
# Copyright (C) 2010 Salvatore Sanfilippo <antirez at gmail dot com> # Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
# This file is released under the BSD license, see the COPYING file # This file is released under the BSD license, see the COPYING file
OBJ = net.o hiredis.o sds.o async.o OBJ=net.o hiredis.o sds.o async.o
BINS = hiredis-example hiredis-test BINS=hiredis-example hiredis-test
LIBNAME=libhiredis
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') HIREDIS_MAJOR=0
HIREDIS_MINOR=10
# Fallback to gcc when $CC is not in $PATH.
CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
OPTIMIZATION?=-O3 OPTIMIZATION?=-O3
ifeq ($(uname_S),SunOS) WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
LDFLAGS?=-L. -Wl,-R,.
DYLIBNAME?=libhiredis.so
DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
else
ifeq ($(uname_S),Darwin)
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread
LDFLAGS?=-L. -Wl,-rpath,.
OBJARCH?=-arch i386 -arch x86_64
DYLIBNAME?=libhiredis.dylib
DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ}
STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
else
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread
LDFLAGS?=-L. -Wl,-rpath,.
DYLIBNAME?=libhiredis.so
DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
endif
endif
CCOPT= $(CFLAGS) $(CCLINK)
DEBUG?= -g -ggdb DEBUG?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG)
REAL_LDFLAGS=$(LDFLAGS)
PREFIX?= /usr/local DYLIBSUFFIX=so
INSTALL_INC= $(PREFIX)/include/hiredis STLIBSUFFIX=a
INSTALL_LIB= $(PREFIX)/lib DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
INSTALL= cp -a DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=ar rcs $(STLIBNAME)
all: ${DYLIBNAME} ${BINS} # Platform-specific overrides
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_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)
DYLIB_MAJOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
endif
all: $(DYLIBNAME) $(BINS)
# Deps (use make dep to generate this) # Deps (use make dep to generate this)
net.o: net.c fmacros.h net.h net.o: net.c fmacros.h net.h hiredis.h
async.o: async.c async.h hiredis.h sds.h util.h dict.c dict.h async.o: async.c async.h hiredis.h sds.h dict.c dict.h
example.o: example.c hiredis.h example.o: example.c hiredis.h
hiredis.o: hiredis.c hiredis.h net.h sds.h util.h hiredis.o: hiredis.c fmacros.h hiredis.h net.h sds.h
sds.o: sds.c sds.h sds.o: sds.c sds.h
test.o: test.c hiredis.h test.o: test.c hiredis.h
${DYLIBNAME}: ${OBJ} $(DYLIBNAME): $(OBJ)
${DYLIB_MAKE_CMD} $(DYLIB_MAKE_CMD) $(OBJ)
${STLIBNAME}: ${OBJ} $(STLIBNAME): $(OBJ)
${STLIB_MAKE_CMD} $(STLIB_MAKE_CMD) $(OBJ)
dynamic: ${DYLIBNAME} dynamic: $(DYLIBNAME)
static: ${STLIBNAME} static: $(STLIBNAME)
# Binaries: # Binaries:
hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME} hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -levent example-libevent.c $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)
hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME} hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -lev example-libev.c $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)
ifndef AE_DIR ifndef AE_DIR
hiredis-example-ae: hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)" @echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@false @false
else else
hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME} hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) -lhiredis example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
endif endif
hiredis-%: %.o ${DYLIBNAME} hiredis-%: %.o $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis $< $(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
test: hiredis-test test: hiredis-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 || \
( kill `cat /tmp/hiredis-test-redis.pid` && false )
kill `cat /tmp/hiredis-test-redis.pid`
.c.o: .c.o:
$(CC) -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $< $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
clean: clean:
rm -rf ${DYLIBNAME} ${STLIBNAME} $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
dep: dep:
$(CC) -MM *.c $(CC) -MM *.c
install: ${DYLIBNAME} ${STLIBNAME} # Installation related variables and target
mkdir -p $(INSTALL_INC) $(INSTALL_LIB) PREFIX?=/usr/local
$(INSTALL) hiredis.h async.h adapters $(INSTALL_INC) INCLUDE_PATH?=include/hiredis
$(INSTALL) ${DYLIBNAME} ${STLIBNAME} $(INSTALL_LIB) LIBRARY_PATH?=lib
INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
ifeq ($(uname_S),SunOS)
INSTALL?= cp -r
endif
INSTALL?= cp -a
install: $(DYLIBNAME) $(STLIBNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
32bit: 32bit:
@echo "" @echo ""
@echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386" @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
@echo "" @echo ""
$(MAKE) ARCH="-m32" $(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
gprof: gprof:
$(MAKE) PROF="-pg" $(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
gcov: gcov:
$(MAKE) PROF="-fprofile-arcs -ftest-coverage" $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
coverage: gcov
make check
mkdir -p tmp/lcov
lcov -d . -c -o tmp/lcov/hiredis.info
genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
noopt: noopt:
$(MAKE) OPTIMIZATION="" $(MAKE) OPTIMIZATION=""
.PHONY: all test check clean dep install 32bit gprof gcov noopt

View File

@ -116,6 +116,12 @@ Note that this function will take care of freeing sub-replies objects
contained in arrays and nested arrays, so there is no need for the user to contained in arrays and nested arrays, so there is no need for the user to
free the sub replies (it is actually harmful and will corrupt the memory). free the sub replies (it is actually harmful and will corrupt the memory).
**Important:** the current version of hiredis (0.10.0) free's replies when the
asynchronous API is used. This means you should not call `freeReplyObject` when
you use this API. The reply is cleaned up by hiredis _after_ the callback
returns. This behavior will probably change in future releases, so make sure to
keep an eye on the changelog when upgrading (see issue #39).
### Cleaning up ### Cleaning up
To disconnect and free the context the following function can be used: To disconnect and free the context the following function can be used:
@ -280,7 +286,8 @@ is being disconnected per user-request, no new commands may be added to the outp
returned on calls to the `redisAsyncCommand` family. returned on calls to the `redisAsyncCommand` family.
If the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback If the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback
for a command is non-`NULL`, it is responsible for cleaning up the reply. for a command is non-`NULL`, the memory is free'd immediately following the callback: the reply is only
valid for the duration of the callback.
All pending callbacks are called with a `NULL` reply when the context encountered an error. All pending callbacks are called with a `NULL` reply when the context encountered an error.
@ -303,7 +310,41 @@ See the `adapters/` directory for bindings to *libev* and *libevent*.
## Reply parsing API ## Reply parsing API
To be done. Hiredis comes with a reply parsing API that makes it easy for writing higher
level language bindings.
The reply parsing API consists of the following functions:
redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *reader);
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
int redisReaderGetReply(redisReader *reader, void **reply);
### Usage
The function `redisReaderCreate` creates a `redisReader` structure that holds a
buffer with unparsed data and state for the protocol parser.
Incoming data -- most likely from a socket -- can be placed in the internal
buffer of the `redisReader` using `redisReaderFeed`. This function will make a
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed
when `redisReaderGetReply` is called. This function returns an integer status
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).
### Customizing replies
The function `redisReaderGetReply` creates `redisReply` and makes the function
argument `reply` point to the created `redisReply` variable. For instance, if
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`
will hold the status as a vanilla C string. However, the functions that are
responsible for creating instances of the `redisReply` can be customized by
setting the `fn` field on the `redisReader` struct. This should be done
immediately after creating the `redisReader`.
For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)
uses customized reply object functions to create Ruby objects.
## AUTHORS ## AUTHORS

2
deps/hiredis/TODO vendored
View File

@ -1,2 +0,0 @@
- add redisCommandVector()
- add support for pipelining

View File

@ -1,3 +1,5 @@
#ifndef __HIREDIS_AE_H__
#define __HIREDIS_AE_H__
#include <sys/types.h> #include <sys/types.h>
#include <ae.h> #include <ae.h>
#include "../hiredis.h" #include "../hiredis.h"
@ -10,21 +12,21 @@ typedef struct redisAeEvents {
int reading, writing; int reading, writing;
} redisAeEvents; } redisAeEvents;
void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask); ((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleRead(e->context); redisAsyncHandleRead(e->context);
} }
void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask); ((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleWrite(e->context); redisAsyncHandleWrite(e->context);
} }
void redisAeAddRead(void *privdata) { static void redisAeAddRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop; aeEventLoop *loop = e->loop;
if (!e->reading) { if (!e->reading) {
@ -33,7 +35,7 @@ void redisAeAddRead(void *privdata) {
} }
} }
void redisAeDelRead(void *privdata) { static void redisAeDelRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop; aeEventLoop *loop = e->loop;
if (e->reading) { if (e->reading) {
@ -42,7 +44,7 @@ void redisAeDelRead(void *privdata) {
} }
} }
void redisAeAddWrite(void *privdata) { static void redisAeAddWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop; aeEventLoop *loop = e->loop;
if (!e->writing) { if (!e->writing) {
@ -51,7 +53,7 @@ void redisAeAddWrite(void *privdata) {
} }
} }
void redisAeDelWrite(void *privdata) { static void redisAeDelWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop; aeEventLoop *loop = e->loop;
if (e->writing) { if (e->writing) {
@ -60,19 +62,19 @@ void redisAeDelWrite(void *privdata) {
} }
} }
void redisAeCleanup(void *privdata) { static void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata; redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata); redisAeDelRead(privdata);
redisAeDelWrite(privdata); redisAeDelWrite(privdata);
free(e); free(e);
} }
int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisAeEvents *e; redisAeEvents *e;
/* Nothing should be attached when something is already attached */ /* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL) if (ac->ev.data != NULL)
return REDIS_ERR; return REDIS_ERR;
/* Create container for context and r/w events */ /* Create container for context and r/w events */
@ -83,13 +85,13 @@ int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
e->reading = e->writing = 0; e->reading = e->writing = 0;
/* Register functions to start/stop listening for events */ /* Register functions to start/stop listening for events */
ac->evAddRead = redisAeAddRead; ac->ev.addRead = redisAeAddRead;
ac->evDelRead = redisAeDelRead; ac->ev.delRead = redisAeDelRead;
ac->evAddWrite = redisAeAddWrite; ac->ev.addWrite = redisAeAddWrite;
ac->evDelWrite = redisAeDelWrite; ac->ev.delWrite = redisAeDelWrite;
ac->evCleanup = redisAeCleanup; ac->ev.cleanup = redisAeCleanup;
ac->_adapter_data = e; ac->ev.data = e;
return REDIS_OK; return REDIS_OK;
} }
#endif

View File

@ -1,3 +1,6 @@
#ifndef __HIREDIS_LIBEV_H__
#define __HIREDIS_LIBEV_H__
#include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <ev.h> #include <ev.h>
#include "../hiredis.h" #include "../hiredis.h"
@ -10,7 +13,7 @@ typedef struct redisLibevEvents {
ev_io rev, wev; ev_io rev, wev;
} redisLibevEvents; } redisLibevEvents;
void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY #if EV_MULTIPLICITY
((void)loop); ((void)loop);
#endif #endif
@ -20,7 +23,7 @@ void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
redisAsyncHandleRead(e->context); redisAsyncHandleRead(e->context);
} }
void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY #if EV_MULTIPLICITY
((void)loop); ((void)loop);
#endif #endif
@ -30,7 +33,7 @@ void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
redisAsyncHandleWrite(e->context); redisAsyncHandleWrite(e->context);
} }
void redisLibevAddRead(void *privdata) { static void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); ((void)loop);
@ -40,7 +43,7 @@ void redisLibevAddRead(void *privdata) {
} }
} }
void redisLibevDelRead(void *privdata) { static void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); ((void)loop);
@ -50,7 +53,7 @@ void redisLibevDelRead(void *privdata) {
} }
} }
void redisLibevAddWrite(void *privdata) { static void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); ((void)loop);
@ -60,7 +63,7 @@ void redisLibevAddWrite(void *privdata) {
} }
} }
void redisLibevDelWrite(void *privdata) { static void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); ((void)loop);
@ -70,19 +73,19 @@ void redisLibevDelWrite(void *privdata) {
} }
} }
void redisLibevCleanup(void *privdata) { static void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata); redisLibevDelRead(privdata);
redisLibevDelWrite(privdata); redisLibevDelWrite(privdata);
free(e); free(e);
} }
int redisLibevAttach(EV_P_ redisAsyncContext *ac) { static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisLibevEvents *e; redisLibevEvents *e;
/* Nothing should be attached when something is already attached */ /* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL) if (ac->ev.data != NULL)
return REDIS_ERR; return REDIS_ERR;
/* Create container for context and r/w events */ /* Create container for context and r/w events */
@ -98,12 +101,12 @@ int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
e->wev.data = e; e->wev.data = e;
/* Register functions to start/stop listening for events */ /* Register functions to start/stop listening for events */
ac->evAddRead = redisLibevAddRead; ac->ev.addRead = redisLibevAddRead;
ac->evDelRead = redisLibevDelRead; ac->ev.delRead = redisLibevDelRead;
ac->evAddWrite = redisLibevAddWrite; ac->ev.addWrite = redisLibevAddWrite;
ac->evDelWrite = redisLibevDelWrite; ac->ev.delWrite = redisLibevDelWrite;
ac->evCleanup = redisLibevCleanup; ac->ev.cleanup = redisLibevCleanup;
ac->_adapter_data = e; ac->ev.data = e;
/* Initialize read/write events */ /* Initialize read/write events */
ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ); ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
@ -111,3 +114,4 @@ int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
return REDIS_OK; return REDIS_OK;
} }
#endif

View File

@ -1,4 +1,5 @@
#include <sys/types.h> #ifndef __HIREDIS_LIBEVENT_H__
#define __HIREDIS_LIBEVENT_H__
#include <event.h> #include <event.h>
#include "../hiredis.h" #include "../hiredis.h"
#include "../async.h" #include "../async.h"
@ -8,51 +9,51 @@ typedef struct redisLibeventEvents {
struct event rev, wev; struct event rev, wev;
} redisLibeventEvents; } redisLibeventEvents;
void redisLibeventReadEvent(int fd, short event, void *arg) { static void redisLibeventReadEvent(int fd, short event, void *arg) {
((void)fd); ((void)event); ((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg; redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleRead(e->context); redisAsyncHandleRead(e->context);
} }
void redisLibeventWriteEvent(int fd, short event, void *arg) { static void redisLibeventWriteEvent(int fd, short event, void *arg) {
((void)fd); ((void)event); ((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg; redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleWrite(e->context); redisAsyncHandleWrite(e->context);
} }
void redisLibeventAddRead(void *privdata) { static void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->rev,NULL); event_add(&e->rev,NULL);
} }
void redisLibeventDelRead(void *privdata) { static void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev); event_del(&e->rev);
} }
void redisLibeventAddWrite(void *privdata) { static void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->wev,NULL); event_add(&e->wev,NULL);
} }
void redisLibeventDelWrite(void *privdata) { static void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->wev); event_del(&e->wev);
} }
void redisLibeventCleanup(void *privdata) { static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev); event_del(&e->rev);
event_del(&e->wev); event_del(&e->wev);
free(e); free(e);
} }
int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) { static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisLibeventEvents *e; redisLibeventEvents *e;
/* Nothing should be attached when something is already attached */ /* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL) if (ac->ev.data != NULL)
return REDIS_ERR; return REDIS_ERR;
/* Create container for context and r/w events */ /* Create container for context and r/w events */
@ -60,12 +61,12 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
e->context = ac; e->context = ac;
/* Register functions to start/stop listening for events */ /* Register functions to start/stop listening for events */
ac->evAddRead = redisLibeventAddRead; ac->ev.addRead = redisLibeventAddRead;
ac->evDelRead = redisLibeventDelRead; ac->ev.delRead = redisLibeventDelRead;
ac->evAddWrite = redisLibeventAddWrite; ac->ev.addWrite = redisLibeventAddWrite;
ac->evDelWrite = redisLibeventDelWrite; ac->ev.delWrite = redisLibeventDelWrite;
ac->evCleanup = redisLibeventCleanup; ac->ev.cleanup = redisLibeventCleanup;
ac->_adapter_data = e; ac->ev.data = e;
/* Initialize and install read/write events */ /* Initialize and install read/write events */
event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e); event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
@ -74,3 +75,4 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
event_base_set(base,&e->wev); event_base_set(base,&e->wev);
return REDIS_OK; return REDIS_OK;
} }
#endif

115
deps/hiredis/async.c vendored
View File

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
* *
@ -29,14 +29,33 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "fmacros.h"
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#include "async.h" #include "async.h"
#include "net.h"
#include "dict.c" #include "dict.c"
#include "sds.h" #include "sds.h"
#include "util.h"
#define _EL_ADD_READ(ctx) do { \
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
} while(0)
#define _EL_DEL_READ(ctx) do { \
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
} while(0)
#define _EL_ADD_WRITE(ctx) do { \
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
} while(0)
#define _EL_DEL_WRITE(ctx) do { \
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
} while(0)
#define _EL_CLEANUP(ctx) do { \
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
} while(0);
/* Forward declaration of function in hiredis.c */ /* Forward declaration of function in hiredis.c */
void __redisAppendCommand(redisContext *c, char *cmd, size_t len); void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
@ -136,11 +155,6 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path) {
return ac; return ac;
} }
int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn) {
redisContext *c = &(ac->c);
return redisSetReplyObjectFunctions(c,fn);
}
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
if (ac->onConnect == NULL) { if (ac->onConnect == NULL) {
ac->onConnect = fn; ac->onConnect = fn;
@ -148,7 +162,7 @@ int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn
/* The common way to detect an established connection is to wait for /* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event * the first write event to be fired. This assumes the related event
* library functions are already set. */ * library functions are already set. */
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data); _EL_ADD_WRITE(ac);
return REDIS_OK; return REDIS_OK;
} }
return REDIS_ERR; return REDIS_ERR;
@ -168,7 +182,6 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
/* Copy callback from stack to heap */ /* Copy callback from stack to heap */
cb = malloc(sizeof(*cb)); cb = malloc(sizeof(*cb));
if (!cb) redisOOM();
if (source != NULL) { if (source != NULL) {
memcpy(cb,source,sizeof(*cb)); memcpy(cb,source,sizeof(*cb));
cb->next = NULL; cb->next = NULL;
@ -237,7 +250,7 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
dictRelease(ac->sub.patterns); dictRelease(ac->sub.patterns);
/* Signal event lib to clean up */ /* Signal event lib to clean up */
if (ac->ev.cleanup) ac->ev.cleanup(ac->ev.data); _EL_CLEANUP(ac);
/* Execute disconnect callback. When redisAsyncFree() initiated destroying /* Execute disconnect callback. When redisAsyncFree() initiated destroying
* this context, the status will always be REDIS_OK. */ * this context, the status will always be REDIS_OK. */
@ -368,14 +381,27 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
/* Even if the context is subscribed, pending regular callbacks will /* Even if the context is subscribed, pending regular callbacks will
* get a reply before pub/sub messages arrive. */ * get a reply before pub/sub messages arrive. */
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
/* No more regular callbacks, the context *must* be subscribed. */ /* A spontaneous reply in a not-subscribed context can only be the
* error reply that is sent when a new connection exceeds the
* maximum number of allowed connections on the server side. This
* is seen as an error instead of a regular reply because the
* server closes the connection after sending it. To prevent the
* error from being overwritten by an EOF error the connection is
* closed here. See issue #43. */
if ( !(c->flags & REDIS_SUBSCRIBED) && ((redisReply*)reply)->type == REDIS_REPLY_ERROR ) {
c->err = REDIS_ERR_OTHER;
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
__redisAsyncDisconnect(ac);
return;
}
/* No more regular callbacks and no errors, the context *must* be subscribed. */
assert(c->flags & REDIS_SUBSCRIBED); assert(c->flags & REDIS_SUBSCRIBED);
__redisGetSubscribeCallback(ac,reply,&cb); __redisGetSubscribeCallback(ac,reply,&cb);
} }
if (cb.fn != NULL) { if (cb.fn != NULL) {
__redisRunCallback(ac,&cb,reply); __redisRunCallback(ac,&cb,reply);
c->fn->freeObject(reply); c->reader->fn->freeObject(reply);
/* Proceed with free'ing when redisAsyncFree() was called. */ /* Proceed with free'ing when redisAsyncFree() was called. */
if (c->flags & REDIS_FREEING) { if (c->flags & REDIS_FREEING) {
@ -387,7 +413,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
* or there were no callbacks to begin with. Either way, don't * or there were no callbacks to begin with. Either way, don't
* abort with an error, but simply ignore it because the client * abort with an error, but simply ignore it because the client
* doesn't know what the server will spit out over the wire. */ * doesn't know what the server will spit out over the wire. */
c->fn->freeObject(reply); c->reader->fn->freeObject(reply);
} }
} }
@ -396,17 +422,48 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
} }
/* Internal helper function to detect socket status the first time a read or
* write event fires. When connecting was not succesful, the connect callback
* is called with a REDIS_ERR status and the context is free'd. */
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
if (redisCheckSocketError(c,c->fd) == 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);
__redisAsyncDisconnect(ac);
return REDIS_ERR;
}
/* 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. /* This function should be called when the socket is readable.
* It processes all replies that can be read and executes their callbacks. * It processes all replies that can be read and executes their callbacks.
*/ */
void redisAsyncHandleRead(redisAsyncContext *ac) { void redisAsyncHandleRead(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
if (!(c->flags & REDIS_CONNECTED)) {
/* Abort connect was not successful. */
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
return;
/* Try again later when the context is still not connected. */
if (!(c->flags & REDIS_CONNECTED))
return;
}
if (redisBufferRead(c) == REDIS_ERR) { if (redisBufferRead(c) == REDIS_ERR) {
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
} else { } else {
/* Always re-schedule reads */ /* Always re-schedule reads */
if (ac->ev.addRead) ac->ev.addRead(ac->ev.data); _EL_ADD_READ(ac);
redisProcessCallbacks(ac); redisProcessCallbacks(ac);
} }
} }
@ -415,24 +472,26 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
int done = 0; int done = 0;
if (!(c->flags & REDIS_CONNECTED)) {
/* Abort connect was not successful. */
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
return;
/* Try again later when the context is still not connected. */
if (!(c->flags & REDIS_CONNECTED))
return;
}
if (redisBufferWrite(c,&done) == REDIS_ERR) { if (redisBufferWrite(c,&done) == REDIS_ERR) {
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
} else { } else {
/* Continue writing when not done, stop writing otherwise */ /* Continue writing when not done, stop writing otherwise */
if (!done) { if (!done)
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data); _EL_ADD_WRITE(ac);
} else { else
if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data); _EL_DEL_WRITE(ac);
}
/* Always schedule reads after writes */ /* Always schedule reads after writes */
if (ac->ev.addRead) ac->ev.addRead(ac->ev.data); _EL_ADD_READ(ac);
/* Fire onConnect when this is the first write event. */
if (!(c->flags & REDIS_CONNECTED)) {
c->flags |= REDIS_CONNECTED;
if (ac->onConnect) ac->onConnect(ac);
}
} }
} }
@ -510,7 +569,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
__redisAppendCommand(c,cmd,len); __redisAppendCommand(c,cmd,len);
/* Always schedule a write when the write buffer is non-empty */ /* Always schedule a write when the write buffer is non-empty */
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data); _EL_ADD_WRITE(ac);
return REDIS_OK; return REDIS_OK;
} }

View File

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
* *
@ -55,7 +55,7 @@ typedef struct redisCallbackList {
/* Connection callback prototypes */ /* Connection callback prototypes */
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallback)(const struct redisAsyncContext*); typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
/* Context for an async connection to Redis */ /* Context for an async connection to Redis */
typedef struct redisAsyncContext { typedef struct redisAsyncContext {
@ -103,7 +103,6 @@ typedef struct redisAsyncContext {
/* Functions that proxy to hiredis */ /* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port); redisAsyncContext *redisAsyncConnect(const char *ip, int port);
redisAsyncContext *redisAsyncConnectUnix(const char *path); redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
void redisAsyncDisconnect(redisAsyncContext *ac); void redisAsyncDisconnect(redisAsyncContext *ac);

View File

@ -18,17 +18,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c); redisAsyncDisconnect(c);
} }
void connectCallback(const redisAsyncContext *c) { void connectCallback(const redisAsyncContext *c, int status) {
((void)c); if (status != REDIS_OK) {
printf("connected...\n"); printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
} }
void disconnectCallback(const redisAsyncContext *c, int status) { void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) { if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr); printf("Error: %s\n", c->errstr);
return;
} }
printf("disconnected...\n"); printf("Disconnected...\n");
aeStop(loop);
} }
int main (int argc, char **argv) { int main (int argc, char **argv) {

View File

@ -15,16 +15,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c); redisAsyncDisconnect(c);
} }
void connectCallback(const redisAsyncContext *c) { void connectCallback(const redisAsyncContext *c, int status) {
((void)c); if (status != REDIS_OK) {
printf("connected...\n"); printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
} }
void disconnectCallback(const redisAsyncContext *c, int status) { void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) { if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr); printf("Error: %s\n", c->errstr);
return;
} }
printf("disconnected...\n"); printf("Disconnected...\n");
} }
int main (int argc, char **argv) { int main (int argc, char **argv) {

View File

@ -15,16 +15,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c); redisAsyncDisconnect(c);
} }
void connectCallback(const redisAsyncContext *c) { void connectCallback(const redisAsyncContext *c, int status) {
((void)c); if (status != REDIS_OK) {
printf("connected...\n"); printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
} }
void disconnectCallback(const redisAsyncContext *c, int status) { void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) { if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr); printf("Error: %s\n", c->errstr);
return;
} }
printf("disconnected...\n"); printf("Disconnected...\n");
} }
int main (int argc, char **argv) { int main (int argc, char **argv) {

View File

@ -1,12 +1,14 @@
#ifndef __HIREDIS_FMACRO_H #ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H #define __HIREDIS_FMACRO_H
#ifndef _BSD_SOURCE #if !defined(_BSD_SOURCE)
#define _BSD_SOURCE #define _BSD_SOURCE
#endif #endif
#ifdef __linux__ #if defined(__sun__)
#define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 200112L
#elif defined(__linux__)
#define _XOPEN_SOURCE 600
#else #else
#define _XOPEN_SOURCE #define _XOPEN_SOURCE
#endif #endif

712
deps/hiredis/hiredis.c vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
* *
@ -36,8 +36,8 @@
#include <sys/time.h> /* for struct timeval */ #include <sys/time.h> /* for struct timeval */
#define HIREDIS_MAJOR 0 #define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 9 #define HIREDIS_MINOR 10
#define HIREDIS_PATCH 2 #define HIREDIS_PATCH 1
#define REDIS_ERR -1 #define REDIS_ERR -1
#define REDIS_OK 0 #define REDIS_OK 0
@ -46,10 +46,11 @@
* error that occured. REDIS_ERR_IO means there was an I/O error and you * error that occured. REDIS_ERR_IO means there was an I/O error and you
* should use the "errno" variable to find out what is wrong. * should use the "errno" variable to find out what is wrong.
* For other values, the "errstr" field will hold a description. */ * For other values, the "errstr" field will hold a description. */
#define REDIS_ERR_IO 1 /* error in read or write */ #define REDIS_ERR_IO 1 /* Error in read or write */
#define REDIS_ERR_EOF 3 /* eof */ #define REDIS_ERR_EOF 3 /* End of file */
#define REDIS_ERR_PROTOCOL 4 /* protocol error */ #define REDIS_ERR_PROTOCOL 4 /* Protocol error */
#define REDIS_ERR_OTHER 2 /* something else */ #define REDIS_ERR_OOM 5 /* Out of memory */
#define REDIS_ERR_OTHER 2 /* Everything else... */
/* Connection type can be blocking or non-blocking and is set in the /* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */ * least significant bit of the flags field in redisContext. */
@ -113,36 +114,56 @@ typedef struct redisReplyObjectFunctions {
void (*freeObject)(void*); void (*freeObject)(void*);
} redisReplyObjectFunctions; } redisReplyObjectFunctions;
struct redisContext; /* need forward declaration of redisContext */ /* State for the protocol parser */
typedef struct redisReader {
/* Context for a connection to Redis */
typedef struct redisContext {
int fd;
int flags;
char *obuf; /* Write buffer */
int err; /* Error flags, 0 when there is no error */ int err; /* Error flags, 0 when there is no error */
char *errstr; /* String representation of error when applicable */ char errstr[128]; /* String representation of error when applicable */
char *buf; /* Read buffer */
size_t pos; /* Buffer cursor */
size_t len; /* Buffer length */
redisReadTask rstack[4];
int ridx; /* Index of current read task */
void *reply; /* Temporary reply pointer */
/* Function set for reply buildup and reply reader */
redisReplyObjectFunctions *fn; redisReplyObjectFunctions *fn;
void *reader; void *privdata;
} redisContext; } redisReader;
/* Public API for the protocol parser. */
redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *r);
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
int redisReaderGetReply(redisReader *r, void **reply);
/* Backwards compatibility, can be removed on big version bump. */
#define redisReplyReaderCreate redisReaderCreate
#define redisReplyReaderFree redisReaderFree
#define redisReplyReaderFeed redisReaderFeed
#define redisReplyReaderGetReply redisReaderGetReply
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
/* Function to free the reply objects hiredis returns by default. */
void freeReplyObject(void *reply); void freeReplyObject(void *reply);
void *redisReplyReaderCreate(void);
int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
int redisReplyReaderSetPrivdata(void *reader, void *privdata);
void *redisReplyReaderGetObject(void *reader);
char *redisReplyReaderGetError(void *reader);
void redisReplyReaderFree(void *ptr);
void redisReplyReaderFeed(void *reader, const char *buf, size_t len);
int redisReplyReaderGetReply(void *reader, void **reply);
/* Functions to format a command according to the protocol. */ /* Functions to format a command according to the protocol. */
int redisvFormatCommand(char **target, const char *format, va_list ap); int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...); int redisFormatCommand(char **target, const char *format, ...);
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
/* Context for a connection to Redis */
typedef struct redisContext {
int err; /* Error flags, 0 when there is no error */
char errstr[128]; /* String representation of error when applicable */
int fd;
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
} redisContext;
redisContext *redisConnect(const char *ip, int port); redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv); redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port); redisContext *redisConnectNonBlock(const char *ip, int port);
@ -150,7 +171,6 @@ redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv); redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path); redisContext *redisConnectUnixNonBlock(const char *path);
int redisSetTimeout(redisContext *c, struct timeval tv); int redisSetTimeout(redisContext *c, struct timeval tv);
int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn);
void redisFree(redisContext *c); void redisFree(redisContext *c);
int redisBufferRead(redisContext *c); int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done); int redisBufferWrite(redisContext *c, int *done);
@ -164,9 +184,9 @@ int redisGetReplyFromReader(redisContext *c, void **reply);
/* Write a command to the output buffer. Use these functions in blocking mode /* Write a command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */ * to get a pipeline of commands. */
void redisvAppendCommand(redisContext *c, const char *format, va_list ap); int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
void redisAppendCommand(redisContext *c, const char *format, ...); int redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/* Issue a command to Redis. In a blocking context, it is identical to calling /* Issue a command to Redis. In a blocking context, it is identical to calling
* redisAppendCommand, followed by redisGetReply. The function will return * redisAppendCommand, followed by redisGetReply. The function will return

177
deps/hiredis/net.c vendored
View File

@ -1,7 +1,7 @@
/* Extracted from anet.c to work properly with Hiredis error reporting. /* Extracted from anet.c to work properly with Hiredis error reporting.
* *
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
* *
@ -49,19 +49,37 @@
#include "net.h" #include "net.h"
#include "sds.h" #include "sds.h"
/* Forward declaration */ /* Defined in hiredis.c */
void __redisSetError(redisContext *c, int type, sds err); void __redisSetError(redisContext *c, int type, const char *str);
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
char buf[128];
size_t len = 0;
if (prefix != NULL)
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
strerror_r(errno,buf+len,sizeof(buf)-len);
__redisSetError(c,type,buf);
}
static int redisSetReuseAddr(redisContext *c, int fd) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
static int redisCreateSocket(redisContext *c, int type) { static int redisCreateSocket(redisContext *c, int type) {
int s, on = 1; int s;
if ((s = socket(type, SOCK_STREAM, 0)) == -1) { if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
__redisSetError(c,REDIS_ERR_IO,NULL); __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
return REDIS_ERR; return REDIS_ERR;
} }
if (type == AF_INET) { if (type == AF_INET) {
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { if (redisSetReuseAddr(c,s) == REDIS_ERR) {
__redisSetError(c,REDIS_ERR_IO,NULL);
close(s);
return REDIS_ERR; return REDIS_ERR;
} }
} }
@ -75,8 +93,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
* Note that fcntl(2) for F_GETFL and F_SETFL can't be * Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */ * interrupted by a signal. */
if ((flags = fcntl(fd, F_GETFL)) == -1) { if ((flags = fcntl(fd, F_GETFL)) == -1) {
__redisSetError(c,REDIS_ERR_IO, __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
sdscatprintf(sdsempty(), "fcntl(F_GETFL): %s", strerror(errno)));
close(fd); close(fd);
return REDIS_ERR; return REDIS_ERR;
} }
@ -87,8 +104,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
flags |= O_NONBLOCK; flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) { if (fcntl(fd, F_SETFL, flags) == -1) {
__redisSetError(c,REDIS_ERR_IO, __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
sdscatprintf(sdsempty(), "fcntl(F_SETFL): %s", strerror(errno)));
close(fd); close(fd);
return REDIS_ERR; return REDIS_ERR;
} }
@ -98,8 +114,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
static int redisSetTcpNoDelay(redisContext *c, int fd) { static int redisSetTcpNoDelay(redisContext *c, int fd) {
int yes = 1; int yes = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
__redisSetError(c,REDIS_ERR_IO, __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %s", strerror(errno)));
close(fd); close(fd);
return REDIS_ERR; return REDIS_ERR;
} }
@ -110,8 +125,6 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
struct timeval to; struct timeval to;
struct timeval *toptr = NULL; struct timeval *toptr = NULL;
fd_set wfd; fd_set wfd;
int err;
socklen_t errlen;
/* Only use timeout when not NULL. */ /* Only use timeout when not NULL. */
if (timeout != NULL) { if (timeout != NULL) {
@ -124,101 +137,115 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
FD_SET(fd, &wfd); FD_SET(fd, &wfd);
if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) { if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) {
__redisSetError(c,REDIS_ERR_IO, __redisSetErrorFromErrno(c,REDIS_ERR_IO,"select(2)");
sdscatprintf(sdsempty(), "select(2): %s", strerror(errno)));
close(fd); close(fd);
return REDIS_ERR; return REDIS_ERR;
} }
if (!FD_ISSET(fd, &wfd)) { if (!FD_ISSET(fd, &wfd)) {
errno = ETIMEDOUT; errno = ETIMEDOUT;
__redisSetError(c,REDIS_ERR_IO,NULL); __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd); close(fd);
return REDIS_ERR; return REDIS_ERR;
} }
err = 0; if (redisCheckSocketError(c, fd) != REDIS_OK)
errlen = sizeof(err);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
__redisSetError(c,REDIS_ERR_IO,
sdscatprintf(sdsempty(), "getsockopt(SO_ERROR): %s", strerror(errno)));
close(fd);
return REDIS_ERR; return REDIS_ERR;
}
if (err) {
errno = err;
__redisSetError(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
return REDIS_OK; return REDIS_OK;
} }
__redisSetError(c,REDIS_ERR_IO,NULL); __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd); close(fd);
return REDIS_ERR; return REDIS_ERR;
} }
int redisCheckSocketError(redisContext *c, int fd) {
int err = 0;
socklen_t errlen = sizeof(err);
if (getsockopt(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, struct timeval tv) {
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
__redisSetError(c,REDIS_ERR_IO, __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
sdscatprintf(sdsempty(), "setsockopt(SO_RCVTIMEO): %s", strerror(errno)));
return REDIS_ERR; return REDIS_ERR;
} }
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
__redisSetError(c,REDIS_ERR_IO, __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
sdscatprintf(sdsempty(), "setsockopt(SO_SNDTIMEO): %s", strerror(errno)));
return REDIS_ERR; return REDIS_ERR;
} }
return REDIS_OK; return REDIS_OK;
} }
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) { int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
int s; int s, rv;
char _port[6]; /* strlen("65535"); */
struct addrinfo hints, *servinfo, *p;
int blocking = (c->flags & REDIS_BLOCK); int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_in sa;
if ((s = redisCreateSocket(c,AF_INET)) < 0) snprintf(_port, 6, "%d", port);
return REDIS_ERR; memset(&hints,0,sizeof(hints));
if (redisSetBlocking(c,s,0) != REDIS_OK) hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
return REDIS_ERR; return REDIS_ERR;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
continue;
sa.sin_family = AF_INET; if (redisSetBlocking(c,s,0) != REDIS_OK)
sa.sin_port = htons(port); goto error;
if (inet_aton(addr, &sa.sin_addr) == 0) { if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
struct hostent *he; if (errno == EHOSTUNREACH) {
close(s);
he = gethostbyname(addr); continue;
if (he == NULL) { } else if (errno == EINPROGRESS && !blocking) {
__redisSetError(c,REDIS_ERR_OTHER, /* This is ok. */
sdscatprintf(sdsempty(),"Can't resolve: %s",addr)); } else {
close(s); if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
return REDIS_ERR; goto error;
}
} }
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
goto error;
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
goto error;
c->fd = s;
c->flags |= REDIS_CONNECTED;
rv = REDIS_OK;
goto end;
}
if (p == NULL) {
char buf[128];
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
__redisSetError(c,REDIS_ERR_OTHER,buf);
goto error;
} }
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { error:
if (errno == EINPROGRESS && !blocking) { rv = REDIS_ERR;
/* This is ok. */ end:
} else { freeaddrinfo(servinfo);
if (redisContextWaitReady(c,s,timeout) != REDIS_OK) return rv; // Need to return REDIS_OK if alright
return REDIS_ERR;
}
}
/* Reset socket to be blocking after connect(2). */
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
return REDIS_ERR;
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
return REDIS_ERR;
c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK;
} }
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) { int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {

5
deps/hiredis/net.h vendored
View File

@ -1,7 +1,7 @@
/* Extracted from anet.c to work properly with Hiredis error reporting. /* Extracted from anet.c to work properly with Hiredis error reporting.
* *
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
* *
@ -39,6 +39,7 @@
#define AF_LOCAL AF_UNIX #define AF_LOCAL AF_UNIX
#endif #endif
int redisCheckSocketError(redisContext *c, int fd);
int redisContextSetTimeout(redisContext *c, struct timeval tv); int redisContextSetTimeout(redisContext *c, struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout); int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout); int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);

35
deps/hiredis/sds.c vendored
View File

@ -28,18 +28,18 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#define SDS_ABORT_ON_OOM
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include "sds.h" #include "sds.h"
#ifdef SDS_ABORT_ON_OOM
static void sdsOomAbort(void) { static void sdsOomAbort(void) {
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n"); fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
abort(); abort();
} }
#endif
sds sdsnewlen(const void *init, size_t initlen) { sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh; struct sdshdr *sh;
@ -378,17 +378,19 @@ sds sdsfromlonglong(long long value) {
sds sdscatrepr(sds s, char *p, size_t len) { sds sdscatrepr(sds s, char *p, size_t len) {
s = sdscatlen(s,"\"",1); s = sdscatlen(s,"\"",1);
if (s == NULL) return NULL;
while(len--) { while(len--) {
switch(*p) { switch(*p) {
case '\\': case '\\':
case '"': case '"':
s = sdscatprintf(s,"\\%c",*p); s = sdscatprintf(s,"\\%c",*p);
break; break;
case '\n': s = sdscatlen(s,"\\n",1); break; case '\n': s = sdscatlen(s,"\\n",2); break;
case '\r': s = sdscatlen(s,"\\r",1); break; case '\r': s = sdscatlen(s,"\\r",2); break;
case '\t': s = sdscatlen(s,"\\t",1); break; case '\t': s = sdscatlen(s,"\\t",2); break;
case '\a': s = sdscatlen(s,"\\a",1); break; case '\a': s = sdscatlen(s,"\\a",2); break;
case '\b': s = sdscatlen(s,"\\b",1); break; case '\b': s = sdscatlen(s,"\\b",2); break;
default: default:
if (isprint(*p)) if (isprint(*p))
s = sdscatprintf(s,"%c",*p); s = sdscatprintf(s,"%c",*p);
@ -397,6 +399,7 @@ sds sdscatrepr(sds s, char *p, size_t len) {
break; break;
} }
p++; p++;
if (s == NULL) return NULL;
} }
return sdscatlen(s,"\"",1); return sdscatlen(s,"\"",1);
} }
@ -416,7 +419,7 @@ sds sdscatrepr(sds s, char *p, size_t len) {
sds *sdssplitargs(char *line, int *argc) { sds *sdssplitargs(char *line, int *argc) {
char *p = line; char *p = line;
char *current = NULL; char *current = NULL;
char **vector = NULL; char **vector = NULL, **_vector = NULL;
*argc = 0; *argc = 0;
while(1) { while(1) {
@ -427,7 +430,11 @@ sds *sdssplitargs(char *line, int *argc) {
int inq=0; /* set to 1 if we are in "quotes" */ int inq=0; /* set to 1 if we are in "quotes" */
int done=0; int done=0;
if (current == NULL) current = sdsempty(); if (current == NULL) {
current = sdsempty();
if (current == NULL) goto err;
}
while(!done) { while(!done) {
if (inq) { if (inq) {
if (*p == '\\' && *(p+1)) { if (*p == '\\' && *(p+1)) {
@ -471,9 +478,13 @@ sds *sdssplitargs(char *line, int *argc) {
} }
} }
if (*p) p++; if (*p) p++;
if (current == NULL) goto err;
} }
/* add the token to the vector */ /* add the token to the vector */
vector = realloc(vector,((*argc)+1)*sizeof(char*)); _vector = realloc(vector,((*argc)+1)*sizeof(char*));
if (_vector == NULL) goto err;
vector = _vector;
vector[*argc] = current; vector[*argc] = current;
(*argc)++; (*argc)++;
current = NULL; current = NULL;
@ -485,8 +496,8 @@ sds *sdssplitargs(char *line, int *argc) {
err: err:
while((*argc)--) while((*argc)--)
sdsfree(vector[*argc]); sdsfree(vector[*argc]);
free(vector); if (vector != NULL) free(vector);
if (current) sdsfree(current); if (current != NULL) sdsfree(current);
return NULL; return NULL;
} }

420
deps/hiredis/test.c vendored
View File

@ -1,3 +1,4 @@
#include "fmacros.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -10,10 +11,28 @@
#include "hiredis.h" #include "hiredis.h"
enum connection_type {
CONN_TCP,
CONN_UNIX
};
struct config {
enum connection_type type;
struct {
const char *host;
int port;
} tcp;
struct {
const char *path;
} unix;
};
/* The following lines make up our testing "framework" :) */ /* The following lines make up our testing "framework" :) */
static int tests = 0, fails = 0; static int tests = 0, fails = 0;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); } #define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test_cond(_c) if(_c) printf("PASSED\n"); else {printf("FAILED\n"); fails++;} #define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
static long long usec(void) { static long long usec(void) {
struct timeval tv; struct timeval tv;
@ -21,15 +40,60 @@ static long long usec(void) {
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
} }
static int use_unix = 0; static redisContext *select_database(redisContext *c) {
static redisContext *blocking_context = NULL; redisReply *reply;
static void __connect(redisContext **target) {
*target = blocking_context = (use_unix ? /* Switch to DB 9 for testing, now that we know we can chat. */
redisConnectUnix("/tmp/redis.sock") : redisConnect((char*)"127.0.0.1", 6379)); reply = redisCommand(c,"SELECT 9");
if (blocking_context->err) { assert(reply != NULL);
printf("Connection error: %s\n", blocking_context->errstr); freeReplyObject(reply);
/* Make sure the DB is emtpy */
reply = redisCommand(c,"DBSIZE");
assert(reply != NULL);
if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
/* Awesome, DB 9 is empty and we can continue. */
freeReplyObject(reply);
} else {
printf("Database #9 is not empty, test can not continue\n");
exit(1); exit(1);
} }
return c;
}
static void disconnect(redisContext *c) {
redisReply *reply;
/* Make sure we're on DB 9. */
reply = redisCommand(c,"SELECT 9");
assert(reply != NULL);
freeReplyObject(reply);
reply = redisCommand(c,"FLUSHDB");
assert(reply != NULL);
freeReplyObject(reply);
/* Free the context as well. */
redisFree(c);
}
static redisContext *connect(struct config config) {
redisContext *c = NULL;
if (config.type == CONN_TCP) {
c = redisConnect(config.tcp.host, config.tcp.port);
} else if (config.type == CONN_UNIX) {
c = redisConnectUnix(config.unix.path);
} else {
assert(NULL);
}
if (c->err) {
printf("Connection error: %s\n", c->errstr);
exit(1);
}
return select_database(c);
} }
static void test_format_commands(void) { static void test_format_commands(void) {
@ -78,29 +142,43 @@ static void test_format_commands(void) {
len == 4+4+(3+2)+4+(1+2)+4+(1+2)); len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd); free(cmd);
test("Format command with printf-delegation (long long): "); /* Vararg width depends on the type. These tests make sure that the
len = redisFormatCommand(&cmd,"key:%08lld",1234ll); * width is correctly determined using the format and subsequent varargs
test_cond(strncmp(cmd,"*1\r\n$12\r\nkey:00001234\r\n",len) == 0 && * can correctly be interpolated. */
len == 4+5+(12+2)); #define INTEGER_WIDTH_TEST(fmt, type) do { \
free(cmd); type value = 123; \
test("Format command with printf-delegation (" #type "): "); \
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \
} while(0)
test("Format command with printf-delegation (float): "); #define FLOAT_WIDTH_TEST(type) do { \
len = redisFormatCommand(&cmd,"v:%06.1f",12.34f); type value = 123.0; \
test_cond(strncmp(cmd,"*1\r\n$8\r\nv:0012.3\r\n",len) == 0 && test("Format command with printf-delegation (" #type "): "); \
len == 4+4+(8+2)); len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
free(cmd); test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \
} while(0)
test("Format command with printf-delegation and extra interpolation: "); INTEGER_WIDTH_TEST("d", int);
len = redisFormatCommand(&cmd,"key:%d %b",1234,"foo",3); INTEGER_WIDTH_TEST("hhd", char);
test_cond(strncmp(cmd,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len) == 0 && INTEGER_WIDTH_TEST("hd", short);
len == 4+4+(8+2)+4+(3+2)); INTEGER_WIDTH_TEST("ld", long);
free(cmd); INTEGER_WIDTH_TEST("lld", long long);
INTEGER_WIDTH_TEST("u", unsigned int);
INTEGER_WIDTH_TEST("hhu", unsigned char);
INTEGER_WIDTH_TEST("hu", unsigned short);
INTEGER_WIDTH_TEST("lu", unsigned long);
INTEGER_WIDTH_TEST("llu", unsigned long long);
FLOAT_WIDTH_TEST(float);
FLOAT_WIDTH_TEST(double);
test("Format command with wrong printf format and extra interpolation: "); test("Format command with invalid printf format: ");
len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3); len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",3);
test_cond(strncmp(cmd,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len) == 0 && test_cond(len == -1);
len == 4+4+(6+2)+4+(3+2));
free(cmd);
const char *argv[3]; const char *argv[3];
argv[0] = "SET"; argv[0] = "SET";
@ -122,42 +200,117 @@ static void test_format_commands(void) {
free(cmd); free(cmd);
} }
static void test_blocking_connection(void) { static void test_reply_reader(void) {
redisReader *reader;
void *reply;
int ret;
test("Error handling in reply parser: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
/* when the reply already contains multiple items, they must be free'd
* on an error. valgrind will bark when this doesn't happen. */
test("Memory cleanup in reply parser: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"*2\r\n",4);
redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
redisReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
test("Set error on nested multi bulks with depth > 2: ");
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);
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strncasecmp(reader->errstr,"No support for",14) == 0);
redisReaderFree(reader);
test("Works with NULL functions for reply: ");
reader = redisReaderCreate();
reader->fn = NULL;
redisReaderFeed(reader,(char*)"+OK\r\n",5);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReaderFree(reader);
test("Works when a single newline (\\r\\n) covers two calls to feed: ");
reader = redisReaderCreate();
reader->fn = NULL;
redisReaderFeed(reader,(char*)"+OK\r",4);
ret = redisReaderGetReply(reader,&reply);
assert(ret == REDIS_OK && reply == NULL);
redisReaderFeed(reader,(char*)"\n",1);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReaderFree(reader);
test("Don't reset state after protocol error: ");
reader = redisReaderCreate();
reader->fn = NULL;
redisReaderFeed(reader,(char*)"x",1);
ret = redisReaderGetReply(reader,&reply);
assert(ret == REDIS_ERR);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR && reply == NULL);
redisReaderFree(reader);
/* Regression test for issue #45 on GitHub. */
test("Don't do empty allocation for empty multi bulk: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"*0\r\n",4);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
((redisReply*)reply)->elements == 0);
freeReplyObject(reply);
redisReaderFree(reader);
}
static void test_blocking_connection_errors(void) {
redisContext *c; redisContext *c;
redisReply *reply;
int major, minor;
test("Returns error when host cannot be resolved: "); test("Returns error when host cannot be resolved: ");
c = redisConnect((char*)"idontexist.local", 6379); c = redisConnect((char*)"idontexist.local", 6379);
test_cond(c->err == REDIS_ERR_OTHER && test_cond(c->err == REDIS_ERR_OTHER &&
strcmp(c->errstr,"Can't resolve: idontexist.local") == 0); (strcmp(c->errstr,"Name or service not known") == 0 ||
strcmp(c->errstr,"Can't resolve: idontexist.local") == 0));
redisFree(c); redisFree(c);
test("Returns error when the port is not open: "); test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 56380); c = redisConnect((char*)"localhost", 1);
test_cond(c->err == REDIS_ERR_IO && test_cond(c->err == REDIS_ERR_IO &&
strcmp(c->errstr,"Connection refused") == 0); strcmp(c->errstr,"Connection refused") == 0);
redisFree(c); redisFree(c);
__connect(&c); test("Returns error when the unix socket path doesn't accept connections: ");
c = redisConnectUnix((char*)"/tmp/idontexist.sock");
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
redisFree(c);
}
static void test_blocking_connection(struct config config) {
redisContext *c;
redisReply *reply;
c = connect(config);
test("Is able to deliver commands: "); test("Is able to deliver commands: ");
reply = redisCommand(c,"PING"); reply = redisCommand(c,"PING");
test_cond(reply->type == REDIS_REPLY_STATUS && test_cond(reply->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->str,"pong") == 0) strcasecmp(reply->str,"pong") == 0)
freeReplyObject(reply); freeReplyObject(reply);
/* Switch to DB 9 for testing, now that we know we can chat. */
reply = redisCommand(c,"SELECT 9");
freeReplyObject(reply);
/* Make sure the DB is emtpy */
reply = redisCommand(c,"DBSIZE");
if (reply->type != REDIS_REPLY_INTEGER || reply->integer != 0) {
printf("Database #9 is not empty, test can not continue\n");
exit(1);
}
freeReplyObject(reply);
test("Is a able to send commands verbatim: "); test("Is a able to send commands verbatim: ");
reply = redisCommand(c,"SET foo bar"); reply = redisCommand(c,"SET foo bar");
test_cond (reply->type == REDIS_REPLY_STATUS && test_cond (reply->type == REDIS_REPLY_STATUS &&
@ -221,6 +374,17 @@ static void test_blocking_connection(void) {
strcasecmp(reply->element[1]->str,"pong") == 0); strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply); freeReplyObject(reply);
disconnect(c);
}
static void test_blocking_io_errors(struct config config) {
redisContext *c;
redisReply *reply;
void *_reply;
int major, minor;
/* Connect to target given by config. */
c = connect(config);
{ {
/* Find out Redis version to determine the path for the next test */ /* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:"; const char *field = "redis_version:";
@ -240,7 +404,7 @@ static void test_blocking_connection(void) {
/* > 2.0 returns OK on QUIT and read() should be issued once more /* > 2.0 returns OK on QUIT and read() should be issued once more
* to know the descriptor is at EOF. */ * to know the descriptor is at EOF. */
test_cond(strcasecmp(reply->str,"OK") == 0 && test_cond(strcasecmp(reply->str,"OK") == 0 &&
redisGetReply(c,(void**)&reply) == REDIS_ERR); redisGetReply(c,&_reply) == REDIS_ERR);
freeReplyObject(reply); freeReplyObject(reply);
} else { } else {
test_cond(reply == NULL); test_cond(reply == NULL);
@ -255,91 +419,20 @@ static void test_blocking_connection(void) {
strcmp(c->errstr,"Server closed the connection") == 0); strcmp(c->errstr,"Server closed the connection") == 0);
redisFree(c); redisFree(c);
__connect(&c); c = connect(config);
test("Returns I/O error on socket timeout: "); test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 }; struct timeval tv = { 0, 1000 };
assert(redisSetTimeout(c,tv) == REDIS_OK); assert(redisSetTimeout(c,tv) == REDIS_OK);
test_cond(redisGetReply(c,(void**)&reply) == REDIS_ERR && test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
c->err == REDIS_ERR_IO && errno == EAGAIN); c->err == REDIS_ERR_IO && errno == EAGAIN);
redisFree(c); redisFree(c);
/* Context should be connected */
__connect(&c);
} }
static void test_reply_reader(void) { static void test_throughput(struct config config) {
void *reader; redisContext *c = connect(config);
void *reply; redisReply **replies;
char *err;
int ret;
test("Error handling in reply parser: ");
reader = redisReplyReaderCreate();
redisReplyReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR &&
strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
redisReplyReaderFree(reader);
/* when the reply already contains multiple items, they must be free'd
* on an error. valgrind will bark when this doesn't happen. */
test("Memory cleanup in reply parser: ");
reader = redisReplyReaderCreate();
redisReplyReaderFeed(reader,(char*)"*2\r\n",4);
redisReplyReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
redisReplyReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR &&
strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
redisReplyReaderFree(reader);
test("Set error on nested multi bulks with depth > 1: ");
reader = redisReplyReaderCreate();
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR &&
strncasecmp(err,"No support for",14) == 0);
redisReplyReaderFree(reader);
test("Works with NULL functions for reply: ");
reader = redisReplyReaderCreate();
redisReplyReaderSetReplyObjectFunctions(reader,NULL);
redisReplyReaderFeed(reader,(char*)"+OK\r\n",5);
ret = redisReplyReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReplyReaderFree(reader);
test("Works when a single newline (\\r\\n) covers two calls to feed: ");
reader = redisReplyReaderCreate();
redisReplyReaderSetReplyObjectFunctions(reader,NULL);
redisReplyReaderFeed(reader,(char*)"+OK\r",4);
ret = redisReplyReaderGetReply(reader,&reply);
assert(ret == REDIS_OK && reply == NULL);
redisReplyReaderFeed(reader,(char*)"\n",1);
ret = redisReplyReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReplyReaderFree(reader);
test("Properly reset state after protocol error: ");
reader = redisReplyReaderCreate();
redisReplyReaderSetReplyObjectFunctions(reader,NULL);
redisReplyReaderFeed(reader,(char*)"x",1);
ret = redisReplyReaderGetReply(reader,&reply);
assert(ret == REDIS_ERR);
ret = redisReplyReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == NULL)
}
static void test_throughput(void) {
int i, num; int i, num;
long long t1, t2; long long t1, t2;
redisContext *c = blocking_context;
redisReply **replies;
test("Throughput:\n"); test("Throughput:\n");
for (i = 0; i < 500; i++) for (i = 0; i < 500; i++)
@ -396,18 +489,8 @@ static void test_throughput(void) {
for (i = 0; i < num; i++) freeReplyObject(replies[i]); for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies); free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
}
static void cleanup(void) { disconnect(c);
redisContext *c = blocking_context;
redisReply *reply;
/* Make sure we're on DB 9 */
reply = redisCommand(c,"SELECT 9");
assert(reply != NULL); freeReplyObject(reply);
reply = redisCommand(c,"FLUSHDB");
assert(reply != NULL); freeReplyObject(reply);
redisFree(c);
} }
// static long __test_callback_flags = 0; // static long __test_callback_flags = 0;
@ -429,7 +512,7 @@ static void cleanup(void) {
// static redisContext *__connect_nonblock() { // static redisContext *__connect_nonblock() {
// /* Reset callback flags */ // /* Reset callback flags */
// __test_callback_flags = 0; // __test_callback_flags = 0;
// return redisConnectNonBlock("127.0.0.1", 6379, NULL); // return redisConnectNonBlock("127.0.0.1", port, NULL);
// } // }
// //
// static void test_nonblocking_connection() { // static void test_nonblocking_connection() {
@ -510,23 +593,62 @@ static void cleanup(void) {
// } // }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc > 1) { struct config cfg = {
if (strcmp(argv[1],"-s") == 0) .tcp = {
use_unix = 1; .host = "127.0.0.1",
} .port = 6379
},
.unix = {
.path = "/tmp/redis.sock"
}
};
int throughput = 1;
/* Ignore broken pipe signal (for I/O error tests). */
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
test_format_commands();
test_blocking_connection();
test_reply_reader();
// test_nonblocking_connection();
test_throughput();
cleanup();
if (fails == 0) { /* Parse command line options. */
printf("ALL TESTS PASSED\n"); argv++; argc--;
} else { while (argc) {
printf("*** %d TESTS FAILED ***\n", fails); if (argc >= 2 && !strcmp(argv[0],"-h")) {
argv++; argc--;
cfg.tcp.host = argv[0];
} else if (argc >= 2 && !strcmp(argv[0],"-p")) {
argv++; argc--;
cfg.tcp.port = atoi(argv[0]);
} else if (argc >= 2 && !strcmp(argv[0],"-s")) {
argv++; argc--;
cfg.unix.path = argv[0];
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
throughput = 0;
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
exit(1);
}
argv++; argc--;
} }
test_format_commands();
test_reply_reader();
test_blocking_connection_errors();
printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
cfg.type = CONN_TCP;
test_blocking_connection(cfg);
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
cfg.type = CONN_UNIX;
test_blocking_connection(cfg);
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
if (fails) {
printf("*** %d TESTS FAILED ***\n", fails);
return 1;
}
printf("ALL TESTS PASSED\n");
return 0; return 0;
} }

40
deps/hiredis/util.h vendored
View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __UTIL_H
#define __UTIL_H
#include <stdlib.h>
/* Abort on out of memory */
static void redisOOM(void) {
fprintf(stderr,"Out of memory in hiredis");
exit(1);
}
#endif

View File

@ -267,7 +267,7 @@ static client createClient(const char *cmd, size_t len) {
} }
} }
redisSetReplyObjectFunctions(c->context,NULL); /* redisSetReplyObjectFunctions(c->context,NULL); */
aeCreateFileEvent(config.el,c->context->fd,AE_WRITABLE,writeHandler,c); aeCreateFileEvent(config.el,c->context->fd,AE_WRITABLE,writeHandler,c);
listAddNodeTail(config.clients,c); listAddNodeTail(config.clients,c);
config.liveclients++; config.liveclients++;