hiredis 0.6.1 → 0.6.3

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/vendor/hiredis/sds.h CHANGED
@@ -1,6 +1,8 @@
1
- /* SDS (Simple Dynamic Strings), A C dynamic strings library.
1
+ /* SDSLib 2.0 -- A C dynamic strings library
2
2
  *
3
- * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2015, Oran Agra
5
+ * Copyright (c) 2015, Redis Labs, Inc
4
6
  * All rights reserved.
5
7
  *
6
8
  * Redistribution and use in source and binary forms, with or without
@@ -35,32 +37,188 @@
35
37
 
36
38
  #include <sys/types.h>
37
39
  #include <stdarg.h>
40
+ #include <stdint.h>
38
41
 
39
42
  typedef char *sds;
40
43
 
41
- struct sdshdr {
42
- int len;
43
- int free;
44
+ /* Note: sdshdr5 is never used, we just access the flags byte directly.
45
+ * However is here to document the layout of type 5 SDS strings. */
46
+ struct __attribute__ ((__packed__)) sdshdr5 {
47
+ unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
48
+ char buf[];
49
+ };
50
+ struct __attribute__ ((__packed__)) sdshdr8 {
51
+ uint8_t len; /* used */
52
+ uint8_t alloc; /* excluding the header and null terminator */
53
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
54
+ char buf[];
55
+ };
56
+ struct __attribute__ ((__packed__)) sdshdr16 {
57
+ uint16_t len; /* used */
58
+ uint16_t alloc; /* excluding the header and null terminator */
59
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
60
+ char buf[];
61
+ };
62
+ struct __attribute__ ((__packed__)) sdshdr32 {
63
+ uint32_t len; /* used */
64
+ uint32_t alloc; /* excluding the header and null terminator */
65
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
66
+ char buf[];
67
+ };
68
+ struct __attribute__ ((__packed__)) sdshdr64 {
69
+ uint64_t len; /* used */
70
+ uint64_t alloc; /* excluding the header and null terminator */
71
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
44
72
  char buf[];
45
73
  };
46
74
 
75
+ #define SDS_TYPE_5 0
76
+ #define SDS_TYPE_8 1
77
+ #define SDS_TYPE_16 2
78
+ #define SDS_TYPE_32 3
79
+ #define SDS_TYPE_64 4
80
+ #define SDS_TYPE_MASK 7
81
+ #define SDS_TYPE_BITS 3
82
+ #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
83
+ #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
84
+ #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
85
+
47
86
  static inline size_t sdslen(const sds s) {
48
- struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);
49
- return sh->len;
87
+ unsigned char flags = s[-1];
88
+ switch(flags&SDS_TYPE_MASK) {
89
+ case SDS_TYPE_5:
90
+ return SDS_TYPE_5_LEN(flags);
91
+ case SDS_TYPE_8:
92
+ return SDS_HDR(8,s)->len;
93
+ case SDS_TYPE_16:
94
+ return SDS_HDR(16,s)->len;
95
+ case SDS_TYPE_32:
96
+ return SDS_HDR(32,s)->len;
97
+ case SDS_TYPE_64:
98
+ return SDS_HDR(64,s)->len;
99
+ }
100
+ return 0;
50
101
  }
51
102
 
52
103
  static inline size_t sdsavail(const sds s) {
53
- struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);
54
- return sh->free;
104
+ unsigned char flags = s[-1];
105
+ switch(flags&SDS_TYPE_MASK) {
106
+ case SDS_TYPE_5: {
107
+ return 0;
108
+ }
109
+ case SDS_TYPE_8: {
110
+ SDS_HDR_VAR(8,s);
111
+ return sh->alloc - sh->len;
112
+ }
113
+ case SDS_TYPE_16: {
114
+ SDS_HDR_VAR(16,s);
115
+ return sh->alloc - sh->len;
116
+ }
117
+ case SDS_TYPE_32: {
118
+ SDS_HDR_VAR(32,s);
119
+ return sh->alloc - sh->len;
120
+ }
121
+ case SDS_TYPE_64: {
122
+ SDS_HDR_VAR(64,s);
123
+ return sh->alloc - sh->len;
124
+ }
125
+ }
126
+ return 0;
127
+ }
128
+
129
+ static inline void sdssetlen(sds s, size_t newlen) {
130
+ unsigned char flags = s[-1];
131
+ switch(flags&SDS_TYPE_MASK) {
132
+ case SDS_TYPE_5:
133
+ {
134
+ unsigned char *fp = ((unsigned char*)s)-1;
135
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
136
+ }
137
+ break;
138
+ case SDS_TYPE_8:
139
+ SDS_HDR(8,s)->len = newlen;
140
+ break;
141
+ case SDS_TYPE_16:
142
+ SDS_HDR(16,s)->len = newlen;
143
+ break;
144
+ case SDS_TYPE_32:
145
+ SDS_HDR(32,s)->len = newlen;
146
+ break;
147
+ case SDS_TYPE_64:
148
+ SDS_HDR(64,s)->len = newlen;
149
+ break;
150
+ }
151
+ }
152
+
153
+ static inline void sdsinclen(sds s, size_t inc) {
154
+ unsigned char flags = s[-1];
155
+ switch(flags&SDS_TYPE_MASK) {
156
+ case SDS_TYPE_5:
157
+ {
158
+ unsigned char *fp = ((unsigned char*)s)-1;
159
+ unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
160
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
161
+ }
162
+ break;
163
+ case SDS_TYPE_8:
164
+ SDS_HDR(8,s)->len += inc;
165
+ break;
166
+ case SDS_TYPE_16:
167
+ SDS_HDR(16,s)->len += inc;
168
+ break;
169
+ case SDS_TYPE_32:
170
+ SDS_HDR(32,s)->len += inc;
171
+ break;
172
+ case SDS_TYPE_64:
173
+ SDS_HDR(64,s)->len += inc;
174
+ break;
175
+ }
176
+ }
177
+
178
+ /* sdsalloc() = sdsavail() + sdslen() */
179
+ static inline size_t sdsalloc(const sds s) {
180
+ unsigned char flags = s[-1];
181
+ switch(flags&SDS_TYPE_MASK) {
182
+ case SDS_TYPE_5:
183
+ return SDS_TYPE_5_LEN(flags);
184
+ case SDS_TYPE_8:
185
+ return SDS_HDR(8,s)->alloc;
186
+ case SDS_TYPE_16:
187
+ return SDS_HDR(16,s)->alloc;
188
+ case SDS_TYPE_32:
189
+ return SDS_HDR(32,s)->alloc;
190
+ case SDS_TYPE_64:
191
+ return SDS_HDR(64,s)->alloc;
192
+ }
193
+ return 0;
194
+ }
195
+
196
+ static inline void sdssetalloc(sds s, size_t newlen) {
197
+ unsigned char flags = s[-1];
198
+ switch(flags&SDS_TYPE_MASK) {
199
+ case SDS_TYPE_5:
200
+ /* Nothing to do, this type has no total allocation info. */
201
+ break;
202
+ case SDS_TYPE_8:
203
+ SDS_HDR(8,s)->alloc = newlen;
204
+ break;
205
+ case SDS_TYPE_16:
206
+ SDS_HDR(16,s)->alloc = newlen;
207
+ break;
208
+ case SDS_TYPE_32:
209
+ SDS_HDR(32,s)->alloc = newlen;
210
+ break;
211
+ case SDS_TYPE_64:
212
+ SDS_HDR(64,s)->alloc = newlen;
213
+ break;
214
+ }
55
215
  }
56
216
 
57
217
  sds sdsnewlen(const void *init, size_t initlen);
58
218
  sds sdsnew(const char *init);
59
219
  sds sdsempty(void);
60
- size_t sdslen(const sds s);
61
220
  sds sdsdup(const sds s);
62
221
  void sdsfree(sds s);
63
- size_t sdsavail(const sds s);
64
222
  sds sdsgrowzero(sds s, size_t len);
65
223
  sds sdscatlen(sds s, const void *t, size_t len);
66
224
  sds sdscat(sds s, const char *t);
@@ -77,7 +235,7 @@ sds sdscatprintf(sds s, const char *fmt, ...);
77
235
  #endif
78
236
 
79
237
  sds sdscatfmt(sds s, char const *fmt, ...);
80
- void sdstrim(sds s, const char *cset);
238
+ sds sdstrim(sds s, const char *cset);
81
239
  void sdsrange(sds s, int start, int end);
82
240
  void sdsupdatelen(sds s);
83
241
  void sdsclear(sds s);
@@ -90,7 +248,7 @@ sds sdsfromlonglong(long long value);
90
248
  sds sdscatrepr(sds s, const char *p, size_t len);
91
249
  sds *sdssplitargs(const char *line, int *argc);
92
250
  sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
93
- sds sdsjoin(char **argv, int argc, char *sep, size_t seplen);
251
+ sds sdsjoin(char **argv, int argc, char *sep);
94
252
  sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
95
253
 
96
254
  /* Low level functions exposed to the user API */
@@ -98,5 +256,18 @@ sds sdsMakeRoomFor(sds s, size_t addlen);
98
256
  void sdsIncrLen(sds s, int incr);
99
257
  sds sdsRemoveFreeSpace(sds s);
100
258
  size_t sdsAllocSize(sds s);
259
+ void *sdsAllocPtr(sds s);
260
+
261
+ /* Export the allocator used by SDS to the program using SDS.
262
+ * Sometimes the program SDS is linked to, may use a different set of
263
+ * allocators, but may want to allocate or free things that SDS will
264
+ * respectively free or allocate. */
265
+ void *sds_malloc(size_t size);
266
+ void *sds_realloc(void *ptr, size_t size);
267
+ void sds_free(void *ptr);
268
+
269
+ #ifdef REDIS_TEST
270
+ int sdsTest(int argc, char *argv[]);
271
+ #endif
101
272
 
102
273
  #endif
@@ -0,0 +1,42 @@
1
+ /* SDSLib 2.0 -- A C dynamic strings library
2
+ *
3
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2015, Oran Agra
5
+ * Copyright (c) 2015, Redis Labs, Inc
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without
9
+ * modification, are permitted provided that the following conditions are met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright notice,
12
+ * this list of conditions and the following disclaimer.
13
+ * * Redistributions in binary form must reproduce the above copyright
14
+ * notice, this list of conditions and the following disclaimer in the
15
+ * documentation and/or other materials provided with the distribution.
16
+ * * Neither the name of Redis nor the names of its contributors may be used
17
+ * to endorse or promote products derived from this software without
18
+ * specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ * POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+ /* SDS allocator selection.
34
+ *
35
+ * This file is used in order to change the SDS allocator at compile time.
36
+ * Just define the following defines to what you want to use. Also add
37
+ * the include of your alternate allocator if needed (not needed in order
38
+ * to use the default libc allocator). */
39
+
40
+ #define s_malloc malloc
41
+ #define s_realloc realloc
42
+ #define s_free free
@@ -11,6 +11,7 @@
11
11
  #include <limits.h>
12
12
 
13
13
  #include "hiredis.h"
14
+ #include "net.h"
14
15
 
15
16
  enum connection_type {
16
17
  CONN_TCP,
@@ -29,7 +30,7 @@ struct config {
29
30
 
30
31
  struct {
31
32
  const char *path;
32
- } unix;
33
+ } unix_sock;
33
34
  };
34
35
 
35
36
  /* The following lines make up our testing "framework" :) */
@@ -43,6 +44,13 @@ static long long usec(void) {
43
44
  return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
44
45
  }
45
46
 
47
+ /* The assert() calls below have side effects, so we need assert()
48
+ * even if we are compiling without asserts (-DNDEBUG). */
49
+ #ifdef NDEBUG
50
+ #undef assert
51
+ #define assert(e) (void)(e)
52
+ #endif
53
+
46
54
  static redisContext *select_database(redisContext *c) {
47
55
  redisReply *reply;
48
56
 
@@ -89,10 +97,10 @@ static redisContext *connect(struct config config) {
89
97
  if (config.type == CONN_TCP) {
90
98
  c = redisConnect(config.tcp.host, config.tcp.port);
91
99
  } else if (config.type == CONN_UNIX) {
92
- c = redisConnectUnix(config.unix.path);
100
+ c = redisConnectUnix(config.unix_sock.path);
93
101
  } else if (config.type == CONN_FD) {
94
102
  /* Create a dummy connection just to get an fd to inherit */
95
- redisContext *dummy_ctx = redisConnectUnix(config.unix.path);
103
+ redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);
96
104
  if (dummy_ctx) {
97
105
  int fd = disconnect(dummy_ctx, 1);
98
106
  printf("Connecting to inherited fd %d\n", fd);
@@ -216,6 +224,22 @@ static void test_format_commands(void) {
216
224
  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 &&
217
225
  len == 4+4+(3+2)+4+(7+2)+4+(3+2));
218
226
  free(cmd);
227
+
228
+ sds sds_cmd;
229
+
230
+ sds_cmd = sdsempty();
231
+ test("Format command into sds by passing argc/argv without lengths: ");
232
+ len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
233
+ test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
234
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
235
+ sdsfree(sds_cmd);
236
+
237
+ sds_cmd = sdsempty();
238
+ test("Format command into sds by passing argc/argv with lengths: ");
239
+ len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
240
+ test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
241
+ len == 4+4+(3+2)+4+(7+2)+4+(3+2));
242
+ sdsfree(sds_cmd);
219
243
  }
220
244
 
221
245
  static void test_append_formatted_commands(struct config config) {
@@ -278,6 +302,82 @@ static void test_reply_reader(void) {
278
302
  strncasecmp(reader->errstr,"No support for",14) == 0);
279
303
  redisReaderFree(reader);
280
304
 
305
+ test("Correctly parses LLONG_MAX: ");
306
+ reader = redisReaderCreate();
307
+ redisReaderFeed(reader, ":9223372036854775807\r\n",22);
308
+ ret = redisReaderGetReply(reader,&reply);
309
+ test_cond(ret == REDIS_OK &&
310
+ ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
311
+ ((redisReply*)reply)->integer == LLONG_MAX);
312
+ freeReplyObject(reply);
313
+ redisReaderFree(reader);
314
+
315
+ test("Set error when > LLONG_MAX: ");
316
+ reader = redisReaderCreate();
317
+ redisReaderFeed(reader, ":9223372036854775808\r\n",22);
318
+ ret = redisReaderGetReply(reader,&reply);
319
+ test_cond(ret == REDIS_ERR &&
320
+ strcasecmp(reader->errstr,"Bad integer value") == 0);
321
+ freeReplyObject(reply);
322
+ redisReaderFree(reader);
323
+
324
+ test("Correctly parses LLONG_MIN: ");
325
+ reader = redisReaderCreate();
326
+ redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
327
+ ret = redisReaderGetReply(reader,&reply);
328
+ test_cond(ret == REDIS_OK &&
329
+ ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
330
+ ((redisReply*)reply)->integer == LLONG_MIN);
331
+ freeReplyObject(reply);
332
+ redisReaderFree(reader);
333
+
334
+ test("Set error when < LLONG_MIN: ");
335
+ reader = redisReaderCreate();
336
+ redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
337
+ ret = redisReaderGetReply(reader,&reply);
338
+ test_cond(ret == REDIS_ERR &&
339
+ strcasecmp(reader->errstr,"Bad integer value") == 0);
340
+ freeReplyObject(reply);
341
+ redisReaderFree(reader);
342
+
343
+ test("Set error when array < -1: ");
344
+ reader = redisReaderCreate();
345
+ redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12);
346
+ ret = redisReaderGetReply(reader,&reply);
347
+ test_cond(ret == REDIS_ERR &&
348
+ strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
349
+ freeReplyObject(reply);
350
+ redisReaderFree(reader);
351
+
352
+ test("Set error when bulk < -1: ");
353
+ reader = redisReaderCreate();
354
+ redisReaderFeed(reader, "$-2\r\nasdf\r\n",11);
355
+ ret = redisReaderGetReply(reader,&reply);
356
+ test_cond(ret == REDIS_ERR &&
357
+ strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
358
+ freeReplyObject(reply);
359
+ redisReaderFree(reader);
360
+
361
+ test("Set error when array > INT_MAX: ");
362
+ reader = redisReaderCreate();
363
+ redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
364
+ ret = redisReaderGetReply(reader,&reply);
365
+ test_cond(ret == REDIS_ERR &&
366
+ strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
367
+ freeReplyObject(reply);
368
+ redisReaderFree(reader);
369
+
370
+ #if LLONG_MAX > SIZE_MAX
371
+ test("Set error when bulk > SIZE_MAX: ");
372
+ reader = redisReaderCreate();
373
+ redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
374
+ ret = redisReaderGetReply(reader,&reply);
375
+ test_cond(ret == REDIS_ERR &&
376
+ strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
377
+ freeReplyObject(reply);
378
+ redisReaderFree(reader);
379
+ #endif
380
+
281
381
  test("Works with NULL functions for reply: ");
282
382
  reader = redisReaderCreate();
283
383
  reader->fn = NULL;
@@ -320,12 +420,12 @@ static void test_reply_reader(void) {
320
420
  }
321
421
 
322
422
  static void test_free_null(void) {
323
- void *redisContext = NULL;
423
+ void *redisCtx = NULL;
324
424
  void *reply = NULL;
325
425
 
326
426
  test("Don't fail when redisFree is passed a NULL value: ");
327
- redisFree(redisContext);
328
- test_cond(redisContext == NULL);
427
+ redisFree(redisCtx);
428
+ test_cond(redisCtx == NULL);
329
429
 
330
430
  test("Don't fail when freeReplyObject is passed a NULL value: ");
331
431
  freeReplyObject(reply);
@@ -343,6 +443,7 @@ static void test_blocking_connection_errors(void) {
343
443
  strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 ||
344
444
  strcmp(c->errstr,"No address associated with hostname") == 0 ||
345
445
  strcmp(c->errstr,"Temporary failure in name resolution") == 0 ||
446
+ strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 ||
346
447
  strcmp(c->errstr,"no address associated with name") == 0));
347
448
  redisFree(c);
348
449
 
@@ -352,7 +453,7 @@ static void test_blocking_connection_errors(void) {
352
453
  strcmp(c->errstr,"Connection refused") == 0);
353
454
  redisFree(c);
354
455
 
355
- test("Returns error when the unix socket path doesn't accept connections: ");
456
+ test("Returns error when the unix_sock socket path doesn't accept connections: ");
356
457
  c = redisConnectUnix((char*)"/tmp/idontexist.sock");
357
458
  test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
358
459
  redisFree(c);
@@ -436,6 +537,52 @@ static void test_blocking_connection(struct config config) {
436
537
  disconnect(c, 0);
437
538
  }
438
539
 
540
+ static void test_blocking_connection_timeouts(struct config config) {
541
+ redisContext *c;
542
+ redisReply *reply;
543
+ ssize_t s;
544
+ const char *cmd = "DEBUG SLEEP 3\r\n";
545
+ struct timeval tv;
546
+
547
+ c = connect(config);
548
+ test("Successfully completes a command when the timeout is not exceeded: ");
549
+ reply = redisCommand(c,"SET foo fast");
550
+ freeReplyObject(reply);
551
+ tv.tv_sec = 0;
552
+ tv.tv_usec = 10000;
553
+ redisSetTimeout(c, tv);
554
+ reply = redisCommand(c, "GET foo");
555
+ test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0);
556
+ freeReplyObject(reply);
557
+ disconnect(c, 0);
558
+
559
+ c = connect(config);
560
+ test("Does not return a reply when the command times out: ");
561
+ s = write(c->fd, cmd, strlen(cmd));
562
+ tv.tv_sec = 0;
563
+ tv.tv_usec = 10000;
564
+ redisSetTimeout(c, tv);
565
+ reply = redisCommand(c, "GET foo");
566
+ test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0);
567
+ freeReplyObject(reply);
568
+
569
+ test("Reconnect properly reconnects after a timeout: ");
570
+ redisReconnect(c);
571
+ reply = redisCommand(c, "PING");
572
+ test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
573
+ freeReplyObject(reply);
574
+
575
+ test("Reconnect properly uses owned parameters: ");
576
+ config.tcp.host = "foo";
577
+ config.unix_sock.path = "foo";
578
+ redisReconnect(c);
579
+ reply = redisCommand(c, "PING");
580
+ test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
581
+ freeReplyObject(reply);
582
+
583
+ disconnect(c, 0);
584
+ }
585
+
439
586
  static void test_blocking_io_errors(struct config config) {
440
587
  redisContext *c;
441
588
  redisReply *reply;
@@ -459,7 +606,7 @@ static void test_blocking_io_errors(struct config config) {
459
606
 
460
607
  test("Returns I/O error when the connection is lost: ");
461
608
  reply = redisCommand(c,"QUIT");
462
- if (major >= 2 && minor > 0) {
609
+ if (major > 2 || (major == 2 && minor > 0)) {
463
610
  /* > 2.0 returns OK on QUIT and read() should be issued once more
464
611
  * to know the descriptor is at EOF. */
465
612
  test_cond(strcasecmp(reply->str,"OK") == 0 &&
@@ -497,7 +644,7 @@ static void test_invalid_timeout_errors(struct config config) {
497
644
 
498
645
  c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
499
646
 
500
- test_cond(c->err == REDIS_ERR_IO);
647
+ test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
501
648
  redisFree(c);
502
649
 
503
650
  test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
@@ -507,7 +654,7 @@ static void test_invalid_timeout_errors(struct config config) {
507
654
 
508
655
  c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
509
656
 
510
- test_cond(c->err == REDIS_ERR_IO);
657
+ test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
511
658
  redisFree(c);
512
659
  }
513
660
 
@@ -545,6 +692,17 @@ static void test_throughput(struct config config) {
545
692
  free(replies);
546
693
  printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
547
694
 
695
+ replies = malloc(sizeof(redisReply*)*num);
696
+ t1 = usec();
697
+ for (i = 0; i < num; i++) {
698
+ replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
699
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
700
+ }
701
+ t2 = usec();
702
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
703
+ free(replies);
704
+ printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
705
+
548
706
  num = 10000;
549
707
  replies = malloc(sizeof(redisReply*)*num);
550
708
  for (i = 0; i < num; i++)
@@ -573,6 +731,19 @@ static void test_throughput(struct config config) {
573
731
  free(replies);
574
732
  printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
575
733
 
734
+ replies = malloc(sizeof(redisReply*)*num);
735
+ for (i = 0; i < num; i++)
736
+ redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
737
+ t1 = usec();
738
+ for (i = 0; i < num; i++) {
739
+ assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
740
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
741
+ }
742
+ t2 = usec();
743
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
744
+ free(replies);
745
+ printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
746
+
576
747
  disconnect(c, 0);
577
748
  }
578
749
 
@@ -681,7 +852,7 @@ int main(int argc, char **argv) {
681
852
  .host = "127.0.0.1",
682
853
  .port = 6379
683
854
  },
684
- .unix = {
855
+ .unix_sock = {
685
856
  .path = "/tmp/redis.sock"
686
857
  }
687
858
  };
@@ -702,7 +873,7 @@ int main(int argc, char **argv) {
702
873
  cfg.tcp.port = atoi(argv[0]);
703
874
  } else if (argc >= 2 && !strcmp(argv[0],"-s")) {
704
875
  argv++; argc--;
705
- cfg.unix.path = argv[0];
876
+ cfg.unix_sock.path = argv[0];
706
877
  } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
707
878
  throughput = 0;
708
879
  } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
@@ -722,19 +893,21 @@ int main(int argc, char **argv) {
722
893
  printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
723
894
  cfg.type = CONN_TCP;
724
895
  test_blocking_connection(cfg);
896
+ test_blocking_connection_timeouts(cfg);
725
897
  test_blocking_io_errors(cfg);
726
898
  test_invalid_timeout_errors(cfg);
727
899
  test_append_formatted_commands(cfg);
728
900
  if (throughput) test_throughput(cfg);
729
901
 
730
- printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
902
+ printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path);
731
903
  cfg.type = CONN_UNIX;
732
904
  test_blocking_connection(cfg);
905
+ test_blocking_connection_timeouts(cfg);
733
906
  test_blocking_io_errors(cfg);
734
907
  if (throughput) test_throughput(cfg);
735
908
 
736
909
  if (test_inherit_fd) {
737
- printf("\nTesting against inherited fd (%s):\n", cfg.unix.path);
910
+ printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
738
911
  cfg.type = CONN_FD;
739
912
  test_blocking_connection(cfg);
740
913
  }