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.

async.c 23KB

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