Update hiredis to 0.9.2

This commit is contained in:
Pieter Noordhuis 2010-12-16 23:32:02 +01:00
parent 59aee5513d
commit 021321e0ef
16 changed files with 678 additions and 173 deletions

46
deps/hiredis/Makefile vendored
View File

@ -6,32 +6,35 @@ OBJ = net.o hiredis.o sds.o async.o
BINS = hiredis-example hiredis-test BINS = hiredis-example hiredis-test
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
OPTIMIZATION?=-O2 OPTIMIZATION?=-O3
ifeq ($(uname_S),SunOS) ifeq ($(uname_S),SunOS)
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
CCLINK?= -ldl -lnsl -lsocket -lm -lpthread CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
LDFLAGS?=-L. -Wl,-R,.
DYLIBNAME?=libhiredis.so DYLIBNAME?=libhiredis.so
DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ} DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
STLIBNAME?=libhiredis.a STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ} STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
else ifeq ($(uname_S),Darwin) else ifeq ($(uname_S),Darwin)
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF) CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
CCLINK?= -lm -pthread CCLINK?=-lm -pthread
OBJARCH?= -arch i386 -arch x86_64 LDFLAGS?=-L. -Wl,-rpath,.
OBJARCH?=-arch i386 -arch x86_64
DYLIBNAME?=libhiredis.dylib DYLIBNAME?=libhiredis.dylib
DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ} DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ}
STLIBNAME?=libhiredis.a STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ} STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
else else
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF) CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
CCLINK?= -lm -pthread CCLINK?=-lm -pthread
LDFLAGS?=-L. -Wl,-rpath,.
DYLIBNAME?=libhiredis.so DYLIBNAME?=libhiredis.so
DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ} DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
STLIBNAME?=libhiredis.a STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ} STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
endif endif
CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF) CCOPT= $(CFLAGS) $(CCLINK)
DEBUG?= -g -ggdb DEBUG?= -g -ggdb
PREFIX?= /usr/local PREFIX?= /usr/local
INSTALL_INC= $(PREFIX)/include/hiredis INSTALL_INC= $(PREFIX)/include/hiredis
@ -43,8 +46,6 @@ 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
async.o: async.c async.h hiredis.h sds.h util.h async.o: async.c async.h hiredis.h sds.h util.h
example-libev.o: example-libev.c hiredis.h async.h adapters/libev.h
example-libevent.o: example-libevent.c hiredis.h async.h adapters/libevent.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 hiredis.h net.h sds.h util.h
sds.o: sds.c sds.h sds.o: sds.c sds.h
@ -60,14 +61,23 @@ dynamic: ${DYLIBNAME}
static: ${STLIBNAME} static: ${STLIBNAME}
# Binaries: # Binaries:
hiredis-example-libevent: example-libevent.o ${DYLIBNAME} hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -levent -Wl,-rpath,. example-libevent.c $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -levent example-libevent.c
hiredis-example-libev: example-libev.o ${DYLIBNAME} hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -lev -Wl,-rpath,. example-libev.c $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -lev example-libev.c
ifndef AE_DIR
hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@false
else
hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) -lhiredis example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
endif
hiredis-%: %.o ${DYLIBNAME} hiredis-%: %.o ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -Wl,-rpath,. $< $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis $<
test: hiredis-test test: hiredis-test
./hiredis-test ./hiredis-test

View File

@ -35,16 +35,18 @@ To consume the synchronous API, there are only a few function calls that need to
### Connecting ### Connecting
The function `redisConnect` is used to create a so-called `redisContext`. The context is where The function `redisConnect` is used to create a so-called `redisContext`. The
Hiredis holds state for a connection. The `redisContext` struct has an `error` field that is context is where Hiredis holds state for a connection. The `redisContext`
non-NULL when the connection is in an error state. It contains a string with a textual struct has an integer `err` field that is non-zero when an the connection is in
representation of the error. After trying to connect to Redis using `redisConnect` you should an error state. The field `errstr` will contain a string with a description of
check the `error` field to see if establishing the connection was successful: the error. More information on errors can be found in the **Errors** section.
After trying to connect to Redis using `redisConnect` you should
check the `err` field to see if establishing the connection was successful:
redisContext *c = redisConnect("127.0.0.1", 6379); redisContext *c = redisConnect("127.0.0.1", 6379);
if (c->error != NULL) { if (c->err) {
printf("Error: %s\n", c->error); printf("Error: %s\n", c->errstr);
// handle error // handle error
} }
### Sending commands ### Sending commands
@ -76,8 +78,8 @@ anywhere in an argument:
### Using replies ### Using replies
The return value of `redisCommand` holds a reply when the command was The return value of `redisCommand` holds a reply when the command was
successfully executed. When the return value is `NULL`, the `error` field successfully executed. When an error occurs, the return value is `NULL` and
in the context can be used to find out what was the cause of failure. the `err` field in the context will be set (see section on **Errors**).
Once an error is returned the context cannot be reused and you should set up Once an error is returned the context cannot be reused and you should set up
a new connection. a new connection.
@ -166,7 +168,7 @@ to the `redisCommand` family, apart from not returning a reply:
After calling either function one or more times, `redisGetReply` can be used to receive the After calling either function one or more times, `redisGetReply` can be used to receive the
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
the latter means an error occurred while reading a reply. Just as with the other commands, the latter means an error occurred while reading a reply. Just as with the other commands,
the `error` field in the context can be used to find out what the cause of this error is. the `err` field in the context can be used to find out what the cause of this error is.
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
a single call to `write(2)`): a single call to `write(2)`):
@ -184,10 +186,35 @@ This API can also be used to implement a blocking subscriber:
reply = redisCommand(context,"SUBSCRIBE foo"); reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply); freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) { while(redisGetReply(context,&reply) == REDIS_OK) {
// consume message // consume message
freeReplyObject(reply); freeReplyObject(reply);
} }
### Errors
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
returned. The `err` field inside the context will be non-zero and set to one of the
following constants:
* **`REDIS_ERR_IO`**:
There was an I/O error while creating the connection, trying to write
to the socket or read from the socket. If you included `errno.h` in your
application, you can use the global `errno` variable to find out what is
wrong.
* **`REDIS_ERR_EOF`**:
The server closed the connection which resulted in an empty read.
* **`REDIS_ERR_PROTOCOL`**:
There was an error while parsing the protocol.
* **`REDIS_ERR_OTHER`**:
Any other error. Currently, it is only used when a specified hostname to connect
to cannot be resolved.
In every case, the `errstr` field in the context will be set to hold a string representation
of the error.
## Asynchronous API ## Asynchronous API
Hiredis comes with an asynchronous API that works easily with any event library. Hiredis comes with an asynchronous API that works easily with any event library.
@ -197,15 +224,15 @@ and [libevent](http://monkey.org/~provos/libevent/).
### Connecting ### Connecting
The function `redisAsyncConnect` can be used to establish a non-blocking connection to The function `redisAsyncConnect` can be used to establish a non-blocking connection to
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `error` field Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
should be checked after creation to see if there were errors creating the connection. should be checked after creation to see if there were errors creating the connection.
Because the connection that will be created is non-blocking, the kernel is not able to Because the connection that will be created is non-blocking, the kernel is not able to
instantly return if the specified host and port is able to accept a connection. instantly return if the specified host and port is able to accept a connection.
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->error != NULL) { if (c->err) {
printf("Error: %s\n", c->error); printf("Error: %s\n", c->errstr);
// handle error // handle error
} }
The asynchronous context can hold a disconnect callback function that is called when the The asynchronous context can hold a disconnect callback function that is called when the
@ -215,7 +242,7 @@ have the following prototype:
void(const redisAsyncContext *c, int status); void(const redisAsyncContext *c, int status);
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `error` user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
field in the context can be accessed to find out the cause of the error. field in the context can be accessed to find out the cause of the error.
The context object is always free'd after the disconnect callback fired. When a reconnect is needed, The context object is always free'd after the disconnect callback fired. When a reconnect is needed,

95
deps/hiredis/adapters/ae.h vendored Normal file
View File

@ -0,0 +1,95 @@
#include <sys/types.h>
#include <ae.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisAeEvents {
redisAsyncContext *context;
aeEventLoop *loop;
int fd;
int reading, writing;
} redisAeEvents;
void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleRead(e->context);
}
void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleWrite(e->context);
}
void redisAeAddRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->reading) {
e->reading = 1;
aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
}
}
void redisAeDelRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->reading) {
e->reading = 0;
aeDeleteFileEvent(loop,e->fd,AE_READABLE);
}
}
void redisAeAddWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->writing) {
e->writing = 1;
aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
}
}
void redisAeDelWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->writing) {
e->writing = 0;
aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
}
}
void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata);
redisAeDelWrite(privdata);
free(e);
}
int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisAeEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisAeEvents*)malloc(sizeof(*e));
e->context = ac;
e->loop = loop;
e->fd = c->fd;
e->reading = e->writing = 0;
/* Register functions to start/stop listening for events */
ac->evAddRead = redisAeAddRead;
ac->evDelRead = redisAeDelRead;
ac->evAddWrite = redisAeAddWrite;
ac->evDelWrite = redisAeDelWrite;
ac->evCleanup = redisAeCleanup;
ac->_adapter_data = e;
return REDIS_OK;
}

View File

@ -10,69 +10,89 @@ typedef struct redisLibevEvents {
ev_io rev, wev; ev_io rev, wev;
} redisLibevEvents; } redisLibevEvents;
void redisLibevReadEvent(struct ev_loop *loop, ev_io *watcher, int revents) { void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
((void)loop); ((void)revents); #if EV_MULTIPLICITY
redisLibevEvents *e = watcher->data; ((void)loop);
#endif
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
redisAsyncHandleRead(e->context); redisAsyncHandleRead(e->context);
} }
void redisLibevWriteEvent(struct ev_loop *loop, ev_io *watcher, int revents) { void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
((void)loop); ((void)revents); #if EV_MULTIPLICITY
redisLibevEvents *e = watcher->data; ((void)loop);
#endif
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
redisAsyncHandleWrite(e->context); redisAsyncHandleWrite(e->context);
} }
void redisLibevAddRead(void *privdata) { void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!e->reading) { if (!e->reading) {
e->reading = 1; e->reading = 1;
ev_io_start(e->loop,&e->rev); ev_io_start(EV_A_ &e->rev);
} }
} }
void redisLibevDelRead(void *privdata) { void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (e->reading) { if (e->reading) {
e->reading = 0; e->reading = 0;
ev_io_stop(e->loop,&e->rev); ev_io_stop(EV_A_ &e->rev);
} }
} }
void redisLibevAddWrite(void *privdata) { void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!e->writing) { if (!e->writing) {
e->writing = 1; e->writing = 1;
ev_io_start(e->loop,&e->wev); ev_io_start(EV_A_ &e->wev);
} }
} }
void redisLibevDelWrite(void *privdata) { void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (e->writing) { if (e->writing) {
e->writing = 0; e->writing = 0;
ev_io_stop(e->loop,&e->wev); ev_io_stop(EV_A_ &e->wev);
} }
} }
void redisLibevCleanup(void *privdata) { void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata); redisLibevDelRead(privdata);
redisLibevDelWrite(privdata); redisLibevDelWrite(privdata);
free(e); free(e);
} }
int redisLibevAttach(redisAsyncContext *ac, struct ev_loop *loop) { 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->data != NULL) if (ac->_adapter_data != NULL)
return REDIS_ERR; return REDIS_ERR;
/* Create container for context and r/w events */ /* Create container for context and r/w events */
e = malloc(sizeof(*e)); e = (redisLibevEvents*)malloc(sizeof(*e));
e->context = ac; e->context = ac;
#if EV_MULTIPLICITY
e->loop = loop; e->loop = loop;
#else
e->loop = NULL;
#endif
e->reading = e->writing = 0; e->reading = e->writing = 0;
e->rev.data = e; e->rev.data = e;
e->wev.data = e; e->wev.data = e;
@ -83,10 +103,11 @@ int redisLibevAttach(redisAsyncContext *ac, struct ev_loop *loop) {
ac->evAddWrite = redisLibevAddWrite; ac->evAddWrite = redisLibevAddWrite;
ac->evDelWrite = redisLibevDelWrite; ac->evDelWrite = redisLibevDelWrite;
ac->evCleanup = redisLibevCleanup; ac->evCleanup = redisLibevCleanup;
ac->data = e; ac->_adapter_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);
ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE); ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
return REDIS_OK; return REDIS_OK;
} }

View File

@ -10,38 +10,38 @@ typedef struct redisLibeventEvents {
void redisLibeventReadEvent(int fd, short event, void *arg) { void redisLibeventReadEvent(int fd, short event, void *arg) {
((void)fd); ((void)event); ((void)fd); ((void)event);
redisLibeventEvents *e = arg; redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleRead(e->context); redisAsyncHandleRead(e->context);
} }
void redisLibeventWriteEvent(int fd, short event, void *arg) { void redisLibeventWriteEvent(int fd, short event, void *arg) {
((void)fd); ((void)event); ((void)fd); ((void)event);
redisLibeventEvents *e = arg; redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleWrite(e->context); redisAsyncHandleWrite(e->context);
} }
void redisLibeventAddRead(void *privdata) { void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->rev,NULL); event_add(&e->rev,NULL);
} }
void redisLibeventDelRead(void *privdata) { void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev); event_del(&e->rev);
} }
void redisLibeventAddWrite(void *privdata) { void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->wev,NULL); event_add(&e->wev,NULL);
} }
void redisLibeventDelWrite(void *privdata) { void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->wev); event_del(&e->wev);
} }
void redisLibeventCleanup(void *privdata) { void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = 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);
@ -52,11 +52,11 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
redisLibeventEvents *e; redisLibeventEvents *e;
/* Nothing should be attached when something is already attached */ /* Nothing should be attached when something is already attached */
if (ac->data != NULL) if (ac->_adapter_data != NULL)
return REDIS_ERR; return REDIS_ERR;
/* Create container for context and r/w events */ /* Create container for context and r/w events */
e = malloc(sizeof(*e)); e = (redisLibeventEvents*)malloc(sizeof(*e));
e->context = ac; e->context = ac;
/* Register functions to start/stop listening for events */ /* Register functions to start/stop listening for events */
@ -65,7 +65,7 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
ac->evAddWrite = redisLibeventAddWrite; ac->evAddWrite = redisLibeventAddWrite;
ac->evDelWrite = redisLibeventDelWrite; ac->evDelWrite = redisLibeventDelWrite;
ac->evCleanup = redisLibeventCleanup; ac->evCleanup = redisLibeventCleanup;
ac->data = e; ac->_adapter_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);

55
deps/hiredis/async.c vendored
View File

@ -1,5 +1,7 @@
/* /*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -38,8 +40,29 @@ void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
static redisAsyncContext *redisAsyncInitialize(redisContext *c) { static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext)); redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
/* Set all bytes in the async part of the context to 0 */ c = &(ac->c);
memset(ac+sizeof(redisContext),0,sizeof(redisAsyncContext)-sizeof(redisContext));
/* The regular connect functions will always set the flag REDIS_CONNECTED.
* For the async API, we want to wait until the first write event is
* received up before setting this flag, so reset it here. */
c->flags &= ~REDIS_CONNECTED;
ac->err = 0;
ac->errstr = NULL;
ac->data = NULL;
ac->_adapter_data = NULL;
ac->evAddRead = NULL;
ac->evDelRead = NULL;
ac->evAddWrite = NULL;
ac->evDelWrite = NULL;
ac->evCleanup = NULL;
ac->onConnect = NULL;
ac->onDisconnect = NULL;
ac->replies.head = NULL;
ac->replies.tail = NULL;
return ac; return ac;
} }
@ -70,6 +93,14 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
return redisSetReplyObjectFunctions(c,fn); return redisSetReplyObjectFunctions(c,fn);
} }
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
if (ac->onConnect == NULL) {
ac->onConnect = fn;
return REDIS_OK;
}
return REDIS_ERR;
}
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
if (ac->onDisconnect == NULL) { if (ac->onDisconnect == NULL) {
ac->onDisconnect = fn; ac->onDisconnect = fn;
@ -153,7 +184,7 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
} }
/* Signal event lib to clean up */ /* Signal event lib to clean up */
if (ac->evCleanup) ac->evCleanup(ac->data); if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
/* Execute callback with proper status */ /* Execute callback with proper status */
if (ac->onDisconnect) ac->onDisconnect(ac,status); if (ac->onDisconnect) ac->onDisconnect(ac,status);
@ -206,7 +237,7 @@ void redisAsyncHandleRead(redisAsyncContext *ac) {
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
} else { } else {
/* Always re-schedule reads */ /* Always re-schedule reads */
if (ac->evAddRead) ac->evAddRead(ac->data); if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
redisProcessCallbacks(ac); redisProcessCallbacks(ac);
} }
} }
@ -220,13 +251,19 @@ void redisAsyncHandleWrite(redisAsyncContext *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->evAddWrite) ac->evAddWrite(ac->data); if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
} else { } else {
if (ac->evDelWrite) ac->evDelWrite(ac->data); if (ac->evDelWrite) ac->evDelWrite(ac->_adapter_data);
} }
/* Always schedule reads when something was written */ /* Always schedule reads after writes */
if (ac->evAddRead) ac->evAddRead(ac->data); if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
/* Fire onConnect when this is the first write event. */
if (!(c->flags & REDIS_CONNECTED)) {
c->flags |= REDIS_CONNECTED;
if (ac->onConnect) ac->onConnect(ac);
}
} }
} }
@ -249,7 +286,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
__redisPushCallback(&ac->replies,&cb); __redisPushCallback(&ac->replies,&cb);
/* Always schedule a write when the write buffer is non-empty */ /* Always schedule a write when the write buffer is non-empty */
if (ac->evAddWrite) ac->evAddWrite(ac->data); if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
return REDIS_OK; return REDIS_OK;
} }

22
deps/hiredis/async.h vendored
View File

@ -31,6 +31,10 @@
#define __HIREDIS_ASYNC_H #define __HIREDIS_ASYNC_H
#include "hiredis.h" #include "hiredis.h"
#ifdef __cplusplus
extern "C" {
#endif
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
/* Reply callback prototype and container */ /* Reply callback prototype and container */
@ -46,8 +50,9 @@ typedef struct redisCallbackList {
redisCallback *head, *tail; redisCallback *head, *tail;
} redisCallbackList; } redisCallbackList;
/* Disconnect callback prototype */ /* 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*);
/* Context for an async connection to Redis */ /* Context for an async connection to Redis */
typedef struct redisAsyncContext { typedef struct redisAsyncContext {
@ -58,6 +63,12 @@ typedef struct redisAsyncContext {
int err; int err;
char *errstr; char *errstr;
/* Not used by hiredis */
void *data;
/* Used by the different event lib adapters to store their private data */
void *_adapter_data;
/* Called when the library expects to start reading/writing. /* Called when the library expects to start reading/writing.
* The supplied functions should be idempotent. */ * The supplied functions should be idempotent. */
void (*evAddRead)(void *privdata); void (*evAddRead)(void *privdata);
@ -65,12 +76,14 @@ typedef struct redisAsyncContext {
void (*evAddWrite)(void *privdata); void (*evAddWrite)(void *privdata);
void (*evDelWrite)(void *privdata); void (*evDelWrite)(void *privdata);
void (*evCleanup)(void *privdata); void (*evCleanup)(void *privdata);
void *data;
/* Called when either the connection is terminated due to an error or per /* Called when either the connection is terminated due to an error or per
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
redisDisconnectCallback *onDisconnect; redisDisconnectCallback *onDisconnect;
/* Called when the first write event was received. */
redisConnectCallback *onConnect;
/* Reply callbacks */ /* Reply callbacks */
redisCallbackList replies; redisCallbackList replies;
} redisAsyncContext; } redisAsyncContext;
@ -78,6 +91,7 @@ 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);
int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn); int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *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);
@ -91,4 +105,8 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif #endif

53
deps/hiredis/example-ae.c vendored Normal file
View File

@ -0,0 +1,53 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/ae.h"
/* Put event loop in the global scope, so it can be explicitly stopped */
static aeEventLoop *loop;
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c) {
((void)c);
printf("connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
}
printf("disconnected...\n");
aeStop(loop);
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
loop = aeCreateEventLoop();
redisAeAttach(loop, c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
aeMain(loop);
return 0;
}

View File

@ -15,15 +15,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c); redisAsyncDisconnect(c);
} }
void connectCallback(const redisAsyncContext *c) {
((void)c);
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);
} }
printf("disconnected...\n");
} }
int main (int argc, char **argv) { int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
struct ev_loop *loop = ev_default_loop(0);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) { if (c->err) {
@ -32,10 +37,11 @@ int main (int argc, char **argv) {
return 1; return 1;
} }
redisLibevAttach(c,loop); redisLibevAttach(EV_DEFAULT_ c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback); redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
ev_loop(loop, 0); ev_loop(EV_DEFAULT_ 0);
return 0; return 0;
} }

View File

@ -15,10 +15,16 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c); redisAsyncDisconnect(c);
} }
void connectCallback(const redisAsyncContext *c) {
((void)c);
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);
} }
printf("disconnected...\n");
} }
int main (int argc, char **argv) { int main (int argc, char **argv) {
@ -33,6 +39,7 @@ int main (int argc, char **argv) {
} }
redisLibeventAttach(c,base); redisLibeventAttach(c,base);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback); redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");

View File

@ -17,7 +17,7 @@ int main(void) {
/* PING server */ /* PING server */
reply = redisCommand(c,"PING"); reply = redisCommand(c,"PING");
printf("PONG: %s\n", reply->str); printf("PING: %s\n", reply->str);
freeReplyObject(reply); freeReplyObject(reply);
/* Set a key */ /* Set a key */

241
deps/hiredis/hiredis.c vendored
View File

@ -1,5 +1,7 @@
/* /*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,6 +34,7 @@
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <ctype.h>
#include "hiredis.h" #include "hiredis.h"
#include "net.h" #include "net.h"
@ -44,10 +47,12 @@ typedef struct redisReader {
void *reply; /* holds temporary reply */ void *reply; /* holds temporary reply */
sds buf; /* read buffer */ sds buf; /* read buffer */
unsigned int pos; /* buffer cursor */ size_t pos; /* buffer cursor */
size_t len; /* buffer length */
redisReadTask rstack[3]; /* stack of read tasks */ redisReadTask rstack[3]; /* stack of read tasks */
int ridx; /* index of stack */ int ridx; /* index of stack */
void *privdata; /* user-settable arbitrary field */
} redisReader; } redisReader;
static redisReply *createReplyObject(int type); static redisReply *createReplyObject(int type);
@ -68,7 +73,7 @@ static redisReplyObjectFunctions defaultFunctions = {
/* Create a reply object */ /* Create a reply object */
static redisReply *createReplyObject(int type) { static redisReply *createReplyObject(int type) {
redisReply *r = calloc(sizeof(*r),1); redisReply *r = malloc(sizeof(*r));
if (!r) redisOOM(); if (!r) redisOOM();
r->type = type; r->type = type;
@ -88,9 +93,10 @@ void freeReplyObject(void *reply) {
if (r->element[j]) freeReplyObject(r->element[j]); if (r->element[j]) freeReplyObject(r->element[j]);
free(r->element); free(r->element);
break; break;
default: case REDIS_REPLY_ERROR:
if (r->str != NULL) case REDIS_REPLY_STATUS:
free(r->str); case REDIS_REPLY_STRING:
free(r->str);
break; break;
} }
free(r); free(r);
@ -111,7 +117,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
r->len = len; r->len = len;
if (task->parent) { if (task->parent) {
redisReply *parent = task->parent; redisReply *parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
@ -124,7 +130,7 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL) if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
redisOOM(); redisOOM();
if (task->parent) { if (task->parent) {
redisReply *parent = task->parent; redisReply *parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
@ -135,7 +141,7 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
redisReply *r = createReplyObject(REDIS_REPLY_INTEGER); redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
r->integer = value; r->integer = value;
if (task->parent) { if (task->parent) {
redisReply *parent = task->parent; redisReply *parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
@ -145,7 +151,7 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
static void *createNilObject(const redisReadTask *task) { static void *createNilObject(const redisReadTask *task) {
redisReply *r = createReplyObject(REDIS_REPLY_NIL); redisReply *r = createReplyObject(REDIS_REPLY_NIL);
if (task->parent) { if (task->parent) {
redisReply *parent = task->parent; redisReply *parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
@ -154,7 +160,7 @@ static void *createNilObject(const redisReadTask *task) {
static char *readBytes(redisReader *r, unsigned int bytes) { static char *readBytes(redisReader *r, unsigned int bytes) {
char *p; char *p;
if (sdslen(r->buf)-r->pos >= bytes) { if (r->len-r->pos >= bytes) {
p = r->buf+r->pos; p = r->buf+r->pos;
r->pos += bytes; r->pos += bytes;
return p; return p;
@ -162,20 +168,60 @@ static char *readBytes(redisReader *r, unsigned int bytes) {
return NULL; return NULL;
} }
static char *seekNewline(char *s) { /* Find pointer to \r\n. */
/* Find pointer to \r\n without strstr */ static char *seekNewline(char *s, size_t len) {
while (s != NULL) { int pos = 0;
s = strchr(s,'\r'); int _len = len-1;
if (s != NULL) {
if (s[1] == '\n') /* Position should be < len-1 because the character at "pos" should be
break; * followed by a \n. Note that strchr cannot be used because it doesn't
else * allow to search a limited length and the buffer that is being searched
s++; * might not have a trailing NULL character. */
while (pos < _len) {
while(pos < _len && s[pos] != '\r') pos++;
if (s[pos] != '\r') {
/* Not found. */
return NULL;
} else { } else {
break; if (s[pos+1] == '\n') {
/* Found. */
return s+pos;
} else {
/* Continue searching. */
pos++;
}
} }
} }
return s; 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++;
}
while ((c = *(s++)) != '\r') {
dec = c - '0';
if (dec >= 0 && dec < 10) {
v *= 10;
v += dec;
} else {
/* Should not happen... */
return -1;
}
}
return mult*v;
} }
static char *readLine(redisReader *r, int *_len) { static char *readLine(redisReader *r, int *_len) {
@ -183,7 +229,7 @@ static char *readLine(redisReader *r, int *_len) {
int len; int len;
p = r->buf+r->pos; p = r->buf+r->pos;
s = seekNewline(p); s = seekNewline(p,(r->len-r->pos));
if (s != NULL) { if (s != NULL) {
len = s-(r->buf+r->pos); len = s-(r->buf+r->pos);
r->pos += len+2; /* skip \r\n */ r->pos += len+2; /* skip \r\n */
@ -227,7 +273,7 @@ static int processLineItem(redisReader *r) {
if ((p = readLine(r,&len)) != NULL) { if ((p = readLine(r,&len)) != NULL) {
if (r->fn) { if (r->fn) {
if (cur->type == REDIS_REPLY_INTEGER) { if (cur->type == REDIS_REPLY_INTEGER) {
obj = r->fn->createInteger(cur,strtoll(p,NULL,10)); obj = r->fn->createInteger(cur,readLongLong(p));
} else { } else {
obj = r->fn->createString(cur,p,len); obj = r->fn->createString(cur,p,len);
} }
@ -235,9 +281,8 @@ static int processLineItem(redisReader *r) {
obj = (void*)(size_t)(cur->type); obj = (void*)(size_t)(cur->type);
} }
/* If there is no root yet, register this object as root. */ /* Set reply if this is the root object. */
if (r->reply == NULL) if (r->ridx == 0) r->reply = obj;
r->reply = obj;
moveToNextTask(r); moveToNextTask(r);
return 0; return 0;
} }
@ -250,32 +295,36 @@ static int processBulkItem(redisReader *r) {
char *p, *s; char *p, *s;
long len; long len;
unsigned long bytelen; unsigned long bytelen;
int success = 0;
p = r->buf+r->pos; p = r->buf+r->pos;
s = seekNewline(p); s = seekNewline(p,r->len-r->pos);
if (s != NULL) { if (s != NULL) {
p = r->buf+r->pos; p = r->buf+r->pos;
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
len = strtol(p,NULL,10); len = readLongLong(p);
if (len < 0) { if (len < 0) {
/* The nil object can always be created. */ /* The nil object can always be created. */
obj = r->fn ? r->fn->createNil(cur) : obj = r->fn ? r->fn->createNil(cur) :
(void*)REDIS_REPLY_NIL; (void*)REDIS_REPLY_NIL;
success = 1;
} else { } else {
/* Only continue when the buffer contains the entire bulk item. */ /* Only continue when the buffer contains the entire bulk item. */
bytelen += len+2; /* include \r\n */ bytelen += len+2; /* include \r\n */
if (r->pos+bytelen <= sdslen(r->buf)) { if (r->pos+bytelen <= r->len) {
obj = r->fn ? r->fn->createString(cur,s+2,len) : obj = r->fn ? r->fn->createString(cur,s+2,len) :
(void*)REDIS_REPLY_STRING; (void*)REDIS_REPLY_STRING;
success = 1;
} }
} }
/* Proceed when obj was created. */ /* Proceed when obj was created. */
if (obj != NULL) { if (success) {
r->pos += bytelen; r->pos += bytelen;
if (r->reply == NULL)
r->reply = obj; /* Set reply if this is the root object. */
if (r->ridx == 0) r->reply = obj;
moveToNextTask(r); moveToNextTask(r);
return 0; return 0;
} }
@ -288,9 +337,19 @@ static int processMultiBulkItem(redisReader *r) {
void *obj; void *obj;
char *p; char *p;
long elements; long elements;
int root = 0;
/* Set error for nested multi bulks with depth > 1 */
if (r->ridx == 2) {
redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
"No support for nested multi bulk replies with depth > 1"));
return -1;
}
if ((p = readLine(r,NULL)) != NULL) { if ((p = readLine(r,NULL)) != NULL) {
elements = strtol(p,NULL,10); elements = readLongLong(p);
root = (r->ridx == 0);
if (elements == -1) { if (elements == -1) {
obj = r->fn ? r->fn->createNil(cur) : obj = r->fn ? r->fn->createNil(cur) :
(void*)REDIS_REPLY_NIL; (void*)REDIS_REPLY_NIL;
@ -302,19 +361,21 @@ static int processMultiBulkItem(redisReader *r) {
/* Modify task stack when there are more than 0 elements. */ /* Modify task stack when there are more than 0 elements. */
if (elements > 0) { if (elements > 0) {
cur->elements = elements; cur->elements = elements;
cur->obj = obj;
r->ridx++; r->ridx++;
r->rstack[r->ridx].type = -1; r->rstack[r->ridx].type = -1;
r->rstack[r->ridx].elements = -1; r->rstack[r->ridx].elements = -1;
r->rstack[r->ridx].parent = obj;
r->rstack[r->ridx].idx = 0; r->rstack[r->ridx].idx = 0;
r->rstack[r->ridx].obj = NULL;
r->rstack[r->ridx].parent = cur;
r->rstack[r->ridx].privdata = r->privdata;
} else { } else {
moveToNextTask(r); moveToNextTask(r);
} }
} }
/* Object was created, so we can always continue. */ /* Set reply if this is the root object. */
if (r->reply == NULL) if (root) r->reply = obj;
r->reply = obj;
return 0; return 0;
} }
return -1; return -1;
@ -347,7 +408,7 @@ static int processItem(redisReader *r) {
default: default:
byte = sdscatrepr(sdsempty(),p,1); byte = sdscatrepr(sdsempty(),p,1);
redisSetReplyReaderError(r,sdscatprintf(sdsempty(), redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
"protocol error, got %s as reply type byte", byte)); "Protocol error, got %s as reply type byte", byte));
sdsfree(byte); sdsfree(byte);
return -1; return -1;
} }
@ -368,8 +429,7 @@ static int processItem(redisReader *r) {
case REDIS_REPLY_ARRAY: case REDIS_REPLY_ARRAY:
return processMultiBulkItem(r); return processMultiBulkItem(r);
default: default:
redisSetReplyReaderError(r,sdscatprintf(sdsempty(), assert(NULL);
"unknown item type '%d'", cur->type));
return -1; return -1;
} }
} }
@ -394,6 +454,17 @@ int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFuncti
return REDIS_ERR; return REDIS_ERR;
} }
/* Set the private data field that is used in the read tasks. This argument can
* be used to curry arbitrary data to the custom reply object functions. */
int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
redisReader *r = reader;
if (r->reply == NULL) {
r->privdata = privdata;
return REDIS_OK;
}
return REDIS_ERR;
}
/* External libraries wrapping hiredis might need access to the temporary /* External libraries wrapping hiredis might need access to the temporary
* variable while the reply is built up. When the reader contains an * variable while the reply is built up. When the reader contains an
* object in between receiving some bytes to parse, this object might * object in between receiving some bytes to parse, this object might
@ -437,8 +508,10 @@ void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
redisReader *r = reader; redisReader *r = reader;
/* Copy the provided buffer. */ /* Copy the provided buffer. */
if (buf != NULL && len >= 1) if (buf != NULL && len >= 1) {
r->buf = sdscatlen(r->buf,buf,len); r->buf = sdscatlen(r->buf,buf,len);
r->len = sdslen(r->buf);
}
} }
int redisReplyReaderGetReply(void *reader, void **reply) { int redisReplyReaderGetReply(void *reader, void **reply) {
@ -446,15 +519,17 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
if (reply != NULL) *reply = NULL; if (reply != NULL) *reply = NULL;
/* When the buffer is empty, there will never be a reply. */ /* When the buffer is empty, there will never be a reply. */
if (sdslen(r->buf) == 0) if (r->len == 0)
return REDIS_OK; return REDIS_OK;
/* Set first item to process when the stack is empty. */ /* Set first item to process when the stack is empty. */
if (r->ridx == -1) { if (r->ridx == -1) {
r->rstack[0].type = -1; r->rstack[0].type = -1;
r->rstack[0].elements = -1; r->rstack[0].elements = -1;
r->rstack[0].parent = NULL;
r->rstack[0].idx = -1; r->rstack[0].idx = -1;
r->rstack[0].obj = NULL;
r->rstack[0].parent = NULL;
r->rstack[0].privdata = r->privdata;
r->ridx = 0; r->ridx = 0;
} }
@ -465,14 +540,15 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
/* Discard the consumed part of the buffer. */ /* Discard the consumed part of the buffer. */
if (r->pos > 0) { if (r->pos > 0) {
if (r->pos == sdslen(r->buf)) { if (r->pos == r->len) {
/* sdsrange has a quirck on this edge case. */ /* sdsrange has a quirck on this edge case. */
sdsfree(r->buf); sdsfree(r->buf);
r->buf = sdsempty(); r->buf = sdsempty();
} else { } else {
r->buf = sdsrange(r->buf,r->pos,sdslen(r->buf)); r->buf = sdsrange(r->buf,r->pos,r->len);
} }
r->pos = 0; r->pos = 0;
r->len = sdslen(r->buf);
} }
/* Emit a reply when there is one. */ /* Emit a reply when there is one. */
@ -481,7 +557,7 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
r->reply = NULL; r->reply = NULL;
/* Destroy the buffer when it is empty and is quite large. */ /* Destroy the buffer when it is empty and is quite large. */
if (sdslen(r->buf) == 0 && sdsavail(r->buf) > 16*1024) { if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
sdsfree(r->buf); sdsfree(r->buf);
r->buf = sdsempty(); r->buf = sdsempty();
r->pos = 0; r->pos = 0;
@ -525,6 +601,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
char *cmd = NULL; /* final command */ char *cmd = NULL; /* final command */
int pos; /* position in final command */ int pos; /* position in final command */
sds current; /* current argument */ sds current; /* current argument */
int interpolated = 0; /* did we do interpolation on an argument? */
char **argv = NULL; char **argv = NULL;
int argc = 0, j; int argc = 0, j;
int totlen = 0; int totlen = 0;
@ -541,6 +618,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
if (sdslen(current) != 0) { if (sdslen(current) != 0) {
addArgument(current, &argv, &argc, &totlen); addArgument(current, &argv, &argc, &totlen);
current = sdsempty(); current = sdsempty();
interpolated = 0;
} }
} else { } else {
current = sdscatlen(current,c,1); current = sdscatlen(current,c,1);
@ -549,16 +627,74 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
switch(c[1]) { switch(c[1]) {
case 's': case 's':
arg = va_arg(ap,char*); arg = va_arg(ap,char*);
current = sdscat(current,arg); size = strlen(arg);
if (size > 0)
current = sdscatlen(current,arg,size);
interpolated = 1;
break; break;
case 'b': case 'b':
arg = va_arg(ap,char*); arg = va_arg(ap,char*);
size = va_arg(ap,size_t); size = va_arg(ap,size_t);
current = sdscatlen(current,arg,size); if (size > 0)
current = sdscatlen(current,arg,size);
interpolated = 1;
break; break;
case '%': case '%':
cmd = sdscat(cmd,"%"); current = sdscat(current,"%");
break; break;
default:
/* Try to detect printf format */
{
char _format[16];
const char *_p = c+1;
size_t _l = 0;
va_list _cpy;
/* Flags */
if (*_p != '\0' && *_p == '#') _p++;
if (*_p != '\0' && *_p == '0') _p++;
if (*_p != '\0' && *_p == '-') _p++;
if (*_p != '\0' && *_p == ' ') _p++;
if (*_p != '\0' && *_p == '+') _p++;
/* Field width */
while (*_p != '\0' && isdigit(*_p)) _p++;
/* Precision */
if (*_p == '.') {
_p++;
while (*_p != '\0' && isdigit(*_p)) _p++;
}
/* Modifiers */
if (*_p != '\0') {
if (*_p == 'h' || *_p == 'l') {
/* Allow a single repetition for these modifiers */
if (_p[0] == _p[1]) _p++;
_p++;
}
}
/* Conversion specifier */
if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
_l = (_p+1)-c;
if (_l < sizeof(_format)-2) {
memcpy(_format,c,_l);
_format[_l] = '\0';
va_copy(_cpy,ap);
current = sdscatvprintf(current,_format,_cpy);
interpolated = 1;
va_end(_cpy);
/* Update current position (note: outer blocks
* increment c twice so compensate here) */
c = _p-1;
}
}
/* Consume and discard vararg */
va_arg(ap,void);
}
} }
c++; c++;
} }
@ -566,7 +702,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
} }
/* Add the last argument if needed */ /* Add the last argument if needed */
if (sdslen(current) != 0) { if (interpolated || sdslen(current) != 0) {
addArgument(current, &argv, &argc, &totlen); addArgument(current, &argv, &argc, &totlen);
} else { } else {
sdsfree(current); sdsfree(current);
@ -664,7 +800,6 @@ void __redisSetError(redisContext *c, int type, const sds errstr) {
static redisContext *redisContextInit() { static redisContext *redisContextInit() {
redisContext *c = calloc(sizeof(redisContext),1); redisContext *c = calloc(sizeof(redisContext),1);
c->fd = -1; /* quick fix for a bug that should be addressed differently */
c->err = 0; c->err = 0;
c->errstr = NULL; c->errstr = NULL;
c->obuf = sdsempty(); c->obuf = sdsempty();
@ -692,7 +827,6 @@ void redisFree(redisContext *c) {
redisContext *redisConnect(const char *ip, int port) { redisContext *redisConnect(const char *ip, int port) {
redisContext *c = redisContextInit(); redisContext *c = redisContextInit();
c->flags |= REDIS_BLOCK; c->flags |= REDIS_BLOCK;
c->flags |= REDIS_CONNECTED;
redisContextConnectTcp(c,ip,port); redisContextConnectTcp(c,ip,port);
return c; return c;
} }
@ -700,7 +834,6 @@ redisContext *redisConnect(const char *ip, int port) {
redisContext *redisConnectNonBlock(const char *ip, int port) { redisContext *redisConnectNonBlock(const char *ip, int port) {
redisContext *c = redisContextInit(); redisContext *c = redisContextInit();
c->flags &= ~REDIS_BLOCK; c->flags &= ~REDIS_BLOCK;
c->flags |= REDIS_CONNECTED;
redisContextConnectTcp(c,ip,port); redisContextConnectTcp(c,ip,port);
return c; return c;
} }
@ -708,7 +841,6 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
redisContext *redisConnectUnix(const char *path) { redisContext *redisConnectUnix(const char *path) {
redisContext *c = redisContextInit(); redisContext *c = redisContextInit();
c->flags |= REDIS_BLOCK; c->flags |= REDIS_BLOCK;
c->flags |= REDIS_CONNECTED;
redisContextConnectUnix(c,path); redisContextConnectUnix(c,path);
return c; return c;
} }
@ -716,7 +848,6 @@ redisContext *redisConnectUnix(const char *path) {
redisContext *redisConnectUnixNonBlock(const char *path) { redisContext *redisConnectUnixNonBlock(const char *path) {
redisContext *c = redisContextInit(); redisContext *c = redisContextInit();
c->flags &= ~REDIS_BLOCK; c->flags &= ~REDIS_BLOCK;
c->flags |= REDIS_CONNECTED;
redisContextConnectUnix(c,path); redisContextConnectUnix(c,path);
return c; return c;
} }

View File

@ -1,5 +1,7 @@
/* /*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -34,7 +36,7 @@
#define HIREDIS_MAJOR 0 #define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 9 #define HIREDIS_MINOR 9
#define HIREDIS_PATCH 0 #define HIREDIS_PATCH 2
#define REDIS_ERR -1 #define REDIS_ERR -1
#define REDIS_OK 0 #define REDIS_OK 0
@ -62,12 +64,16 @@
* should be terminated once all replies have been read. */ * should be terminated once all replies have been read. */
#define REDIS_DISCONNECTING 0x4 #define REDIS_DISCONNECTING 0x4
#define REDIS_REPLY_ERROR 0
#define REDIS_REPLY_STRING 1 #define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2 #define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3 #define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4 #define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5 #define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
#ifdef __cplusplus
extern "C" {
#endif
/* This is the reply object returned by redisCommand() */ /* This is the reply object returned by redisCommand() */
typedef struct redisReply { typedef struct redisReply {
@ -82,8 +88,10 @@ typedef struct redisReply {
typedef struct redisReadTask { typedef struct redisReadTask {
int type; int type;
int elements; /* number of elements in multibulk container */ int elements; /* number of elements in multibulk container */
void *parent; /* optional pointer to parent object */
int idx; /* index in parent (array) object */ int idx; /* index in parent (array) object */
void *obj; /* holds user-generated value for a read task */
struct redisReadTask *parent; /* parent task */
void *privdata; /* user-settable arbitrary field */
} redisReadTask; } redisReadTask;
typedef struct redisReplyObjectFunctions { typedef struct redisReplyObjectFunctions {
@ -112,6 +120,7 @@ typedef struct redisContext {
void freeReplyObject(void *reply); void freeReplyObject(void *reply);
void *redisReplyReaderCreate(); void *redisReplyReaderCreate();
int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn); int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
int redisReplyReaderSetPrivdata(void *reader, void *privdata);
void *redisReplyReaderGetObject(void *reader); void *redisReplyReaderGetObject(void *reader);
char *redisReplyReaderGetError(void *reader); char *redisReplyReaderGetError(void *reader);
void redisReplyReaderFree(void *ptr); void redisReplyReaderFree(void *ptr);
@ -154,4 +163,8 @@ void *redisvCommand(redisContext *c, const char *format, va_list ap);
void *redisCommand(redisContext *c, const char *format, ...); void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif #endif

9
deps/hiredis/net.c vendored
View File

@ -1,8 +1,9 @@
/* 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-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved. * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved.
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
@ -43,7 +44,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include "hiredis.h" #include "net.h"
#include "sds.h" #include "sds.h"
/* Forward declaration */ /* Forward declaration */
@ -114,7 +115,7 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
he = gethostbyname(addr); he = gethostbyname(addr);
if (he == NULL) { if (he == NULL) {
__redisSetError(c,REDIS_ERR_OTHER, __redisSetError(c,REDIS_ERR_OTHER,
sdscatprintf(sdsempty(),"can't resolve: %s",addr)); sdscatprintf(sdsempty(),"Can't resolve: %s",addr));
close(s); close(s);
return REDIS_ERR; return REDIS_ERR;
} }
@ -137,6 +138,7 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
} }
c->fd = s; c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK; return REDIS_OK;
} }
@ -163,5 +165,6 @@ int redisContextConnectUnix(redisContext *c, const char *path) {
} }
c->fd = s; c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK; return REDIS_OK;
} }

6
deps/hiredis/net.h vendored
View File

@ -31,6 +31,12 @@
#ifndef __NET_H #ifndef __NET_H
#define __NET_H #define __NET_H
#include "hiredis.h"
#if defined(__sun)
#define AF_LOCAL AF_UNIX
#endif
int redisContextConnectTcp(redisContext *c, const char *addr, int port); int redisContextConnectTcp(redisContext *c, const char *addr, int port);
int redisContextConnectUnix(redisContext *c, const char *path); int redisContextConnectUnix(redisContext *c, const char *path);

142
deps/hiredis/test.c vendored
View File

@ -47,17 +47,59 @@ static void test_format_commands() {
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd); free(cmd);
test("Format command with %%s and an empty string: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
test("Format command with %%b string interpolation: "); test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3); len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd); free(cmd);
test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
test("Format command with literal %%: ");
len = redisFormatCommand(&cmd,"SET %% %%");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd);
test("Format command with printf-delegation (long long): ");
len = redisFormatCommand(&cmd,"key:%08lld",1234ll);
test_cond(strncmp(cmd,"*1\r\n$12\r\nkey:00001234\r\n",len) == 0 &&
len == 4+5+(12+2));
free(cmd);
test("Format command with printf-delegation (float): ");
len = redisFormatCommand(&cmd,"v:%06.1f",12.34f);
test_cond(strncmp(cmd,"*1\r\n$8\r\nv:0012.3\r\n",len) == 0 &&
len == 4+4+(8+2));
free(cmd);
test("Format command with printf-delegation and extra interpolation: ");
len = redisFormatCommand(&cmd,"key:%d %b",1234,"foo",3);
test_cond(strncmp(cmd,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(8+2)+4+(3+2));
free(cmd);
test("Format command with wrong printf format and extra interpolation: ");
len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3);
test_cond(strncmp(cmd,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(6+2)+4+(3+2));
free(cmd);
const char *argv[3]; const char *argv[3];
argv[0] = "SET"; argv[0] = "SET";
argv[1] = "foo"; argv[1] = "foo\0xxx";
argv[2] = "bar"; argv[2] = "bar";
size_t lens[3] = { 3, 3, 3 }; size_t lens[3] = { 3, 7, 3 };
int argc = 3; int argc = 3;
test("Format command by passing argc/argv without lengths: "); test("Format command by passing argc/argv without lengths: ");
@ -68,38 +110,29 @@ static void test_format_commands() {
test("Format command by passing argc/argv with lengths: "); test("Format command by passing argc/argv with lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,lens); len = redisFormatCommandArgv(&cmd,argc,argv,lens);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2)); len == 4+4+(3+2)+4+(7+2)+4+(3+2));
free(cmd); free(cmd);
} }
static void test_blocking_connection() { static void test_blocking_connection() {
redisContext *c; redisContext *c;
redisReply *reply; redisReply *reply;
int major, minor;
__connect(&c); test("Returns error when host cannot be resolved: ");
test("Returns I/O error when the connection is lost: "); c = redisConnect((char*)"idontexist.local", 6379);
reply = redisCommand(c,"QUIT"); test_cond(c->err == REDIS_ERR_OTHER &&
test_cond(strcasecmp(reply->str,"OK") == 0 && redisCommand(c,"PING") == NULL); strcmp(c->errstr,"Can't resolve: idontexist.local") == 0);
/* Two conditions may happen, depending on the type of connection.
* When connected via TCP, the socket will not yet be aware of the closed
* connection and the write(2) call will succeed, but the read(2) will
* result in an EOF. When connected via Unix sockets, the socket will be
* immediately aware that it was closed and fail on the write(2) call. */
if (use_unix) {
fprintf(stderr,"Error: %s\n", c->errstr);
assert(c->err == REDIS_ERR_IO &&
strcmp(c->errstr,"Broken pipe") == 0);
} else {
fprintf(stderr,"Error: %s\n", c->errstr);
assert(c->err == REDIS_ERR_EOF &&
strcmp(c->errstr,"Server closed the connection") == 0);
}
freeReplyObject(reply);
redisFree(c); redisFree(c);
__connect(&c); /* reconnect */ test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 56380);
test_cond(c->err == REDIS_ERR_IO &&
strcmp(c->errstr,"Connection refused") == 0);
redisFree(c);
__connect(&c);
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 &&
@ -112,12 +145,9 @@ static void test_blocking_connection() {
/* Make sure the DB is emtpy */ /* Make sure the DB is emtpy */
reply = redisCommand(c,"DBSIZE"); reply = redisCommand(c,"DBSIZE");
if (reply->type != REDIS_REPLY_INTEGER || if (reply->type != REDIS_REPLY_INTEGER || reply->integer != 0) {
reply->integer != 0) { printf("Database #9 is not empty, test can not continue\n");
printf("Sorry DB 9 is not empty, test can not continue\n");
exit(1); exit(1);
} else {
printf("DB 9 is empty... test can continue\n");
} }
freeReplyObject(reply); freeReplyObject(reply);
@ -183,6 +213,43 @@ static void test_blocking_connection() {
reply->element[1]->type == REDIS_REPLY_STATUS && reply->element[1]->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->element[1]->str,"pong") == 0); strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply); freeReplyObject(reply);
{
/* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:";
char *p, *eptr;
reply = redisCommand(c,"INFO");
p = strstr(reply->str,field);
major = strtol(p+strlen(field),&eptr,10);
p = eptr+1; /* char next to the first "." */
minor = strtol(p,&eptr,10);
freeReplyObject(reply);
}
test("Returns I/O error when the connection is lost: ");
reply = redisCommand(c,"QUIT");
if (major >= 2 && minor > 0) {
/* > 2.0 returns OK on QUIT and read() should be issued once more
* to know the descriptor is at EOF. */
test_cond(strcasecmp(reply->str,"OK") == 0 &&
redisGetReply(c,(void**)&reply) == REDIS_ERR);
freeReplyObject(reply);
} else {
test_cond(reply == NULL);
}
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
* issued to find out the socket was closed by the server. In both
* conditions, the error will be set to EOF. */
assert(c->err == REDIS_ERR_EOF &&
strcmp(c->errstr,"Server closed the connection") == 0);
/* Clean up context and reconnect again */
redisFree(c);
__connect(&c);
} }
static void test_reply_reader() { static void test_reply_reader() {
@ -197,7 +264,7 @@ static void test_reply_reader() {
ret = redisReplyReaderGetReply(reader,NULL); ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader); err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR && test_cond(ret == REDIS_ERR &&
strcasecmp(err,"protocol error, got \"@\" as reply type byte") == 0); strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
redisReplyReaderFree(reader); redisReplyReaderFree(reader);
/* when the reply already contains multiple items, they must be free'd /* when the reply already contains multiple items, they must be free'd
@ -210,7 +277,18 @@ static void test_reply_reader() {
ret = redisReplyReaderGetReply(reader,NULL); ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader); err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR && test_cond(ret == REDIS_ERR &&
strcasecmp(err,"protocol error, got \"@\" as reply type byte") == 0); 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); redisReplyReaderFree(reader);
test("Works with NULL functions for reply: "); test("Works with NULL functions for reply: ");