hiredis 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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"
|