Update hiredis.

master
Nicolas Favre-Felix 13 years ago
parent e9da83d268
commit 28ec18582a

@ -1,5 +1,6 @@
# 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
OBJ=net.o hiredis.o sds.o async.o
@ -9,61 +10,37 @@ LIBNAME=libhiredis
HIREDIS_MAJOR=0
HIREDIS_MINOR=10
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
# 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
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
DEBUG?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG)
REAL_LDFLAGS=$(LDFLAGS)
DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
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)
# Platform-specific overrides
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
ifeq ($(uname_S),SunOS)
CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
LDFLAGS?=-L.
DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME?=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
DYLIB_MAJOR_NAME?=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD?=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME)
STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD?=ar rcs $(STLIBNAME)
REAL_LDFLAGS+= -ldl -lnsl -lsocket
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
INSTALL= cp -r
else
endif
ifeq ($(uname_S),Darwin)
CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread
LDFLAGS?=-L.
OBJARCH?=-arch i386 -arch x86_64
DYLIBSUFFIX=dylib
STLIBSUFFIX=a
DYLIB_MINOR_NAME?=$(LIBNAME).$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)
DYLIB_MAJOR_NAME?=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)
DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD?=libtool -dynamic -o $(DYLIBNAME) -install_name $(DYLIB_MINOR_NAME) -lm $(DEBUG) -
STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD?=libtool -static -o $(STLIBNAME) -
INSTALL= cp -a
else
CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread
LDFLAGS?=-L.
DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME?=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
DYLIB_MAJOR_NAME?=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME)
STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD?=ar rcs $(STLIBNAME)
INSTALL= cp -a
endif
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
CCOPT= $(CFLAGS) $(CCLINK)
DEBUG?= -g -ggdb
PREFIX?=/usr/local
INCLUDE_PATH?=include/hiredis
LIBRARY_PATH?=lib
INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
all: $(DYLIBNAME) $(BINS)
# Deps (use make dep to generate this)
@ -85,10 +62,10 @@ static: $(STLIBNAME)
# Binaries:
hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -levent example-libevent.c $(STLIBNAME)
$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)
hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lev example-libev.c $(STLIBNAME)
$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)
ifndef AE_DIR
hiredis-example-ae:
@ -96,11 +73,11 @@ hiredis-example-ae:
@false
else
hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
endif
hiredis-%: %.o $(STLIBNAME)
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $< $(STLIBNAME)
$(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
test: hiredis-test
./hiredis-test
@ -118,7 +95,7 @@ check: hiredis-test
kill `cat /tmp/hiredis-test-redis.pid`
.c.o:
$(CC) -std=c99 -pedantic -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $<
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
clean:
rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
@ -126,6 +103,19 @@ clean:
dep:
$(CC) -MM *.c
# Installation related variables and target
PREFIX?=/usr/local
INCLUDE_PATH?=include/hiredis
LIBRARY_PATH?=lib
INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
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)
@ -134,18 +124,25 @@ install: $(DYLIBNAME) $(STLIBNAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
32bit:
@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 ""
$(MAKE) ARCH="-m32"
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
gprof:
$(MAKE) PROF="-pg"
$(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
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:
$(MAKE) OPTIMIZATION=""
.PHONY: all test check clean dep install 32bit gprof gcov noopt

@ -286,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.
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.

@ -1,3 +1,5 @@
#ifndef __HIREDIS_AE_H__
#define __HIREDIS_AE_H__
#include <sys/types.h>
#include <ae.h>
#include "../hiredis.h"
@ -10,21 +12,21 @@ typedef struct redisAeEvents {
int reading, writing;
} 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);
redisAeEvents *e = (redisAeEvents*)privdata;
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);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleWrite(e->context);
}
void redisAeAddRead(void *privdata) {
static void redisAeAddRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->reading) {
@ -33,7 +35,7 @@ void redisAeAddRead(void *privdata) {
}
}
void redisAeDelRead(void *privdata) {
static void redisAeDelRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->reading) {
@ -42,7 +44,7 @@ void redisAeDelRead(void *privdata) {
}
}
void redisAeAddWrite(void *privdata) {
static void redisAeAddWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->writing) {
@ -51,7 +53,7 @@ void redisAeAddWrite(void *privdata) {
}
}
void redisAeDelWrite(void *privdata) {
static void redisAeDelWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->writing) {
@ -60,14 +62,14 @@ void redisAeDelWrite(void *privdata) {
}
}
void redisAeCleanup(void *privdata) {
static void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata);
redisAeDelWrite(privdata);
free(e);
}
int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisAeEvents *e;
@ -92,4 +94,4 @@ int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
return REDIS_OK;
}
#endif

@ -1,3 +1,6 @@
#ifndef __HIREDIS_LIBEV_H__
#define __HIREDIS_LIBEV_H__
#include <stdlib.h>
#include <sys/types.h>
#include <ev.h>
#include "../hiredis.h"
@ -10,7 +13,7 @@ typedef struct redisLibevEvents {
ev_io rev, wev;
} redisLibevEvents;
void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
@ -20,7 +23,7 @@ void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
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
((void)loop);
#endif
@ -30,7 +33,7 @@ void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
redisAsyncHandleWrite(e->context);
}
void redisLibevAddRead(void *privdata) {
static void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@ -40,7 +43,7 @@ void redisLibevAddRead(void *privdata) {
}
}
void redisLibevDelRead(void *privdata) {
static void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@ -50,7 +53,7 @@ void redisLibevDelRead(void *privdata) {
}
}
void redisLibevAddWrite(void *privdata) {
static void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@ -60,7 +63,7 @@ void redisLibevAddWrite(void *privdata) {
}
}
void redisLibevDelWrite(void *privdata) {
static void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@ -70,14 +73,14 @@ void redisLibevDelWrite(void *privdata) {
}
}
void redisLibevCleanup(void *privdata) {
static void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata);
redisLibevDelWrite(privdata);
free(e);
}
int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisLibevEvents *e;
@ -111,3 +114,4 @@ int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
return REDIS_OK;
}
#endif

@ -1,3 +1,5 @@
#ifndef __HIREDIS_LIBEVENT_H__
#define __HIREDIS_LIBEVENT_H__
#include <event.h>
#include "../hiredis.h"
#include "../async.h"
@ -7,46 +9,46 @@ typedef struct redisLibeventEvents {
struct event rev, wev;
} redisLibeventEvents;
void redisLibeventReadEvent(int fd, short event, void *arg) {
static void redisLibeventReadEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleRead(e->context);
}
void redisLibeventWriteEvent(int fd, short event, void *arg) {
static void redisLibeventWriteEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleWrite(e->context);
}
void redisLibeventAddRead(void *privdata) {
static void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->rev,NULL);
}
void redisLibeventDelRead(void *privdata) {
static void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
}
void redisLibeventAddWrite(void *privdata) {
static void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->wev,NULL);
}
void redisLibeventDelWrite(void *privdata) {
static void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->wev);
}
void redisLibeventCleanup(void *privdata) {
static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
event_del(&e->wev);
free(e);
}
int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
redisContext *c = &(ac->c);
redisLibeventEvents *e;
@ -73,3 +75,4 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
event_base_set(base,&e->wev);
return REDIS_OK;
}
#endif

@ -29,14 +29,34 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include "async.h"
#include "net.h"
#include "dict.c"
#include "sds.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 */
void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
@ -142,7 +162,7 @@ int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn
/* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event
* library functions are already set. */
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
_EL_ADD_WRITE(ac);
return REDIS_OK;
}
return REDIS_ERR;
@ -230,7 +250,7 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
dictRelease(ac->sub.patterns);
/* 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
* this context, the status will always be REDIS_OK. */
@ -346,7 +366,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
while((status = redisGetReply(c,&reply)) == REDIS_OK) {
if (reply == NULL) {
/* When the connection is being disconnected and there are
/* When the connection is being disconnected and there are
* no more replies, this is the cue to really disconnect. */
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {
__redisAsyncDisconnect(ac);
@ -397,23 +417,53 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
}
}
/* Disconnect when there was an error reading the reply */
if (status != REDIS_OK)
__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.
* It processes all replies that can be read and executes their callbacks.
*/
void redisAsyncHandleRead(redisAsyncContext *ac) {
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) {
__redisAsyncDisconnect(ac);
} else {
/* Always re-schedule reads */
if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
_EL_ADD_READ(ac);
redisProcessCallbacks(ac);
}
}
@ -422,24 +472,26 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
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) {
__redisAsyncDisconnect(ac);
} else {
/* Continue writing when not done, stop writing otherwise */
if (!done) {
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
} else {
if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data);
}
if (!done)
_EL_ADD_WRITE(ac);
else
_EL_DEL_WRITE(ac);
/* Always schedule reads after writes */
if (ac->ev.addRead) ac->ev.addRead(ac->ev.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);
}
_EL_ADD_READ(ac);
}
}
@ -517,7 +569,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
__redisAppendCommand(c,cmd,len);
/* 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;
}

@ -55,7 +55,7 @@ typedef struct redisCallbackList {
/* Connection callback prototypes */
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 */
typedef struct redisAsyncContext {

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

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

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

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

@ -358,12 +358,7 @@ static int processLineItem(redisReader *r) {
char *p;
int len;
cur->poff = (r->pos-r->roff)-1;
cur->coff = cur->poff+1;
if ((p = readLine(r,&len)) != NULL) {
cur->plen = 1+len+2; /* include \r\n */
cur->clen = len;
if (cur->type == REDIS_REPLY_INTEGER) {
if (r->fn && r->fn->createInteger)
obj = r->fn->createInteger(cur,readLongLong(p));
@ -402,13 +397,10 @@ static int processBulkItem(redisReader *r) {
p = r->buf+r->pos;
s = seekNewline(p,r->len-r->pos);
if (s != NULL) {
p = r->buf+r->pos;
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
cur->poff = (r->pos-r->roff)-1;
cur->plen = bytelen+1;
cur->coff = cur->poff+1+bytelen;
cur->clen = 0;
len = readLongLong(p);
if (len < 0) {
/* The nil object can always be created. */
if (r->fn && r->fn->createNil)
@ -420,8 +412,6 @@ static int processBulkItem(redisReader *r) {
/* Only continue when the buffer contains the entire bulk item. */
bytelen += len+2; /* include \r\n */
if (r->pos+bytelen <= r->len) {
cur->plen += len+2;
cur->clen = len;
if (r->fn && r->fn->createString)
obj = r->fn->createString(cur,s+2,len);
else
@ -456,19 +446,14 @@ static int processMultiBulkItem(redisReader *r) {
long elements;
int root = 0;
/* Set error for nested multi bulks with depth > 1 */
if (r->ridx == 2) {
/* Set error for nested multi bulks with depth > 2 */
if (r->ridx == 3) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"No support for nested multi bulk replies with depth > 1");
"No support for nested multi bulk replies with depth > 2");
return REDIS_ERR;
}
cur->poff = (r->pos-r->roff)-1;
cur->coff = 0;
if ((p = readLine(r,NULL)) != NULL) {
cur->plen = (r->pos-r->roff)-cur->poff; /* includes \r\n */
cur->clen = 0;
elements = readLongLong(p);
root = (r->ridx == 0);
@ -605,7 +590,7 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
/* Copy the provided buffer. */
if (buf != NULL && len >= 1) {
/* Destroy buffer when it is empty and is quite large. */
/* Destroy internal buffer when it is empty and is quite large. */
if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
sdsfree(r->buf);
r->buf = sdsempty();
@ -615,15 +600,6 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
assert(r->buf != NULL);
}
/* Discard consumed part of the buffer when the offset for the reply
* that is currently being read is high enough. */
if (r->roff >= 1024) {
r->buf = sdsrange(r->buf,r->roff,-1);
r->pos -= r->roff;
r->roff = 0;
r->len = sdslen(r->buf);
}
newbuf = sdscatlen(r->buf,buf,len);
if (newbuf == NULL) {
__redisReaderSetErrorOOM(r);
@ -659,7 +635,6 @@ int redisReaderGetReply(redisReader *r, void **reply) {
r->rstack[0].parent = NULL;
r->rstack[0].privdata = r->privdata;
r->ridx = 0;
r->roff = r->pos; /* Start offset in buffer. */
}
/* Process items in reply. */
@ -671,6 +646,14 @@ int redisReaderGetReply(redisReader *r, void **reply) {
if (r->err)
return REDIS_ERR;
/* Discard part of the buffer when we've consumed at least 1k, to avoid
* doing unnecessary calls to memmove() in sds.c. */
if (r->pos >= 1024) {
r->buf = sdsrange(r->buf,r->pos,-1);
r->pos = 0;
r->len = sdslen(r->buf);
}
/* Emit a reply when there is one. */
if (r->ridx == -1) {
if (reply != NULL)
@ -680,17 +663,6 @@ int redisReaderGetReply(redisReader *r, void **reply) {
return REDIS_OK;
}
const char *redisReaderGetRaw(redisReader *r, size_t *len) {
/* ridx == -1: No or a full reply has been read. */
/* pos > roff: Buffer position is larger than start offset, meaning
* the buffer has not yet been truncated. */
if (r->ridx == -1 && r->pos > r->roff) {
if (len) *len = (r->pos-r->roff);
return r->buf+r->roff;
}
return NULL;
}
/* Calculate the number of bytes needed to represent an integer as string. */
static int intlen(int i) {
int len = 0;
@ -777,6 +749,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
default:
/* Try to detect printf format */
{
static const char intfmts[] = "diouxX";
char _format[16];
const char *_p = c+1;
size_t _l = 0;
@ -798,33 +771,79 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
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++;
/* Copy va_list before consuming with va_arg */
va_copy(_cpy,ap);
/* Integer conversion (without modifiers) */
if (strchr(intfmts,*_p) != NULL) {
va_arg(ap,int);
goto fmt_valid;
}
/* Double conversion (without modifiers) */
if (strchr("eEfFgGaA",*_p) != NULL) {
va_arg(ap,double);
goto fmt_valid;
}
/* Size: char */
if (_p[0] == 'h' && _p[1] == 'h') {
_p += 2;
if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
va_arg(ap,int); /* char gets promoted to int */
goto fmt_valid;
}
goto fmt_invalid;
}
/* Size: short */
if (_p[0] == 'h') {
_p += 1;
if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
va_arg(ap,int); /* short gets promoted to int */
goto fmt_valid;
}
goto fmt_invalid;
}
/* 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);
newarg = sdscatvprintf(curarg,_format,_cpy);
va_end(_cpy);
/* Update current position (note: outer blocks
* increment c twice so compensate here) */
c = _p-1;
/* Size: long long */
if (_p[0] == 'l' && _p[1] == 'l') {
_p += 2;
if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
va_arg(ap,long long);
goto fmt_valid;
}
goto fmt_invalid;
}
/* Size: long */
if (_p[0] == 'l') {
_p += 1;
if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
va_arg(ap,long);
goto fmt_valid;
}
goto fmt_invalid;
}
fmt_invalid:
va_end(_cpy);
goto err;
fmt_valid:
_l = (_p+1)-c;
if (_l < sizeof(_format)-2) {
memcpy(_format,c,_l);
_format[_l] = '\0';
newarg = sdscatvprintf(curarg,_format,_cpy);
/* Update current position (note: outer blocks
* increment c twice so compensate here) */
c = _p-1;
}
/* Consume and discard vararg */
va_arg(ap,void);
va_end(_cpy);
break;
}
}
@ -1079,10 +1098,10 @@ int redisBufferRead(redisContext *c) {
*
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
* succesfully written to the socket. When the buffer is empty after the
* write operation, "wdone" is set to 1 (if given).
* write operation, "done" is set to 1 (if given).
*
* Returns REDIS_ERR if an error occured trying to write and sets
* c->error to hold the appropriate error string.
* c->errstr to hold the appropriate error string.
*/
int redisBufferWrite(redisContext *c, int *done) {
int nwritten;
@ -1133,7 +1152,6 @@ int redisGetReply(redisContext *c, void **reply) {
/* For the blocking context, flush output buffer and read reply */
if (aux == NULL && c->flags & REDIS_BLOCK) {
printf("BLOCKING CONTEXT\n");
/* Write until done */
do {
if (redisBufferWrite(c,&wdone) == REDIS_ERR)

@ -37,7 +37,7 @@
#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 10
#define HIREDIS_PATCH 0
#define HIREDIS_PATCH 1
#define REDIS_ERR -1
#define REDIS_OK 0
@ -98,11 +98,6 @@ typedef struct redisReply {
} redisReply;
typedef struct redisReadTask {
size_t poff; /* Protocol offset */
size_t plen; /* Protocol length */
size_t coff; /* Content offset */
size_t clen; /* Content length */
int type;
int elements; /* number of elements in multibulk container */
int idx; /* index in parent (array) object */
@ -127,9 +122,8 @@ typedef struct redisReader {
char *buf; /* Read buffer */
size_t pos; /* Buffer cursor */
size_t len; /* Buffer length */
size_t roff; /* Reply offset */
redisReadTask rstack[3];
redisReadTask rstack[4];
int ridx; /* Index of current read task */
void *reply; /* Temporary reply pointer */
@ -142,7 +136,6 @@ redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *r);
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
int redisReaderGetReply(redisReader *r, void **reply);
const char *redisReaderGetRaw(redisReader *r, size_t *len);
/* Backwards compatibility, can be removed on big version bump. */
#define redisReplyReaderCreate redisReaderCreate

@ -45,6 +45,8 @@
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <poll.h>
#include <limits.h>
#include "net.h"
#include "sds.h"
@ -62,16 +64,24 @@ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *pref
__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) {
int s, on = 1;
int s;
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
return REDIS_ERR;
}
if (type == AF_INET) {
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(s);
if (redisSetReuseAddr(c,s) == REDIS_ERR) {
return REDIS_ERR;
}
}
@ -113,50 +123,46 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
return REDIS_OK;
}
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
struct timeval to;
struct timeval *toptr = NULL;
fd_set wfd;
int err;
socklen_t errlen;
struct pollfd wfd[1];
long msec;
msec = -1;
wfd[0].fd = fd;
wfd[0].events = POLLOUT;
/* Only use timeout when not NULL. */
if (timeout != NULL) {
to = *timeout;
toptr = &to;
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
close(fd);
return REDIS_ERR;
}
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
if (msec < 0 || msec > INT_MAX) {
msec = INT_MAX;
}
}
if (errno == EINPROGRESS) {
FD_ZERO(&wfd);
FD_SET(fd, &wfd);
int res;
if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"select(2)");
if ((res = poll(wfd, 1, msec)) == -1) {
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
close(fd);
return REDIS_ERR;
}
if (!FD_ISSET(fd, &wfd)) {
} else if (res == 0) {
errno = ETIMEDOUT;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
err = 0;
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);
if (redisCheckSocketError(c, fd) != REDIS_OK)
return REDIS_ERR;
}
return REDIS_OK;
}
@ -166,6 +172,26 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
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) {
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
@ -179,50 +205,59 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
}
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);
struct sockaddr_in sa;
if ((s = redisCreateSocket(c,AF_INET)) < 0)
return REDIS_ERR;
if (redisSetBlocking(c,s,0) != REDIS_OK)
return REDIS_ERR;
snprintf(_port, 6, "%d", port);
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if (inet_aton(addr, &sa.sin_addr) == 0) {
struct hostent *he;
he = gethostbyname(addr);
if (he == NULL) {
char buf[128];
snprintf(buf,sizeof(buf),"Can't resolve: %s", addr);
__redisSetError(c,REDIS_ERR_OTHER,buf);
close(s);
return REDIS_ERR;
}
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
return REDIS_ERR;
}
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
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;
if (redisSetBlocking(c,s,0) != REDIS_OK)
goto error;
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) {
close(s);
continue;
} else if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
goto error;
}
}
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;
}
/* 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;
error:
rv = REDIS_ERR;
end:
freeaddrinfo(servinfo);
return rv; // Need to return REDIS_OK if alright
}
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {

@ -39,6 +39,7 @@
#define AF_LOCAL AF_UNIX
#endif
int redisCheckSocketError(redisContext *c, int fd);
int redisContextSetTimeout(redisContext *c, struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);

@ -1,3 +1,4 @@
#include "fmacros.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -31,7 +32,7 @@ struct config {
/* The following lines make up our testing "framework" :) */
static int tests = 0, fails = 0;
#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) {
struct timeval tv;
@ -77,7 +78,7 @@ static void disconnect(redisContext *c) {
}
static redisContext *connect(struct config config) {
redisContext *c;
redisContext *c = NULL;
if (config.type == CONN_TCP) {
c = redisConnect(config.tcp.host, config.tcp.port);
@ -141,29 +142,43 @@ static void test_format_commands(void) {
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);
/* Vararg width depends on the type. These tests make sure that the
* width is correctly determined using the format and subsequent varargs
* can correctly be interpolated. */
#define INTEGER_WIDTH_TEST(fmt, type) do { \
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)
#define FLOAT_WIDTH_TEST(type) do { \
type value = 123.0; \
test("Format command with printf-delegation (" #type "): "); \
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
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)
INTEGER_WIDTH_TEST("d", int);
INTEGER_WIDTH_TEST("hhd", char);
INTEGER_WIDTH_TEST("hd", short);
INTEGER_WIDTH_TEST("ld", long);
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 invalid printf format: ");
len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",3);
test_cond(len == -1);
const char *argv[3];
argv[0] = "SET";
@ -210,11 +225,12 @@ static void test_reply_reader(void) {
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
test("Set error on nested multi bulks with depth > 1: ");
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);
@ -261,105 +277,14 @@ static void test_reply_reader(void) {
redisReaderFree(reader);
}
static void *test_create_string(const redisReadTask *task, char *str, size_t len) {
redisReader *r = (redisReader*)task->privdata;
const char *roff = r->buf+r->roff;
((void)str); ((void)len);
assert(task->plen > 0);
assert(task->clen > 0);
switch(task->type) {
case REDIS_REPLY_STATUS:
assert(strncmp("+status\r\n", roff+task->poff, task->plen) == 0);
assert(strncmp("status", roff+task->coff, task->clen) == 0);
break;
case REDIS_REPLY_ERROR:
assert(strncmp("-error\r\n", roff+task->poff, task->plen) == 0);
assert(strncmp("error", roff+task->coff, task->clen) == 0);
break;
case REDIS_REPLY_STRING: /* bulk */
assert(strncmp("$4\r\nbulk\r\n", roff+task->poff, task->plen) == 0);
assert(strncmp("bulk", roff+task->coff, task->clen) == 0);
break;
default:
assert(NULL);
}
return (void*)1;
}
static void *test_create_array(const redisReadTask *task, int len) {
redisReader *r = (redisReader*)task->privdata;
const char *roff = r->buf+r->roff;
((void)len);
assert(task->plen > 0);
assert(task->clen == 0);
assert(strncmp("*5\r\n", roff+task->poff, task->plen) == 0);
return (void*)1;
}
static void *test_create_integer(const redisReadTask *task, long long value) {
redisReader *r = (redisReader*)task->privdata;
const char *roff = r->buf+r->roff;
((void)value);
assert(task->plen > 0);
assert(task->clen > 0);
assert(strncmp(":1234\r\n", roff+task->poff, task->plen) == 0);
assert(strncmp("1234", roff+task->coff, task->clen) == 0);
return (void*)1;
}
static void *test_create_nil(const redisReadTask *task) {
redisReader *r = (redisReader*)task->privdata;
const char *roff = r->buf+r->roff;
assert(task->plen > 0);
assert(task->clen == 0);
assert(strncmp("$-1\r\n", roff+task->poff, task->plen) == 0);
return (void*)1;
}
static redisReplyObjectFunctions test_reader_fn = {
test_create_string,
test_create_array,
test_create_integer,
test_create_nil,
NULL
};
static void test_reader_functions(void) {
redisReader *reader;
const char *input;
int ret;
void *obj;
input =
"*5\r\n"
"$-1\r\n"
":1234\r\n"
"+status\r\n"
"-error\r\n"
"$4\r\nbulk\r\n";
test("Custom object functions in reply reader: ");
reader = redisReaderCreate();
reader->fn = &test_reader_fn;
reader->privdata = reader;
redisReaderFeed(reader,input,strlen(input));
ret = redisReaderGetReply(reader,&obj);
test_cond(ret == REDIS_OK && obj == (void*)1);
redisReaderFree(reader);
}
static void test_blocking_connection_errors(void) {
redisContext *c;
test("Returns error when host cannot be resolved: ");
c = redisConnect((char*)"idontexist.local", 6379);
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);
test("Returns error when the port is not open: ");
@ -455,6 +380,7 @@ static void test_blocking_connection(struct config config) {
static void test_blocking_io_errors(struct config config) {
redisContext *c;
redisReply *reply;
void *_reply;
int major, minor;
/* Connect to target given by config. */
@ -478,7 +404,7 @@ static void test_blocking_io_errors(struct config config) {
/* > 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);
redisGetReply(c,&_reply) == REDIS_ERR);
freeReplyObject(reply);
} else {
test_cond(reply == NULL);
@ -497,7 +423,7 @@ static void test_blocking_io_errors(struct config config) {
test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 };
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);
redisFree(c);
}
@ -704,7 +630,6 @@ int main(int argc, char **argv) {
test_format_commands();
test_reply_reader();
test_reader_functions();
test_blocking_connection_errors();
printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);

@ -22,11 +22,11 @@ pool_new(struct worker *w, int count) {
}
static void
pool_on_connect(const redisAsyncContext *ac) {
pool_on_connect(const redisAsyncContext *ac, int status) {
struct pool *p = ac->data;
int i = 0;
if(!p || ac->err) {
if(!p || status == REDIS_ERR || ac->err) {
return;
}
/* connected to redis! */

Loading…
Cancel
Save