hiredis 0.5.2 → 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.
@@ -58,11 +58,12 @@
58
58
  } while(0);
59
59
 
60
60
  /* Forward declaration of function in hiredis.c */
61
- void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
61
+ int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
62
62
 
63
63
  /* Functions managing dictionary of callbacks for pub/sub. */
64
64
  static unsigned int callbackHash(const void *key) {
65
- return dictGenHashFunction((unsigned char*)key,sdslen((char*)key));
65
+ return dictGenHashFunction((const unsigned char *)key,
66
+ sdslen((const sds)key));
66
67
  }
67
68
 
68
69
  static void *callbackValDup(void *privdata, const void *src) {
@@ -76,8 +77,8 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
76
77
  int l1, l2;
77
78
  ((void) privdata);
78
79
 
79
- l1 = sdslen((sds)key1);
80
- l2 = sdslen((sds)key2);
80
+ l1 = sdslen((const sds)key1);
81
+ l2 = sdslen((const sds)key2);
81
82
  if (l1 != l2) return 0;
82
83
  return memcmp(key1,key2,l1) == 0;
83
84
  }
@@ -102,7 +103,12 @@ static dictType callbackDict = {
102
103
  };
103
104
 
104
105
  static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
105
- redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
106
+ redisAsyncContext *ac;
107
+
108
+ ac = realloc(c,sizeof(redisAsyncContext));
109
+ if (ac == NULL)
110
+ return NULL;
111
+
106
112
  c = &(ac->c);
107
113
 
108
114
  /* The regular connect functions will always set the flag REDIS_CONNECTED.
@@ -136,25 +142,66 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
136
142
  /* We want the error field to be accessible directly instead of requiring
137
143
  * an indirection to the redisContext struct. */
138
144
  static void __redisAsyncCopyError(redisAsyncContext *ac) {
145
+ if (!ac)
146
+ return;
147
+
139
148
  redisContext *c = &(ac->c);
140
149
  ac->err = c->err;
141
150
  ac->errstr = c->errstr;
142
151
  }
143
152
 
144
153
  redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
145
- redisContext *c = redisConnectNonBlock(ip,port);
154
+ redisContext *c;
155
+ redisAsyncContext *ac;
156
+
157
+ c = redisConnectNonBlock(ip,port);
158
+ if (c == NULL)
159
+ return NULL;
160
+
161
+ ac = redisAsyncInitialize(c);
162
+ if (ac == NULL) {
163
+ redisFree(c);
164
+ return NULL;
165
+ }
166
+
167
+ __redisAsyncCopyError(ac);
168
+ return ac;
169
+ }
170
+
171
+ redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
172
+ const char *source_addr) {
173
+ redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
146
174
  redisAsyncContext *ac = redisAsyncInitialize(c);
147
175
  __redisAsyncCopyError(ac);
148
176
  return ac;
149
177
  }
150
178
 
151
- redisAsyncContext *redisAsyncConnectUnix(const char *path) {
152
- redisContext *c = redisConnectUnixNonBlock(path);
179
+ redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
180
+ const char *source_addr) {
181
+ redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr);
153
182
  redisAsyncContext *ac = redisAsyncInitialize(c);
154
183
  __redisAsyncCopyError(ac);
155
184
  return ac;
156
185
  }
157
186
 
187
+ redisAsyncContext *redisAsyncConnectUnix(const char *path) {
188
+ redisContext *c;
189
+ redisAsyncContext *ac;
190
+
191
+ c = redisConnectUnixNonBlock(path);
192
+ if (c == NULL)
193
+ return NULL;
194
+
195
+ ac = redisAsyncInitialize(c);
196
+ if (ac == NULL) {
197
+ redisFree(c);
198
+ return NULL;
199
+ }
200
+
201
+ __redisAsyncCopyError(ac);
202
+ return ac;
203
+ }
204
+
158
205
  int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
159
206
  if (ac->onConnect == NULL) {
160
207
  ac->onConnect = fn;
@@ -182,6 +229,9 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
182
229
 
183
230
  /* Copy callback from stack to heap */
184
231
  cb = malloc(sizeof(*cb));
232
+ if (cb == NULL)
233
+ return REDIS_ERR_OOM;
234
+
185
235
  if (source != NULL) {
186
236
  memcpy(cb,source,sizeof(*cb));
187
237
  cb->next = NULL;
@@ -286,7 +336,8 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
286
336
 
287
337
  if (ac->err == 0) {
288
338
  /* For clean disconnects, there should be no pending callbacks. */
289
- assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
339
+ int ret = __redisShiftCallback(&ac->replies,NULL);
340
+ assert(ret == REDIS_ERR);
290
341
  } else {
291
342
  /* Disconnection is caused by an error, make sure that pending
292
343
  * callbacks cannot call new commands. */
@@ -314,6 +365,7 @@ void redisAsyncDisconnect(redisAsyncContext *ac) {
314
365
  static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
315
366
  redisContext *c = &(ac->c);
316
367
  dict *callbacks;
368
+ redisCallback *cb;
317
369
  dictEntry *de;
318
370
  int pvariant;
319
371
  char *stype;
@@ -337,16 +389,28 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
337
389
  sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
338
390
  de = dictFind(callbacks,sname);
339
391
  if (de != NULL) {
340
- memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));
392
+ cb = dictGetEntryVal(de);
393
+
394
+ /* If this is an subscribe reply decrease pending counter. */
395
+ if (strcasecmp(stype+pvariant,"subscribe") == 0) {
396
+ cb->pending_subs -= 1;
397
+ }
398
+
399
+ memcpy(dstcb,cb,sizeof(*dstcb));
341
400
 
342
401
  /* If this is an unsubscribe message, remove it. */
343
402
  if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
344
- dictDelete(callbacks,sname);
403
+ if (cb->pending_subs == 0)
404
+ dictDelete(callbacks,sname);
345
405
 
346
406
  /* If this was the last unsubscribe message, revert to
347
407
  * non-subscribe mode. */
348
408
  assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
349
- if (reply->element[2]->integer == 0)
409
+
410
+ /* Unset subscribed flag only when no pipelined pending subscribe. */
411
+ if (reply->element[2]->integer == 0
412
+ && dictSize(ac->sub.channels) == 0
413
+ && dictSize(ac->sub.patterns) == 0)
350
414
  c->flags &= ~REDIS_SUBSCRIBED;
351
415
  }
352
416
  }
@@ -360,7 +424,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
360
424
 
361
425
  void redisProcessCallbacks(redisAsyncContext *ac) {
362
426
  redisContext *c = &(ac->c);
363
- redisCallback cb;
427
+ redisCallback cb = {NULL, NULL, 0, NULL};
364
428
  void *reply = NULL;
365
429
  int status;
366
430
 
@@ -368,11 +432,12 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
368
432
  if (reply == NULL) {
369
433
  /* When the connection is being disconnected and there are
370
434
  * no more replies, this is the cue to really disconnect. */
371
- if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {
435
+ if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
436
+ && ac->replies.head == NULL) {
372
437
  __redisAsyncDisconnect(ac);
373
438
  return;
374
439
  }
375
-
440
+
376
441
  /* If monitor mode, repush callback */
377
442
  if(c->flags & REDIS_MONITORING) {
378
443
  __redisPushCallback(&ac->replies,&cb);
@@ -404,6 +469,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
404
469
  if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
405
470
  c->err = REDIS_ERR_OTHER;
406
471
  snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
472
+ c->reader->fn->freeObject(reply);
407
473
  __redisAsyncDisconnect(ac);
408
474
  return;
409
475
  }
@@ -437,12 +503,12 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
437
503
  }
438
504
 
439
505
  /* Internal helper function to detect socket status the first time a read or
440
- * write event fires. When connecting was not succesful, the connect callback
506
+ * write event fires. When connecting was not successful, the connect callback
441
507
  * is called with a REDIS_ERR status and the context is free'd. */
442
508
  static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
443
509
  redisContext *c = &(ac->c);
444
510
 
445
- if (redisCheckSocketError(c,c->fd) == REDIS_ERR) {
511
+ if (redisCheckSocketError(c) == REDIS_ERR) {
446
512
  /* Try again later when connect(2) is still in progress. */
447
513
  if (errno == EINPROGRESS)
448
514
  return REDIS_OK;
@@ -511,8 +577,8 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
511
577
 
512
578
  /* Sets a pointer to the first argument and its length starting at p. Returns
513
579
  * the number of bytes to skip to get to the following argument. */
514
- static char *nextArgument(char *start, char **str, size_t *len) {
515
- char *p = start;
580
+ static const char *nextArgument(const char *start, const char **str, size_t *len) {
581
+ const char *p = start;
516
582
  if (p[0] != '$') {
517
583
  p = strchr(p,'$');
518
584
  if (p == NULL) return NULL;
@@ -528,14 +594,18 @@ static char *nextArgument(char *start, char **str, size_t *len) {
528
594
  /* Helper function for the redisAsyncCommand* family of functions. Writes a
529
595
  * formatted command to the output buffer and registers the provided callback
530
596
  * function with the context. */
531
- static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {
597
+ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
532
598
  redisContext *c = &(ac->c);
533
599
  redisCallback cb;
600
+ struct dict *cbdict;
601
+ dictEntry *de;
602
+ redisCallback *existcb;
534
603
  int pvariant, hasnext;
535
- char *cstr, *astr;
604
+ const char *cstr, *astr;
536
605
  size_t clen, alen;
537
- char *p;
606
+ const char *p;
538
607
  sds sname;
608
+ int ret;
539
609
 
540
610
  /* Don't accept new commands when the connection is about to be closed. */
541
611
  if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
@@ -543,6 +613,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
543
613
  /* Setup callback */
544
614
  cb.fn = fn;
545
615
  cb.privdata = privdata;
616
+ cb.pending_subs = 1;
546
617
 
547
618
  /* Find out which command will be appended. */
548
619
  p = nextArgument(cmd,&cstr,&clen);
@@ -559,9 +630,20 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
559
630
  while ((p = nextArgument(p,&astr,&alen)) != NULL) {
560
631
  sname = sdsnewlen(astr,alen);
561
632
  if (pvariant)
562
- dictReplace(ac->sub.patterns,sname,&cb);
633
+ cbdict = ac->sub.patterns;
563
634
  else
564
- dictReplace(ac->sub.channels,sname,&cb);
635
+ cbdict = ac->sub.channels;
636
+
637
+ de = dictFind(cbdict,sname);
638
+
639
+ if (de != NULL) {
640
+ existcb = dictGetEntryVal(de);
641
+ cb.pending_subs = existcb->pending_subs + 1;
642
+ }
643
+
644
+ ret = dictReplace(cbdict,sname,&cb);
645
+
646
+ if (ret == 0) sdsfree(sname);
565
647
  }
566
648
  } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
567
649
  /* It is only useful to call (P)UNSUBSCRIBE when the context is
@@ -597,6 +679,11 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
597
679
  int len;
598
680
  int status;
599
681
  len = redisvFormatCommand(&cmd,format,ap);
682
+
683
+ /* We don't want to pass -1 or -2 to future functions as a length. */
684
+ if (len < 0)
685
+ return REDIS_ERR;
686
+
600
687
  status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
601
688
  free(cmd);
602
689
  return status;
@@ -612,11 +699,18 @@ int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata
612
699
  }
613
700
 
614
701
  int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
615
- char *cmd;
702
+ sds cmd;
616
703
  int len;
617
704
  int status;
618
- len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
705
+ len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
706
+ if (len < 0)
707
+ return REDIS_ERR;
619
708
  status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
620
- free(cmd);
709
+ sdsfree(cmd);
710
+ return status;
711
+ }
712
+
713
+ int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
714
+ int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
621
715
  return status;
622
716
  }
@@ -45,6 +45,7 @@ typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
45
45
  typedef struct redisCallback {
46
46
  struct redisCallback *next; /* simple singly linked list */
47
47
  redisCallbackFn *fn;
48
+ int pending_subs;
48
49
  void *privdata;
49
50
  } redisCallback;
50
51
 
@@ -102,6 +103,9 @@ typedef struct redisAsyncContext {
102
103
 
103
104
  /* Functions that proxy to hiredis */
104
105
  redisAsyncContext *redisAsyncConnect(const char *ip, int port);
106
+ redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
107
+ redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
108
+ const char *source_addr);
105
109
  redisAsyncContext *redisAsyncConnectUnix(const char *path);
106
110
  int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
107
111
  int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
@@ -117,6 +121,7 @@ void redisAsyncHandleWrite(redisAsyncContext *ac);
117
121
  int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
118
122
  int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
119
123
  int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
124
+ int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
120
125
 
121
126
  #ifdef __cplusplus
122
127
  }
@@ -161,7 +161,7 @@ static int dictReplace(dict *ht, void *key, void *val) {
161
161
  dictEntry *entry, auxentry;
162
162
 
163
163
  /* Try to add the element. If the key
164
- * does not exists dictAdd will suceed. */
164
+ * does not exists dictAdd will succeed. */
165
165
  if (dictAdd(ht, key, val) == DICT_OK)
166
166
  return 1;
167
167
  /* It already exists, get the entry */
@@ -293,7 +293,7 @@ static void dictReleaseIterator(dictIterator *iter) {
293
293
 
294
294
  /* Expand the hash table if needed */
295
295
  static int _dictExpandIfNeeded(dict *ht) {
296
- /* If the hash table is empty expand it to the intial size,
296
+ /* If the hash table is empty expand it to the initial size,
297
297
  * if the table is "full" dobule its size. */
298
298
  if (ht->size == 0)
299
299
  return dictExpand(ht, DICT_HT_INITIAL_SIZE);
@@ -1,16 +1,12 @@
1
1
  #ifndef __HIREDIS_FMACRO_H
2
2
  #define __HIREDIS_FMACRO_H
3
3
 
4
- #if !defined(_BSD_SOURCE)
5
- #define _BSD_SOURCE
6
- #endif
7
-
8
- #if defined(__sun__)
9
- #define _POSIX_C_SOURCE 200112L
10
- #elif defined(__linux__)
11
4
  #define _XOPEN_SOURCE 600
12
- #else
13
- #define _XOPEN_SOURCE
5
+ #define _POSIX_C_SOURCE 200112L
6
+
7
+ #if defined(__APPLE__) && defined(__MACH__)
8
+ /* Enable TCP_KEEPALIVE */
9
+ #define _DARWIN_C_SOURCE
14
10
  #endif
15
11
 
16
12
  #endif