async.c 33 KB

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