You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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. #include "fmacros.h"
  32. #include <string.h>
  33. #include <strings.h>
  34. #include <assert.h>
  35. #include <ctype.h>
  36. #include "async.h"
  37. #include "dict.c"
  38. #include "sds.h"
  39. /* Forward declaration of function in hiredis.c */
  40. void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
  41. /* Functions managing dictionary of callbacks for pub/sub. */
  42. static unsigned int callbackHash(const void *key) {
  43. return dictGenHashFunction((unsigned char*)key,sdslen((char*)key));
  44. }
  45. static void *callbackValDup(void *privdata, const void *src) {
  46. ((void) privdata);
  47. redisCallback *dup = malloc(sizeof(*dup));
  48. memcpy(dup,src,sizeof(*dup));
  49. return dup;
  50. }
  51. static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
  52. int l1, l2;
  53. ((void) privdata);
  54. l1 = sdslen((sds)key1);
  55. l2 = sdslen((sds)key2);
  56. if (l1 != l2) return 0;
  57. return memcmp(key1,key2,l1) == 0;
  58. }
  59. static void callbackKeyDestructor(void *privdata, void *key) {
  60. ((void) privdata);
  61. sdsfree((sds)key);
  62. }
  63. static void callbackValDestructor(void *privdata, void *val) {
  64. ((void) privdata);
  65. free(val);
  66. }
  67. static dictType callbackDict = {
  68. callbackHash,
  69. NULL,
  70. callbackValDup,
  71. callbackKeyCompare,
  72. callbackKeyDestructor,
  73. callbackValDestructor
  74. };
  75. static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
  76. redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
  77. c = &(ac->c);
  78. /* The regular connect functions will always set the flag REDIS_CONNECTED.
  79. * For the async API, we want to wait until the first write event is
  80. * received up before setting this flag, so reset it here. */
  81. c->flags &= ~REDIS_CONNECTED;
  82. ac->err = 0;
  83. ac->errstr = NULL;
  84. ac->data = NULL;
  85. ac->ev.data = NULL;
  86. ac->ev.addRead = NULL;
  87. ac->ev.delRead = NULL;
  88. ac->ev.addWrite = NULL;
  89. ac->ev.delWrite = NULL;
  90. ac->ev.cleanup = NULL;
  91. ac->onConnect = NULL;
  92. ac->onDisconnect = NULL;
  93. ac->replies.head = NULL;
  94. ac->replies.tail = NULL;
  95. ac->sub.invalid.head = NULL;
  96. ac->sub.invalid.tail = NULL;
  97. ac->sub.channels = dictCreate(&callbackDict,NULL);
  98. ac->sub.patterns = dictCreate(&callbackDict,NULL);
  99. return ac;
  100. }
  101. /* We want the error field to be accessible directly instead of requiring
  102. * an indirection to the redisContext struct. */
  103. static void __redisAsyncCopyError(redisAsyncContext *ac) {
  104. redisContext *c = &(ac->c);
  105. ac->err = c->err;
  106. ac->errstr = c->errstr;
  107. }
  108. redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
  109. redisContext *c = redisConnectNonBlock(ip,port);
  110. redisAsyncContext *ac = redisAsyncInitialize(c);
  111. __redisAsyncCopyError(ac);
  112. return ac;
  113. }
  114. redisAsyncContext *redisAsyncConnectUnix(const char *path) {
  115. redisContext *c = redisConnectUnixNonBlock(path);
  116. redisAsyncContext *ac = redisAsyncInitialize(c);
  117. __redisAsyncCopyError(ac);
  118. return ac;
  119. }
  120. int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
  121. if (ac->onConnect == NULL) {
  122. ac->onConnect = fn;
  123. /* The common way to detect an established connection is to wait for
  124. * the first write event to be fired. This assumes the related event
  125. * library functions are already set. */
  126. if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
  127. return REDIS_OK;
  128. }
  129. return REDIS_ERR;
  130. }
  131. int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
  132. if (ac->onDisconnect == NULL) {
  133. ac->onDisconnect = fn;
  134. return REDIS_OK;
  135. }
  136. return REDIS_ERR;
  137. }
  138. /* Helper functions to push/shift callbacks */
  139. static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
  140. redisCallback *cb;
  141. /* Copy callback from stack to heap */
  142. cb = malloc(sizeof(*cb));
  143. if (source != NULL) {
  144. memcpy(cb,source,sizeof(*cb));
  145. cb->next = NULL;
  146. }
  147. /* Store callback in list */
  148. if (list->head == NULL)
  149. list->head = cb;
  150. if (list->tail != NULL)
  151. list->tail->next = cb;
  152. list->tail = cb;
  153. return REDIS_OK;
  154. }
  155. static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
  156. redisCallback *cb = list->head;
  157. if (cb != NULL) {
  158. list->head = cb->next;
  159. if (cb == list->tail)
  160. list->tail = NULL;
  161. /* Copy callback from heap to stack */
  162. if (target != NULL)
  163. memcpy(target,cb,sizeof(*cb));
  164. free(cb);
  165. return REDIS_OK;
  166. }
  167. return REDIS_ERR;
  168. }
  169. static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
  170. redisContext *c = &(ac->c);
  171. if (cb->fn != NULL) {
  172. c->flags |= REDIS_IN_CALLBACK;
  173. cb->fn(ac,reply,cb->privdata);
  174. c->flags &= ~REDIS_IN_CALLBACK;
  175. }
  176. }
  177. /* Helper function to free the context. */
  178. static void __redisAsyncFree(redisAsyncContext *ac) {
  179. redisContext *c = &(ac->c);
  180. redisCallback cb;
  181. dictIterator *it;
  182. dictEntry *de;
  183. /* Execute pending callbacks with NULL reply. */
  184. while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
  185. __redisRunCallback(ac,&cb,NULL);
  186. /* Execute callbacks for invalid commands */
  187. while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
  188. __redisRunCallback(ac,&cb,NULL);
  189. /* Run subscription callbacks callbacks with NULL reply */
  190. it = dictGetIterator(ac->sub.channels);
  191. while ((de = dictNext(it)) != NULL)
  192. __redisRunCallback(ac,dictGetEntryVal(de),NULL);
  193. dictReleaseIterator(it);
  194. dictRelease(ac->sub.channels);
  195. it = dictGetIterator(ac->sub.patterns);
  196. while ((de = dictNext(it)) != NULL)
  197. __redisRunCallback(ac,dictGetEntryVal(de),NULL);
  198. dictReleaseIterator(it);
  199. dictRelease(ac->sub.patterns);
  200. /* Signal event lib to clean up */
  201. if (ac->ev.cleanup) ac->ev.cleanup(ac->ev.data);
  202. /* Execute disconnect callback. When redisAsyncFree() initiated destroying
  203. * this context, the status will always be REDIS_OK. */
  204. if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
  205. if (c->flags & REDIS_FREEING) {
  206. ac->onDisconnect(ac,REDIS_OK);
  207. } else {
  208. ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
  209. }
  210. }
  211. /* Cleanup self */
  212. redisFree(c);
  213. }
  214. /* Free the async context. When this function is called from a callback,
  215. * control needs to be returned to redisProcessCallbacks() before actual
  216. * free'ing. To do so, a flag is set on the context which is picked up by
  217. * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
  218. void redisAsyncFree(redisAsyncContext *ac) {
  219. redisContext *c = &(ac->c);
  220. c->flags |= REDIS_FREEING;
  221. if (!(c->flags & REDIS_IN_CALLBACK))
  222. __redisAsyncFree(ac);
  223. }
  224. /* Helper function to make the disconnect happen and clean up. */
  225. static void __redisAsyncDisconnect(redisAsyncContext *ac) {
  226. redisContext *c = &(ac->c);
  227. /* Make sure error is accessible if there is any */
  228. __redisAsyncCopyError(ac);
  229. if (ac->err == 0) {
  230. /* For clean disconnects, there should be no pending callbacks. */
  231. assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
  232. } else {
  233. /* Disconnection is caused by an error, make sure that pending
  234. * callbacks cannot call new commands. */
  235. c->flags |= REDIS_DISCONNECTING;
  236. }
  237. /* For non-clean disconnects, __redisAsyncFree() will execute pending
  238. * callbacks with a NULL-reply. */
  239. __redisAsyncFree(ac);
  240. }
  241. /* Tries to do a clean disconnect from Redis, meaning it stops new commands
  242. * from being issued, but tries to flush the output buffer and execute
  243. * callbacks for all remaining replies. When this function is called from a
  244. * callback, there might be more replies and we can safely defer disconnecting
  245. * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
  246. * when there are no pending callbacks. */
  247. void redisAsyncDisconnect(redisAsyncContext *ac) {
  248. redisContext *c = &(ac->c);
  249. c->flags |= REDIS_DISCONNECTING;
  250. if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
  251. __redisAsyncDisconnect(ac);
  252. }
  253. static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
  254. redisContext *c = &(ac->c);
  255. dict *callbacks;
  256. dictEntry *de;
  257. int pvariant;
  258. char *stype;
  259. sds sname;
  260. /* Custom reply functions are not supported for pub/sub. This will fail
  261. * very hard when they are used... */
  262. if (reply->type == REDIS_REPLY_ARRAY) {
  263. assert(reply->elements >= 2);
  264. assert(reply->element[0]->type == REDIS_REPLY_STRING);
  265. stype = reply->element[0]->str;
  266. pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
  267. if (pvariant)
  268. callbacks = ac->sub.patterns;
  269. else
  270. callbacks = ac->sub.channels;
  271. /* Locate the right callback */
  272. assert(reply->element[1]->type == REDIS_REPLY_STRING);
  273. sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
  274. de = dictFind(callbacks,sname);
  275. if (de != NULL) {
  276. memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));
  277. /* If this is an unsubscribe message, remove it. */
  278. if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
  279. dictDelete(callbacks,sname);
  280. /* If this was the last unsubscribe message, revert to
  281. * non-subscribe mode. */
  282. assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
  283. if (reply->element[2]->integer == 0)
  284. c->flags &= ~REDIS_SUBSCRIBED;
  285. }
  286. }
  287. sdsfree(sname);
  288. } else {
  289. /* Shift callback for invalid commands. */
  290. __redisShiftCallback(&ac->sub.invalid,dstcb);
  291. }
  292. return REDIS_OK;
  293. }
  294. void redisProcessCallbacks(redisAsyncContext *ac) {
  295. redisContext *c = &(ac->c);
  296. redisCallback cb;
  297. void *reply = NULL;
  298. int status;
  299. while((status = redisGetReply(c,&reply)) == REDIS_OK) {
  300. if (reply == NULL) {
  301. /* When the connection is being disconnected and there are
  302. * no more replies, this is the cue to really disconnect. */
  303. if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {
  304. __redisAsyncDisconnect(ac);
  305. return;
  306. }
  307. /* When the connection is not being disconnected, simply stop
  308. * trying to get replies and wait for the next loop tick. */
  309. break;
  310. }
  311. /* Even if the context is subscribed, pending regular callbacks will
  312. * get a reply before pub/sub messages arrive. */
  313. if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
  314. /* A spontaneous reply in a not-subscribed context can only be the
  315. * error reply that is sent when a new connection exceeds the
  316. * maximum number of allowed connections on the server side. This
  317. * is seen as an error instead of a regular reply because the
  318. * server closes the connection after sending it. To prevent the
  319. * error from being overwritten by an EOF error the connection is
  320. * closed here. See issue #43. */
  321. if ( !(c->flags & REDIS_SUBSCRIBED) && ((redisReply*)reply)->type == REDIS_REPLY_ERROR ) {
  322. c->err = REDIS_ERR_OTHER;
  323. snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
  324. __redisAsyncDisconnect(ac);
  325. return;
  326. }
  327. /* No more regular callbacks and no errors, the context *must* be subscribed. */
  328. assert(c->flags & REDIS_SUBSCRIBED);
  329. __redisGetSubscribeCallback(ac,reply,&cb);
  330. }
  331. if (cb.fn != NULL) {
  332. __redisRunCallback(ac,&cb,reply);
  333. c->reader->fn->freeObject(reply);
  334. /* Proceed with free'ing when redisAsyncFree() was called. */
  335. if (c->flags & REDIS_FREEING) {
  336. __redisAsyncFree(ac);
  337. return;
  338. }
  339. } else {
  340. /* No callback for this reply. This can either be a NULL callback,
  341. * or there were no callbacks to begin with. Either way, don't
  342. * abort with an error, but simply ignore it because the client
  343. * doesn't know what the server will spit out over the wire. */
  344. c->reader->fn->freeObject(reply);
  345. }
  346. }
  347. /* Disconnect when there was an error reading the reply */
  348. if (status != REDIS_OK)
  349. __redisAsyncDisconnect(ac);
  350. }
  351. /* This function should be called when the socket is readable.
  352. * It processes all replies that can be read and executes their callbacks.
  353. */
  354. void redisAsyncHandleRead(redisAsyncContext *ac) {
  355. redisContext *c = &(ac->c);
  356. if (redisBufferRead(c) == REDIS_ERR) {
  357. __redisAsyncDisconnect(ac);
  358. } else {
  359. /* Always re-schedule reads */
  360. if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
  361. redisProcessCallbacks(ac);
  362. }
  363. }
  364. void redisAsyncHandleWrite(redisAsyncContext *ac) {
  365. redisContext *c = &(ac->c);
  366. int done = 0;
  367. if (redisBufferWrite(c,&done) == REDIS_ERR) {
  368. __redisAsyncDisconnect(ac);
  369. } else {
  370. /* Continue writing when not done, stop writing otherwise */
  371. if (!done) {
  372. if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
  373. } else {
  374. if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data);
  375. }
  376. /* Always schedule reads after writes */
  377. if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
  378. /* Fire onConnect when this is the first write event. */
  379. if (!(c->flags & REDIS_CONNECTED)) {
  380. c->flags |= REDIS_CONNECTED;
  381. if (ac->onConnect) ac->onConnect(ac);
  382. }
  383. }
  384. }
  385. /* Sets a pointer to the first argument and its length starting at p. Returns
  386. * the number of bytes to skip to get to the following argument. */
  387. static char *nextArgument(char *start, char **str, size_t *len) {
  388. char *p = start;
  389. if (p[0] != '$') {
  390. p = strchr(p,'$');
  391. if (p == NULL) return NULL;
  392. }
  393. *len = (int)strtol(p+1,NULL,10);
  394. p = strchr(p,'\r');
  395. assert(p);
  396. *str = p+2;
  397. return p+2+(*len)+2;
  398. }
  399. /* Helper function for the redisAsyncCommand* family of functions. Writes a
  400. * formatted command to the output buffer and registers the provided callback
  401. * function with the context. */
  402. static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {
  403. redisContext *c = &(ac->c);
  404. redisCallback cb;
  405. int pvariant, hasnext;
  406. char *cstr, *astr;
  407. size_t clen, alen;
  408. char *p;
  409. sds sname;
  410. /* Don't accept new commands when the connection is about to be closed. */
  411. if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
  412. /* Setup callback */
  413. cb.fn = fn;
  414. cb.privdata = privdata;
  415. /* Find out which command will be appended. */
  416. p = nextArgument(cmd,&cstr,&clen);
  417. assert(p != NULL);
  418. hasnext = (p[0] == '$');
  419. pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
  420. cstr += pvariant;
  421. clen -= pvariant;
  422. if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
  423. c->flags |= REDIS_SUBSCRIBED;
  424. /* Add every channel/pattern to the list of subscription callbacks. */
  425. while ((p = nextArgument(p,&astr,&alen)) != NULL) {
  426. sname = sdsnewlen(astr,alen);
  427. if (pvariant)
  428. dictReplace(ac->sub.patterns,sname,&cb);
  429. else
  430. dictReplace(ac->sub.channels,sname,&cb);
  431. }
  432. } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
  433. /* It is only useful to call (P)UNSUBSCRIBE when the context is
  434. * subscribed to one or more channels or patterns. */
  435. if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
  436. /* (P)UNSUBSCRIBE does not have its own response: every channel or
  437. * pattern that is unsubscribed will receive a message. This means we
  438. * should not append a callback function for this command. */
  439. } else {
  440. if (c->flags & REDIS_SUBSCRIBED)
  441. /* This will likely result in an error reply, but it needs to be
  442. * received and passed to the callback. */
  443. __redisPushCallback(&ac->sub.invalid,&cb);
  444. else
  445. __redisPushCallback(&ac->replies,&cb);
  446. }
  447. __redisAppendCommand(c,cmd,len);
  448. /* Always schedule a write when the write buffer is non-empty */
  449. if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
  450. return REDIS_OK;
  451. }
  452. int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
  453. char *cmd;
  454. int len;
  455. int status;
  456. len = redisvFormatCommand(&cmd,format,ap);
  457. status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  458. free(cmd);
  459. return status;
  460. }
  461. int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
  462. va_list ap;
  463. int status;
  464. va_start(ap,format);
  465. status = redisvAsyncCommand(ac,fn,privdata,format,ap);
  466. va_end(ap);
  467. return status;
  468. }
  469. int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
  470. char *cmd;
  471. int len;
  472. int status;
  473. len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
  474. status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  475. free(cmd);
  476. return status;
  477. }