hiredis 0.4.1 → 0.4.2.pre
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/Rakefile +0 -1
- data/lib/hiredis/ruby/reader.rb +7 -4
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +76 -52
- data/vendor/hiredis/async.c +83 -17
- data/vendor/hiredis/async.h +1 -1
- data/vendor/hiredis/fmacros.h +5 -3
- data/vendor/hiredis/hiredis.c +79 -31
- data/vendor/hiredis/hiredis.h +2 -2
- data/vendor/hiredis/net.c +79 -56
- data/vendor/hiredis/net.h +1 -0
- data/vendor/hiredis/test.c +268 -143
- metadata +10 -7
data/vendor/hiredis/hiredis.h
CHANGED
@@ -37,7 +37,7 @@
|
|
37
37
|
|
38
38
|
#define HIREDIS_MAJOR 0
|
39
39
|
#define HIREDIS_MINOR 10
|
40
|
-
#define HIREDIS_PATCH
|
40
|
+
#define HIREDIS_PATCH 1
|
41
41
|
|
42
42
|
#define REDIS_ERR -1
|
43
43
|
#define REDIS_OK 0
|
@@ -123,7 +123,7 @@ typedef struct redisReader {
|
|
123
123
|
size_t pos; /* Buffer cursor */
|
124
124
|
size_t len; /* Buffer length */
|
125
125
|
|
126
|
-
redisReadTask rstack[
|
126
|
+
redisReadTask rstack[4];
|
127
127
|
int ridx; /* Index of current read task */
|
128
128
|
void *reply; /* Temporary reply pointer */
|
129
129
|
|
data/vendor/hiredis/net.c
CHANGED
@@ -62,16 +62,24 @@ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *pref
|
|
62
62
|
__redisSetError(c,type,buf);
|
63
63
|
}
|
64
64
|
|
65
|
+
static int redisSetReuseAddr(redisContext *c, int fd) {
|
66
|
+
int on = 1;
|
67
|
+
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
68
|
+
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
69
|
+
close(fd);
|
70
|
+
return REDIS_ERR;
|
71
|
+
}
|
72
|
+
return REDIS_OK;
|
73
|
+
}
|
74
|
+
|
65
75
|
static int redisCreateSocket(redisContext *c, int type) {
|
66
|
-
int s
|
76
|
+
int s;
|
67
77
|
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
|
68
78
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
69
79
|
return REDIS_ERR;
|
70
80
|
}
|
71
81
|
if (type == AF_INET) {
|
72
|
-
if (
|
73
|
-
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
74
|
-
close(s);
|
82
|
+
if (redisSetReuseAddr(c,s) == REDIS_ERR) {
|
75
83
|
return REDIS_ERR;
|
76
84
|
}
|
77
85
|
}
|
@@ -117,8 +125,6 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
|
|
117
125
|
struct timeval to;
|
118
126
|
struct timeval *toptr = NULL;
|
119
127
|
fd_set wfd;
|
120
|
-
int err;
|
121
|
-
socklen_t errlen;
|
122
128
|
|
123
129
|
/* Only use timeout when not NULL. */
|
124
130
|
if (timeout != NULL) {
|
@@ -143,20 +149,8 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
|
|
143
149
|
return REDIS_ERR;
|
144
150
|
}
|
145
151
|
|
146
|
-
|
147
|
-
errlen = sizeof(err);
|
148
|
-
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
149
|
-
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
150
|
-
close(fd);
|
152
|
+
if (redisCheckSocketError(c, fd) != REDIS_OK)
|
151
153
|
return REDIS_ERR;
|
152
|
-
}
|
153
|
-
|
154
|
-
if (err) {
|
155
|
-
errno = err;
|
156
|
-
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
157
|
-
close(fd);
|
158
|
-
return REDIS_ERR;
|
159
|
-
}
|
160
154
|
|
161
155
|
return REDIS_OK;
|
162
156
|
}
|
@@ -166,6 +160,26 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
|
|
166
160
|
return REDIS_ERR;
|
167
161
|
}
|
168
162
|
|
163
|
+
int redisCheckSocketError(redisContext *c, int fd) {
|
164
|
+
int err = 0;
|
165
|
+
socklen_t errlen = sizeof(err);
|
166
|
+
|
167
|
+
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
168
|
+
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
169
|
+
close(fd);
|
170
|
+
return REDIS_ERR;
|
171
|
+
}
|
172
|
+
|
173
|
+
if (err) {
|
174
|
+
errno = err;
|
175
|
+
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
176
|
+
close(fd);
|
177
|
+
return REDIS_ERR;
|
178
|
+
}
|
179
|
+
|
180
|
+
return REDIS_OK;
|
181
|
+
}
|
182
|
+
|
169
183
|
int redisContextSetTimeout(redisContext *c, struct timeval tv) {
|
170
184
|
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
|
171
185
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
@@ -179,50 +193,59 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
|
|
179
193
|
}
|
180
194
|
|
181
195
|
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
|
182
|
-
int s;
|
196
|
+
int s, rv;
|
197
|
+
char _port[6]; /* strlen("65535"); */
|
198
|
+
struct addrinfo hints, *servinfo, *p;
|
183
199
|
int blocking = (c->flags & REDIS_BLOCK);
|
184
|
-
struct sockaddr_in sa;
|
185
200
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
201
|
+
snprintf(_port, 6, "%d", port);
|
202
|
+
memset(&hints,0,sizeof(hints));
|
203
|
+
hints.ai_family = AF_INET;
|
204
|
+
hints.ai_socktype = SOCK_STREAM;
|
190
205
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
struct hostent *he;
|
195
|
-
|
196
|
-
he = gethostbyname(addr);
|
197
|
-
if (he == NULL) {
|
198
|
-
char buf[128];
|
199
|
-
snprintf(buf,sizeof(buf),"Can't resolve: %s", addr);
|
200
|
-
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
201
|
-
close(s);
|
202
|
-
return REDIS_ERR;
|
203
|
-
}
|
204
|
-
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
206
|
+
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
207
|
+
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
208
|
+
return REDIS_ERR;
|
205
209
|
}
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
210
|
+
for (p = servinfo; p != NULL; p = p->ai_next) {
|
211
|
+
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
212
|
+
continue;
|
213
|
+
|
214
|
+
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
215
|
+
goto error;
|
216
|
+
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
217
|
+
if (errno == EHOSTUNREACH) {
|
218
|
+
close(s);
|
219
|
+
continue;
|
220
|
+
} else if (errno == EINPROGRESS && !blocking) {
|
221
|
+
/* This is ok. */
|
222
|
+
} else {
|
223
|
+
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
224
|
+
goto error;
|
225
|
+
}
|
213
226
|
}
|
227
|
+
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
228
|
+
goto error;
|
229
|
+
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
|
230
|
+
goto error;
|
231
|
+
|
232
|
+
c->fd = s;
|
233
|
+
c->flags |= REDIS_CONNECTED;
|
234
|
+
rv = REDIS_OK;
|
235
|
+
goto end;
|
236
|
+
}
|
237
|
+
if (p == NULL) {
|
238
|
+
char buf[128];
|
239
|
+
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
|
240
|
+
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
241
|
+
goto error;
|
214
242
|
}
|
215
243
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
return REDIS_ERR;
|
222
|
-
|
223
|
-
c->fd = s;
|
224
|
-
c->flags |= REDIS_CONNECTED;
|
225
|
-
return REDIS_OK;
|
244
|
+
error:
|
245
|
+
rv = REDIS_ERR;
|
246
|
+
end:
|
247
|
+
freeaddrinfo(servinfo);
|
248
|
+
return rv; // Need to return REDIS_OK if alright
|
226
249
|
}
|
227
250
|
|
228
251
|
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
|
data/vendor/hiredis/net.h
CHANGED
@@ -39,6 +39,7 @@
|
|
39
39
|
#define AF_LOCAL AF_UNIX
|
40
40
|
#endif
|
41
41
|
|
42
|
+
int redisCheckSocketError(redisContext *c, int fd);
|
42
43
|
int redisContextSetTimeout(redisContext *c, struct timeval tv);
|
43
44
|
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
|
44
45
|
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
|
data/vendor/hiredis/test.c
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#include "fmacros.h"
|
1
2
|
#include <stdio.h>
|
2
3
|
#include <stdlib.h>
|
3
4
|
#include <string.h>
|
@@ -10,10 +11,28 @@
|
|
10
11
|
|
11
12
|
#include "hiredis.h"
|
12
13
|
|
14
|
+
enum connection_type {
|
15
|
+
CONN_TCP,
|
16
|
+
CONN_UNIX
|
17
|
+
};
|
18
|
+
|
19
|
+
struct config {
|
20
|
+
enum connection_type type;
|
21
|
+
|
22
|
+
struct {
|
23
|
+
const char *host;
|
24
|
+
int port;
|
25
|
+
} tcp;
|
26
|
+
|
27
|
+
struct {
|
28
|
+
const char *path;
|
29
|
+
} unix;
|
30
|
+
};
|
31
|
+
|
13
32
|
/* The following lines make up our testing "framework" :) */
|
14
33
|
static int tests = 0, fails = 0;
|
15
34
|
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
|
16
|
-
#define test_cond(_c) if(_c) printf("
|
35
|
+
#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
|
17
36
|
|
18
37
|
static long long usec(void) {
|
19
38
|
struct timeval tv;
|
@@ -21,15 +40,60 @@ static long long usec(void) {
|
|
21
40
|
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
|
22
41
|
}
|
23
42
|
|
24
|
-
static
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
43
|
+
static redisContext *select_database(redisContext *c) {
|
44
|
+
redisReply *reply;
|
45
|
+
|
46
|
+
/* Switch to DB 9 for testing, now that we know we can chat. */
|
47
|
+
reply = redisCommand(c,"SELECT 9");
|
48
|
+
assert(reply != NULL);
|
49
|
+
freeReplyObject(reply);
|
50
|
+
|
51
|
+
/* Make sure the DB is emtpy */
|
52
|
+
reply = redisCommand(c,"DBSIZE");
|
53
|
+
assert(reply != NULL);
|
54
|
+
if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
|
55
|
+
/* Awesome, DB 9 is empty and we can continue. */
|
56
|
+
freeReplyObject(reply);
|
57
|
+
} else {
|
58
|
+
printf("Database #9 is not empty, test can not continue\n");
|
31
59
|
exit(1);
|
32
60
|
}
|
61
|
+
|
62
|
+
return c;
|
63
|
+
}
|
64
|
+
|
65
|
+
static void disconnect(redisContext *c) {
|
66
|
+
redisReply *reply;
|
67
|
+
|
68
|
+
/* Make sure we're on DB 9. */
|
69
|
+
reply = redisCommand(c,"SELECT 9");
|
70
|
+
assert(reply != NULL);
|
71
|
+
freeReplyObject(reply);
|
72
|
+
reply = redisCommand(c,"FLUSHDB");
|
73
|
+
assert(reply != NULL);
|
74
|
+
freeReplyObject(reply);
|
75
|
+
|
76
|
+
/* Free the context as well. */
|
77
|
+
redisFree(c);
|
78
|
+
}
|
79
|
+
|
80
|
+
static redisContext *connect(struct config config) {
|
81
|
+
redisContext *c = NULL;
|
82
|
+
|
83
|
+
if (config.type == CONN_TCP) {
|
84
|
+
c = redisConnect(config.tcp.host, config.tcp.port);
|
85
|
+
} else if (config.type == CONN_UNIX) {
|
86
|
+
c = redisConnectUnix(config.unix.path);
|
87
|
+
} else {
|
88
|
+
assert(NULL);
|
89
|
+
}
|
90
|
+
|
91
|
+
if (c->err) {
|
92
|
+
printf("Connection error: %s\n", c->errstr);
|
93
|
+
exit(1);
|
94
|
+
}
|
95
|
+
|
96
|
+
return select_database(c);
|
33
97
|
}
|
34
98
|
|
35
99
|
static void test_format_commands(void) {
|
@@ -78,29 +142,43 @@ static void test_format_commands(void) {
|
|
78
142
|
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
|
79
143
|
free(cmd);
|
80
144
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
145
|
+
/* Vararg width depends on the type. These tests make sure that the
|
146
|
+
* width is correctly determined using the format and subsequent varargs
|
147
|
+
* can correctly be interpolated. */
|
148
|
+
#define INTEGER_WIDTH_TEST(fmt, type) do { \
|
149
|
+
type value = 123; \
|
150
|
+
test("Format command with printf-delegation (" #type "): "); \
|
151
|
+
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
|
152
|
+
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
|
153
|
+
len == 4+5+(12+2)+4+(9+2)); \
|
154
|
+
free(cmd); \
|
155
|
+
} while(0)
|
156
|
+
|
157
|
+
#define FLOAT_WIDTH_TEST(type) do { \
|
158
|
+
type value = 123.0; \
|
159
|
+
test("Format command with printf-delegation (" #type "): "); \
|
160
|
+
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
|
161
|
+
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
|
162
|
+
len == 4+5+(12+2)+4+(9+2)); \
|
163
|
+
free(cmd); \
|
164
|
+
} while(0)
|
165
|
+
|
166
|
+
INTEGER_WIDTH_TEST("d", int);
|
167
|
+
INTEGER_WIDTH_TEST("hhd", char);
|
168
|
+
INTEGER_WIDTH_TEST("hd", short);
|
169
|
+
INTEGER_WIDTH_TEST("ld", long);
|
170
|
+
INTEGER_WIDTH_TEST("lld", long long);
|
171
|
+
INTEGER_WIDTH_TEST("u", unsigned int);
|
172
|
+
INTEGER_WIDTH_TEST("hhu", unsigned char);
|
173
|
+
INTEGER_WIDTH_TEST("hu", unsigned short);
|
174
|
+
INTEGER_WIDTH_TEST("lu", unsigned long);
|
175
|
+
INTEGER_WIDTH_TEST("llu", unsigned long long);
|
176
|
+
FLOAT_WIDTH_TEST(float);
|
177
|
+
FLOAT_WIDTH_TEST(double);
|
178
|
+
|
179
|
+
test("Format command with invalid printf format: ");
|
100
180
|
len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3);
|
101
|
-
test_cond(
|
102
|
-
len == 4+4+(6+2)+4+(3+2));
|
103
|
-
free(cmd);
|
181
|
+
test_cond(len == -1);
|
104
182
|
|
105
183
|
const char *argv[3];
|
106
184
|
argv[0] = "SET";
|
@@ -122,10 +200,85 @@ static void test_format_commands(void) {
|
|
122
200
|
free(cmd);
|
123
201
|
}
|
124
202
|
|
125
|
-
static void
|
203
|
+
static void test_reply_reader(void) {
|
204
|
+
redisReader *reader;
|
205
|
+
void *reply;
|
206
|
+
int ret;
|
207
|
+
|
208
|
+
test("Error handling in reply parser: ");
|
209
|
+
reader = redisReaderCreate();
|
210
|
+
redisReaderFeed(reader,(char*)"@foo\r\n",6);
|
211
|
+
ret = redisReaderGetReply(reader,NULL);
|
212
|
+
test_cond(ret == REDIS_ERR &&
|
213
|
+
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
|
214
|
+
redisReaderFree(reader);
|
215
|
+
|
216
|
+
/* when the reply already contains multiple items, they must be free'd
|
217
|
+
* on an error. valgrind will bark when this doesn't happen. */
|
218
|
+
test("Memory cleanup in reply parser: ");
|
219
|
+
reader = redisReaderCreate();
|
220
|
+
redisReaderFeed(reader,(char*)"*2\r\n",4);
|
221
|
+
redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
|
222
|
+
redisReaderFeed(reader,(char*)"@foo\r\n",6);
|
223
|
+
ret = redisReaderGetReply(reader,NULL);
|
224
|
+
test_cond(ret == REDIS_ERR &&
|
225
|
+
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
|
226
|
+
redisReaderFree(reader);
|
227
|
+
|
228
|
+
test("Set error on nested multi bulks with depth > 2: ");
|
229
|
+
reader = redisReaderCreate();
|
230
|
+
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
231
|
+
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
232
|
+
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
233
|
+
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
234
|
+
ret = redisReaderGetReply(reader,NULL);
|
235
|
+
test_cond(ret == REDIS_ERR &&
|
236
|
+
strncasecmp(reader->errstr,"No support for",14) == 0);
|
237
|
+
redisReaderFree(reader);
|
238
|
+
|
239
|
+
test("Works with NULL functions for reply: ");
|
240
|
+
reader = redisReaderCreate();
|
241
|
+
reader->fn = NULL;
|
242
|
+
redisReaderFeed(reader,(char*)"+OK\r\n",5);
|
243
|
+
ret = redisReaderGetReply(reader,&reply);
|
244
|
+
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
|
245
|
+
redisReaderFree(reader);
|
246
|
+
|
247
|
+
test("Works when a single newline (\\r\\n) covers two calls to feed: ");
|
248
|
+
reader = redisReaderCreate();
|
249
|
+
reader->fn = NULL;
|
250
|
+
redisReaderFeed(reader,(char*)"+OK\r",4);
|
251
|
+
ret = redisReaderGetReply(reader,&reply);
|
252
|
+
assert(ret == REDIS_OK && reply == NULL);
|
253
|
+
redisReaderFeed(reader,(char*)"\n",1);
|
254
|
+
ret = redisReaderGetReply(reader,&reply);
|
255
|
+
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
|
256
|
+
redisReaderFree(reader);
|
257
|
+
|
258
|
+
test("Don't reset state after protocol error: ");
|
259
|
+
reader = redisReaderCreate();
|
260
|
+
reader->fn = NULL;
|
261
|
+
redisReaderFeed(reader,(char*)"x",1);
|
262
|
+
ret = redisReaderGetReply(reader,&reply);
|
263
|
+
assert(ret == REDIS_ERR);
|
264
|
+
ret = redisReaderGetReply(reader,&reply);
|
265
|
+
test_cond(ret == REDIS_ERR && reply == NULL);
|
266
|
+
redisReaderFree(reader);
|
267
|
+
|
268
|
+
/* Regression test for issue #45 on GitHub. */
|
269
|
+
test("Don't do empty allocation for empty multi bulk: ");
|
270
|
+
reader = redisReaderCreate();
|
271
|
+
redisReaderFeed(reader,(char*)"*0\r\n",4);
|
272
|
+
ret = redisReaderGetReply(reader,&reply);
|
273
|
+
test_cond(ret == REDIS_OK &&
|
274
|
+
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
|
275
|
+
((redisReply*)reply)->elements == 0);
|
276
|
+
freeReplyObject(reply);
|
277
|
+
redisReaderFree(reader);
|
278
|
+
}
|
279
|
+
|
280
|
+
static void test_blocking_connection_errors(void) {
|
126
281
|
redisContext *c;
|
127
|
-
redisReply *reply;
|
128
|
-
int major, minor;
|
129
282
|
|
130
283
|
test("Returns error when host cannot be resolved: ");
|
131
284
|
c = redisConnect((char*)"idontexist.local", 6379);
|
@@ -134,30 +287,29 @@ static void test_blocking_connection(void) {
|
|
134
287
|
redisFree(c);
|
135
288
|
|
136
289
|
test("Returns error when the port is not open: ");
|
137
|
-
c = redisConnect((char*)"localhost",
|
290
|
+
c = redisConnect((char*)"localhost", 1);
|
138
291
|
test_cond(c->err == REDIS_ERR_IO &&
|
139
292
|
strcmp(c->errstr,"Connection refused") == 0);
|
140
293
|
redisFree(c);
|
141
294
|
|
142
|
-
|
295
|
+
test("Returns error when the unix socket path doesn't accept connections: ");
|
296
|
+
c = redisConnectUnix((char*)"/tmp/idontexist.sock");
|
297
|
+
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
|
298
|
+
redisFree(c);
|
299
|
+
}
|
300
|
+
|
301
|
+
static void test_blocking_connection(struct config config) {
|
302
|
+
redisContext *c;
|
303
|
+
redisReply *reply;
|
304
|
+
|
305
|
+
c = connect(config);
|
306
|
+
|
143
307
|
test("Is able to deliver commands: ");
|
144
308
|
reply = redisCommand(c,"PING");
|
145
309
|
test_cond(reply->type == REDIS_REPLY_STATUS &&
|
146
310
|
strcasecmp(reply->str,"pong") == 0)
|
147
311
|
freeReplyObject(reply);
|
148
312
|
|
149
|
-
/* Switch to DB 9 for testing, now that we know we can chat. */
|
150
|
-
reply = redisCommand(c,"SELECT 9");
|
151
|
-
freeReplyObject(reply);
|
152
|
-
|
153
|
-
/* Make sure the DB is emtpy */
|
154
|
-
reply = redisCommand(c,"DBSIZE");
|
155
|
-
if (reply->type != REDIS_REPLY_INTEGER || reply->integer != 0) {
|
156
|
-
printf("Database #9 is not empty, test can not continue\n");
|
157
|
-
exit(1);
|
158
|
-
}
|
159
|
-
freeReplyObject(reply);
|
160
|
-
|
161
313
|
test("Is a able to send commands verbatim: ");
|
162
314
|
reply = redisCommand(c,"SET foo bar");
|
163
315
|
test_cond (reply->type == REDIS_REPLY_STATUS &&
|
@@ -221,6 +373,17 @@ static void test_blocking_connection(void) {
|
|
221
373
|
strcasecmp(reply->element[1]->str,"pong") == 0);
|
222
374
|
freeReplyObject(reply);
|
223
375
|
|
376
|
+
disconnect(c);
|
377
|
+
}
|
378
|
+
|
379
|
+
static void test_blocking_io_errors(struct config config) {
|
380
|
+
redisContext *c;
|
381
|
+
redisReply *reply;
|
382
|
+
void *_reply;
|
383
|
+
int major, minor;
|
384
|
+
|
385
|
+
/* Connect to target given by config. */
|
386
|
+
c = connect(config);
|
224
387
|
{
|
225
388
|
/* Find out Redis version to determine the path for the next test */
|
226
389
|
const char *field = "redis_version:";
|
@@ -240,7 +403,7 @@ static void test_blocking_connection(void) {
|
|
240
403
|
/* > 2.0 returns OK on QUIT and read() should be issued once more
|
241
404
|
* to know the descriptor is at EOF. */
|
242
405
|
test_cond(strcasecmp(reply->str,"OK") == 0 &&
|
243
|
-
redisGetReply(c
|
406
|
+
redisGetReply(c,&_reply) == REDIS_ERR);
|
244
407
|
freeReplyObject(reply);
|
245
408
|
} else {
|
246
409
|
test_cond(reply == NULL);
|
@@ -255,87 +418,20 @@ static void test_blocking_connection(void) {
|
|
255
418
|
strcmp(c->errstr,"Server closed the connection") == 0);
|
256
419
|
redisFree(c);
|
257
420
|
|
258
|
-
|
421
|
+
c = connect(config);
|
259
422
|
test("Returns I/O error on socket timeout: ");
|
260
423
|
struct timeval tv = { 0, 1000 };
|
261
424
|
assert(redisSetTimeout(c,tv) == REDIS_OK);
|
262
|
-
test_cond(redisGetReply(c
|
425
|
+
test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
|
263
426
|
c->err == REDIS_ERR_IO && errno == EAGAIN);
|
264
427
|
redisFree(c);
|
265
|
-
|
266
|
-
/* Context should be connected */
|
267
|
-
__connect(&c);
|
268
|
-
}
|
269
|
-
|
270
|
-
static void test_reply_reader(void) {
|
271
|
-
redisReader *reader;
|
272
|
-
void *reply;
|
273
|
-
int ret;
|
274
|
-
|
275
|
-
test("Error handling in reply parser: ");
|
276
|
-
reader = redisReaderCreate();
|
277
|
-
redisReaderFeed(reader,(char*)"@foo\r\n",6);
|
278
|
-
ret = redisReaderGetReply(reader,NULL);
|
279
|
-
test_cond(ret == REDIS_ERR &&
|
280
|
-
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
|
281
|
-
redisReaderFree(reader);
|
282
|
-
|
283
|
-
/* when the reply already contains multiple items, they must be free'd
|
284
|
-
* on an error. valgrind will bark when this doesn't happen. */
|
285
|
-
test("Memory cleanup in reply parser: ");
|
286
|
-
reader = redisReaderCreate();
|
287
|
-
redisReaderFeed(reader,(char*)"*2\r\n",4);
|
288
|
-
redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
|
289
|
-
redisReaderFeed(reader,(char*)"@foo\r\n",6);
|
290
|
-
ret = redisReaderGetReply(reader,NULL);
|
291
|
-
test_cond(ret == REDIS_ERR &&
|
292
|
-
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
|
293
|
-
redisReaderFree(reader);
|
294
|
-
|
295
|
-
test("Set error on nested multi bulks with depth > 1: ");
|
296
|
-
reader = redisReaderCreate();
|
297
|
-
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
298
|
-
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
299
|
-
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
300
|
-
ret = redisReaderGetReply(reader,NULL);
|
301
|
-
test_cond(ret == REDIS_ERR &&
|
302
|
-
strncasecmp(reader->errstr,"No support for",14) == 0);
|
303
|
-
redisReaderFree(reader);
|
304
|
-
|
305
|
-
test("Works with NULL functions for reply: ");
|
306
|
-
reader = redisReaderCreate();
|
307
|
-
reader->fn = NULL;
|
308
|
-
redisReaderFeed(reader,(char*)"+OK\r\n",5);
|
309
|
-
ret = redisReaderGetReply(reader,&reply);
|
310
|
-
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
|
311
|
-
redisReaderFree(reader);
|
312
|
-
|
313
|
-
test("Works when a single newline (\\r\\n) covers two calls to feed: ");
|
314
|
-
reader = redisReaderCreate();
|
315
|
-
reader->fn = NULL;
|
316
|
-
redisReaderFeed(reader,(char*)"+OK\r",4);
|
317
|
-
ret = redisReaderGetReply(reader,&reply);
|
318
|
-
assert(ret == REDIS_OK && reply == NULL);
|
319
|
-
redisReaderFeed(reader,(char*)"\n",1);
|
320
|
-
ret = redisReaderGetReply(reader,&reply);
|
321
|
-
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
|
322
|
-
redisReaderFree(reader);
|
323
|
-
|
324
|
-
test("Don't reset state after protocol error: ");
|
325
|
-
reader = redisReaderCreate();
|
326
|
-
reader->fn = NULL;
|
327
|
-
redisReaderFeed(reader,(char*)"x",1);
|
328
|
-
ret = redisReaderGetReply(reader,&reply);
|
329
|
-
assert(ret == REDIS_ERR);
|
330
|
-
ret = redisReaderGetReply(reader,&reply);
|
331
|
-
test_cond(ret == REDIS_ERR && reply == NULL);
|
332
428
|
}
|
333
429
|
|
334
|
-
static void test_throughput(
|
430
|
+
static void test_throughput(struct config config) {
|
431
|
+
redisContext *c = connect(config);
|
432
|
+
redisReply **replies;
|
335
433
|
int i, num;
|
336
434
|
long long t1, t2;
|
337
|
-
redisContext *c = blocking_context;
|
338
|
-
redisReply **replies;
|
339
435
|
|
340
436
|
test("Throughput:\n");
|
341
437
|
for (i = 0; i < 500; i++)
|
@@ -392,18 +488,8 @@ static void test_throughput(void) {
|
|
392
488
|
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
393
489
|
free(replies);
|
394
490
|
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
395
|
-
}
|
396
|
-
|
397
|
-
static void cleanup(void) {
|
398
|
-
redisContext *c = blocking_context;
|
399
|
-
redisReply *reply;
|
400
491
|
|
401
|
-
|
402
|
-
reply = redisCommand(c,"SELECT 9");
|
403
|
-
assert(reply != NULL); freeReplyObject(reply);
|
404
|
-
reply = redisCommand(c,"FLUSHDB");
|
405
|
-
assert(reply != NULL); freeReplyObject(reply);
|
406
|
-
redisFree(c);
|
492
|
+
disconnect(c);
|
407
493
|
}
|
408
494
|
|
409
495
|
// static long __test_callback_flags = 0;
|
@@ -425,7 +511,7 @@ static void cleanup(void) {
|
|
425
511
|
// static redisContext *__connect_nonblock() {
|
426
512
|
// /* Reset callback flags */
|
427
513
|
// __test_callback_flags = 0;
|
428
|
-
// return redisConnectNonBlock("127.0.0.1",
|
514
|
+
// return redisConnectNonBlock("127.0.0.1", port, NULL);
|
429
515
|
// }
|
430
516
|
//
|
431
517
|
// static void test_nonblocking_connection() {
|
@@ -506,23 +592,62 @@ static void cleanup(void) {
|
|
506
592
|
// }
|
507
593
|
|
508
594
|
int main(int argc, char **argv) {
|
509
|
-
|
510
|
-
|
511
|
-
|
595
|
+
struct config cfg = {
|
596
|
+
.tcp = {
|
597
|
+
.host = "127.0.0.1",
|
598
|
+
.port = 6379
|
599
|
+
},
|
600
|
+
.unix = {
|
601
|
+
.path = "/tmp/redis.sock"
|
602
|
+
}
|
603
|
+
};
|
604
|
+
int throughput = 1;
|
605
|
+
|
606
|
+
/* Ignore broken pipe signal (for I/O error tests). */
|
607
|
+
signal(SIGPIPE, SIG_IGN);
|
608
|
+
|
609
|
+
/* Parse command line options. */
|
610
|
+
argv++; argc--;
|
611
|
+
while (argc) {
|
612
|
+
if (argc >= 2 && !strcmp(argv[0],"-h")) {
|
613
|
+
argv++; argc--;
|
614
|
+
cfg.tcp.host = argv[0];
|
615
|
+
} else if (argc >= 2 && !strcmp(argv[0],"-p")) {
|
616
|
+
argv++; argc--;
|
617
|
+
cfg.tcp.port = atoi(argv[0]);
|
618
|
+
} else if (argc >= 2 && !strcmp(argv[0],"-s")) {
|
619
|
+
argv++; argc--;
|
620
|
+
cfg.unix.path = argv[0];
|
621
|
+
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
|
622
|
+
throughput = 0;
|
623
|
+
} else {
|
624
|
+
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
|
625
|
+
exit(1);
|
626
|
+
}
|
627
|
+
argv++; argc--;
|
512
628
|
}
|
513
629
|
|
514
|
-
signal(SIGPIPE, SIG_IGN);
|
515
630
|
test_format_commands();
|
516
|
-
test_blocking_connection();
|
517
631
|
test_reply_reader();
|
518
|
-
|
519
|
-
test_throughput();
|
520
|
-
cleanup();
|
632
|
+
test_blocking_connection_errors();
|
521
633
|
|
522
|
-
|
523
|
-
|
524
|
-
|
634
|
+
printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
|
635
|
+
cfg.type = CONN_TCP;
|
636
|
+
test_blocking_connection(cfg);
|
637
|
+
test_blocking_io_errors(cfg);
|
638
|
+
if (throughput) test_throughput(cfg);
|
639
|
+
|
640
|
+
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
|
641
|
+
cfg.type = CONN_UNIX;
|
642
|
+
test_blocking_connection(cfg);
|
643
|
+
test_blocking_io_errors(cfg);
|
644
|
+
if (throughput) test_throughput(cfg);
|
645
|
+
|
646
|
+
if (fails) {
|
525
647
|
printf("*** %d TESTS FAILED ***\n", fails);
|
648
|
+
return 1;
|
526
649
|
}
|
650
|
+
|
651
|
+
printf("ALL TESTS PASSED\n");
|
527
652
|
return 0;
|
528
653
|
}
|