hiredis 0.5.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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