hiredis 0.5.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/vendor/hiredis/net.h CHANGED
@@ -1,7 +1,9 @@
1
1
  /* Extracted from anet.c to work properly with Hiredis error reporting.
2
2
  *
3
- * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
- * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
6
+ * Jan-Erik Rediger <janerik at fnordig dot com>
5
7
  *
6
8
  * All rights reserved.
7
9
  *
@@ -35,13 +37,13 @@
35
37
 
36
38
  #include "hiredis.h"
37
39
 
38
- #if defined(__sun)
39
- #define AF_LOCAL AF_UNIX
40
- #endif
41
-
42
- int redisCheckSocketError(redisContext *c, int fd);
43
- int redisContextSetTimeout(redisContext *c, struct timeval tv);
44
- int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
45
- int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
40
+ int redisCheckSocketError(redisContext *c);
41
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv);
42
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
43
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
44
+ const struct timeval *timeout,
45
+ const char *source_addr);
46
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
47
+ int redisKeepAlive(redisContext *c, int interval);
46
48
 
47
49
  #endif
@@ -0,0 +1,598 @@
1
+ /*
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
+ *
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * * Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ * * Redistributions in binary form must reproduce the above copyright
13
+ * notice, this list of conditions and the following disclaimer in the
14
+ * documentation and/or other materials provided with the distribution.
15
+ * * Neither the name of Redis nor the names of its contributors may be used
16
+ * to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ * POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+
32
+
33
+ #include "fmacros.h"
34
+ #include <string.h>
35
+ #include <stdlib.h>
36
+ #ifndef _MSC_VER
37
+ #include <unistd.h>
38
+ #endif
39
+ #include <assert.h>
40
+ #include <errno.h>
41
+ #include <ctype.h>
42
+ #include <limits.h>
43
+
44
+ #include "read.h"
45
+ #include "sds.h"
46
+
47
+ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
48
+ size_t len;
49
+
50
+ if (r->reply != NULL && r->fn && r->fn->freeObject) {
51
+ r->fn->freeObject(r->reply);
52
+ r->reply = NULL;
53
+ }
54
+
55
+ /* Clear input buffer on errors. */
56
+ sdsfree(r->buf);
57
+ r->buf = NULL;
58
+ r->pos = r->len = 0;
59
+
60
+ /* Reset task stack. */
61
+ r->ridx = -1;
62
+
63
+ /* Set error. */
64
+ r->err = type;
65
+ len = strlen(str);
66
+ len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
67
+ memcpy(r->errstr,str,len);
68
+ r->errstr[len] = '\0';
69
+ }
70
+
71
+ static size_t chrtos(char *buf, size_t size, char byte) {
72
+ size_t len = 0;
73
+
74
+ switch(byte) {
75
+ case '\\':
76
+ case '"':
77
+ len = snprintf(buf,size,"\"\\%c\"",byte);
78
+ break;
79
+ case '\n': len = snprintf(buf,size,"\"\\n\""); break;
80
+ case '\r': len = snprintf(buf,size,"\"\\r\""); break;
81
+ case '\t': len = snprintf(buf,size,"\"\\t\""); break;
82
+ case '\a': len = snprintf(buf,size,"\"\\a\""); break;
83
+ case '\b': len = snprintf(buf,size,"\"\\b\""); break;
84
+ default:
85
+ if (isprint(byte))
86
+ len = snprintf(buf,size,"\"%c\"",byte);
87
+ else
88
+ len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
89
+ break;
90
+ }
91
+
92
+ return len;
93
+ }
94
+
95
+ static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
96
+ char cbuf[8], sbuf[128];
97
+
98
+ chrtos(cbuf,sizeof(cbuf),byte);
99
+ snprintf(sbuf,sizeof(sbuf),
100
+ "Protocol error, got %s as reply type byte", cbuf);
101
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
102
+ }
103
+
104
+ static void __redisReaderSetErrorOOM(redisReader *r) {
105
+ __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
106
+ }
107
+
108
+ static char *readBytes(redisReader *r, unsigned int bytes) {
109
+ char *p;
110
+ if (r->len-r->pos >= bytes) {
111
+ p = r->buf+r->pos;
112
+ r->pos += bytes;
113
+ return p;
114
+ }
115
+ return NULL;
116
+ }
117
+
118
+ /* Find pointer to \r\n. */
119
+ static char *seekNewline(char *s, size_t len) {
120
+ int pos = 0;
121
+ int _len = len-1;
122
+
123
+ /* Position should be < len-1 because the character at "pos" should be
124
+ * followed by a \n. Note that strchr cannot be used because it doesn't
125
+ * allow to search a limited length and the buffer that is being searched
126
+ * might not have a trailing NULL character. */
127
+ while (pos < _len) {
128
+ while(pos < _len && s[pos] != '\r') pos++;
129
+ if (pos==_len) {
130
+ /* Not found. */
131
+ return NULL;
132
+ } else {
133
+ if (s[pos+1] == '\n') {
134
+ /* Found. */
135
+ return s+pos;
136
+ } else {
137
+ /* Continue searching. */
138
+ pos++;
139
+ }
140
+ }
141
+ }
142
+ return NULL;
143
+ }
144
+
145
+ /* Convert a string into a long long. Returns REDIS_OK if the string could be
146
+ * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
147
+ * will be set to the parsed value when appropriate.
148
+ *
149
+ * Note that this function demands that the string strictly represents
150
+ * a long long: no spaces or other characters before or after the string
151
+ * representing the number are accepted, nor zeroes at the start if not
152
+ * for the string "0" representing the zero number.
153
+ *
154
+ * Because of its strictness, it is safe to use this function to check if
155
+ * you can convert a string into a long long, and obtain back the string
156
+ * from the number without any loss in the string representation. */
157
+ static int string2ll(const char *s, size_t slen, long long *value) {
158
+ const char *p = s;
159
+ size_t plen = 0;
160
+ int negative = 0;
161
+ unsigned long long v;
162
+
163
+ if (plen == slen)
164
+ return REDIS_ERR;
165
+
166
+ /* Special case: first and only digit is 0. */
167
+ if (slen == 1 && p[0] == '0') {
168
+ if (value != NULL) *value = 0;
169
+ return REDIS_OK;
170
+ }
171
+
172
+ if (p[0] == '-') {
173
+ negative = 1;
174
+ p++; plen++;
175
+
176
+ /* Abort on only a negative sign. */
177
+ if (plen == slen)
178
+ return REDIS_ERR;
179
+ }
180
+
181
+ /* First digit should be 1-9, otherwise the string should just be 0. */
182
+ if (p[0] >= '1' && p[0] <= '9') {
183
+ v = p[0]-'0';
184
+ p++; plen++;
185
+ } else if (p[0] == '0' && slen == 1) {
186
+ *value = 0;
187
+ return REDIS_OK;
188
+ } else {
189
+ return REDIS_ERR;
190
+ }
191
+
192
+ while (plen < slen && p[0] >= '0' && p[0] <= '9') {
193
+ if (v > (ULLONG_MAX / 10)) /* Overflow. */
194
+ return REDIS_ERR;
195
+ v *= 10;
196
+
197
+ if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
198
+ return REDIS_ERR;
199
+ v += p[0]-'0';
200
+
201
+ p++; plen++;
202
+ }
203
+
204
+ /* Return if not all bytes were used. */
205
+ if (plen < slen)
206
+ return REDIS_ERR;
207
+
208
+ if (negative) {
209
+ if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
210
+ return REDIS_ERR;
211
+ if (value != NULL) *value = -v;
212
+ } else {
213
+ if (v > LLONG_MAX) /* Overflow. */
214
+ return REDIS_ERR;
215
+ if (value != NULL) *value = v;
216
+ }
217
+ return REDIS_OK;
218
+ }
219
+
220
+ static char *readLine(redisReader *r, int *_len) {
221
+ char *p, *s;
222
+ int len;
223
+
224
+ p = r->buf+r->pos;
225
+ s = seekNewline(p,(r->len-r->pos));
226
+ if (s != NULL) {
227
+ len = s-(r->buf+r->pos);
228
+ r->pos += len+2; /* skip \r\n */
229
+ if (_len) *_len = len;
230
+ return p;
231
+ }
232
+ return NULL;
233
+ }
234
+
235
+ static void moveToNextTask(redisReader *r) {
236
+ redisReadTask *cur, *prv;
237
+ while (r->ridx >= 0) {
238
+ /* Return a.s.a.p. when the stack is now empty. */
239
+ if (r->ridx == 0) {
240
+ r->ridx--;
241
+ return;
242
+ }
243
+
244
+ cur = &(r->rstack[r->ridx]);
245
+ prv = &(r->rstack[r->ridx-1]);
246
+ assert(prv->type == REDIS_REPLY_ARRAY);
247
+ if (cur->idx == prv->elements-1) {
248
+ r->ridx--;
249
+ } else {
250
+ /* Reset the type because the next item can be anything */
251
+ assert(cur->idx < prv->elements);
252
+ cur->type = -1;
253
+ cur->elements = -1;
254
+ cur->idx++;
255
+ return;
256
+ }
257
+ }
258
+ }
259
+
260
+ static int processLineItem(redisReader *r) {
261
+ redisReadTask *cur = &(r->rstack[r->ridx]);
262
+ void *obj;
263
+ char *p;
264
+ int len;
265
+
266
+ if ((p = readLine(r,&len)) != NULL) {
267
+ if (cur->type == REDIS_REPLY_INTEGER) {
268
+ if (r->fn && r->fn->createInteger) {
269
+ long long v;
270
+ if (string2ll(p, len, &v) == REDIS_ERR) {
271
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
272
+ "Bad integer value");
273
+ return REDIS_ERR;
274
+ }
275
+ obj = r->fn->createInteger(cur,v);
276
+ } else {
277
+ obj = (void*)REDIS_REPLY_INTEGER;
278
+ }
279
+ } else {
280
+ /* Type will be error or status. */
281
+ if (r->fn && r->fn->createString)
282
+ obj = r->fn->createString(cur,p,len);
283
+ else
284
+ obj = (void*)(size_t)(cur->type);
285
+ }
286
+
287
+ if (obj == NULL) {
288
+ __redisReaderSetErrorOOM(r);
289
+ return REDIS_ERR;
290
+ }
291
+
292
+ /* Set reply if this is the root object. */
293
+ if (r->ridx == 0) r->reply = obj;
294
+ moveToNextTask(r);
295
+ return REDIS_OK;
296
+ }
297
+
298
+ return REDIS_ERR;
299
+ }
300
+
301
+ static int processBulkItem(redisReader *r) {
302
+ redisReadTask *cur = &(r->rstack[r->ridx]);
303
+ void *obj = NULL;
304
+ char *p, *s;
305
+ long long len;
306
+ unsigned long bytelen;
307
+ int success = 0;
308
+
309
+ p = r->buf+r->pos;
310
+ s = seekNewline(p,r->len-r->pos);
311
+ if (s != NULL) {
312
+ p = r->buf+r->pos;
313
+ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
314
+
315
+ if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
316
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
317
+ "Bad bulk string length");
318
+ return REDIS_ERR;
319
+ }
320
+
321
+ if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
322
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
323
+ "Bulk string length out of range");
324
+ return REDIS_ERR;
325
+ }
326
+
327
+ if (len == -1) {
328
+ /* The nil object can always be created. */
329
+ if (r->fn && r->fn->createNil)
330
+ obj = r->fn->createNil(cur);
331
+ else
332
+ obj = (void*)REDIS_REPLY_NIL;
333
+ success = 1;
334
+ } else {
335
+ /* Only continue when the buffer contains the entire bulk item. */
336
+ bytelen += len+2; /* include \r\n */
337
+ if (r->pos+bytelen <= r->len) {
338
+ if (r->fn && r->fn->createString)
339
+ obj = r->fn->createString(cur,s+2,len);
340
+ else
341
+ obj = (void*)REDIS_REPLY_STRING;
342
+ success = 1;
343
+ }
344
+ }
345
+
346
+ /* Proceed when obj was created. */
347
+ if (success) {
348
+ if (obj == NULL) {
349
+ __redisReaderSetErrorOOM(r);
350
+ return REDIS_ERR;
351
+ }
352
+
353
+ r->pos += bytelen;
354
+
355
+ /* Set reply if this is the root object. */
356
+ if (r->ridx == 0) r->reply = obj;
357
+ moveToNextTask(r);
358
+ return REDIS_OK;
359
+ }
360
+ }
361
+
362
+ return REDIS_ERR;
363
+ }
364
+
365
+ static int processMultiBulkItem(redisReader *r) {
366
+ redisReadTask *cur = &(r->rstack[r->ridx]);
367
+ void *obj;
368
+ char *p;
369
+ long long elements;
370
+ int root = 0, len;
371
+
372
+ /* Set error for nested multi bulks with depth > 7 */
373
+ if (r->ridx == 8) {
374
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
375
+ "No support for nested multi bulk replies with depth > 7");
376
+ return REDIS_ERR;
377
+ }
378
+
379
+ if ((p = readLine(r,&len)) != NULL) {
380
+ if (string2ll(p, len, &elements) == REDIS_ERR) {
381
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
382
+ "Bad multi-bulk length");
383
+ return REDIS_ERR;
384
+ }
385
+
386
+ root = (r->ridx == 0);
387
+
388
+ if (elements < -1 || elements > INT_MAX) {
389
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
390
+ "Multi-bulk length out of range");
391
+ return REDIS_ERR;
392
+ }
393
+
394
+ if (elements == -1) {
395
+ if (r->fn && r->fn->createNil)
396
+ obj = r->fn->createNil(cur);
397
+ else
398
+ obj = (void*)REDIS_REPLY_NIL;
399
+
400
+ if (obj == NULL) {
401
+ __redisReaderSetErrorOOM(r);
402
+ return REDIS_ERR;
403
+ }
404
+
405
+ moveToNextTask(r);
406
+ } else {
407
+ if (r->fn && r->fn->createArray)
408
+ obj = r->fn->createArray(cur,elements);
409
+ else
410
+ obj = (void*)REDIS_REPLY_ARRAY;
411
+
412
+ if (obj == NULL) {
413
+ __redisReaderSetErrorOOM(r);
414
+ return REDIS_ERR;
415
+ }
416
+
417
+ /* Modify task stack when there are more than 0 elements. */
418
+ if (elements > 0) {
419
+ cur->elements = elements;
420
+ cur->obj = obj;
421
+ r->ridx++;
422
+ r->rstack[r->ridx].type = -1;
423
+ r->rstack[r->ridx].elements = -1;
424
+ r->rstack[r->ridx].idx = 0;
425
+ r->rstack[r->ridx].obj = NULL;
426
+ r->rstack[r->ridx].parent = cur;
427
+ r->rstack[r->ridx].privdata = r->privdata;
428
+ } else {
429
+ moveToNextTask(r);
430
+ }
431
+ }
432
+
433
+ /* Set reply if this is the root object. */
434
+ if (root) r->reply = obj;
435
+ return REDIS_OK;
436
+ }
437
+
438
+ return REDIS_ERR;
439
+ }
440
+
441
+ static int processItem(redisReader *r) {
442
+ redisReadTask *cur = &(r->rstack[r->ridx]);
443
+ char *p;
444
+
445
+ /* check if we need to read type */
446
+ if (cur->type < 0) {
447
+ if ((p = readBytes(r,1)) != NULL) {
448
+ switch (p[0]) {
449
+ case '-':
450
+ cur->type = REDIS_REPLY_ERROR;
451
+ break;
452
+ case '+':
453
+ cur->type = REDIS_REPLY_STATUS;
454
+ break;
455
+ case ':':
456
+ cur->type = REDIS_REPLY_INTEGER;
457
+ break;
458
+ case '$':
459
+ cur->type = REDIS_REPLY_STRING;
460
+ break;
461
+ case '*':
462
+ cur->type = REDIS_REPLY_ARRAY;
463
+ break;
464
+ default:
465
+ __redisReaderSetErrorProtocolByte(r,*p);
466
+ return REDIS_ERR;
467
+ }
468
+ } else {
469
+ /* could not consume 1 byte */
470
+ return REDIS_ERR;
471
+ }
472
+ }
473
+
474
+ /* process typed item */
475
+ switch(cur->type) {
476
+ case REDIS_REPLY_ERROR:
477
+ case REDIS_REPLY_STATUS:
478
+ case REDIS_REPLY_INTEGER:
479
+ return processLineItem(r);
480
+ case REDIS_REPLY_STRING:
481
+ return processBulkItem(r);
482
+ case REDIS_REPLY_ARRAY:
483
+ return processMultiBulkItem(r);
484
+ default:
485
+ assert(NULL);
486
+ return REDIS_ERR; /* Avoid warning. */
487
+ }
488
+ }
489
+
490
+ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
491
+ redisReader *r;
492
+
493
+ r = calloc(1,sizeof(redisReader));
494
+ if (r == NULL)
495
+ return NULL;
496
+
497
+ r->fn = fn;
498
+ r->buf = sdsempty();
499
+ r->maxbuf = REDIS_READER_MAX_BUF;
500
+ if (r->buf == NULL) {
501
+ free(r);
502
+ return NULL;
503
+ }
504
+
505
+ r->ridx = -1;
506
+ return r;
507
+ }
508
+
509
+ void redisReaderFree(redisReader *r) {
510
+ if (r == NULL)
511
+ return;
512
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
513
+ r->fn->freeObject(r->reply);
514
+ sdsfree(r->buf);
515
+ free(r);
516
+ }
517
+
518
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
519
+ sds newbuf;
520
+
521
+ /* Return early when this reader is in an erroneous state. */
522
+ if (r->err)
523
+ return REDIS_ERR;
524
+
525
+ /* Copy the provided buffer. */
526
+ if (buf != NULL && len >= 1) {
527
+ /* Destroy internal buffer when it is empty and is quite large. */
528
+ if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
529
+ sdsfree(r->buf);
530
+ r->buf = sdsempty();
531
+ r->pos = 0;
532
+
533
+ /* r->buf should not be NULL since we just free'd a larger one. */
534
+ assert(r->buf != NULL);
535
+ }
536
+
537
+ newbuf = sdscatlen(r->buf,buf,len);
538
+ if (newbuf == NULL) {
539
+ __redisReaderSetErrorOOM(r);
540
+ return REDIS_ERR;
541
+ }
542
+
543
+ r->buf = newbuf;
544
+ r->len = sdslen(r->buf);
545
+ }
546
+
547
+ return REDIS_OK;
548
+ }
549
+
550
+ int redisReaderGetReply(redisReader *r, void **reply) {
551
+ /* Default target pointer to NULL. */
552
+ if (reply != NULL)
553
+ *reply = NULL;
554
+
555
+ /* Return early when this reader is in an erroneous state. */
556
+ if (r->err)
557
+ return REDIS_ERR;
558
+
559
+ /* When the buffer is empty, there will never be a reply. */
560
+ if (r->len == 0)
561
+ return REDIS_OK;
562
+
563
+ /* Set first item to process when the stack is empty. */
564
+ if (r->ridx == -1) {
565
+ r->rstack[0].type = -1;
566
+ r->rstack[0].elements = -1;
567
+ r->rstack[0].idx = -1;
568
+ r->rstack[0].obj = NULL;
569
+ r->rstack[0].parent = NULL;
570
+ r->rstack[0].privdata = r->privdata;
571
+ r->ridx = 0;
572
+ }
573
+
574
+ /* Process items in reply. */
575
+ while (r->ridx >= 0)
576
+ if (processItem(r) != REDIS_OK)
577
+ break;
578
+
579
+ /* Return ASAP when an error occurred. */
580
+ if (r->err)
581
+ return REDIS_ERR;
582
+
583
+ /* Discard part of the buffer when we've consumed at least 1k, to avoid
584
+ * doing unnecessary calls to memmove() in sds.c. */
585
+ if (r->pos >= 1024) {
586
+ sdsrange(r->buf,r->pos,-1);
587
+ r->pos = 0;
588
+ r->len = sdslen(r->buf);
589
+ }
590
+
591
+ /* Emit a reply when there is one. */
592
+ if (r->ridx == -1) {
593
+ if (reply != NULL)
594
+ *reply = r->reply;
595
+ r->reply = NULL;
596
+ }
597
+ return REDIS_OK;
598
+ }