hiredis 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/hiredis_ext/reader.c +1 -1
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +13 -6
- data/vendor/hiredis/async.c +18 -8
- data/vendor/hiredis/async.h +14 -1
- data/vendor/hiredis/hiredis.c +184 -54
- data/vendor/hiredis/hiredis.h +13 -2
- data/vendor/hiredis/net.c +3 -1
- data/vendor/hiredis/test.c +111 -32
- metadata +8 -3
data/ext/hiredis_ext/reader.c
CHANGED
@@ -13,7 +13,7 @@ ID ivar_hiredis_error;
|
|
13
13
|
* Note that the parent should always be of type T_ARRAY. */
|
14
14
|
static void *tryParentize(const redisReadTask *task, VALUE v) {
|
15
15
|
if (task && task->parent != NULL) {
|
16
|
-
VALUE parent = (VALUE)task->parent;
|
16
|
+
VALUE parent = (VALUE)task->parent->obj;
|
17
17
|
assert(TYPE(parent) == T_ARRAY);
|
18
18
|
rb_ary_store(parent,task->idx,v);
|
19
19
|
}
|
data/lib/hiredis/version.rb
CHANGED
data/vendor/hiredis/Makefile
CHANGED
@@ -6,7 +6,7 @@ OBJ = net.o hiredis.o sds.o async.o
|
|
6
6
|
BINS = hiredis-example hiredis-test
|
7
7
|
|
8
8
|
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
9
|
-
OPTIMIZATION?=-
|
9
|
+
OPTIMIZATION?=-O3
|
10
10
|
ifeq ($(uname_S),SunOS)
|
11
11
|
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6
|
12
12
|
CCLINK?= -ldl -lnsl -lsocket -lm -lpthread
|
@@ -31,7 +31,7 @@ else
|
|
31
31
|
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
|
32
32
|
endif
|
33
33
|
CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
|
34
|
-
DEBUG?= -g -ggdb
|
34
|
+
DEBUG?= -g -ggdb
|
35
35
|
|
36
36
|
PREFIX?= /usr/local
|
37
37
|
INSTALL_INC= $(PREFIX)/include/hiredis
|
@@ -43,8 +43,6 @@ all: ${DYLIBNAME} ${BINS}
|
|
43
43
|
# Deps (use make dep to generate this)
|
44
44
|
net.o: net.c fmacros.h net.h
|
45
45
|
async.o: async.c async.h hiredis.h sds.h util.h
|
46
|
-
example-libev.o: example-libev.c hiredis.h async.h adapters/libev.h
|
47
|
-
example-libevent.o: example-libevent.c hiredis.h async.h adapters/libevent.h
|
48
46
|
example.o: example.c hiredis.h
|
49
47
|
hiredis.o: hiredis.c hiredis.h net.h sds.h util.h
|
50
48
|
sds.o: sds.c sds.h
|
@@ -60,12 +58,21 @@ dynamic: ${DYLIBNAME}
|
|
60
58
|
static: ${STLIBNAME}
|
61
59
|
|
62
60
|
# Binaries:
|
63
|
-
hiredis-example-libevent: example-libevent.
|
61
|
+
hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME}
|
64
62
|
$(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -levent -Wl,-rpath,. example-libevent.c
|
65
63
|
|
66
|
-
hiredis-example-libev: example-libev.
|
64
|
+
hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME}
|
67
65
|
$(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -lev -Wl,-rpath,. example-libev.c
|
68
66
|
|
67
|
+
ifndef AE_DIR
|
68
|
+
hiredis-example-ae:
|
69
|
+
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
|
70
|
+
@false
|
71
|
+
else
|
72
|
+
hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME}
|
73
|
+
$(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) -L. -lhiredis -Wl,-rpath,. example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
|
74
|
+
endif
|
75
|
+
|
69
76
|
hiredis-%: %.o ${DYLIBNAME}
|
70
77
|
$(CC) -o $@ $(CCOPT) $(DEBUG) -L. -lhiredis -Wl,-rpath,. $<
|
71
78
|
|
data/vendor/hiredis/async.c
CHANGED
@@ -38,8 +38,18 @@ void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
|
|
38
38
|
|
39
39
|
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
40
40
|
redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
|
41
|
-
|
42
|
-
|
41
|
+
ac->err = 0;
|
42
|
+
ac->errstr = NULL;
|
43
|
+
ac->data = NULL;
|
44
|
+
ac->_adapter_data = NULL;
|
45
|
+
ac->evAddRead = NULL;
|
46
|
+
ac->evDelRead = NULL;
|
47
|
+
ac->evAddWrite = NULL;
|
48
|
+
ac->evDelWrite = NULL;
|
49
|
+
ac->evCleanup = NULL;
|
50
|
+
ac->onDisconnect = NULL;
|
51
|
+
ac->replies.head = NULL;
|
52
|
+
ac->replies.tail = NULL;
|
43
53
|
return ac;
|
44
54
|
}
|
45
55
|
|
@@ -153,7 +163,7 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
|
153
163
|
}
|
154
164
|
|
155
165
|
/* Signal event lib to clean up */
|
156
|
-
if (ac->evCleanup) ac->evCleanup(ac->
|
166
|
+
if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
|
157
167
|
|
158
168
|
/* Execute callback with proper status */
|
159
169
|
if (ac->onDisconnect) ac->onDisconnect(ac,status);
|
@@ -206,7 +216,7 @@ void redisAsyncHandleRead(redisAsyncContext *ac) {
|
|
206
216
|
__redisAsyncDisconnect(ac);
|
207
217
|
} else {
|
208
218
|
/* Always re-schedule reads */
|
209
|
-
if (ac->evAddRead) ac->evAddRead(ac->
|
219
|
+
if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
|
210
220
|
redisProcessCallbacks(ac);
|
211
221
|
}
|
212
222
|
}
|
@@ -220,13 +230,13 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
|
220
230
|
} else {
|
221
231
|
/* Continue writing when not done, stop writing otherwise */
|
222
232
|
if (!done) {
|
223
|
-
if (ac->evAddWrite) ac->evAddWrite(ac->
|
233
|
+
if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
|
224
234
|
} else {
|
225
|
-
if (ac->evDelWrite) ac->evDelWrite(ac->
|
235
|
+
if (ac->evDelWrite) ac->evDelWrite(ac->_adapter_data);
|
226
236
|
}
|
227
237
|
|
228
238
|
/* Always schedule reads when something was written */
|
229
|
-
if (ac->evAddRead) ac->evAddRead(ac->
|
239
|
+
if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
|
230
240
|
}
|
231
241
|
}
|
232
242
|
|
@@ -249,7 +259,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
|
249
259
|
__redisPushCallback(&ac->replies,&cb);
|
250
260
|
|
251
261
|
/* Always schedule a write when the write buffer is non-empty */
|
252
|
-
if (ac->evAddWrite) ac->evAddWrite(ac->
|
262
|
+
if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
|
253
263
|
|
254
264
|
return REDIS_OK;
|
255
265
|
}
|
data/vendor/hiredis/async.h
CHANGED
@@ -31,6 +31,10 @@
|
|
31
31
|
#define __HIREDIS_ASYNC_H
|
32
32
|
#include "hiredis.h"
|
33
33
|
|
34
|
+
#ifdef __cplusplus
|
35
|
+
extern "C" {
|
36
|
+
#endif
|
37
|
+
|
34
38
|
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
|
35
39
|
|
36
40
|
/* Reply callback prototype and container */
|
@@ -58,6 +62,12 @@ typedef struct redisAsyncContext {
|
|
58
62
|
int err;
|
59
63
|
char *errstr;
|
60
64
|
|
65
|
+
/* Not used by hiredis */
|
66
|
+
void *data;
|
67
|
+
|
68
|
+
/* Used by the different event lib adapters to store their private data */
|
69
|
+
void *_adapter_data;
|
70
|
+
|
61
71
|
/* Called when the library expects to start reading/writing.
|
62
72
|
* The supplied functions should be idempotent. */
|
63
73
|
void (*evAddRead)(void *privdata);
|
@@ -65,7 +75,6 @@ typedef struct redisAsyncContext {
|
|
65
75
|
void (*evAddWrite)(void *privdata);
|
66
76
|
void (*evDelWrite)(void *privdata);
|
67
77
|
void (*evCleanup)(void *privdata);
|
68
|
-
void *data;
|
69
78
|
|
70
79
|
/* Called when either the connection is terminated due to an error or per
|
71
80
|
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
|
@@ -91,4 +100,8 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
|
|
91
100
|
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
|
92
101
|
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
|
93
102
|
|
103
|
+
#ifdef __cplusplus
|
104
|
+
}
|
105
|
+
#endif
|
106
|
+
|
94
107
|
#endif
|
data/vendor/hiredis/hiredis.c
CHANGED
@@ -32,6 +32,7 @@
|
|
32
32
|
#include <unistd.h>
|
33
33
|
#include <assert.h>
|
34
34
|
#include <errno.h>
|
35
|
+
#include <ctype.h>
|
35
36
|
|
36
37
|
#include "hiredis.h"
|
37
38
|
#include "net.h"
|
@@ -44,10 +45,12 @@ typedef struct redisReader {
|
|
44
45
|
void *reply; /* holds temporary reply */
|
45
46
|
|
46
47
|
sds buf; /* read buffer */
|
47
|
-
|
48
|
+
size_t pos; /* buffer cursor */
|
49
|
+
size_t len; /* buffer length */
|
48
50
|
|
49
51
|
redisReadTask rstack[3]; /* stack of read tasks */
|
50
52
|
int ridx; /* index of stack */
|
53
|
+
void *privdata; /* user-settable arbitrary field */
|
51
54
|
} redisReader;
|
52
55
|
|
53
56
|
static redisReply *createReplyObject(int type);
|
@@ -68,7 +71,7 @@ static redisReplyObjectFunctions defaultFunctions = {
|
|
68
71
|
|
69
72
|
/* Create a reply object */
|
70
73
|
static redisReply *createReplyObject(int type) {
|
71
|
-
redisReply *r =
|
74
|
+
redisReply *r = malloc(sizeof(*r));
|
72
75
|
|
73
76
|
if (!r) redisOOM();
|
74
77
|
r->type = type;
|
@@ -88,9 +91,10 @@ void freeReplyObject(void *reply) {
|
|
88
91
|
if (r->element[j]) freeReplyObject(r->element[j]);
|
89
92
|
free(r->element);
|
90
93
|
break;
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
+
case REDIS_REPLY_ERROR:
|
95
|
+
case REDIS_REPLY_STATUS:
|
96
|
+
case REDIS_REPLY_STRING:
|
97
|
+
free(r->str);
|
94
98
|
break;
|
95
99
|
}
|
96
100
|
free(r);
|
@@ -111,7 +115,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
|
111
115
|
r->len = len;
|
112
116
|
|
113
117
|
if (task->parent) {
|
114
|
-
redisReply *parent = task->parent;
|
118
|
+
redisReply *parent = task->parent->obj;
|
115
119
|
assert(parent->type == REDIS_REPLY_ARRAY);
|
116
120
|
parent->element[task->idx] = r;
|
117
121
|
}
|
@@ -124,7 +128,7 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
|
|
124
128
|
if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
|
125
129
|
redisOOM();
|
126
130
|
if (task->parent) {
|
127
|
-
redisReply *parent = task->parent;
|
131
|
+
redisReply *parent = task->parent->obj;
|
128
132
|
assert(parent->type == REDIS_REPLY_ARRAY);
|
129
133
|
parent->element[task->idx] = r;
|
130
134
|
}
|
@@ -135,7 +139,7 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
|
|
135
139
|
redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
|
136
140
|
r->integer = value;
|
137
141
|
if (task->parent) {
|
138
|
-
redisReply *parent = task->parent;
|
142
|
+
redisReply *parent = task->parent->obj;
|
139
143
|
assert(parent->type == REDIS_REPLY_ARRAY);
|
140
144
|
parent->element[task->idx] = r;
|
141
145
|
}
|
@@ -145,7 +149,7 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
|
|
145
149
|
static void *createNilObject(const redisReadTask *task) {
|
146
150
|
redisReply *r = createReplyObject(REDIS_REPLY_NIL);
|
147
151
|
if (task->parent) {
|
148
|
-
redisReply *parent = task->parent;
|
152
|
+
redisReply *parent = task->parent->obj;
|
149
153
|
assert(parent->type == REDIS_REPLY_ARRAY);
|
150
154
|
parent->element[task->idx] = r;
|
151
155
|
}
|
@@ -154,7 +158,7 @@ static void *createNilObject(const redisReadTask *task) {
|
|
154
158
|
|
155
159
|
static char *readBytes(redisReader *r, unsigned int bytes) {
|
156
160
|
char *p;
|
157
|
-
if (
|
161
|
+
if (r->len-r->pos >= bytes) {
|
158
162
|
p = r->buf+r->pos;
|
159
163
|
r->pos += bytes;
|
160
164
|
return p;
|
@@ -162,20 +166,60 @@ static char *readBytes(redisReader *r, unsigned int bytes) {
|
|
162
166
|
return NULL;
|
163
167
|
}
|
164
168
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
169
|
+
/* Find pointer to \r\n. */
|
170
|
+
static char *seekNewline(char *s, size_t len) {
|
171
|
+
int pos = 0;
|
172
|
+
int _len = len-1;
|
173
|
+
|
174
|
+
/* Position should be < len-1 because the character at "pos" should be
|
175
|
+
* followed by a \n. Note that strchr cannot be used because it doesn't
|
176
|
+
* allow to search a limited length and the buffer that is being searched
|
177
|
+
* might not have a trailing NULL character. */
|
178
|
+
while (pos < _len) {
|
179
|
+
while(pos < _len && s[pos] != '\r') pos++;
|
180
|
+
if (s[pos] != '\r') {
|
181
|
+
/* Not found. */
|
182
|
+
return NULL;
|
174
183
|
} else {
|
175
|
-
|
184
|
+
if (s[pos+1] == '\n') {
|
185
|
+
/* Found. */
|
186
|
+
return s+pos;
|
187
|
+
} else {
|
188
|
+
/* Continue searching. */
|
189
|
+
pos++;
|
190
|
+
}
|
176
191
|
}
|
177
192
|
}
|
178
|
-
return
|
193
|
+
return NULL;
|
194
|
+
}
|
195
|
+
|
196
|
+
/* Read a long long value starting at *s, under the assumption that it will be
|
197
|
+
* terminated by \r\n. Ambiguously returns -1 for unexpected input. */
|
198
|
+
static long long readLongLong(char *s) {
|
199
|
+
long long v = 0;
|
200
|
+
int dec, mult = 1;
|
201
|
+
char c;
|
202
|
+
|
203
|
+
if (*s == '-') {
|
204
|
+
mult = -1;
|
205
|
+
s++;
|
206
|
+
} else if (*s == '+') {
|
207
|
+
mult = 1;
|
208
|
+
s++;
|
209
|
+
}
|
210
|
+
|
211
|
+
while ((c = *(s++)) != '\r') {
|
212
|
+
dec = c - '0';
|
213
|
+
if (dec >= 0 && dec < 10) {
|
214
|
+
v *= 10;
|
215
|
+
v += dec;
|
216
|
+
} else {
|
217
|
+
/* Should not happen... */
|
218
|
+
return -1;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
return mult*v;
|
179
223
|
}
|
180
224
|
|
181
225
|
static char *readLine(redisReader *r, int *_len) {
|
@@ -183,7 +227,7 @@ static char *readLine(redisReader *r, int *_len) {
|
|
183
227
|
int len;
|
184
228
|
|
185
229
|
p = r->buf+r->pos;
|
186
|
-
s = seekNewline(p);
|
230
|
+
s = seekNewline(p,(r->len-r->pos));
|
187
231
|
if (s != NULL) {
|
188
232
|
len = s-(r->buf+r->pos);
|
189
233
|
r->pos += len+2; /* skip \r\n */
|
@@ -227,7 +271,7 @@ static int processLineItem(redisReader *r) {
|
|
227
271
|
if ((p = readLine(r,&len)) != NULL) {
|
228
272
|
if (r->fn) {
|
229
273
|
if (cur->type == REDIS_REPLY_INTEGER) {
|
230
|
-
obj = r->fn->createInteger(cur,
|
274
|
+
obj = r->fn->createInteger(cur,readLongLong(p));
|
231
275
|
} else {
|
232
276
|
obj = r->fn->createString(cur,p,len);
|
233
277
|
}
|
@@ -235,9 +279,8 @@ static int processLineItem(redisReader *r) {
|
|
235
279
|
obj = (void*)(size_t)(cur->type);
|
236
280
|
}
|
237
281
|
|
238
|
-
/*
|
239
|
-
if (r->
|
240
|
-
r->reply = obj;
|
282
|
+
/* Set reply if this is the root object. */
|
283
|
+
if (r->ridx == 0) r->reply = obj;
|
241
284
|
moveToNextTask(r);
|
242
285
|
return 0;
|
243
286
|
}
|
@@ -250,32 +293,36 @@ static int processBulkItem(redisReader *r) {
|
|
250
293
|
char *p, *s;
|
251
294
|
long len;
|
252
295
|
unsigned long bytelen;
|
296
|
+
int success = 0;
|
253
297
|
|
254
298
|
p = r->buf+r->pos;
|
255
|
-
s = seekNewline(p);
|
299
|
+
s = seekNewline(p,r->len-r->pos);
|
256
300
|
if (s != NULL) {
|
257
301
|
p = r->buf+r->pos;
|
258
302
|
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
|
259
|
-
len =
|
303
|
+
len = readLongLong(p);
|
260
304
|
|
261
305
|
if (len < 0) {
|
262
306
|
/* The nil object can always be created. */
|
263
307
|
obj = r->fn ? r->fn->createNil(cur) :
|
264
308
|
(void*)REDIS_REPLY_NIL;
|
309
|
+
success = 1;
|
265
310
|
} else {
|
266
311
|
/* Only continue when the buffer contains the entire bulk item. */
|
267
312
|
bytelen += len+2; /* include \r\n */
|
268
|
-
if (r->pos+bytelen <=
|
313
|
+
if (r->pos+bytelen <= r->len) {
|
269
314
|
obj = r->fn ? r->fn->createString(cur,s+2,len) :
|
270
315
|
(void*)REDIS_REPLY_STRING;
|
316
|
+
success = 1;
|
271
317
|
}
|
272
318
|
}
|
273
319
|
|
274
320
|
/* Proceed when obj was created. */
|
275
|
-
if (
|
321
|
+
if (success) {
|
276
322
|
r->pos += bytelen;
|
277
|
-
|
278
|
-
|
323
|
+
|
324
|
+
/* Set reply if this is the root object. */
|
325
|
+
if (r->ridx == 0) r->reply = obj;
|
279
326
|
moveToNextTask(r);
|
280
327
|
return 0;
|
281
328
|
}
|
@@ -288,9 +335,19 @@ static int processMultiBulkItem(redisReader *r) {
|
|
288
335
|
void *obj;
|
289
336
|
char *p;
|
290
337
|
long elements;
|
338
|
+
int root = 0;
|
339
|
+
|
340
|
+
/* Set error for nested multi bulks with depth > 1 */
|
341
|
+
if (r->ridx == 2) {
|
342
|
+
redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
|
343
|
+
"No support for nested multi bulk replies with depth > 1"));
|
344
|
+
return -1;
|
345
|
+
}
|
291
346
|
|
292
347
|
if ((p = readLine(r,NULL)) != NULL) {
|
293
|
-
elements =
|
348
|
+
elements = readLongLong(p);
|
349
|
+
root = (r->ridx == 0);
|
350
|
+
|
294
351
|
if (elements == -1) {
|
295
352
|
obj = r->fn ? r->fn->createNil(cur) :
|
296
353
|
(void*)REDIS_REPLY_NIL;
|
@@ -302,19 +359,21 @@ static int processMultiBulkItem(redisReader *r) {
|
|
302
359
|
/* Modify task stack when there are more than 0 elements. */
|
303
360
|
if (elements > 0) {
|
304
361
|
cur->elements = elements;
|
362
|
+
cur->obj = obj;
|
305
363
|
r->ridx++;
|
306
364
|
r->rstack[r->ridx].type = -1;
|
307
365
|
r->rstack[r->ridx].elements = -1;
|
308
|
-
r->rstack[r->ridx].parent = obj;
|
309
366
|
r->rstack[r->ridx].idx = 0;
|
367
|
+
r->rstack[r->ridx].obj = NULL;
|
368
|
+
r->rstack[r->ridx].parent = cur;
|
369
|
+
r->rstack[r->ridx].privdata = r->privdata;
|
310
370
|
} else {
|
311
371
|
moveToNextTask(r);
|
312
372
|
}
|
313
373
|
}
|
314
374
|
|
315
|
-
/*
|
316
|
-
if (r->reply
|
317
|
-
r->reply = obj;
|
375
|
+
/* Set reply if this is the root object. */
|
376
|
+
if (root) r->reply = obj;
|
318
377
|
return 0;
|
319
378
|
}
|
320
379
|
return -1;
|
@@ -347,7 +406,7 @@ static int processItem(redisReader *r) {
|
|
347
406
|
default:
|
348
407
|
byte = sdscatrepr(sdsempty(),p,1);
|
349
408
|
redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
|
350
|
-
"
|
409
|
+
"Protocol error, got %s as reply type byte", byte));
|
351
410
|
sdsfree(byte);
|
352
411
|
return -1;
|
353
412
|
}
|
@@ -368,8 +427,7 @@ static int processItem(redisReader *r) {
|
|
368
427
|
case REDIS_REPLY_ARRAY:
|
369
428
|
return processMultiBulkItem(r);
|
370
429
|
default:
|
371
|
-
|
372
|
-
"unknown item type '%d'", cur->type));
|
430
|
+
assert(NULL);
|
373
431
|
return -1;
|
374
432
|
}
|
375
433
|
}
|
@@ -394,6 +452,17 @@ int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFuncti
|
|
394
452
|
return REDIS_ERR;
|
395
453
|
}
|
396
454
|
|
455
|
+
/* Set the private data field that is used in the read tasks. This argument can
|
456
|
+
* be used to curry arbitrary data to the custom reply object functions. */
|
457
|
+
int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
|
458
|
+
redisReader *r = reader;
|
459
|
+
if (r->reply == NULL) {
|
460
|
+
r->privdata = privdata;
|
461
|
+
return REDIS_OK;
|
462
|
+
}
|
463
|
+
return REDIS_ERR;
|
464
|
+
}
|
465
|
+
|
397
466
|
/* External libraries wrapping hiredis might need access to the temporary
|
398
467
|
* variable while the reply is built up. When the reader contains an
|
399
468
|
* object in between receiving some bytes to parse, this object might
|
@@ -437,8 +506,10 @@ void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
|
|
437
506
|
redisReader *r = reader;
|
438
507
|
|
439
508
|
/* Copy the provided buffer. */
|
440
|
-
if (buf != NULL && len >= 1)
|
509
|
+
if (buf != NULL && len >= 1) {
|
441
510
|
r->buf = sdscatlen(r->buf,buf,len);
|
511
|
+
r->len = sdslen(r->buf);
|
512
|
+
}
|
442
513
|
}
|
443
514
|
|
444
515
|
int redisReplyReaderGetReply(void *reader, void **reply) {
|
@@ -446,15 +517,17 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
|
|
446
517
|
if (reply != NULL) *reply = NULL;
|
447
518
|
|
448
519
|
/* When the buffer is empty, there will never be a reply. */
|
449
|
-
if (
|
520
|
+
if (r->len == 0)
|
450
521
|
return REDIS_OK;
|
451
522
|
|
452
523
|
/* Set first item to process when the stack is empty. */
|
453
524
|
if (r->ridx == -1) {
|
454
525
|
r->rstack[0].type = -1;
|
455
526
|
r->rstack[0].elements = -1;
|
456
|
-
r->rstack[0].parent = NULL;
|
457
527
|
r->rstack[0].idx = -1;
|
528
|
+
r->rstack[0].obj = NULL;
|
529
|
+
r->rstack[0].parent = NULL;
|
530
|
+
r->rstack[0].privdata = r->privdata;
|
458
531
|
r->ridx = 0;
|
459
532
|
}
|
460
533
|
|
@@ -465,14 +538,15 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
|
|
465
538
|
|
466
539
|
/* Discard the consumed part of the buffer. */
|
467
540
|
if (r->pos > 0) {
|
468
|
-
if (r->pos ==
|
541
|
+
if (r->pos == r->len) {
|
469
542
|
/* sdsrange has a quirck on this edge case. */
|
470
543
|
sdsfree(r->buf);
|
471
544
|
r->buf = sdsempty();
|
472
545
|
} else {
|
473
|
-
r->buf = sdsrange(r->buf,r->pos,
|
546
|
+
r->buf = sdsrange(r->buf,r->pos,r->len);
|
474
547
|
}
|
475
548
|
r->pos = 0;
|
549
|
+
r->len = sdslen(r->buf);
|
476
550
|
}
|
477
551
|
|
478
552
|
/* Emit a reply when there is one. */
|
@@ -481,7 +555,7 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
|
|
481
555
|
r->reply = NULL;
|
482
556
|
|
483
557
|
/* Destroy the buffer when it is empty and is quite large. */
|
484
|
-
if (
|
558
|
+
if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
|
485
559
|
sdsfree(r->buf);
|
486
560
|
r->buf = sdsempty();
|
487
561
|
r->pos = 0;
|
@@ -525,6 +599,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
525
599
|
char *cmd = NULL; /* final command */
|
526
600
|
int pos; /* position in final command */
|
527
601
|
sds current; /* current argument */
|
602
|
+
int interpolated = 0; /* did we do interpolation on an argument? */
|
528
603
|
char **argv = NULL;
|
529
604
|
int argc = 0, j;
|
530
605
|
int totlen = 0;
|
@@ -541,6 +616,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
541
616
|
if (sdslen(current) != 0) {
|
542
617
|
addArgument(current, &argv, &argc, &totlen);
|
543
618
|
current = sdsempty();
|
619
|
+
interpolated = 0;
|
544
620
|
}
|
545
621
|
} else {
|
546
622
|
current = sdscatlen(current,c,1);
|
@@ -549,16 +625,74 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
549
625
|
switch(c[1]) {
|
550
626
|
case 's':
|
551
627
|
arg = va_arg(ap,char*);
|
552
|
-
|
628
|
+
size = strlen(arg);
|
629
|
+
if (size > 0)
|
630
|
+
current = sdscatlen(current,arg,size);
|
631
|
+
interpolated = 1;
|
553
632
|
break;
|
554
633
|
case 'b':
|
555
634
|
arg = va_arg(ap,char*);
|
556
635
|
size = va_arg(ap,size_t);
|
557
|
-
|
636
|
+
if (size > 0)
|
637
|
+
current = sdscatlen(current,arg,size);
|
638
|
+
interpolated = 1;
|
558
639
|
break;
|
559
640
|
case '%':
|
560
|
-
|
641
|
+
current = sdscat(current,"%");
|
561
642
|
break;
|
643
|
+
default:
|
644
|
+
/* Try to detect printf format */
|
645
|
+
{
|
646
|
+
char _format[16];
|
647
|
+
const char *_p = c+1;
|
648
|
+
size_t _l = 0;
|
649
|
+
va_list _cpy;
|
650
|
+
|
651
|
+
/* Flags */
|
652
|
+
if (*_p != '\0' && *_p == '#') _p++;
|
653
|
+
if (*_p != '\0' && *_p == '0') _p++;
|
654
|
+
if (*_p != '\0' && *_p == '-') _p++;
|
655
|
+
if (*_p != '\0' && *_p == ' ') _p++;
|
656
|
+
if (*_p != '\0' && *_p == '+') _p++;
|
657
|
+
|
658
|
+
/* Field width */
|
659
|
+
while (*_p != '\0' && isdigit(*_p)) _p++;
|
660
|
+
|
661
|
+
/* Precision */
|
662
|
+
if (*_p == '.') {
|
663
|
+
_p++;
|
664
|
+
while (*_p != '\0' && isdigit(*_p)) _p++;
|
665
|
+
}
|
666
|
+
|
667
|
+
/* Modifiers */
|
668
|
+
if (*_p != '\0') {
|
669
|
+
if (*_p == 'h' || *_p == 'l') {
|
670
|
+
/* Allow a single repetition for these modifiers */
|
671
|
+
if (_p[0] == _p[1]) _p++;
|
672
|
+
_p++;
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
/* Conversion specifier */
|
677
|
+
if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
|
678
|
+
_l = (_p+1)-c;
|
679
|
+
if (_l < sizeof(_format)-2) {
|
680
|
+
memcpy(_format,c,_l);
|
681
|
+
_format[_l] = '\0';
|
682
|
+
va_copy(_cpy,ap);
|
683
|
+
current = sdscatvprintf(current,_format,_cpy);
|
684
|
+
interpolated = 1;
|
685
|
+
va_end(_cpy);
|
686
|
+
|
687
|
+
/* Update current position (note: outer blocks
|
688
|
+
* increment c twice so compensate here) */
|
689
|
+
c = _p-1;
|
690
|
+
}
|
691
|
+
}
|
692
|
+
|
693
|
+
/* Consume and discard vararg */
|
694
|
+
va_arg(ap,void);
|
695
|
+
}
|
562
696
|
}
|
563
697
|
c++;
|
564
698
|
}
|
@@ -566,7 +700,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
566
700
|
}
|
567
701
|
|
568
702
|
/* Add the last argument if needed */
|
569
|
-
if (sdslen(current) != 0) {
|
703
|
+
if (interpolated || sdslen(current) != 0) {
|
570
704
|
addArgument(current, &argv, &argc, &totlen);
|
571
705
|
} else {
|
572
706
|
sdsfree(current);
|
@@ -691,7 +825,6 @@ void redisFree(redisContext *c) {
|
|
691
825
|
redisContext *redisConnect(const char *ip, int port) {
|
692
826
|
redisContext *c = redisContextInit();
|
693
827
|
c->flags |= REDIS_BLOCK;
|
694
|
-
c->flags |= REDIS_CONNECTED;
|
695
828
|
redisContextConnectTcp(c,ip,port);
|
696
829
|
return c;
|
697
830
|
}
|
@@ -699,7 +832,6 @@ redisContext *redisConnect(const char *ip, int port) {
|
|
699
832
|
redisContext *redisConnectNonBlock(const char *ip, int port) {
|
700
833
|
redisContext *c = redisContextInit();
|
701
834
|
c->flags &= ~REDIS_BLOCK;
|
702
|
-
c->flags |= REDIS_CONNECTED;
|
703
835
|
redisContextConnectTcp(c,ip,port);
|
704
836
|
return c;
|
705
837
|
}
|
@@ -707,7 +839,6 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
|
|
707
839
|
redisContext *redisConnectUnix(const char *path) {
|
708
840
|
redisContext *c = redisContextInit();
|
709
841
|
c->flags |= REDIS_BLOCK;
|
710
|
-
c->flags |= REDIS_CONNECTED;
|
711
842
|
redisContextConnectUnix(c,path);
|
712
843
|
return c;
|
713
844
|
}
|
@@ -715,7 +846,6 @@ redisContext *redisConnectUnix(const char *path) {
|
|
715
846
|
redisContext *redisConnectUnixNonBlock(const char *path) {
|
716
847
|
redisContext *c = redisContextInit();
|
717
848
|
c->flags &= ~REDIS_BLOCK;
|
718
|
-
c->flags |= REDIS_CONNECTED;
|
719
849
|
redisContextConnectUnix(c,path);
|
720
850
|
return c;
|
721
851
|
}
|
data/vendor/hiredis/hiredis.h
CHANGED
@@ -34,7 +34,7 @@
|
|
34
34
|
|
35
35
|
#define HIREDIS_MAJOR 0
|
36
36
|
#define HIREDIS_MINOR 9
|
37
|
-
#define HIREDIS_PATCH
|
37
|
+
#define HIREDIS_PATCH 1
|
38
38
|
|
39
39
|
#define REDIS_ERR -1
|
40
40
|
#define REDIS_OK 0
|
@@ -69,6 +69,10 @@
|
|
69
69
|
#define REDIS_REPLY_NIL 4
|
70
70
|
#define REDIS_REPLY_STATUS 5
|
71
71
|
|
72
|
+
#ifdef __cplusplus
|
73
|
+
extern "C" {
|
74
|
+
#endif
|
75
|
+
|
72
76
|
/* This is the reply object returned by redisCommand() */
|
73
77
|
typedef struct redisReply {
|
74
78
|
int type; /* REDIS_REPLY_* */
|
@@ -82,8 +86,10 @@ typedef struct redisReply {
|
|
82
86
|
typedef struct redisReadTask {
|
83
87
|
int type;
|
84
88
|
int elements; /* number of elements in multibulk container */
|
85
|
-
void *parent; /* optional pointer to parent object */
|
86
89
|
int idx; /* index in parent (array) object */
|
90
|
+
void *obj; /* holds user-generated value for a read task */
|
91
|
+
struct redisReadTask *parent; /* parent task */
|
92
|
+
void *privdata; /* user-settable arbitrary field */
|
87
93
|
} redisReadTask;
|
88
94
|
|
89
95
|
typedef struct redisReplyObjectFunctions {
|
@@ -112,6 +118,7 @@ typedef struct redisContext {
|
|
112
118
|
void freeReplyObject(void *reply);
|
113
119
|
void *redisReplyReaderCreate();
|
114
120
|
int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
|
121
|
+
int redisReplyReaderSetPrivdata(void *reader, void *privdata);
|
115
122
|
void *redisReplyReaderGetObject(void *reader);
|
116
123
|
char *redisReplyReaderGetError(void *reader);
|
117
124
|
void redisReplyReaderFree(void *ptr);
|
@@ -154,4 +161,8 @@ void *redisvCommand(redisContext *c, const char *format, va_list ap);
|
|
154
161
|
void *redisCommand(redisContext *c, const char *format, ...);
|
155
162
|
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
156
163
|
|
164
|
+
#ifdef __cplusplus
|
165
|
+
}
|
166
|
+
#endif
|
167
|
+
|
157
168
|
#endif
|
data/vendor/hiredis/net.c
CHANGED
@@ -114,7 +114,7 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
|
|
114
114
|
he = gethostbyname(addr);
|
115
115
|
if (he == NULL) {
|
116
116
|
__redisSetError(c,REDIS_ERR_OTHER,
|
117
|
-
sdscatprintf(sdsempty(),"
|
117
|
+
sdscatprintf(sdsempty(),"Can't resolve: %s",addr));
|
118
118
|
close(s);
|
119
119
|
return REDIS_ERR;
|
120
120
|
}
|
@@ -137,6 +137,7 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
|
|
137
137
|
}
|
138
138
|
|
139
139
|
c->fd = s;
|
140
|
+
c->flags |= REDIS_CONNECTED;
|
140
141
|
return REDIS_OK;
|
141
142
|
}
|
142
143
|
|
@@ -163,5 +164,6 @@ int redisContextConnectUnix(redisContext *c, const char *path) {
|
|
163
164
|
}
|
164
165
|
|
165
166
|
c->fd = s;
|
167
|
+
c->flags |= REDIS_CONNECTED;
|
166
168
|
return REDIS_OK;
|
167
169
|
}
|
data/vendor/hiredis/test.c
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
#include <sys/time.h>
|
6
6
|
#include <assert.h>
|
7
7
|
#include <unistd.h>
|
8
|
+
#include <signal.h>
|
8
9
|
|
9
10
|
#include "hiredis.h"
|
10
11
|
|
@@ -46,17 +47,59 @@ static void test_format_commands() {
|
|
46
47
|
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
47
48
|
free(cmd);
|
48
49
|
|
50
|
+
test("Format command with %%s and an empty string: ");
|
51
|
+
len = redisFormatCommand(&cmd,"SET %s %s","foo","");
|
52
|
+
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
|
53
|
+
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
|
54
|
+
free(cmd);
|
55
|
+
|
49
56
|
test("Format command with %%b string interpolation: ");
|
50
57
|
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
|
51
58
|
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 &&
|
52
59
|
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
53
60
|
free(cmd);
|
54
61
|
|
62
|
+
test("Format command with %%b and an empty string: ");
|
63
|
+
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);
|
64
|
+
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
|
65
|
+
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
|
66
|
+
free(cmd);
|
67
|
+
|
68
|
+
test("Format command with literal %%: ");
|
69
|
+
len = redisFormatCommand(&cmd,"SET %% %%");
|
70
|
+
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
|
71
|
+
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
|
72
|
+
free(cmd);
|
73
|
+
|
74
|
+
test("Format command with printf-delegation (long long): ");
|
75
|
+
len = redisFormatCommand(&cmd,"key:%08lld",1234ll);
|
76
|
+
test_cond(strncmp(cmd,"*1\r\n$12\r\nkey:00001234\r\n",len) == 0 &&
|
77
|
+
len == 4+5+(12+2));
|
78
|
+
free(cmd);
|
79
|
+
|
80
|
+
test("Format command with printf-delegation (float): ");
|
81
|
+
len = redisFormatCommand(&cmd,"v:%06.1f",12.34f);
|
82
|
+
test_cond(strncmp(cmd,"*1\r\n$8\r\nv:0012.3\r\n",len) == 0 &&
|
83
|
+
len == 4+4+(8+2));
|
84
|
+
free(cmd);
|
85
|
+
|
86
|
+
test("Format command with printf-delegation and extra interpolation: ");
|
87
|
+
len = redisFormatCommand(&cmd,"key:%d %b",1234,"foo",3);
|
88
|
+
test_cond(strncmp(cmd,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len) == 0 &&
|
89
|
+
len == 4+4+(8+2)+4+(3+2));
|
90
|
+
free(cmd);
|
91
|
+
|
92
|
+
test("Format command with wrong printf format and extra interpolation: ");
|
93
|
+
len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3);
|
94
|
+
test_cond(strncmp(cmd,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len) == 0 &&
|
95
|
+
len == 4+4+(6+2)+4+(3+2));
|
96
|
+
free(cmd);
|
97
|
+
|
55
98
|
const char *argv[3];
|
56
99
|
argv[0] = "SET";
|
57
|
-
argv[1] = "foo";
|
100
|
+
argv[1] = "foo\0xxx";
|
58
101
|
argv[2] = "bar";
|
59
|
-
size_t lens[3] = { 3,
|
102
|
+
size_t lens[3] = { 3, 7, 3 };
|
60
103
|
int argc = 3;
|
61
104
|
|
62
105
|
test("Format command by passing argc/argv without lengths: ");
|
@@ -67,38 +110,29 @@ static void test_format_commands() {
|
|
67
110
|
|
68
111
|
test("Format command by passing argc/argv with lengths: ");
|
69
112
|
len = redisFormatCommandArgv(&cmd,argc,argv,lens);
|
70
|
-
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$
|
71
|
-
len == 4+4+(3+2)+4+(
|
113
|
+
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 &&
|
114
|
+
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
|
72
115
|
free(cmd);
|
73
116
|
}
|
74
117
|
|
75
118
|
static void test_blocking_connection() {
|
76
119
|
redisContext *c;
|
77
120
|
redisReply *reply;
|
121
|
+
int major, minor;
|
78
122
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
/* Two conditions may happen, depending on the type of connection.
|
85
|
-
* When connected via TCP, the socket will not yet be aware of the closed
|
86
|
-
* connection and the write(2) call will succeed, but the read(2) will
|
87
|
-
* result in an EOF. When connected via Unix sockets, the socket will be
|
88
|
-
* immediately aware that it was closed and fail on the write(2) call. */
|
89
|
-
if (use_unix) {
|
90
|
-
fprintf(stderr,"Error: %s\n", c->errstr);
|
91
|
-
assert(c->err == REDIS_ERR_IO &&
|
92
|
-
strcmp(c->errstr,"Broken pipe") == 0);
|
93
|
-
} else {
|
94
|
-
fprintf(stderr,"Error: %s\n", c->errstr);
|
95
|
-
assert(c->err == REDIS_ERR_EOF &&
|
96
|
-
strcmp(c->errstr,"Server closed the connection") == 0);
|
97
|
-
}
|
98
|
-
freeReplyObject(reply);
|
123
|
+
test("Returns error when host cannot be resolved: ");
|
124
|
+
c = redisConnect((char*)"idontexist.local", 6379);
|
125
|
+
test_cond(c->err == REDIS_ERR_OTHER &&
|
126
|
+
strcmp(c->errstr,"Can't resolve: idontexist.local") == 0);
|
99
127
|
redisFree(c);
|
100
128
|
|
101
|
-
|
129
|
+
test("Returns error when the port is not open: ");
|
130
|
+
c = redisConnect((char*)"localhost", 56380);
|
131
|
+
test_cond(c->err == REDIS_ERR_IO &&
|
132
|
+
strcmp(c->errstr,"Connection refused") == 0);
|
133
|
+
redisFree(c);
|
134
|
+
|
135
|
+
__connect(&c);
|
102
136
|
test("Is able to deliver commands: ");
|
103
137
|
reply = redisCommand(c,"PING");
|
104
138
|
test_cond(reply->type == REDIS_REPLY_STATUS &&
|
@@ -111,12 +145,9 @@ static void test_blocking_connection() {
|
|
111
145
|
|
112
146
|
/* Make sure the DB is emtpy */
|
113
147
|
reply = redisCommand(c,"DBSIZE");
|
114
|
-
if (reply->type != REDIS_REPLY_INTEGER ||
|
115
|
-
|
116
|
-
printf("Sorry DB 9 is not empty, test can not continue\n");
|
148
|
+
if (reply->type != REDIS_REPLY_INTEGER || reply->integer != 0) {
|
149
|
+
printf("Database #9 is not empty, test can not continue\n");
|
117
150
|
exit(1);
|
118
|
-
} else {
|
119
|
-
printf("DB 9 is empty... test can continue\n");
|
120
151
|
}
|
121
152
|
freeReplyObject(reply);
|
122
153
|
|
@@ -182,6 +213,43 @@ static void test_blocking_connection() {
|
|
182
213
|
reply->element[1]->type == REDIS_REPLY_STATUS &&
|
183
214
|
strcasecmp(reply->element[1]->str,"pong") == 0);
|
184
215
|
freeReplyObject(reply);
|
216
|
+
|
217
|
+
{
|
218
|
+
/* Find out Redis version to determine the path for the next test */
|
219
|
+
const char *field = "redis_version:";
|
220
|
+
char *p, *eptr;
|
221
|
+
|
222
|
+
reply = redisCommand(c,"INFO");
|
223
|
+
p = strstr(reply->str,field);
|
224
|
+
major = strtol(p+strlen(field),&eptr,10);
|
225
|
+
p = eptr+1; /* char next to the first "." */
|
226
|
+
minor = strtol(p,&eptr,10);
|
227
|
+
freeReplyObject(reply);
|
228
|
+
}
|
229
|
+
|
230
|
+
test("Returns I/O error when the connection is lost: ");
|
231
|
+
reply = redisCommand(c,"QUIT");
|
232
|
+
if (major >= 2 && minor > 0) {
|
233
|
+
/* > 2.0 returns OK on QUIT and read() should be issued once more
|
234
|
+
* to know the descriptor is at EOF. */
|
235
|
+
test_cond(strcasecmp(reply->str,"OK") == 0 &&
|
236
|
+
redisGetReply(c,(void**)&reply) == REDIS_ERR);
|
237
|
+
freeReplyObject(reply);
|
238
|
+
} else {
|
239
|
+
test_cond(reply == NULL);
|
240
|
+
}
|
241
|
+
|
242
|
+
/* On 2.0, QUIT will cause the connection to be closed immediately and
|
243
|
+
* the read(2) for the reply on QUIT will set the error to EOF.
|
244
|
+
* On >2.0, QUIT will return with OK and another read(2) needed to be
|
245
|
+
* issued to find out the socket was closed by the server. In both
|
246
|
+
* conditions, the error will be set to EOF. */
|
247
|
+
assert(c->err == REDIS_ERR_EOF &&
|
248
|
+
strcmp(c->errstr,"Server closed the connection") == 0);
|
249
|
+
|
250
|
+
/* Clean up context and reconnect again */
|
251
|
+
redisFree(c);
|
252
|
+
__connect(&c);
|
185
253
|
}
|
186
254
|
|
187
255
|
static void test_reply_reader() {
|
@@ -196,7 +264,7 @@ static void test_reply_reader() {
|
|
196
264
|
ret = redisReplyReaderGetReply(reader,NULL);
|
197
265
|
err = redisReplyReaderGetError(reader);
|
198
266
|
test_cond(ret == REDIS_ERR &&
|
199
|
-
strcasecmp(err,"
|
267
|
+
strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
|
200
268
|
redisReplyReaderFree(reader);
|
201
269
|
|
202
270
|
/* when the reply already contains multiple items, they must be free'd
|
@@ -209,7 +277,18 @@ static void test_reply_reader() {
|
|
209
277
|
ret = redisReplyReaderGetReply(reader,NULL);
|
210
278
|
err = redisReplyReaderGetError(reader);
|
211
279
|
test_cond(ret == REDIS_ERR &&
|
212
|
-
strcasecmp(err,"
|
280
|
+
strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
|
281
|
+
redisReplyReaderFree(reader);
|
282
|
+
|
283
|
+
test("Set error on nested multi bulks with depth > 1: ");
|
284
|
+
reader = redisReplyReaderCreate();
|
285
|
+
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
|
286
|
+
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
|
287
|
+
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
|
288
|
+
ret = redisReplyReaderGetReply(reader,NULL);
|
289
|
+
err = redisReplyReaderGetError(reader);
|
290
|
+
test_cond(ret == REDIS_ERR &&
|
291
|
+
strncasecmp(err,"No support for",14) == 0);
|
213
292
|
redisReplyReaderFree(reader);
|
214
293
|
|
215
294
|
test("Works with NULL functions for reply: ");
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hiredis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
9
|
+
- 4
|
10
|
+
version: 0.1.4
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Pieter Noordhuis
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-12-06 00:00:00 +01:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ~>
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 1
|
28
30
|
segments:
|
29
31
|
- 0
|
30
32
|
- 7
|
@@ -40,6 +42,7 @@ dependencies:
|
|
40
42
|
requirements:
|
41
43
|
- - ~>
|
42
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 9
|
43
46
|
segments:
|
44
47
|
- 2
|
45
48
|
- 1
|
@@ -94,6 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
97
|
requirements:
|
95
98
|
- - ">="
|
96
99
|
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
97
101
|
segments:
|
98
102
|
- 0
|
99
103
|
version: "0"
|
@@ -102,6 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
106
|
requirements:
|
103
107
|
- - ">="
|
104
108
|
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
105
110
|
segments:
|
106
111
|
- 0
|
107
112
|
version: "0"
|