async.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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 "alloc.h"
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #ifndef _MSC_VER
  36. #include <strings.h>
  37. #endif
  38. #include <assert.h>
  39. #include <ctype.h>
  40. #include <errno.h>
  41. #include "async.h"
  42. #include "net.h"
  43. #include "dict.c"
  44. #include "sds.h"
  45. #include "win32.h"
  46. #include "async_private.h"
  47. /* Forward declarations of hiredis.c functions */
  48. int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
  49. void __redisSetError(redisContext *c, int type, const char *str);
  50. /* Functions managing dictionary of callbacks for pub/sub. */
  51. static unsigned int callbackHash(const void *key) {
  52. return dictGenHashFunction((const unsigned char *)key,
  53. sdslen((const sds)key));
  54. }
  55. static void *callbackValDup(void *privdata, const void *src) {
  56. ((void) privdata);
  57. redisCallback *dup;
  58. dup = hi_malloc(sizeof(*dup));
  59. if (dup == NULL)
  60. return NULL;
  61. memcpy(dup,src,sizeof(*dup));
  62. return dup;
  63. }
  64. static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
  65. int l1, l2;
  66. ((void) privdata);
  67. l1 = sdslen((const sds)key1);
  68. l2 = sdslen((const sds)key2);
  69. if (l1 != l2) return 0;
  70. return memcmp(key1,key2,l1) == 0;
  71. }
  72. static void callbackKeyDestructor(void *privdata, void *key) {
  73. ((void) privdata);
  74. sdsfree((sds)key);
  75. }
  76. static void callbackValDestructor(void *privdata, void *val) {
  77. ((void) privdata);
  78. hi_free(val);
  79. }
  80. static dictType callbackDict = {
  81. callbackHash,
  82. NULL,
  83. callbackValDup,
  84. callbackKeyCompare,
  85. callbackKeyDestructor,
  86. callbackValDestructor
  87. };
  88. static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
  89. redisAsyncContext *ac;
  90. dict *channels = NULL, *patterns = NULL;
  91. channels = dictCreate(&callbackDict,NULL);
  92. if (channels == NULL)
  93. goto oom;
  94. patterns = dictCreate(&callbackDict,NULL);
  95. if (patterns == NULL)
  96. goto oom;
  97. ac = hi_realloc(c,sizeof(redisAsyncContext));
  98. if (ac == NULL)
  99. goto oom;
  100. c = &(ac->c);
  101. /* The regular connect functions will always set the flag REDIS_CONNECTED.
  102. * For the async API, we want to wait until the first write event is
  103. * received up before setting this flag, so reset it here. */
  104. c->flags &= ~REDIS_CONNECTED;
  105. ac->err = 0;
  106. ac->errstr = NULL;
  107. ac->data = NULL;
  108. ac->dataCleanup = NULL;
  109. ac->ev.data = NULL;
  110. ac->ev.addRead = NULL;
  111. ac->ev.delRead = NULL;
  112. ac->ev.addWrite = NULL;
  113. ac->ev.delWrite = NULL;
  114. ac->ev.cleanup = NULL;
  115. ac->ev.scheduleTimer = NULL;
  116. ac->onConnect = NULL;
  117. ac->onDisconnect = NULL;
  118. ac->replies.head = NULL;
  119. ac->replies.tail = NULL;
  120. ac->sub.invalid.head = NULL;
  121. ac->sub.invalid.tail = NULL;
  122. ac->sub.channels = channels;
  123. ac->sub.patterns = patterns;
  124. return ac;
  125. oom:
  126. if (channels) dictRelease(channels);
  127. if (patterns) dictRelease(patterns);
  128. return NULL;
  129. }
  130. /* We want the error field to be accessible directly instead of requiring
  131. * an indirection to the redisContext struct. */
  132. static void __redisAsyncCopyError(redisAsyncContext *ac) {
  133. if (!ac)
  134. return;
  135. redisContext *c = &(ac->c);
  136. ac->err = c->err;
  137. ac->errstr = c->errstr;
  138. }
  139. redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
  140. redisOptions myOptions = *options;
  141. redisContext *c;
  142. redisAsyncContext *ac;
  143. /* Clear any erroneously set sync callback and flag that we don't want to
  144. * use freeReplyObject by default. */
  145. myOptions.push_cb = NULL;
  146. myOptions.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
  147. myOptions.options |= REDIS_OPT_NONBLOCK;
  148. c = redisConnectWithOptions(&myOptions);
  149. if (c == NULL) {
  150. return NULL;
  151. }
  152. ac = redisAsyncInitialize(c);
  153. if (ac == NULL) {
  154. redisFree(c);
  155. return NULL;
  156. }
  157. /* Set any configured async push handler */
  158. redisAsyncSetPushCallback(ac, myOptions.async_push_cb);
  159. __redisAsyncCopyError(ac);
  160. return ac;
  161. }
  162. redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
  163. redisOptions options = {0};
  164. REDIS_OPTIONS_SET_TCP(&options, ip, port);
  165. return redisAsyncConnectWithOptions(&options);
  166. }
  167. redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
  168. const char *source_addr) {
  169. redisOptions options = {0};
  170. REDIS_OPTIONS_SET_TCP(&options, ip, port);
  171. options.endpoint.tcp.source_addr = source_addr;
  172. return redisAsyncConnectWithOptions(&options);
  173. }
  174. redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
  175. const char *source_addr) {
  176. redisOptions options = {0};
  177. REDIS_OPTIONS_SET_TCP(&options, ip, port);
  178. options.options |= REDIS_OPT_REUSEADDR;
  179. options.endpoint.tcp.source_addr = source_addr;
  180. return redisAsyncConnectWithOptions(&options);
  181. }
  182. redisAsyncContext *redisAsyncConnectUnix(const char *path) {
  183. redisOptions options = {0};
  184. REDIS_OPTIONS_SET_UNIX(&options, path);
  185. return redisAsyncConnectWithOptions(&options);
  186. }
  187. int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
  188. if (ac->onConnect == NULL) {
  189. ac->onConnect = fn;
  190. /* The common way to detect an established connection is to wait for
  191. * the first write event to be fired. This assumes the related event
  192. * library functions are already set. */
  193. _EL_ADD_WRITE(ac);
  194. return REDIS_OK;
  195. }
  196. return REDIS_ERR;
  197. }
  198. int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
  199. if (ac->onDisconnect == NULL) {
  200. ac->onDisconnect = fn;
  201. return REDIS_OK;
  202. }
  203. return REDIS_ERR;
  204. }
  205. /* Helper functions to push/shift callbacks */
  206. static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
  207. redisCallback *cb;
  208. /* Copy callback from stack to heap */
  209. cb = hi_malloc(sizeof(*cb));
  210. if (cb == NULL)
  211. return REDIS_ERR_OOM;
  212. if (source != NULL) {
  213. memcpy(cb,source,sizeof(*cb));
  214. cb->next = NULL;
  215. }
  216. /* Store callback in list */
  217. if (list->head == NULL)
  218. list->head = cb;
  219. if (list->tail != NULL)
  220. list->tail->next = cb;
  221. list->tail = cb;
  222. return REDIS_OK;
  223. }
  224. static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
  225. redisCallback *cb = list->head;
  226. if (cb != NULL) {
  227. list->head = cb->next;
  228. if (cb == list->tail)
  229. list->tail = NULL;
  230. /* Copy callback from heap to stack */
  231. if (target != NULL)
  232. memcpy(target,cb,sizeof(*cb));
  233. hi_free(cb);
  234. return REDIS_OK;
  235. }
  236. return REDIS_ERR;
  237. }
  238. static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
  239. redisContext *c = &(ac->c);
  240. if (cb->fn != NULL) {
  241. c->flags |= REDIS_IN_CALLBACK;
  242. cb->fn(ac,reply,cb->privdata);
  243. c->flags &= ~REDIS_IN_CALLBACK;
  244. }
  245. }
  246. static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
  247. if (ac->push_cb != NULL) {
  248. ac->c.flags |= REDIS_IN_CALLBACK;
  249. ac->push_cb(ac, reply);
  250. ac->c.flags &= ~REDIS_IN_CALLBACK;
  251. }
  252. }
  253. /* Helper function to free the context. */
  254. static void __redisAsyncFree(redisAsyncContext *ac) {
  255. redisContext *c = &(ac->c);
  256. redisCallback cb;
  257. dictIterator *it;
  258. dictEntry *de;
  259. /* Execute pending callbacks with NULL reply. */
  260. while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
  261. __redisRunCallback(ac,&cb,NULL);
  262. /* Execute callbacks for invalid commands */
  263. while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
  264. __redisRunCallback(ac,&cb,NULL);
  265. /* Run subscription callbacks with NULL reply */
  266. if (ac->sub.channels) {
  267. it = dictGetIterator(ac->sub.channels);
  268. if (it != NULL) {
  269. while ((de = dictNext(it)) != NULL)
  270. __redisRunCallback(ac,dictGetEntryVal(de),NULL);
  271. dictReleaseIterator(it);
  272. }
  273. dictRelease(ac->sub.channels);
  274. }
  275. if (ac->sub.patterns) {
  276. it = dictGetIterator(ac->sub.patterns);
  277. if (it != NULL) {
  278. while ((de = dictNext(it)) != NULL)
  279. __redisRunCallback(ac,dictGetEntryVal(de),NULL);
  280. dictReleaseIterator(it);
  281. }
  282. dictRelease(ac->sub.patterns);
  283. }
  284. /* Signal event lib to clean up */
  285. _EL_CLEANUP(ac);
  286. /* Execute disconnect callback. When redisAsyncFree() initiated destroying
  287. * this context, the status will always be REDIS_OK. */
  288. if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
  289. if (c->flags & REDIS_FREEING) {
  290. ac->onDisconnect(ac,REDIS_OK);
  291. } else {
  292. ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
  293. }
  294. }
  295. if (ac->dataCleanup) {
  296. ac->dataCleanup(ac->data);
  297. }
  298. /* Cleanup self */
  299. redisFree(c);
  300. }
  301. /* Free the async context. When this function is called from a callback,
  302. * control needs to be returned to redisProcessCallbacks() before actual
  303. * free'ing. To do so, a flag is set on the context which is picked up by
  304. * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
  305. void redisAsyncFree(redisAsyncContext *ac) {
  306. redisContext *c = &(ac->c);
  307. c->flags |= REDIS_FREEING;
  308. if (!(c->flags & REDIS_IN_CALLBACK))
  309. __redisAsyncFree(ac);
  310. }
  311. /* Helper function to make the disconnect happen and clean up. */
  312. void __redisAsyncDisconnect(redisAsyncContext *ac) {
  313. redisContext *c = &(ac->c);
  314. /* Make sure error is accessible if there is any */
  315. __redisAsyncCopyError(ac);
  316. if (ac->err == 0) {
  317. /* For clean disconnects, there should be no pending callbacks. */
  318. int ret = __redisShiftCallback(&ac->replies,NULL);
  319. assert(ret == REDIS_ERR);
  320. } else {
  321. /* Disconnection is caused by an error, make sure that pending
  322. * callbacks cannot call new commands. */
  323. c->flags |= REDIS_DISCONNECTING;
  324. }
  325. /* cleanup event library on disconnect.
  326. * this is safe to call multiple times */
  327. _EL_CLEANUP(ac);
  328. /* For non-clean disconnects, __redisAsyncFree() will execute pending
  329. * callbacks with a NULL-reply. */
  330. if (!(c->flags & REDIS_NO_AUTO_FREE)) {
  331. __redisAsyncFree(ac);
  332. }
  333. }
  334. /* Tries to do a clean disconnect from Redis, meaning it stops new commands
  335. * from being issued, but tries to flush the output buffer and execute
  336. * callbacks for all remaining replies. When this function is called from a
  337. * callback, there might be more replies and we can safely defer disconnecting
  338. * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
  339. * when there are no pending callbacks. */
  340. void redisAsyncDisconnect(redisAsyncContext *ac) {
  341. redisContext *c = &(ac->c);
  342. c->flags |= REDIS_DISCONNECTING;
  343. /** unset the auto-free flag here, because disconnect undoes this */
  344. c->flags &= ~REDIS_NO_AUTO_FREE;
  345. if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
  346. __redisAsyncDisconnect(ac);
  347. }
  348. static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
  349. redisContext *c = &(ac->c);
  350. dict *callbacks;
  351. redisCallback *cb;
  352. dictEntry *de;
  353. int pvariant;
  354. char *stype;
  355. sds sname;
  356. /* Custom reply functions are not supported for pub/sub. This will fail
  357. * very hard when they are used... */
  358. if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) {
  359. assert(reply->elements >= 2);
  360. assert(reply->element[0]->type == REDIS_REPLY_STRING);
  361. stype = reply->element[0]->str;
  362. pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
  363. if (pvariant)
  364. callbacks = ac->sub.patterns;
  365. else
  366. callbacks = ac->sub.channels;
  367. /* Locate the right callback */
  368. assert(reply->element[1]->type == REDIS_REPLY_STRING);
  369. sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
  370. if (sname == NULL)
  371. goto oom;
  372. de = dictFind(callbacks,sname);
  373. if (de != NULL) {
  374. cb = dictGetEntryVal(de);
  375. /* If this is an subscribe reply decrease pending counter. */
  376. if (strcasecmp(stype+pvariant,"subscribe") == 0) {
  377. cb->pending_subs -= 1;
  378. }
  379. memcpy(dstcb,cb,sizeof(*dstcb));
  380. /* If this is an unsubscribe message, remove it. */
  381. if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
  382. if (cb->pending_subs == 0)
  383. dictDelete(callbacks,sname);
  384. /* If this was the last unsubscribe message, revert to
  385. * non-subscribe mode. */
  386. assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
  387. /* Unset subscribed flag only when no pipelined pending subscribe. */
  388. if (reply->element[2]->integer == 0
  389. && dictSize(ac->sub.channels) == 0
  390. && dictSize(ac->sub.patterns) == 0)
  391. c->flags &= ~REDIS_SUBSCRIBED;
  392. }
  393. }
  394. sdsfree(sname);
  395. } else {
  396. /* Shift callback for invalid commands. */
  397. __redisShiftCallback(&ac->sub.invalid,dstcb);
  398. }
  399. return REDIS_OK;
  400. oom:
  401. __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
  402. return REDIS_ERR;
  403. }
  404. #define redisIsSpontaneousPushReply(r) \
  405. (redisIsPushReply(r) && !redisIsSubscribeReply(r))
  406. static int redisIsSubscribeReply(redisReply *reply) {
  407. char *str;
  408. size_t len, off;
  409. /* We will always have at least one string with the subscribe/message type */
  410. if (reply->elements < 1 || reply->element[0]->type != REDIS_REPLY_STRING ||
  411. reply->element[0]->len < sizeof("message") - 1)
  412. {
  413. return 0;
  414. }
  415. /* Get the string/len moving past 'p' if needed */
  416. off = tolower(reply->element[0]->str[0]) == 'p';
  417. str = reply->element[0]->str + off;
  418. len = reply->element[0]->len - off;
  419. return !strncasecmp(str, "subscribe", len) ||
  420. !strncasecmp(str, "message", len);
  421. }
  422. void redisProcessCallbacks(redisAsyncContext *ac) {
  423. redisContext *c = &(ac->c);
  424. redisCallback cb = {NULL, NULL, 0, NULL};
  425. void *reply = NULL;
  426. int status;
  427. while((status = redisGetReply(c,&reply)) == REDIS_OK) {
  428. if (reply == NULL) {
  429. /* When the connection is being disconnected and there are
  430. * no more replies, this is the cue to really disconnect. */
  431. if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
  432. && ac->replies.head == NULL) {
  433. __redisAsyncDisconnect(ac);
  434. return;
  435. }
  436. /* If monitor mode, repush callback */
  437. if(c->flags & REDIS_MONITORING) {
  438. __redisPushCallback(&ac->replies,&cb);
  439. }
  440. /* When the connection is not being disconnected, simply stop
  441. * trying to get replies and wait for the next loop tick. */
  442. break;
  443. }
  444. /* Send any non-subscribe related PUSH messages to our PUSH handler
  445. * while allowing subscribe related PUSH messages to pass through.
  446. * This allows existing code to be backward compatible and work in
  447. * either RESP2 or RESP3 mode. */
  448. if (redisIsSpontaneousPushReply(reply)) {
  449. __redisRunPushCallback(ac, reply);
  450. c->reader->fn->freeObject(reply);
  451. continue;
  452. }
  453. /* Even if the context is subscribed, pending regular
  454. * callbacks will get a reply before pub/sub messages arrive. */
  455. if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
  456. /*
  457. * A spontaneous reply in a not-subscribed context can be the error
  458. * reply that is sent when a new connection exceeds the maximum
  459. * number of allowed connections on the server side.
  460. *
  461. * This is seen as an error instead of a regular reply because the
  462. * server closes the connection after sending it.
  463. *
  464. * To prevent the error from being overwritten by an EOF error the
  465. * connection is closed here. See issue #43.
  466. *
  467. * Another possibility is that the server is loading its dataset.
  468. * In this case we also want to close the connection, and have the
  469. * user wait until the server is ready to take our request.
  470. */
  471. if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
  472. c->err = REDIS_ERR_OTHER;
  473. snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
  474. c->reader->fn->freeObject(reply);
  475. __redisAsyncDisconnect(ac);
  476. return;
  477. }
  478. /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */
  479. assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));
  480. if(c->flags & REDIS_SUBSCRIBED)
  481. __redisGetSubscribeCallback(ac,reply,&cb);
  482. }
  483. if (cb.fn != NULL) {
  484. __redisRunCallback(ac,&cb,reply);
  485. c->reader->fn->freeObject(reply);
  486. /* Proceed with free'ing when redisAsyncFree() was called. */
  487. if (c->flags & REDIS_FREEING) {
  488. __redisAsyncFree(ac);
  489. return;
  490. }
  491. } else {
  492. /* No callback for this reply. This can either be a NULL callback,
  493. * or there were no callbacks to begin with. Either way, don't
  494. * abort with an error, but simply ignore it because the client
  495. * doesn't know what the server will spit out over the wire. */
  496. c->reader->fn->freeObject(reply);
  497. }
  498. }
  499. /* Disconnect when there was an error reading the reply */
  500. if (status != REDIS_OK)
  501. __redisAsyncDisconnect(ac);
  502. }
  503. static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) {
  504. if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
  505. __redisAsyncDisconnect(ac);
  506. }
  507. /* Internal helper function to detect socket status the first time a read or
  508. * write event fires. When connecting was not successful, the connect callback
  509. * is called with a REDIS_ERR status and the context is free'd. */
  510. static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
  511. int completed = 0;
  512. redisContext *c = &(ac->c);
  513. if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
  514. /* Error! */
  515. redisCheckSocketError(c);
  516. __redisAsyncHandleConnectFailure(ac);
  517. return REDIS_ERR;
  518. } else if (completed == 1) {
  519. /* connected! */
  520. if (c->connection_type == REDIS_CONN_TCP &&
  521. redisSetTcpNoDelay(c) == REDIS_ERR) {
  522. __redisAsyncHandleConnectFailure(ac);
  523. return REDIS_ERR;
  524. }
  525. if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
  526. c->flags |= REDIS_CONNECTED;
  527. return REDIS_OK;
  528. } else {
  529. return REDIS_OK;
  530. }
  531. }
  532. void redisAsyncRead(redisAsyncContext *ac) {
  533. redisContext *c = &(ac->c);
  534. if (redisBufferRead(c) == REDIS_ERR) {
  535. __redisAsyncDisconnect(ac);
  536. } else {
  537. /* Always re-schedule reads */
  538. _EL_ADD_READ(ac);
  539. redisProcessCallbacks(ac);
  540. }
  541. }
  542. /* This function should be called when the socket is readable.
  543. * It processes all replies that can be read and executes their callbacks.
  544. */
  545. void redisAsyncHandleRead(redisAsyncContext *ac) {
  546. redisContext *c = &(ac->c);
  547. if (!(c->flags & REDIS_CONNECTED)) {
  548. /* Abort connect was not successful. */
  549. if (__redisAsyncHandleConnect(ac) != REDIS_OK)
  550. return;
  551. /* Try again later when the context is still not connected. */
  552. if (!(c->flags & REDIS_CONNECTED))
  553. return;
  554. }
  555. c->funcs->async_read(ac);
  556. }
  557. void redisAsyncWrite(redisAsyncContext *ac) {
  558. redisContext *c = &(ac->c);
  559. int done = 0;
  560. if (redisBufferWrite(c,&done) == REDIS_ERR) {
  561. __redisAsyncDisconnect(ac);
  562. } else {
  563. /* Continue writing when not done, stop writing otherwise */
  564. if (!done)
  565. _EL_ADD_WRITE(ac);
  566. else
  567. _EL_DEL_WRITE(ac);
  568. /* Always schedule reads after writes */
  569. _EL_ADD_READ(ac);
  570. }
  571. }
  572. void redisAsyncHandleWrite(redisAsyncContext *ac) {
  573. redisContext *c = &(ac->c);
  574. if (!(c->flags & REDIS_CONNECTED)) {
  575. /* Abort connect was not successful. */
  576. if (__redisAsyncHandleConnect(ac) != REDIS_OK)
  577. return;
  578. /* Try again later when the context is still not connected. */
  579. if (!(c->flags & REDIS_CONNECTED))
  580. return;
  581. }
  582. c->funcs->async_write(ac);
  583. }
  584. void redisAsyncHandleTimeout(redisAsyncContext *ac) {
  585. redisContext *c = &(ac->c);
  586. redisCallback cb;
  587. if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {
  588. /* Nothing to do - just an idle timeout */
  589. return;
  590. }
  591. if (!c->err) {
  592. __redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout");
  593. }
  594. if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
  595. ac->onConnect(ac, REDIS_ERR);
  596. }
  597. while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {
  598. __redisRunCallback(ac, &cb, NULL);
  599. }
  600. /**
  601. * TODO: Don't automatically sever the connection,
  602. * rather, allow to ignore <x> responses before the queue is clear
  603. */
  604. __redisAsyncDisconnect(ac);
  605. }
  606. /* Sets a pointer to the first argument and its length starting at p. Returns
  607. * the number of bytes to skip to get to the following argument. */
  608. static const char *nextArgument(const char *start, const char **str, size_t *len) {
  609. const char *p = start;
  610. if (p[0] != '$') {
  611. p = strchr(p,'$');
  612. if (p == NULL) return NULL;
  613. }
  614. *len = (int)strtol(p+1,NULL,10);
  615. p = strchr(p,'\r');
  616. assert(p);
  617. *str = p+2;
  618. return p+2+(*len)+2;
  619. }
  620. /* Helper function for the redisAsyncCommand* family of functions. Writes a
  621. * formatted command to the output buffer and registers the provided callback
  622. * function with the context. */
  623. static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
  624. redisContext *c = &(ac->c);
  625. redisCallback cb;
  626. struct dict *cbdict;
  627. dictEntry *de;
  628. redisCallback *existcb;
  629. int pvariant, hasnext;
  630. const char *cstr, *astr;
  631. size_t clen, alen;
  632. const char *p;
  633. sds sname;
  634. int ret;
  635. /* Don't accept new commands when the connection is about to be closed. */
  636. if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
  637. /* Setup callback */
  638. cb.fn = fn;
  639. cb.privdata = privdata;
  640. cb.pending_subs = 1;
  641. /* Find out which command will be appended. */
  642. p = nextArgument(cmd,&cstr,&clen);
  643. assert(p != NULL);
  644. hasnext = (p[0] == '$');
  645. pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
  646. cstr += pvariant;
  647. clen -= pvariant;
  648. if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
  649. c->flags |= REDIS_SUBSCRIBED;
  650. /* Add every channel/pattern to the list of subscription callbacks. */
  651. while ((p = nextArgument(p,&astr,&alen)) != NULL) {
  652. sname = sdsnewlen(astr,alen);
  653. if (sname == NULL)
  654. goto oom;
  655. if (pvariant)
  656. cbdict = ac->sub.patterns;
  657. else
  658. cbdict = ac->sub.channels;
  659. de = dictFind(cbdict,sname);
  660. if (de != NULL) {
  661. existcb = dictGetEntryVal(de);
  662. cb.pending_subs = existcb->pending_subs + 1;
  663. }
  664. ret = dictReplace(cbdict,sname,&cb);
  665. if (ret == 0) sdsfree(sname);
  666. }
  667. } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
  668. /* It is only useful to call (P)UNSUBSCRIBE when the context is
  669. * subscribed to one or more channels or patterns. */
  670. if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
  671. /* (P)UNSUBSCRIBE does not have its own response: every channel or
  672. * pattern that is unsubscribed will receive a message. This means we
  673. * should not append a callback function for this command. */
  674. } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) {
  675. /* Set monitor flag and push callback */
  676. c->flags |= REDIS_MONITORING;
  677. __redisPushCallback(&ac->replies,&cb);
  678. } else {
  679. if (c->flags & REDIS_SUBSCRIBED)
  680. /* This will likely result in an error reply, but it needs to be
  681. * received and passed to the callback. */
  682. __redisPushCallback(&ac->sub.invalid,&cb);
  683. else
  684. __redisPushCallback(&ac->replies,&cb);
  685. }
  686. __redisAppendCommand(c,cmd,len);
  687. /* Always schedule a write when the write buffer is non-empty */
  688. _EL_ADD_WRITE(ac);
  689. return REDIS_OK;
  690. oom:
  691. __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
  692. return REDIS_ERR;
  693. }
  694. int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
  695. char *cmd;
  696. int len;
  697. int status;
  698. len = redisvFormatCommand(&cmd,format,ap);
  699. /* We don't want to pass -1 or -2 to future functions as a length. */
  700. if (len < 0)
  701. return REDIS_ERR;
  702. status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  703. hi_free(cmd);
  704. return status;
  705. }
  706. int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
  707. va_list ap;
  708. int status;
  709. va_start(ap,format);
  710. status = redisvAsyncCommand(ac,fn,privdata,format,ap);
  711. va_end(ap);
  712. return status;
  713. }
  714. int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
  715. sds cmd;
  716. int len;
  717. int status;
  718. len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
  719. if (len < 0)
  720. return REDIS_ERR;
  721. status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  722. sdsfree(cmd);
  723. return status;
  724. }
  725. int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
  726. int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  727. return status;
  728. }
  729. redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn) {
  730. redisAsyncPushFn *old = ac->push_cb;
  731. ac->push_cb = fn;
  732. return old;
  733. }
  734. int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
  735. if (!ac->c.command_timeout) {
  736. ac->c.command_timeout = hi_calloc(1, sizeof(tv));
  737. if (ac->c.command_timeout == NULL) {
  738. __redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory");
  739. __redisAsyncCopyError(ac);
  740. return REDIS_ERR;
  741. }
  742. }
  743. if (tv.tv_sec != ac->c.command_timeout->tv_sec ||
  744. tv.tv_usec != ac->c.command_timeout->tv_usec)
  745. {
  746. *ac->c.command_timeout = tv;
  747. }
  748. return REDIS_OK;
  749. }