redis-client 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +1 -2
  4. data/Gemfile.lock +2 -3
  5. data/README.md +66 -3
  6. data/Rakefile +43 -23
  7. data/lib/redis_client/command_builder.rb +83 -0
  8. data/lib/redis_client/config.rb +9 -48
  9. data/lib/redis_client/connection_mixin.rb +38 -0
  10. data/lib/redis_client/decorator.rb +84 -0
  11. data/lib/redis_client/pooled.rb +38 -30
  12. data/lib/redis_client/ruby_connection/buffered_io.rb +153 -0
  13. data/lib/redis_client/{resp3.rb → ruby_connection/resp3.rb} +0 -26
  14. data/lib/redis_client/{connection.rb → ruby_connection.rb} +26 -31
  15. data/lib/redis_client/version.rb +1 -1
  16. data/lib/redis_client.rb +162 -36
  17. data/redis-client.gemspec +2 -4
  18. metadata +12 -59
  19. data/.rubocop.yml +0 -190
  20. data/ext/redis_client/hiredis/export.clang +0 -2
  21. data/ext/redis_client/hiredis/export.gcc +0 -7
  22. data/ext/redis_client/hiredis/extconf.rb +0 -62
  23. data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
  24. data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
  25. data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
  26. data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
  27. data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
  28. data/ext/redis_client/hiredis/vendor/COPYING +0 -29
  29. data/ext/redis_client/hiredis/vendor/Makefile +0 -308
  30. data/ext/redis_client/hiredis/vendor/README.md +0 -664
  31. data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
  32. data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
  33. data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
  34. data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
  35. data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
  36. data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
  37. data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
  38. data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
  39. data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
  40. data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
  41. data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
  42. data/ext/redis_client/hiredis/vendor/async.c +0 -887
  43. data/ext/redis_client/hiredis/vendor/async.h +0 -147
  44. data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
  45. data/ext/redis_client/hiredis/vendor/dict.c +0 -352
  46. data/ext/redis_client/hiredis/vendor/dict.h +0 -126
  47. data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
  48. data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
  49. data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
  50. data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
  51. data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
  52. data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
  53. data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
  54. data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
  55. data/ext/redis_client/hiredis/vendor/net.c +0 -612
  56. data/ext/redis_client/hiredis/vendor/net.h +0 -56
  57. data/ext/redis_client/hiredis/vendor/read.c +0 -739
  58. data/ext/redis_client/hiredis/vendor/read.h +0 -129
  59. data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
  60. data/ext/redis_client/hiredis/vendor/sds.h +0 -278
  61. data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
  62. data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
  63. data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
  64. data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
  65. data/ext/redis_client/hiredis/vendor/test.c +0 -1401
  66. data/ext/redis_client/hiredis/vendor/test.sh +0 -78
  67. data/ext/redis_client/hiredis/vendor/win32.h +0 -56
  68. data/lib/redis_client/buffered_io.rb +0 -151
  69. data/lib/redis_client/hiredis_connection.rb +0 -80
@@ -1,1174 +0,0 @@
1
- /*
2
- * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
- * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
5
- * Jan-Erik Rediger <janerik at fnordig dot com>
6
- *
7
- * All rights reserved.
8
- *
9
- * Redistribution and use in source and binary forms, with or without
10
- * modification, are permitted provided that the following conditions are met:
11
- *
12
- * * Redistributions of source code must retain the above copyright notice,
13
- * this list of conditions and the following disclaimer.
14
- * * Redistributions in binary form must reproduce the above copyright
15
- * notice, this list of conditions and the following disclaimer in the
16
- * documentation and/or other materials provided with the distribution.
17
- * * Neither the name of Redis nor the names of its contributors may be used
18
- * to endorse or promote products derived from this software without
19
- * specific prior written permission.
20
- *
21
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
- * POSSIBILITY OF SUCH DAMAGE.
32
- */
33
-
34
- #include "fmacros.h"
35
- #include <string.h>
36
- #include <stdlib.h>
37
- #include <assert.h>
38
- #include <errno.h>
39
- #include <ctype.h>
40
-
41
- #include "hiredis.h"
42
- #include "net.h"
43
- #include "sds.h"
44
- #include "async.h"
45
- #include "win32.h"
46
-
47
- extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout);
48
- extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
49
-
50
- static redisContextFuncs redisContextDefaultFuncs = {
51
- .free_privctx = NULL,
52
- .async_read = redisAsyncRead,
53
- .async_write = redisAsyncWrite,
54
- .read = redisNetRead,
55
- .write = redisNetWrite
56
- };
57
-
58
- static redisReply *createReplyObject(int type);
59
- static void *createStringObject(const redisReadTask *task, char *str, size_t len);
60
- static void *createArrayObject(const redisReadTask *task, size_t elements);
61
- static void *createIntegerObject(const redisReadTask *task, long long value);
62
- static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);
63
- static void *createNilObject(const redisReadTask *task);
64
- static void *createBoolObject(const redisReadTask *task, int bval);
65
-
66
- /* Default set of functions to build the reply. Keep in mind that such a
67
- * function returning NULL is interpreted as OOM. */
68
- static redisReplyObjectFunctions defaultFunctions = {
69
- createStringObject,
70
- createArrayObject,
71
- createIntegerObject,
72
- createDoubleObject,
73
- createNilObject,
74
- createBoolObject,
75
- freeReplyObject
76
- };
77
-
78
- /* Create a reply object */
79
- static redisReply *createReplyObject(int type) {
80
- redisReply *r = hi_calloc(1,sizeof(*r));
81
-
82
- if (r == NULL)
83
- return NULL;
84
-
85
- r->type = type;
86
- return r;
87
- }
88
-
89
- /* Free a reply object */
90
- void freeReplyObject(void *reply) {
91
- redisReply *r = reply;
92
- size_t j;
93
-
94
- if (r == NULL)
95
- return;
96
-
97
- switch(r->type) {
98
- case REDIS_REPLY_INTEGER:
99
- break; /* Nothing to free */
100
- case REDIS_REPLY_ARRAY:
101
- case REDIS_REPLY_MAP:
102
- case REDIS_REPLY_SET:
103
- case REDIS_REPLY_PUSH:
104
- if (r->element != NULL) {
105
- for (j = 0; j < r->elements; j++)
106
- freeReplyObject(r->element[j]);
107
- hi_free(r->element);
108
- }
109
- break;
110
- case REDIS_REPLY_ERROR:
111
- case REDIS_REPLY_STATUS:
112
- case REDIS_REPLY_STRING:
113
- case REDIS_REPLY_DOUBLE:
114
- case REDIS_REPLY_VERB:
115
- hi_free(r->str);
116
- break;
117
- }
118
- hi_free(r);
119
- }
120
-
121
- static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
122
- redisReply *r, *parent;
123
- char *buf;
124
-
125
- r = createReplyObject(task->type);
126
- if (r == NULL)
127
- return NULL;
128
-
129
- assert(task->type == REDIS_REPLY_ERROR ||
130
- task->type == REDIS_REPLY_STATUS ||
131
- task->type == REDIS_REPLY_STRING ||
132
- task->type == REDIS_REPLY_VERB);
133
-
134
- /* Copy string value */
135
- if (task->type == REDIS_REPLY_VERB) {
136
- buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
137
- if (buf == NULL) goto oom;
138
-
139
- memcpy(r->vtype,str,3);
140
- r->vtype[3] = '\0';
141
- memcpy(buf,str+4,len-4);
142
- buf[len-4] = '\0';
143
- r->len = len - 4;
144
- } else {
145
- buf = hi_malloc(len+1);
146
- if (buf == NULL) goto oom;
147
-
148
- memcpy(buf,str,len);
149
- buf[len] = '\0';
150
- r->len = len;
151
- }
152
- r->str = buf;
153
-
154
- if (task->parent) {
155
- parent = task->parent->obj;
156
- assert(parent->type == REDIS_REPLY_ARRAY ||
157
- parent->type == REDIS_REPLY_MAP ||
158
- parent->type == REDIS_REPLY_SET ||
159
- parent->type == REDIS_REPLY_PUSH);
160
- parent->element[task->idx] = r;
161
- }
162
- return r;
163
-
164
- oom:
165
- freeReplyObject(r);
166
- return NULL;
167
- }
168
-
169
- static void *createArrayObject(const redisReadTask *task, size_t elements) {
170
- redisReply *r, *parent;
171
-
172
- r = createReplyObject(task->type);
173
- if (r == NULL)
174
- return NULL;
175
-
176
- if (elements > 0) {
177
- if (SIZE_MAX / sizeof(redisReply*) < elements) return NULL; /* Don't overflow */
178
- r->element = hi_calloc(elements,sizeof(redisReply*));
179
- if (r->element == NULL) {
180
- freeReplyObject(r);
181
- return NULL;
182
- }
183
- }
184
-
185
- r->elements = elements;
186
-
187
- if (task->parent) {
188
- parent = task->parent->obj;
189
- assert(parent->type == REDIS_REPLY_ARRAY ||
190
- parent->type == REDIS_REPLY_MAP ||
191
- parent->type == REDIS_REPLY_SET ||
192
- parent->type == REDIS_REPLY_PUSH);
193
- parent->element[task->idx] = r;
194
- }
195
- return r;
196
- }
197
-
198
- static void *createIntegerObject(const redisReadTask *task, long long value) {
199
- redisReply *r, *parent;
200
-
201
- r = createReplyObject(REDIS_REPLY_INTEGER);
202
- if (r == NULL)
203
- return NULL;
204
-
205
- r->integer = value;
206
-
207
- if (task->parent) {
208
- parent = task->parent->obj;
209
- assert(parent->type == REDIS_REPLY_ARRAY ||
210
- parent->type == REDIS_REPLY_MAP ||
211
- parent->type == REDIS_REPLY_SET ||
212
- parent->type == REDIS_REPLY_PUSH);
213
- parent->element[task->idx] = r;
214
- }
215
- return r;
216
- }
217
-
218
- static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {
219
- redisReply *r, *parent;
220
-
221
- r = createReplyObject(REDIS_REPLY_DOUBLE);
222
- if (r == NULL)
223
- return NULL;
224
-
225
- r->dval = value;
226
- r->str = hi_malloc(len+1);
227
- if (r->str == NULL) {
228
- freeReplyObject(r);
229
- return NULL;
230
- }
231
-
232
- /* The double reply also has the original protocol string representing a
233
- * double as a null terminated string. This way the caller does not need
234
- * to format back for string conversion, especially since Redis does efforts
235
- * to make the string more human readable avoiding the calssical double
236
- * decimal string conversion artifacts. */
237
- memcpy(r->str, str, len);
238
- r->str[len] = '\0';
239
-
240
- if (task->parent) {
241
- parent = task->parent->obj;
242
- assert(parent->type == REDIS_REPLY_ARRAY ||
243
- parent->type == REDIS_REPLY_MAP ||
244
- parent->type == REDIS_REPLY_SET);
245
- parent->element[task->idx] = r;
246
- }
247
- return r;
248
- }
249
-
250
- static void *createNilObject(const redisReadTask *task) {
251
- redisReply *r, *parent;
252
-
253
- r = createReplyObject(REDIS_REPLY_NIL);
254
- if (r == NULL)
255
- return NULL;
256
-
257
- if (task->parent) {
258
- parent = task->parent->obj;
259
- assert(parent->type == REDIS_REPLY_ARRAY ||
260
- parent->type == REDIS_REPLY_MAP ||
261
- parent->type == REDIS_REPLY_SET);
262
- parent->element[task->idx] = r;
263
- }
264
- return r;
265
- }
266
-
267
- static void *createBoolObject(const redisReadTask *task, int bval) {
268
- redisReply *r, *parent;
269
-
270
- r = createReplyObject(REDIS_REPLY_BOOL);
271
- if (r == NULL)
272
- return NULL;
273
-
274
- r->integer = bval != 0;
275
-
276
- if (task->parent) {
277
- parent = task->parent->obj;
278
- assert(parent->type == REDIS_REPLY_ARRAY ||
279
- parent->type == REDIS_REPLY_MAP ||
280
- parent->type == REDIS_REPLY_SET);
281
- parent->element[task->idx] = r;
282
- }
283
- return r;
284
- }
285
-
286
- /* Return the number of digits of 'v' when converted to string in radix 10.
287
- * Implementation borrowed from link in redis/src/util.c:string2ll(). */
288
- static uint32_t countDigits(uint64_t v) {
289
- uint32_t result = 1;
290
- for (;;) {
291
- if (v < 10) return result;
292
- if (v < 100) return result + 1;
293
- if (v < 1000) return result + 2;
294
- if (v < 10000) return result + 3;
295
- v /= 10000U;
296
- result += 4;
297
- }
298
- }
299
-
300
- /* Helper that calculates the bulk length given a certain string length. */
301
- static size_t bulklen(size_t len) {
302
- return 1+countDigits(len)+2+len+2;
303
- }
304
-
305
- int redisvFormatCommand(char **target, const char *format, va_list ap) {
306
- const char *c = format;
307
- char *cmd = NULL; /* final command */
308
- int pos; /* position in final command */
309
- sds curarg, newarg; /* current argument */
310
- int touched = 0; /* was the current argument touched? */
311
- char **curargv = NULL, **newargv = NULL;
312
- int argc = 0;
313
- int totlen = 0;
314
- int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */
315
- int j;
316
-
317
- /* Abort if there is not target to set */
318
- if (target == NULL)
319
- return -1;
320
-
321
- /* Build the command string accordingly to protocol */
322
- curarg = sdsempty();
323
- if (curarg == NULL)
324
- return -1;
325
-
326
- while(*c != '\0') {
327
- if (*c != '%' || c[1] == '\0') {
328
- if (*c == ' ') {
329
- if (touched) {
330
- newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
331
- if (newargv == NULL) goto memory_err;
332
- curargv = newargv;
333
- curargv[argc++] = curarg;
334
- totlen += bulklen(sdslen(curarg));
335
-
336
- /* curarg is put in argv so it can be overwritten. */
337
- curarg = sdsempty();
338
- if (curarg == NULL) goto memory_err;
339
- touched = 0;
340
- }
341
- } else {
342
- newarg = sdscatlen(curarg,c,1);
343
- if (newarg == NULL) goto memory_err;
344
- curarg = newarg;
345
- touched = 1;
346
- }
347
- } else {
348
- char *arg;
349
- size_t size;
350
-
351
- /* Set newarg so it can be checked even if it is not touched. */
352
- newarg = curarg;
353
-
354
- switch(c[1]) {
355
- case 's':
356
- arg = va_arg(ap,char*);
357
- size = strlen(arg);
358
- if (size > 0)
359
- newarg = sdscatlen(curarg,arg,size);
360
- break;
361
- case 'b':
362
- arg = va_arg(ap,char*);
363
- size = va_arg(ap,size_t);
364
- if (size > 0)
365
- newarg = sdscatlen(curarg,arg,size);
366
- break;
367
- case '%':
368
- newarg = sdscat(curarg,"%");
369
- break;
370
- default:
371
- /* Try to detect printf format */
372
- {
373
- static const char intfmts[] = "diouxX";
374
- static const char flags[] = "#0-+ ";
375
- char _format[16];
376
- const char *_p = c+1;
377
- size_t _l = 0;
378
- va_list _cpy;
379
-
380
- /* Flags */
381
- while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++;
382
-
383
- /* Field width */
384
- while (*_p != '\0' && isdigit(*_p)) _p++;
385
-
386
- /* Precision */
387
- if (*_p == '.') {
388
- _p++;
389
- while (*_p != '\0' && isdigit(*_p)) _p++;
390
- }
391
-
392
- /* Copy va_list before consuming with va_arg */
393
- va_copy(_cpy,ap);
394
-
395
- /* Integer conversion (without modifiers) */
396
- if (strchr(intfmts,*_p) != NULL) {
397
- va_arg(ap,int);
398
- goto fmt_valid;
399
- }
400
-
401
- /* Double conversion (without modifiers) */
402
- if (strchr("eEfFgGaA",*_p) != NULL) {
403
- va_arg(ap,double);
404
- goto fmt_valid;
405
- }
406
-
407
- /* Size: char */
408
- if (_p[0] == 'h' && _p[1] == 'h') {
409
- _p += 2;
410
- if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
411
- va_arg(ap,int); /* char gets promoted to int */
412
- goto fmt_valid;
413
- }
414
- goto fmt_invalid;
415
- }
416
-
417
- /* Size: short */
418
- if (_p[0] == 'h') {
419
- _p += 1;
420
- if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
421
- va_arg(ap,int); /* short gets promoted to int */
422
- goto fmt_valid;
423
- }
424
- goto fmt_invalid;
425
- }
426
-
427
- /* Size: long long */
428
- if (_p[0] == 'l' && _p[1] == 'l') {
429
- _p += 2;
430
- if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
431
- va_arg(ap,long long);
432
- goto fmt_valid;
433
- }
434
- goto fmt_invalid;
435
- }
436
-
437
- /* Size: long */
438
- if (_p[0] == 'l') {
439
- _p += 1;
440
- if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
441
- va_arg(ap,long);
442
- goto fmt_valid;
443
- }
444
- goto fmt_invalid;
445
- }
446
-
447
- fmt_invalid:
448
- va_end(_cpy);
449
- goto format_err;
450
-
451
- fmt_valid:
452
- _l = (_p+1)-c;
453
- if (_l < sizeof(_format)-2) {
454
- memcpy(_format,c,_l);
455
- _format[_l] = '\0';
456
- newarg = sdscatvprintf(curarg,_format,_cpy);
457
-
458
- /* Update current position (note: outer blocks
459
- * increment c twice so compensate here) */
460
- c = _p-1;
461
- }
462
-
463
- va_end(_cpy);
464
- break;
465
- }
466
- }
467
-
468
- if (newarg == NULL) goto memory_err;
469
- curarg = newarg;
470
-
471
- touched = 1;
472
- c++;
473
- }
474
- c++;
475
- }
476
-
477
- /* Add the last argument if needed */
478
- if (touched) {
479
- newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
480
- if (newargv == NULL) goto memory_err;
481
- curargv = newargv;
482
- curargv[argc++] = curarg;
483
- totlen += bulklen(sdslen(curarg));
484
- } else {
485
- sdsfree(curarg);
486
- }
487
-
488
- /* Clear curarg because it was put in curargv or was free'd. */
489
- curarg = NULL;
490
-
491
- /* Add bytes needed to hold multi bulk count */
492
- totlen += 1+countDigits(argc)+2;
493
-
494
- /* Build the command at protocol level */
495
- cmd = hi_malloc(totlen+1);
496
- if (cmd == NULL) goto memory_err;
497
-
498
- pos = sprintf(cmd,"*%d\r\n",argc);
499
- for (j = 0; j < argc; j++) {
500
- pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
501
- memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
502
- pos += sdslen(curargv[j]);
503
- sdsfree(curargv[j]);
504
- cmd[pos++] = '\r';
505
- cmd[pos++] = '\n';
506
- }
507
- assert(pos == totlen);
508
- cmd[pos] = '\0';
509
-
510
- hi_free(curargv);
511
- *target = cmd;
512
- return totlen;
513
-
514
- format_err:
515
- error_type = -2;
516
- goto cleanup;
517
-
518
- memory_err:
519
- error_type = -1;
520
- goto cleanup;
521
-
522
- cleanup:
523
- if (curargv) {
524
- while(argc--)
525
- sdsfree(curargv[argc]);
526
- hi_free(curargv);
527
- }
528
-
529
- sdsfree(curarg);
530
- hi_free(cmd);
531
-
532
- return error_type;
533
- }
534
-
535
- /* Format a command according to the Redis protocol. This function
536
- * takes a format similar to printf:
537
- *
538
- * %s represents a C null terminated string you want to interpolate
539
- * %b represents a binary safe string
540
- *
541
- * When using %b you need to provide both the pointer to the string
542
- * and the length in bytes as a size_t. Examples:
543
- *
544
- * len = redisFormatCommand(target, "GET %s", mykey);
545
- * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
546
- */
547
- int redisFormatCommand(char **target, const char *format, ...) {
548
- va_list ap;
549
- int len;
550
- va_start(ap,format);
551
- len = redisvFormatCommand(target,format,ap);
552
- va_end(ap);
553
-
554
- /* The API says "-1" means bad result, but we now also return "-2" in some
555
- * cases. Force the return value to always be -1. */
556
- if (len < 0)
557
- len = -1;
558
-
559
- return len;
560
- }
561
-
562
- /* Format a command according to the Redis protocol using an sds string and
563
- * sdscatfmt for the processing of arguments. This function takes the
564
- * number of arguments, an array with arguments and an array with their
565
- * lengths. If the latter is set to NULL, strlen will be used to compute the
566
- * argument lengths.
567
- */
568
- int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
569
- const size_t *argvlen)
570
- {
571
- sds cmd, aux;
572
- unsigned long long totlen;
573
- int j;
574
- size_t len;
575
-
576
- /* Abort on a NULL target */
577
- if (target == NULL)
578
- return -1;
579
-
580
- /* Calculate our total size */
581
- totlen = 1+countDigits(argc)+2;
582
- for (j = 0; j < argc; j++) {
583
- len = argvlen ? argvlen[j] : strlen(argv[j]);
584
- totlen += bulklen(len);
585
- }
586
-
587
- /* Use an SDS string for command construction */
588
- cmd = sdsempty();
589
- if (cmd == NULL)
590
- return -1;
591
-
592
- /* We already know how much storage we need */
593
- aux = sdsMakeRoomFor(cmd, totlen);
594
- if (aux == NULL) {
595
- sdsfree(cmd);
596
- return -1;
597
- }
598
-
599
- cmd = aux;
600
-
601
- /* Construct command */
602
- cmd = sdscatfmt(cmd, "*%i\r\n", argc);
603
- for (j=0; j < argc; j++) {
604
- len = argvlen ? argvlen[j] : strlen(argv[j]);
605
- cmd = sdscatfmt(cmd, "$%u\r\n", len);
606
- cmd = sdscatlen(cmd, argv[j], len);
607
- cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
608
- }
609
-
610
- assert(sdslen(cmd)==totlen);
611
-
612
- *target = cmd;
613
- return totlen;
614
- }
615
-
616
- void redisFreeSdsCommand(sds cmd) {
617
- sdsfree(cmd);
618
- }
619
-
620
- /* Format a command according to the Redis protocol. This function takes the
621
- * number of arguments, an array with arguments and an array with their
622
- * lengths. If the latter is set to NULL, strlen will be used to compute the
623
- * argument lengths.
624
- */
625
- int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
626
- char *cmd = NULL; /* final command */
627
- int pos; /* position in final command */
628
- size_t len;
629
- int totlen, j;
630
-
631
- /* Abort on a NULL target */
632
- if (target == NULL)
633
- return -1;
634
-
635
- /* Calculate number of bytes needed for the command */
636
- totlen = 1+countDigits(argc)+2;
637
- for (j = 0; j < argc; j++) {
638
- len = argvlen ? argvlen[j] : strlen(argv[j]);
639
- totlen += bulklen(len);
640
- }
641
-
642
- /* Build the command at protocol level */
643
- cmd = hi_malloc(totlen+1);
644
- if (cmd == NULL)
645
- return -1;
646
-
647
- pos = sprintf(cmd,"*%d\r\n",argc);
648
- for (j = 0; j < argc; j++) {
649
- len = argvlen ? argvlen[j] : strlen(argv[j]);
650
- pos += sprintf(cmd+pos,"$%zu\r\n",len);
651
- memcpy(cmd+pos,argv[j],len);
652
- pos += len;
653
- cmd[pos++] = '\r';
654
- cmd[pos++] = '\n';
655
- }
656
- assert(pos == totlen);
657
- cmd[pos] = '\0';
658
-
659
- *target = cmd;
660
- return totlen;
661
- }
662
-
663
- void redisFreeCommand(char *cmd) {
664
- hi_free(cmd);
665
- }
666
-
667
- void __redisSetError(redisContext *c, int type, const char *str) {
668
- size_t len;
669
-
670
- c->err = type;
671
- if (str != NULL) {
672
- len = strlen(str);
673
- len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
674
- memcpy(c->errstr,str,len);
675
- c->errstr[len] = '\0';
676
- } else {
677
- /* Only REDIS_ERR_IO may lack a description! */
678
- assert(type == REDIS_ERR_IO);
679
- strerror_r(errno, c->errstr, sizeof(c->errstr));
680
- }
681
- }
682
-
683
- redisReader *redisReaderCreate(void) {
684
- return redisReaderCreateWithFunctions(&defaultFunctions);
685
- }
686
-
687
- static void redisPushAutoFree(void *privdata, void *reply) {
688
- (void)privdata;
689
- freeReplyObject(reply);
690
- }
691
-
692
- static redisContext *redisContextInit(void) {
693
- redisContext *c;
694
-
695
- c = hi_calloc(1, sizeof(*c));
696
- if (c == NULL)
697
- return NULL;
698
-
699
- c->funcs = &redisContextDefaultFuncs;
700
-
701
- c->obuf = sdsempty();
702
- c->reader = redisReaderCreate();
703
- c->fd = REDIS_INVALID_FD;
704
-
705
- if (c->obuf == NULL || c->reader == NULL) {
706
- redisFree(c);
707
- return NULL;
708
- }
709
-
710
- return c;
711
- }
712
-
713
- void redisFree(redisContext *c) {
714
- if (c == NULL)
715
- return;
716
- redisNetClose(c);
717
-
718
- sdsfree(c->obuf);
719
- redisReaderFree(c->reader);
720
- hi_free(c->tcp.host);
721
- hi_free(c->tcp.source_addr);
722
- hi_free(c->unix_sock.path);
723
- hi_free(c->connect_timeout);
724
- hi_free(c->command_timeout);
725
- hi_free(c->saddr);
726
-
727
- if (c->privdata && c->free_privdata)
728
- c->free_privdata(c->privdata);
729
-
730
- if (c->funcs->free_privctx)
731
- c->funcs->free_privctx(c->privctx);
732
-
733
- memset(c, 0xff, sizeof(*c));
734
- hi_free(c);
735
- }
736
-
737
- redisFD redisFreeKeepFd(redisContext *c) {
738
- redisFD fd = c->fd;
739
- c->fd = REDIS_INVALID_FD;
740
- redisFree(c);
741
- return fd;
742
- }
743
-
744
- int redisReconnect(redisContext *c) {
745
- c->err = 0;
746
- memset(c->errstr, '\0', strlen(c->errstr));
747
-
748
- if (c->privctx && c->funcs->free_privctx) {
749
- c->funcs->free_privctx(c->privctx);
750
- c->privctx = NULL;
751
- }
752
-
753
- redisNetClose(c);
754
-
755
- sdsfree(c->obuf);
756
- redisReaderFree(c->reader);
757
-
758
- c->obuf = sdsempty();
759
- c->reader = redisReaderCreate();
760
-
761
- if (c->obuf == NULL || c->reader == NULL) {
762
- __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
763
- return REDIS_ERR;
764
- }
765
-
766
- int ret = REDIS_ERR;
767
- if (c->connection_type == REDIS_CONN_TCP) {
768
- ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
769
- c->connect_timeout, c->tcp.source_addr);
770
- } else if (c->connection_type == REDIS_CONN_UNIX) {
771
- ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);
772
- } else {
773
- /* Something bad happened here and shouldn't have. There isn't
774
- enough information in the context to reconnect. */
775
- __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect");
776
- ret = REDIS_ERR;
777
- }
778
-
779
- if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
780
- redisContextSetTimeout(c, *c->command_timeout);
781
- }
782
-
783
- return ret;
784
- }
785
-
786
- redisContext *redisConnectWithOptions(const redisOptions *options) {
787
- redisContext *c = redisContextInit();
788
- if (c == NULL) {
789
- return NULL;
790
- }
791
- if (!(options->options & REDIS_OPT_NONBLOCK)) {
792
- c->flags |= REDIS_BLOCK;
793
- }
794
- if (options->options & REDIS_OPT_REUSEADDR) {
795
- c->flags |= REDIS_REUSEADDR;
796
- }
797
- if (options->options & REDIS_OPT_NOAUTOFREE) {
798
- c->flags |= REDIS_NO_AUTO_FREE;
799
- }
800
-
801
- /* Set any user supplied RESP3 PUSH handler or use freeReplyObject
802
- * as a default unless specifically flagged that we don't want one. */
803
- if (options->push_cb != NULL)
804
- redisSetPushCallback(c, options->push_cb);
805
- else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))
806
- redisSetPushCallback(c, redisPushAutoFree);
807
-
808
- c->privdata = options->privdata;
809
- c->free_privdata = options->free_privdata;
810
-
811
- if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK ||
812
- redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) {
813
- __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
814
- return c;
815
- }
816
-
817
- if (options->type == REDIS_CONN_TCP) {
818
- redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
819
- options->endpoint.tcp.port, options->connect_timeout,
820
- options->endpoint.tcp.source_addr);
821
- } else if (options->type == REDIS_CONN_UNIX) {
822
- redisContextConnectUnix(c, options->endpoint.unix_socket,
823
- options->connect_timeout);
824
- } else if (options->type == REDIS_CONN_USERFD) {
825
- c->fd = options->endpoint.fd;
826
- c->flags |= REDIS_CONNECTED;
827
- } else {
828
- // Unknown type - FIXME - FREE
829
- return NULL;
830
- }
831
-
832
- if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
833
- redisContextSetTimeout(c, *options->command_timeout);
834
- }
835
-
836
- return c;
837
- }
838
-
839
- /* Connect to a Redis instance. On error the field error in the returned
840
- * context will be set to the return value of the error function.
841
- * When no set of reply functions is given, the default set will be used. */
842
- redisContext *redisConnect(const char *ip, int port) {
843
- redisOptions options = {0};
844
- REDIS_OPTIONS_SET_TCP(&options, ip, port);
845
- return redisConnectWithOptions(&options);
846
- }
847
-
848
- redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
849
- redisOptions options = {0};
850
- REDIS_OPTIONS_SET_TCP(&options, ip, port);
851
- options.connect_timeout = &tv;
852
- return redisConnectWithOptions(&options);
853
- }
854
-
855
- redisContext *redisConnectNonBlock(const char *ip, int port) {
856
- redisOptions options = {0};
857
- REDIS_OPTIONS_SET_TCP(&options, ip, port);
858
- options.options |= REDIS_OPT_NONBLOCK;
859
- return redisConnectWithOptions(&options);
860
- }
861
-
862
- redisContext *redisConnectBindNonBlock(const char *ip, int port,
863
- const char *source_addr) {
864
- redisOptions options = {0};
865
- REDIS_OPTIONS_SET_TCP(&options, ip, port);
866
- options.endpoint.tcp.source_addr = source_addr;
867
- options.options |= REDIS_OPT_NONBLOCK;
868
- return redisConnectWithOptions(&options);
869
- }
870
-
871
- redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
872
- const char *source_addr) {
873
- redisOptions options = {0};
874
- REDIS_OPTIONS_SET_TCP(&options, ip, port);
875
- options.endpoint.tcp.source_addr = source_addr;
876
- options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;
877
- return redisConnectWithOptions(&options);
878
- }
879
-
880
- redisContext *redisConnectUnix(const char *path) {
881
- redisOptions options = {0};
882
- REDIS_OPTIONS_SET_UNIX(&options, path);
883
- return redisConnectWithOptions(&options);
884
- }
885
-
886
- redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
887
- redisOptions options = {0};
888
- REDIS_OPTIONS_SET_UNIX(&options, path);
889
- options.connect_timeout = &tv;
890
- return redisConnectWithOptions(&options);
891
- }
892
-
893
- redisContext *redisConnectUnixNonBlock(const char *path) {
894
- redisOptions options = {0};
895
- REDIS_OPTIONS_SET_UNIX(&options, path);
896
- options.options |= REDIS_OPT_NONBLOCK;
897
- return redisConnectWithOptions(&options);
898
- }
899
-
900
- redisContext *redisConnectFd(redisFD fd) {
901
- redisOptions options = {0};
902
- options.type = REDIS_CONN_USERFD;
903
- options.endpoint.fd = fd;
904
- return redisConnectWithOptions(&options);
905
- }
906
-
907
- /* Set read/write timeout on a blocking socket. */
908
- int redisSetTimeout(redisContext *c, const struct timeval tv) {
909
- if (c->flags & REDIS_BLOCK)
910
- return redisContextSetTimeout(c,tv);
911
- return REDIS_ERR;
912
- }
913
-
914
- /* Enable connection KeepAlive. */
915
- int redisEnableKeepAlive(redisContext *c) {
916
- if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)
917
- return REDIS_ERR;
918
- return REDIS_OK;
919
- }
920
-
921
- /* Set a user provided RESP3 PUSH handler and return any old one set. */
922
- redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) {
923
- redisPushFn *old = c->push_cb;
924
- c->push_cb = fn;
925
- return old;
926
- }
927
-
928
- /* Use this function to handle a read event on the descriptor. It will try
929
- * and read some bytes from the socket and feed them to the reply parser.
930
- *
931
- * After this function is called, you may use redisGetReplyFromReader to
932
- * see if there is a reply available. */
933
- int redisBufferRead(redisContext *c) {
934
- char buf[1024*16];
935
- int nread;
936
-
937
- /* Return early when the context has seen an error. */
938
- if (c->err)
939
- return REDIS_ERR;
940
-
941
- nread = c->funcs->read(c, buf, sizeof(buf));
942
- if (nread > 0) {
943
- if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) {
944
- __redisSetError(c, c->reader->err, c->reader->errstr);
945
- return REDIS_ERR;
946
- } else {
947
- }
948
- } else if (nread < 0) {
949
- return REDIS_ERR;
950
- }
951
- return REDIS_OK;
952
- }
953
-
954
- /* Write the output buffer to the socket.
955
- *
956
- * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
957
- * successfully written to the socket. When the buffer is empty after the
958
- * write operation, "done" is set to 1 (if given).
959
- *
960
- * Returns REDIS_ERR if an error occurred trying to write and sets
961
- * c->errstr to hold the appropriate error string.
962
- */
963
- int redisBufferWrite(redisContext *c, int *done) {
964
-
965
- /* Return early when the context has seen an error. */
966
- if (c->err)
967
- return REDIS_ERR;
968
-
969
- if (sdslen(c->obuf) > 0) {
970
- ssize_t nwritten = c->funcs->write(c);
971
- if (nwritten < 0) {
972
- return REDIS_ERR;
973
- } else if (nwritten > 0) {
974
- if (nwritten == (ssize_t)sdslen(c->obuf)) {
975
- sdsfree(c->obuf);
976
- c->obuf = sdsempty();
977
- if (c->obuf == NULL)
978
- goto oom;
979
- } else {
980
- if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom;
981
- }
982
- }
983
- }
984
- if (done != NULL) *done = (sdslen(c->obuf) == 0);
985
- return REDIS_OK;
986
-
987
- oom:
988
- __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
989
- return REDIS_ERR;
990
- }
991
-
992
- /* Internal helper function to try and get a reply from the reader,
993
- * or set an error in the context otherwise. */
994
- int redisGetReplyFromReader(redisContext *c, void **reply) {
995
- if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
996
- __redisSetError(c,c->reader->err,c->reader->errstr);
997
- return REDIS_ERR;
998
- }
999
-
1000
- return REDIS_OK;
1001
- }
1002
-
1003
- /* Internal helper that returns 1 if the reply was a RESP3 PUSH
1004
- * message and we handled it with a user-provided callback. */
1005
- static int redisHandledPushReply(redisContext *c, void *reply) {
1006
- if (reply && c->push_cb && redisIsPushReply(reply)) {
1007
- c->push_cb(c->privdata, reply);
1008
- return 1;
1009
- }
1010
-
1011
- return 0;
1012
- }
1013
-
1014
- int redisGetReply(redisContext *c, void **reply) {
1015
- int wdone = 0;
1016
- void *aux = NULL;
1017
-
1018
- /* Try to read pending replies */
1019
- if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1020
- return REDIS_ERR;
1021
-
1022
- /* For the blocking context, flush output buffer and read reply */
1023
- if (aux == NULL && c->flags & REDIS_BLOCK) {
1024
- /* Write until done */
1025
- do {
1026
- if (redisBufferWrite(c,&wdone) == REDIS_ERR)
1027
- return REDIS_ERR;
1028
- } while (!wdone);
1029
-
1030
- /* Read until there is a reply */
1031
- do {
1032
- if (redisBufferRead(c) == REDIS_ERR)
1033
- return REDIS_ERR;
1034
-
1035
- /* We loop here in case the user has specified a RESP3
1036
- * PUSH handler (e.g. for client tracking). */
1037
- do {
1038
- if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1039
- return REDIS_ERR;
1040
- } while (redisHandledPushReply(c, aux));
1041
- } while (aux == NULL);
1042
- }
1043
-
1044
- /* Set reply or free it if we were passed NULL */
1045
- if (reply != NULL) {
1046
- *reply = aux;
1047
- } else {
1048
- freeReplyObject(aux);
1049
- }
1050
-
1051
- return REDIS_OK;
1052
- }
1053
-
1054
-
1055
- /* Helper function for the redisAppendCommand* family of functions.
1056
- *
1057
- * Write a formatted command to the output buffer. When this family
1058
- * is used, you need to call redisGetReply yourself to retrieve
1059
- * the reply (or replies in pub/sub).
1060
- */
1061
- int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
1062
- sds newbuf;
1063
-
1064
- newbuf = sdscatlen(c->obuf,cmd,len);
1065
- if (newbuf == NULL) {
1066
- __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1067
- return REDIS_ERR;
1068
- }
1069
-
1070
- c->obuf = newbuf;
1071
- return REDIS_OK;
1072
- }
1073
-
1074
- int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {
1075
-
1076
- if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {
1077
- return REDIS_ERR;
1078
- }
1079
-
1080
- return REDIS_OK;
1081
- }
1082
-
1083
- int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1084
- char *cmd;
1085
- int len;
1086
-
1087
- len = redisvFormatCommand(&cmd,format,ap);
1088
- if (len == -1) {
1089
- __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1090
- return REDIS_ERR;
1091
- } else if (len == -2) {
1092
- __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string");
1093
- return REDIS_ERR;
1094
- }
1095
-
1096
- if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1097
- hi_free(cmd);
1098
- return REDIS_ERR;
1099
- }
1100
-
1101
- hi_free(cmd);
1102
- return REDIS_OK;
1103
- }
1104
-
1105
- int redisAppendCommand(redisContext *c, const char *format, ...) {
1106
- va_list ap;
1107
- int ret;
1108
-
1109
- va_start(ap,format);
1110
- ret = redisvAppendCommand(c,format,ap);
1111
- va_end(ap);
1112
- return ret;
1113
- }
1114
-
1115
- int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1116
- sds cmd;
1117
- int len;
1118
-
1119
- len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
1120
- if (len == -1) {
1121
- __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1122
- return REDIS_ERR;
1123
- }
1124
-
1125
- if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1126
- sdsfree(cmd);
1127
- return REDIS_ERR;
1128
- }
1129
-
1130
- sdsfree(cmd);
1131
- return REDIS_OK;
1132
- }
1133
-
1134
- /* Helper function for the redisCommand* family of functions.
1135
- *
1136
- * Write a formatted command to the output buffer. If the given context is
1137
- * blocking, immediately read the reply into the "reply" pointer. When the
1138
- * context is non-blocking, the "reply" pointer will not be used and the
1139
- * command is simply appended to the write buffer.
1140
- *
1141
- * Returns the reply when a reply was successfully retrieved. Returns NULL
1142
- * otherwise. When NULL is returned in a blocking context, the error field
1143
- * in the context will be set.
1144
- */
1145
- static void *__redisBlockForReply(redisContext *c) {
1146
- void *reply;
1147
-
1148
- if (c->flags & REDIS_BLOCK) {
1149
- if (redisGetReply(c,&reply) != REDIS_OK)
1150
- return NULL;
1151
- return reply;
1152
- }
1153
- return NULL;
1154
- }
1155
-
1156
- void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1157
- if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1158
- return NULL;
1159
- return __redisBlockForReply(c);
1160
- }
1161
-
1162
- void *redisCommand(redisContext *c, const char *format, ...) {
1163
- va_list ap;
1164
- va_start(ap,format);
1165
- void *reply = redisvCommand(c,format,ap);
1166
- va_end(ap);
1167
- return reply;
1168
- }
1169
-
1170
- void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1171
- if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1172
- return NULL;
1173
- return __redisBlockForReply(c);
1174
- }