test.c 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401
  1. #include "fmacros.h"
  2. #include "sockcompat.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #ifndef _WIN32
  7. #include <strings.h>
  8. #include <sys/time.h>
  9. #endif
  10. #include <assert.h>
  11. #include <signal.h>
  12. #include <errno.h>
  13. #include <limits.h>
  14. #include "hiredis.h"
  15. #include "async.h"
  16. #ifdef HIREDIS_TEST_SSL
  17. #include "hiredis_ssl.h"
  18. #endif
  19. #include "net.h"
  20. #include "win32.h"
  21. enum connection_type {
  22. CONN_TCP,
  23. CONN_UNIX,
  24. CONN_FD,
  25. CONN_SSL
  26. };
  27. struct config {
  28. enum connection_type type;
  29. struct {
  30. const char *host;
  31. int port;
  32. struct timeval timeout;
  33. } tcp;
  34. struct {
  35. const char *path;
  36. } unix_sock;
  37. struct {
  38. const char *host;
  39. int port;
  40. const char *ca_cert;
  41. const char *cert;
  42. const char *key;
  43. } ssl;
  44. };
  45. struct privdata {
  46. int dtor_counter;
  47. };
  48. #ifdef HIREDIS_TEST_SSL
  49. redisSSLContext *_ssl_ctx = NULL;
  50. #endif
  51. /* The following lines make up our testing "framework" :) */
  52. static int tests = 0, fails = 0, skips = 0;
  53. #define test(_s) { printf("#%02d ", ++tests); printf(_s); }
  54. #define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
  55. #define test_skipped() { printf("\033[01;33mSKIPPED\033[0;0m\n"); skips++; }
  56. static long long usec(void) {
  57. #ifndef _MSC_VER
  58. struct timeval tv;
  59. gettimeofday(&tv,NULL);
  60. return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
  61. #else
  62. FILETIME ft;
  63. GetSystemTimeAsFileTime(&ft);
  64. return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10;
  65. #endif
  66. }
  67. /* The assert() calls below have side effects, so we need assert()
  68. * even if we are compiling without asserts (-DNDEBUG). */
  69. #ifdef NDEBUG
  70. #undef assert
  71. #define assert(e) (void)(e)
  72. #endif
  73. /* Helper to extract Redis version information. Aborts on any failure. */
  74. #define REDIS_VERSION_FIELD "redis_version:"
  75. void get_redis_version(redisContext *c, int *majorptr, int *minorptr) {
  76. redisReply *reply;
  77. char *eptr, *s, *e;
  78. int major, minor;
  79. reply = redisCommand(c, "INFO");
  80. if (reply == NULL || c->err || reply->type != REDIS_REPLY_STRING)
  81. goto abort;
  82. if ((s = strstr(reply->str, REDIS_VERSION_FIELD)) == NULL)
  83. goto abort;
  84. s += strlen(REDIS_VERSION_FIELD);
  85. /* We need a field terminator and at least 'x.y.z' (5) bytes of data */
  86. if ((e = strstr(s, "\r\n")) == NULL || (e - s) < 5)
  87. goto abort;
  88. /* Extract version info */
  89. major = strtol(s, &eptr, 10);
  90. if (*eptr != '.') goto abort;
  91. minor = strtol(eptr+1, NULL, 10);
  92. /* Push info the caller wants */
  93. if (majorptr) *majorptr = major;
  94. if (minorptr) *minorptr = minor;
  95. freeReplyObject(reply);
  96. return;
  97. abort:
  98. freeReplyObject(reply);
  99. fprintf(stderr, "Error: Cannot determine Redis version, aborting\n");
  100. exit(1);
  101. }
  102. static redisContext *select_database(redisContext *c) {
  103. redisReply *reply;
  104. /* Switch to DB 9 for testing, now that we know we can chat. */
  105. reply = redisCommand(c,"SELECT 9");
  106. assert(reply != NULL);
  107. freeReplyObject(reply);
  108. /* Make sure the DB is emtpy */
  109. reply = redisCommand(c,"DBSIZE");
  110. assert(reply != NULL);
  111. if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
  112. /* Awesome, DB 9 is empty and we can continue. */
  113. freeReplyObject(reply);
  114. } else {
  115. printf("Database #9 is not empty, test can not continue\n");
  116. exit(1);
  117. }
  118. return c;
  119. }
  120. /* Switch protocol */
  121. static void send_hello(redisContext *c, int version) {
  122. redisReply *reply;
  123. int expected;
  124. reply = redisCommand(c, "HELLO %d", version);
  125. expected = version == 3 ? REDIS_REPLY_MAP : REDIS_REPLY_ARRAY;
  126. assert(reply != NULL && reply->type == expected);
  127. freeReplyObject(reply);
  128. }
  129. /* Togggle client tracking */
  130. static void send_client_tracking(redisContext *c, const char *str) {
  131. redisReply *reply;
  132. reply = redisCommand(c, "CLIENT TRACKING %s", str);
  133. assert(reply != NULL && reply->type == REDIS_REPLY_STATUS);
  134. freeReplyObject(reply);
  135. }
  136. static int disconnect(redisContext *c, int keep_fd) {
  137. redisReply *reply;
  138. /* Make sure we're on DB 9. */
  139. reply = redisCommand(c,"SELECT 9");
  140. assert(reply != NULL);
  141. freeReplyObject(reply);
  142. reply = redisCommand(c,"FLUSHDB");
  143. assert(reply != NULL);
  144. freeReplyObject(reply);
  145. /* Free the context as well, but keep the fd if requested. */
  146. if (keep_fd)
  147. return redisFreeKeepFd(c);
  148. redisFree(c);
  149. return -1;
  150. }
  151. static void do_ssl_handshake(redisContext *c) {
  152. #ifdef HIREDIS_TEST_SSL
  153. redisInitiateSSLWithContext(c, _ssl_ctx);
  154. if (c->err) {
  155. printf("SSL error: %s\n", c->errstr);
  156. redisFree(c);
  157. exit(1);
  158. }
  159. #else
  160. (void) c;
  161. #endif
  162. }
  163. static redisContext *do_connect(struct config config) {
  164. redisContext *c = NULL;
  165. if (config.type == CONN_TCP) {
  166. c = redisConnect(config.tcp.host, config.tcp.port);
  167. } else if (config.type == CONN_SSL) {
  168. c = redisConnect(config.ssl.host, config.ssl.port);
  169. } else if (config.type == CONN_UNIX) {
  170. c = redisConnectUnix(config.unix_sock.path);
  171. } else if (config.type == CONN_FD) {
  172. /* Create a dummy connection just to get an fd to inherit */
  173. redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);
  174. if (dummy_ctx) {
  175. int fd = disconnect(dummy_ctx, 1);
  176. printf("Connecting to inherited fd %d\n", fd);
  177. c = redisConnectFd(fd);
  178. }
  179. } else {
  180. assert(NULL);
  181. }
  182. if (c == NULL) {
  183. printf("Connection error: can't allocate redis context\n");
  184. exit(1);
  185. } else if (c->err) {
  186. printf("Connection error: %s\n", c->errstr);
  187. redisFree(c);
  188. exit(1);
  189. }
  190. if (config.type == CONN_SSL) {
  191. do_ssl_handshake(c);
  192. }
  193. return select_database(c);
  194. }
  195. static void do_reconnect(redisContext *c, struct config config) {
  196. redisReconnect(c);
  197. if (config.type == CONN_SSL) {
  198. do_ssl_handshake(c);
  199. }
  200. }
  201. static void test_format_commands(void) {
  202. char *cmd;
  203. int len;
  204. test("Format command without interpolation: ");
  205. len = redisFormatCommand(&cmd,"SET foo bar");
  206. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  207. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  208. hi_free(cmd);
  209. test("Format command with %%s string interpolation: ");
  210. len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
  211. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  212. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  213. hi_free(cmd);
  214. test("Format command with %%s and an empty string: ");
  215. len = redisFormatCommand(&cmd,"SET %s %s","foo","");
  216. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
  217. len == 4+4+(3+2)+4+(3+2)+4+(0+2));
  218. hi_free(cmd);
  219. test("Format command with an empty string in between proper interpolations: ");
  220. len = redisFormatCommand(&cmd,"SET %s %s","","foo");
  221. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
  222. len == 4+4+(3+2)+4+(0+2)+4+(3+2));
  223. hi_free(cmd);
  224. test("Format command with %%b string interpolation: ");
  225. len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3);
  226. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
  227. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  228. hi_free(cmd);
  229. test("Format command with %%b and an empty string: ");
  230. len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0);
  231. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
  232. len == 4+4+(3+2)+4+(3+2)+4+(0+2));
  233. hi_free(cmd);
  234. test("Format command with literal %%: ");
  235. len = redisFormatCommand(&cmd,"SET %% %%");
  236. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
  237. len == 4+4+(3+2)+4+(1+2)+4+(1+2));
  238. hi_free(cmd);
  239. /* Vararg width depends on the type. These tests make sure that the
  240. * width is correctly determined using the format and subsequent varargs
  241. * can correctly be interpolated. */
  242. #define INTEGER_WIDTH_TEST(fmt, type) do { \
  243. type value = 123; \
  244. test("Format command with printf-delegation (" #type "): "); \
  245. len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
  246. test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
  247. len == 4+5+(12+2)+4+(9+2)); \
  248. hi_free(cmd); \
  249. } while(0)
  250. #define FLOAT_WIDTH_TEST(type) do { \
  251. type value = 123.0; \
  252. test("Format command with printf-delegation (" #type "): "); \
  253. len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
  254. test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
  255. len == 4+5+(12+2)+4+(9+2)); \
  256. hi_free(cmd); \
  257. } while(0)
  258. INTEGER_WIDTH_TEST("d", int);
  259. INTEGER_WIDTH_TEST("hhd", char);
  260. INTEGER_WIDTH_TEST("hd", short);
  261. INTEGER_WIDTH_TEST("ld", long);
  262. INTEGER_WIDTH_TEST("lld", long long);
  263. INTEGER_WIDTH_TEST("u", unsigned int);
  264. INTEGER_WIDTH_TEST("hhu", unsigned char);
  265. INTEGER_WIDTH_TEST("hu", unsigned short);
  266. INTEGER_WIDTH_TEST("lu", unsigned long);
  267. INTEGER_WIDTH_TEST("llu", unsigned long long);
  268. FLOAT_WIDTH_TEST(float);
  269. FLOAT_WIDTH_TEST(double);
  270. test("Format command with invalid printf format: ");
  271. len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3);
  272. test_cond(len == -1);
  273. const char *argv[3];
  274. argv[0] = "SET";
  275. argv[1] = "foo\0xxx";
  276. argv[2] = "bar";
  277. size_t lens[3] = { 3, 7, 3 };
  278. int argc = 3;
  279. test("Format command by passing argc/argv without lengths: ");
  280. len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
  281. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  282. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  283. hi_free(cmd);
  284. test("Format command by passing argc/argv with lengths: ");
  285. len = redisFormatCommandArgv(&cmd,argc,argv,lens);
  286. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
  287. len == 4+4+(3+2)+4+(7+2)+4+(3+2));
  288. hi_free(cmd);
  289. sds sds_cmd;
  290. sds_cmd = NULL;
  291. test("Format command into sds by passing argc/argv without lengths: ");
  292. len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
  293. test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  294. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  295. sdsfree(sds_cmd);
  296. sds_cmd = NULL;
  297. test("Format command into sds by passing argc/argv with lengths: ");
  298. len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
  299. test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
  300. len == 4+4+(3+2)+4+(7+2)+4+(3+2));
  301. sdsfree(sds_cmd);
  302. }
  303. static void test_append_formatted_commands(struct config config) {
  304. redisContext *c;
  305. redisReply *reply;
  306. char *cmd;
  307. int len;
  308. c = do_connect(config);
  309. test("Append format command: ");
  310. len = redisFormatCommand(&cmd, "SET foo bar");
  311. test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK);
  312. assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
  313. hi_free(cmd);
  314. freeReplyObject(reply);
  315. disconnect(c, 0);
  316. }
  317. static void test_reply_reader(void) {
  318. redisReader *reader;
  319. void *reply, *root;
  320. int ret;
  321. int i;
  322. test("Error handling in reply parser: ");
  323. reader = redisReaderCreate();
  324. redisReaderFeed(reader,(char*)"@foo\r\n",6);
  325. ret = redisReaderGetReply(reader,NULL);
  326. test_cond(ret == REDIS_ERR &&
  327. strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
  328. redisReaderFree(reader);
  329. /* when the reply already contains multiple items, they must be free'd
  330. * on an error. valgrind will bark when this doesn't happen. */
  331. test("Memory cleanup in reply parser: ");
  332. reader = redisReaderCreate();
  333. redisReaderFeed(reader,(char*)"*2\r\n",4);
  334. redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
  335. redisReaderFeed(reader,(char*)"@foo\r\n",6);
  336. ret = redisReaderGetReply(reader,NULL);
  337. test_cond(ret == REDIS_ERR &&
  338. strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
  339. redisReaderFree(reader);
  340. reader = redisReaderCreate();
  341. test("Can handle arbitrarily nested multi-bulks: ");
  342. for (i = 0; i < 128; i++) {
  343. redisReaderFeed(reader,(char*)"*1\r\n", 4);
  344. }
  345. redisReaderFeed(reader,(char*)"$6\r\nLOLWUT\r\n",12);
  346. ret = redisReaderGetReply(reader,&reply);
  347. root = reply; /* Keep track of the root reply */
  348. test_cond(ret == REDIS_OK &&
  349. ((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
  350. ((redisReply*)reply)->elements == 1);
  351. test("Can parse arbitrarily nested multi-bulks correctly: ");
  352. while(i--) {
  353. assert(reply != NULL && ((redisReply*)reply)->type == REDIS_REPLY_ARRAY);
  354. reply = ((redisReply*)reply)->element[0];
  355. }
  356. test_cond(((redisReply*)reply)->type == REDIS_REPLY_STRING &&
  357. !memcmp(((redisReply*)reply)->str, "LOLWUT", 6));
  358. freeReplyObject(root);
  359. redisReaderFree(reader);
  360. test("Correctly parses LLONG_MAX: ");
  361. reader = redisReaderCreate();
  362. redisReaderFeed(reader, ":9223372036854775807\r\n",22);
  363. ret = redisReaderGetReply(reader,&reply);
  364. test_cond(ret == REDIS_OK &&
  365. ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
  366. ((redisReply*)reply)->integer == LLONG_MAX);
  367. freeReplyObject(reply);
  368. redisReaderFree(reader);
  369. test("Set error when > LLONG_MAX: ");
  370. reader = redisReaderCreate();
  371. redisReaderFeed(reader, ":9223372036854775808\r\n",22);
  372. ret = redisReaderGetReply(reader,&reply);
  373. test_cond(ret == REDIS_ERR &&
  374. strcasecmp(reader->errstr,"Bad integer value") == 0);
  375. freeReplyObject(reply);
  376. redisReaderFree(reader);
  377. test("Correctly parses LLONG_MIN: ");
  378. reader = redisReaderCreate();
  379. redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
  380. ret = redisReaderGetReply(reader,&reply);
  381. test_cond(ret == REDIS_OK &&
  382. ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
  383. ((redisReply*)reply)->integer == LLONG_MIN);
  384. freeReplyObject(reply);
  385. redisReaderFree(reader);
  386. test("Set error when < LLONG_MIN: ");
  387. reader = redisReaderCreate();
  388. redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
  389. ret = redisReaderGetReply(reader,&reply);
  390. test_cond(ret == REDIS_ERR &&
  391. strcasecmp(reader->errstr,"Bad integer value") == 0);
  392. freeReplyObject(reply);
  393. redisReaderFree(reader);
  394. test("Set error when array < -1: ");
  395. reader = redisReaderCreate();
  396. redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12);
  397. ret = redisReaderGetReply(reader,&reply);
  398. test_cond(ret == REDIS_ERR &&
  399. strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
  400. freeReplyObject(reply);
  401. redisReaderFree(reader);
  402. test("Set error when bulk < -1: ");
  403. reader = redisReaderCreate();
  404. redisReaderFeed(reader, "$-2\r\nasdf\r\n",11);
  405. ret = redisReaderGetReply(reader,&reply);
  406. test_cond(ret == REDIS_ERR &&
  407. strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
  408. freeReplyObject(reply);
  409. redisReaderFree(reader);
  410. test("Can configure maximum multi-bulk elements: ");
  411. reader = redisReaderCreate();
  412. reader->maxelements = 1024;
  413. redisReaderFeed(reader, "*1025\r\n", 7);
  414. ret = redisReaderGetReply(reader,&reply);
  415. test_cond(ret == REDIS_ERR &&
  416. strcasecmp(reader->errstr, "Multi-bulk length out of range") == 0);
  417. freeReplyObject(reply);
  418. redisReaderFree(reader);
  419. test("Multi-bulk never overflows regardless of maxelements: ");
  420. size_t bad_mbulk_len = (SIZE_MAX / sizeof(void *)) + 3;
  421. char bad_mbulk_reply[100];
  422. snprintf(bad_mbulk_reply, sizeof(bad_mbulk_reply), "*%llu\r\n+asdf\r\n",
  423. (unsigned long long) bad_mbulk_len);
  424. reader = redisReaderCreate();
  425. reader->maxelements = 0; /* Don't rely on default limit */
  426. redisReaderFeed(reader, bad_mbulk_reply, strlen(bad_mbulk_reply));
  427. ret = redisReaderGetReply(reader,&reply);
  428. test_cond(ret == REDIS_ERR && strcasecmp(reader->errstr, "Out of memory") == 0);
  429. freeReplyObject(reply);
  430. redisReaderFree(reader);
  431. #if LLONG_MAX > SIZE_MAX
  432. test("Set error when array > SIZE_MAX: ");
  433. reader = redisReaderCreate();
  434. redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
  435. ret = redisReaderGetReply(reader,&reply);
  436. test_cond(ret == REDIS_ERR &&
  437. strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
  438. freeReplyObject(reply);
  439. redisReaderFree(reader);
  440. test("Set error when bulk > SIZE_MAX: ");
  441. reader = redisReaderCreate();
  442. redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
  443. ret = redisReaderGetReply(reader,&reply);
  444. test_cond(ret == REDIS_ERR &&
  445. strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
  446. freeReplyObject(reply);
  447. redisReaderFree(reader);
  448. #endif
  449. test("Works with NULL functions for reply: ");
  450. reader = redisReaderCreate();
  451. reader->fn = NULL;
  452. redisReaderFeed(reader,(char*)"+OK\r\n",5);
  453. ret = redisReaderGetReply(reader,&reply);
  454. test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
  455. redisReaderFree(reader);
  456. test("Works when a single newline (\\r\\n) covers two calls to feed: ");
  457. reader = redisReaderCreate();
  458. reader->fn = NULL;
  459. redisReaderFeed(reader,(char*)"+OK\r",4);
  460. ret = redisReaderGetReply(reader,&reply);
  461. assert(ret == REDIS_OK && reply == NULL);
  462. redisReaderFeed(reader,(char*)"\n",1);
  463. ret = redisReaderGetReply(reader,&reply);
  464. test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
  465. redisReaderFree(reader);
  466. test("Don't reset state after protocol error: ");
  467. reader = redisReaderCreate();
  468. reader->fn = NULL;
  469. redisReaderFeed(reader,(char*)"x",1);
  470. ret = redisReaderGetReply(reader,&reply);
  471. assert(ret == REDIS_ERR);
  472. ret = redisReaderGetReply(reader,&reply);
  473. test_cond(ret == REDIS_ERR && reply == NULL);
  474. redisReaderFree(reader);
  475. /* Regression test for issue #45 on GitHub. */
  476. test("Don't do empty allocation for empty multi bulk: ");
  477. reader = redisReaderCreate();
  478. redisReaderFeed(reader,(char*)"*0\r\n",4);
  479. ret = redisReaderGetReply(reader,&reply);
  480. test_cond(ret == REDIS_OK &&
  481. ((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
  482. ((redisReply*)reply)->elements == 0);
  483. freeReplyObject(reply);
  484. redisReaderFree(reader);
  485. /* RESP3 verbatim strings (GitHub issue #802) */
  486. test("Can parse RESP3 verbatim strings: ");
  487. reader = redisReaderCreate();
  488. redisReaderFeed(reader,(char*)"=10\r\ntxt:LOLWUT\r\n",17);
  489. ret = redisReaderGetReply(reader,&reply);
  490. test_cond(ret == REDIS_OK &&
  491. ((redisReply*)reply)->type == REDIS_REPLY_VERB &&
  492. !memcmp(((redisReply*)reply)->str,"LOLWUT", 6));
  493. freeReplyObject(reply);
  494. redisReaderFree(reader);
  495. /* RESP3 push messages (Github issue #815) */
  496. test("Can parse RESP3 push messages: ");
  497. reader = redisReaderCreate();
  498. redisReaderFeed(reader,(char*)">2\r\n$6\r\nLOLWUT\r\n:42\r\n",21);
  499. ret = redisReaderGetReply(reader,&reply);
  500. test_cond(ret == REDIS_OK &&
  501. ((redisReply*)reply)->type == REDIS_REPLY_PUSH &&
  502. ((redisReply*)reply)->elements == 2 &&
  503. ((redisReply*)reply)->element[0]->type == REDIS_REPLY_STRING &&
  504. !memcmp(((redisReply*)reply)->element[0]->str,"LOLWUT",6) &&
  505. ((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER &&
  506. ((redisReply*)reply)->element[1]->integer == 42);
  507. freeReplyObject(reply);
  508. redisReaderFree(reader);
  509. }
  510. static void test_free_null(void) {
  511. void *redisCtx = NULL;
  512. void *reply = NULL;
  513. test("Don't fail when redisFree is passed a NULL value: ");
  514. redisFree(redisCtx);
  515. test_cond(redisCtx == NULL);
  516. test("Don't fail when freeReplyObject is passed a NULL value: ");
  517. freeReplyObject(reply);
  518. test_cond(reply == NULL);
  519. }
  520. static void *hi_malloc_fail(size_t size) {
  521. (void)size;
  522. return NULL;
  523. }
  524. static void *hi_calloc_fail(size_t nmemb, size_t size) {
  525. (void)nmemb;
  526. (void)size;
  527. return NULL;
  528. }
  529. static void *hi_realloc_fail(void *ptr, size_t size) {
  530. (void)ptr;
  531. (void)size;
  532. return NULL;
  533. }
  534. static void test_allocator_injection(void) {
  535. hiredisAllocFuncs ha = {
  536. .mallocFn = hi_malloc_fail,
  537. .callocFn = hi_calloc_fail,
  538. .reallocFn = hi_realloc_fail,
  539. .strdupFn = strdup,
  540. .freeFn = free,
  541. };
  542. // Override hiredis allocators
  543. hiredisSetAllocators(&ha);
  544. test("redisContext uses injected allocators: ");
  545. redisContext *c = redisConnect("localhost", 6379);
  546. test_cond(c == NULL);
  547. test("redisReader uses injected allocators: ");
  548. redisReader *reader = redisReaderCreate();
  549. test_cond(reader == NULL);
  550. // Return allocators to default
  551. hiredisResetAllocators();
  552. }
  553. #define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
  554. static void test_blocking_connection_errors(void) {
  555. redisContext *c;
  556. struct addrinfo hints = {.ai_family = AF_INET};
  557. struct addrinfo *ai_tmp = NULL;
  558. int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, "6379", &hints, &ai_tmp);
  559. if (rv != 0) {
  560. // Address does *not* exist
  561. test("Returns error when host cannot be resolved: ");
  562. // First see if this domain name *actually* resolves to NXDOMAIN
  563. c = redisConnect(HIREDIS_BAD_DOMAIN, 6379);
  564. test_cond(
  565. c->err == REDIS_ERR_OTHER &&
  566. (strcmp(c->errstr, "Name or service not known") == 0 ||
  567. strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 ||
  568. strcmp(c->errstr, "Name does not resolve") == 0 ||
  569. strcmp(c->errstr, "nodename nor servname provided, or not known") == 0 ||
  570. strcmp(c->errstr, "No address associated with hostname") == 0 ||
  571. strcmp(c->errstr, "Temporary failure in name resolution") == 0 ||
  572. strcmp(c->errstr, "hostname nor servname provided, or not known") == 0 ||
  573. strcmp(c->errstr, "no address associated with name") == 0 ||
  574. strcmp(c->errstr, "No such host is known. ") == 0));
  575. redisFree(c);
  576. } else {
  577. printf("Skipping NXDOMAIN test. Found evil ISP!\n");
  578. freeaddrinfo(ai_tmp);
  579. }
  580. #ifndef _WIN32
  581. test("Returns error when the port is not open: ");
  582. c = redisConnect((char*)"localhost", 1);
  583. test_cond(c->err == REDIS_ERR_IO &&
  584. strcmp(c->errstr,"Connection refused") == 0);
  585. redisFree(c);
  586. test("Returns error when the unix_sock socket path doesn't accept connections: ");
  587. c = redisConnectUnix((char*)"/tmp/idontexist.sock");
  588. test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
  589. redisFree(c);
  590. #endif
  591. }
  592. /* Dummy push handler */
  593. void push_handler(void *privdata, void *reply) {
  594. int *counter = privdata;
  595. freeReplyObject(reply);
  596. *counter += 1;
  597. }
  598. /* Dummy function just to test setting a callback with redisOptions */
  599. void push_handler_async(redisAsyncContext *ac, void *reply) {
  600. (void)ac;
  601. (void)reply;
  602. }
  603. static void test_resp3_push_handler(redisContext *c) {
  604. redisPushFn *old = NULL;
  605. redisReply *reply;
  606. void *privdata;
  607. int n = 0;
  608. /* Switch to RESP3 and turn on client tracking */
  609. send_hello(c, 3);
  610. send_client_tracking(c, "ON");
  611. privdata = c->privdata;
  612. c->privdata = &n;
  613. reply = redisCommand(c, "GET key:0");
  614. assert(reply != NULL);
  615. freeReplyObject(reply);
  616. test("RESP3 PUSH messages are handled out of band by default: ");
  617. reply = redisCommand(c, "SET key:0 val:0");
  618. test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS);
  619. freeReplyObject(reply);
  620. assert((reply = redisCommand(c, "GET key:0")) != NULL);
  621. freeReplyObject(reply);
  622. old = redisSetPushCallback(c, push_handler);
  623. test("We can set a custom RESP3 PUSH handler: ");
  624. reply = redisCommand(c, "SET key:0 val:0");
  625. test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && n == 1);
  626. freeReplyObject(reply);
  627. /* Unset the push callback and generate an invalidate message making
  628. * sure it is not handled out of band. */
  629. test("With no handler, PUSH replies come in-band: ");
  630. redisSetPushCallback(c, NULL);
  631. assert((reply = redisCommand(c, "GET key:0")) != NULL);
  632. freeReplyObject(reply);
  633. assert((reply = redisCommand(c, "SET key:0 invalid")) != NULL);
  634. test_cond(reply->type == REDIS_REPLY_PUSH);
  635. freeReplyObject(reply);
  636. test("With no PUSH handler, no replies are lost: ");
  637. assert(redisGetReply(c, (void**)&reply) == REDIS_OK);
  638. test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS);
  639. freeReplyObject(reply);
  640. /* Return to the originally set PUSH handler */
  641. assert(old != NULL);
  642. redisSetPushCallback(c, old);
  643. /* Switch back to RESP2 and disable tracking */
  644. c->privdata = privdata;
  645. send_client_tracking(c, "OFF");
  646. send_hello(c, 2);
  647. }
  648. redisOptions get_redis_tcp_options(struct config config) {
  649. redisOptions options = {0};
  650. REDIS_OPTIONS_SET_TCP(&options, config.tcp.host, config.tcp.port);
  651. return options;
  652. }
  653. static void test_resp3_push_options(struct config config) {
  654. redisAsyncContext *ac;
  655. redisContext *c;
  656. redisOptions options;
  657. test("We set a default RESP3 handler for redisContext: ");
  658. options = get_redis_tcp_options(config);
  659. assert((c = redisConnectWithOptions(&options)) != NULL);
  660. test_cond(c->push_cb != NULL);
  661. redisFree(c);
  662. test("We don't set a default RESP3 push handler for redisAsyncContext: ");
  663. options = get_redis_tcp_options(config);
  664. assert((ac = redisAsyncConnectWithOptions(&options)) != NULL);
  665. test_cond(ac->c.push_cb == NULL);
  666. redisAsyncFree(ac);
  667. test("Our REDIS_OPT_NO_PUSH_AUTOFREE flag works: ");
  668. options = get_redis_tcp_options(config);
  669. options.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
  670. assert((c = redisConnectWithOptions(&options)) != NULL);
  671. test_cond(c->push_cb == NULL);
  672. redisFree(c);
  673. test("We can use redisOptions to set a custom PUSH handler for redisContext: ");
  674. options = get_redis_tcp_options(config);
  675. options.push_cb = push_handler;
  676. assert((c = redisConnectWithOptions(&options)) != NULL);
  677. test_cond(c->push_cb == push_handler);
  678. redisFree(c);
  679. test("We can use redisOptions to set a custom PUSH handler for redisAsyncContext: ");
  680. options = get_redis_tcp_options(config);
  681. options.async_push_cb = push_handler_async;
  682. assert((ac = redisAsyncConnectWithOptions(&options)) != NULL);
  683. test_cond(ac->push_cb == push_handler_async);
  684. redisAsyncFree(ac);
  685. }
  686. void free_privdata(void *privdata) {
  687. struct privdata *data = privdata;
  688. data->dtor_counter++;
  689. }
  690. static void test_privdata_hooks(struct config config) {
  691. struct privdata data = {0};
  692. redisOptions options;
  693. redisContext *c;
  694. test("We can use redisOptions to set privdata: ");
  695. options = get_redis_tcp_options(config);
  696. REDIS_OPTIONS_SET_PRIVDATA(&options, &data, free_privdata);
  697. assert((c = redisConnectWithOptions(&options)) != NULL);
  698. test_cond(c->privdata == &data);
  699. test("Our privdata destructor fires when we free the context: ");
  700. redisFree(c);
  701. test_cond(data.dtor_counter == 1);
  702. }
  703. static void test_blocking_connection(struct config config) {
  704. redisContext *c;
  705. redisReply *reply;
  706. int major;
  707. c = do_connect(config);
  708. test("Is able to deliver commands: ");
  709. reply = redisCommand(c,"PING");
  710. test_cond(reply->type == REDIS_REPLY_STATUS &&
  711. strcasecmp(reply->str,"pong") == 0)
  712. freeReplyObject(reply);
  713. test("Is a able to send commands verbatim: ");
  714. reply = redisCommand(c,"SET foo bar");
  715. test_cond (reply->type == REDIS_REPLY_STATUS &&
  716. strcasecmp(reply->str,"ok") == 0)
  717. freeReplyObject(reply);
  718. test("%%s String interpolation works: ");
  719. reply = redisCommand(c,"SET %s %s","foo","hello world");
  720. freeReplyObject(reply);
  721. reply = redisCommand(c,"GET foo");
  722. test_cond(reply->type == REDIS_REPLY_STRING &&
  723. strcmp(reply->str,"hello world") == 0);
  724. freeReplyObject(reply);
  725. test("%%b String interpolation works: ");
  726. reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11);
  727. freeReplyObject(reply);
  728. reply = redisCommand(c,"GET foo");
  729. test_cond(reply->type == REDIS_REPLY_STRING &&
  730. memcmp(reply->str,"hello\x00world",11) == 0)
  731. test("Binary reply length is correct: ");
  732. test_cond(reply->len == 11)
  733. freeReplyObject(reply);
  734. test("Can parse nil replies: ");
  735. reply = redisCommand(c,"GET nokey");
  736. test_cond(reply->type == REDIS_REPLY_NIL)
  737. freeReplyObject(reply);
  738. /* test 7 */
  739. test("Can parse integer replies: ");
  740. reply = redisCommand(c,"INCR mycounter");
  741. test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1)
  742. freeReplyObject(reply);
  743. test("Can parse multi bulk replies: ");
  744. freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
  745. freeReplyObject(redisCommand(c,"LPUSH mylist bar"));
  746. reply = redisCommand(c,"LRANGE mylist 0 -1");
  747. test_cond(reply->type == REDIS_REPLY_ARRAY &&
  748. reply->elements == 2 &&
  749. !memcmp(reply->element[0]->str,"bar",3) &&
  750. !memcmp(reply->element[1]->str,"foo",3))
  751. freeReplyObject(reply);
  752. /* m/e with multi bulk reply *before* other reply.
  753. * specifically test ordering of reply items to parse. */
  754. test("Can handle nested multi bulk replies: ");
  755. freeReplyObject(redisCommand(c,"MULTI"));
  756. freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1"));
  757. freeReplyObject(redisCommand(c,"PING"));
  758. reply = (redisCommand(c,"EXEC"));
  759. test_cond(reply->type == REDIS_REPLY_ARRAY &&
  760. reply->elements == 2 &&
  761. reply->element[0]->type == REDIS_REPLY_ARRAY &&
  762. reply->element[0]->elements == 2 &&
  763. !memcmp(reply->element[0]->element[0]->str,"bar",3) &&
  764. !memcmp(reply->element[0]->element[1]->str,"foo",3) &&
  765. reply->element[1]->type == REDIS_REPLY_STATUS &&
  766. strcasecmp(reply->element[1]->str,"pong") == 0);
  767. freeReplyObject(reply);
  768. /* Make sure passing NULL to redisGetReply is safe */
  769. test("Can pass NULL to redisGetReply: ");
  770. assert(redisAppendCommand(c, "PING") == REDIS_OK);
  771. test_cond(redisGetReply(c, NULL) == REDIS_OK);
  772. get_redis_version(c, &major, NULL);
  773. if (major >= 6) test_resp3_push_handler(c);
  774. test_resp3_push_options(config);
  775. test_privdata_hooks(config);
  776. disconnect(c, 0);
  777. }
  778. /* Send DEBUG SLEEP 0 to detect if we have this command */
  779. static int detect_debug_sleep(redisContext *c) {
  780. int detected;
  781. redisReply *reply = redisCommand(c, "DEBUG SLEEP 0\r\n");
  782. if (reply == NULL || c->err) {
  783. const char *cause = c->err ? c->errstr : "(none)";
  784. fprintf(stderr, "Error testing for DEBUG SLEEP (Redis error: %s), exiting\n", cause);
  785. exit(-1);
  786. }
  787. detected = reply->type == REDIS_REPLY_STATUS;
  788. freeReplyObject(reply);
  789. return detected;
  790. }
  791. static void test_blocking_connection_timeouts(struct config config) {
  792. redisContext *c;
  793. redisReply *reply;
  794. ssize_t s;
  795. const char *sleep_cmd = "DEBUG SLEEP 3\r\n";
  796. struct timeval tv;
  797. c = do_connect(config);
  798. test("Successfully completes a command when the timeout is not exceeded: ");
  799. reply = redisCommand(c,"SET foo fast");
  800. freeReplyObject(reply);
  801. tv.tv_sec = 0;
  802. tv.tv_usec = 10000;
  803. redisSetTimeout(c, tv);
  804. reply = redisCommand(c, "GET foo");
  805. test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0);
  806. freeReplyObject(reply);
  807. disconnect(c, 0);
  808. c = do_connect(config);
  809. test("Does not return a reply when the command times out: ");
  810. if (detect_debug_sleep(c)) {
  811. redisAppendFormattedCommand(c, sleep_cmd, strlen(sleep_cmd));
  812. s = c->funcs->write(c);
  813. tv.tv_sec = 0;
  814. tv.tv_usec = 10000;
  815. redisSetTimeout(c, tv);
  816. reply = redisCommand(c, "GET foo");
  817. #ifndef _WIN32
  818. test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO &&
  819. strcmp(c->errstr, "Resource temporarily unavailable") == 0);
  820. #else
  821. test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_TIMEOUT &&
  822. strcmp(c->errstr, "recv timeout") == 0);
  823. #endif
  824. freeReplyObject(reply);
  825. } else {
  826. test_skipped();
  827. }
  828. test("Reconnect properly reconnects after a timeout: ");
  829. do_reconnect(c, config);
  830. reply = redisCommand(c, "PING");
  831. test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
  832. freeReplyObject(reply);
  833. test("Reconnect properly uses owned parameters: ");
  834. config.tcp.host = "foo";
  835. config.unix_sock.path = "foo";
  836. do_reconnect(c, config);
  837. reply = redisCommand(c, "PING");
  838. test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
  839. freeReplyObject(reply);
  840. disconnect(c, 0);
  841. }
  842. static void test_blocking_io_errors(struct config config) {
  843. redisContext *c;
  844. redisReply *reply;
  845. void *_reply;
  846. int major, minor;
  847. /* Connect to target given by config. */
  848. c = do_connect(config);
  849. get_redis_version(c, &major, &minor);
  850. test("Returns I/O error when the connection is lost: ");
  851. reply = redisCommand(c,"QUIT");
  852. if (major > 2 || (major == 2 && minor > 0)) {
  853. /* > 2.0 returns OK on QUIT and read() should be issued once more
  854. * to know the descriptor is at EOF. */
  855. test_cond(strcasecmp(reply->str,"OK") == 0 &&
  856. redisGetReply(c,&_reply) == REDIS_ERR);
  857. freeReplyObject(reply);
  858. } else {
  859. test_cond(reply == NULL);
  860. }
  861. #ifndef _WIN32
  862. /* On 2.0, QUIT will cause the connection to be closed immediately and
  863. * the read(2) for the reply on QUIT will set the error to EOF.
  864. * On >2.0, QUIT will return with OK and another read(2) needed to be
  865. * issued to find out the socket was closed by the server. In both
  866. * conditions, the error will be set to EOF. */
  867. assert(c->err == REDIS_ERR_EOF &&
  868. strcmp(c->errstr,"Server closed the connection") == 0);
  869. #endif
  870. redisFree(c);
  871. c = do_connect(config);
  872. test("Returns I/O error on socket timeout: ");
  873. struct timeval tv = { 0, 1000 };
  874. assert(redisSetTimeout(c,tv) == REDIS_OK);
  875. int respcode = redisGetReply(c,&_reply);
  876. #ifndef _WIN32
  877. test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_IO && errno == EAGAIN);
  878. #else
  879. test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_TIMEOUT);
  880. #endif
  881. redisFree(c);
  882. }
  883. static void test_invalid_timeout_errors(struct config config) {
  884. redisContext *c;
  885. test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: ");
  886. config.tcp.timeout.tv_sec = 0;
  887. config.tcp.timeout.tv_usec = 10000001;
  888. c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
  889. test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
  890. redisFree(c);
  891. test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
  892. config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;
  893. config.tcp.timeout.tv_usec = 0;
  894. c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
  895. test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
  896. redisFree(c);
  897. }
  898. /* Wrap malloc to abort on failure so OOM checks don't make the test logic
  899. * harder to follow. */
  900. void *hi_malloc_safe(size_t size) {
  901. void *ptr = hi_malloc(size);
  902. if (ptr == NULL) {
  903. fprintf(stderr, "Error: Out of memory\n");
  904. exit(-1);
  905. }
  906. return ptr;
  907. }
  908. static void test_throughput(struct config config) {
  909. redisContext *c = do_connect(config);
  910. redisReply **replies;
  911. int i, num;
  912. long long t1, t2;
  913. test("Throughput:\n");
  914. for (i = 0; i < 500; i++)
  915. freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
  916. num = 1000;
  917. replies = hi_malloc_safe(sizeof(redisReply*)*num);
  918. t1 = usec();
  919. for (i = 0; i < num; i++) {
  920. replies[i] = redisCommand(c,"PING");
  921. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
  922. }
  923. t2 = usec();
  924. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  925. hi_free(replies);
  926. printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
  927. replies = hi_malloc_safe(sizeof(redisReply*)*num);
  928. t1 = usec();
  929. for (i = 0; i < num; i++) {
  930. replies[i] = redisCommand(c,"LRANGE mylist 0 499");
  931. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
  932. assert(replies[i] != NULL && replies[i]->elements == 500);
  933. }
  934. t2 = usec();
  935. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  936. hi_free(replies);
  937. printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
  938. replies = hi_malloc_safe(sizeof(redisReply*)*num);
  939. t1 = usec();
  940. for (i = 0; i < num; i++) {
  941. replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
  942. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
  943. }
  944. t2 = usec();
  945. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  946. hi_free(replies);
  947. printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
  948. num = 10000;
  949. replies = hi_malloc_safe(sizeof(redisReply*)*num);
  950. for (i = 0; i < num; i++)
  951. redisAppendCommand(c,"PING");
  952. t1 = usec();
  953. for (i = 0; i < num; i++) {
  954. assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
  955. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
  956. }
  957. t2 = usec();
  958. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  959. hi_free(replies);
  960. printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
  961. replies = hi_malloc_safe(sizeof(redisReply*)*num);
  962. for (i = 0; i < num; i++)
  963. redisAppendCommand(c,"LRANGE mylist 0 499");
  964. t1 = usec();
  965. for (i = 0; i < num; i++) {
  966. assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
  967. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
  968. assert(replies[i] != NULL && replies[i]->elements == 500);
  969. }
  970. t2 = usec();
  971. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  972. hi_free(replies);
  973. printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
  974. replies = hi_malloc_safe(sizeof(redisReply*)*num);
  975. for (i = 0; i < num; i++)
  976. redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
  977. t1 = usec();
  978. for (i = 0; i < num; i++) {
  979. assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
  980. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
  981. }
  982. t2 = usec();
  983. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  984. hi_free(replies);
  985. printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
  986. disconnect(c, 0);
  987. }
  988. // static long __test_callback_flags = 0;
  989. // static void __test_callback(redisContext *c, void *privdata) {
  990. // ((void)c);
  991. // /* Shift to detect execution order */
  992. // __test_callback_flags <<= 8;
  993. // __test_callback_flags |= (long)privdata;
  994. // }
  995. //
  996. // static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {
  997. // ((void)c);
  998. // /* Shift to detect execution order */
  999. // __test_callback_flags <<= 8;
  1000. // __test_callback_flags |= (long)privdata;
  1001. // if (reply) freeReplyObject(reply);
  1002. // }
  1003. //
  1004. // static redisContext *__connect_nonblock() {
  1005. // /* Reset callback flags */
  1006. // __test_callback_flags = 0;
  1007. // return redisConnectNonBlock("127.0.0.1", port, NULL);
  1008. // }
  1009. //
  1010. // static void test_nonblocking_connection() {
  1011. // redisContext *c;
  1012. // int wdone = 0;
  1013. //
  1014. // test("Calls command callback when command is issued: ");
  1015. // c = __connect_nonblock();
  1016. // redisSetCommandCallback(c,__test_callback,(void*)1);
  1017. // redisCommand(c,"PING");
  1018. // test_cond(__test_callback_flags == 1);
  1019. // redisFree(c);
  1020. //
  1021. // test("Calls disconnect callback on redisDisconnect: ");
  1022. // c = __connect_nonblock();
  1023. // redisSetDisconnectCallback(c,__test_callback,(void*)2);
  1024. // redisDisconnect(c);
  1025. // test_cond(__test_callback_flags == 2);
  1026. // redisFree(c);
  1027. //
  1028. // test("Calls disconnect callback and free callback on redisFree: ");
  1029. // c = __connect_nonblock();
  1030. // redisSetDisconnectCallback(c,__test_callback,(void*)2);
  1031. // redisSetFreeCallback(c,__test_callback,(void*)4);
  1032. // redisFree(c);
  1033. // test_cond(__test_callback_flags == ((2 << 8) | 4));
  1034. //
  1035. // test("redisBufferWrite against empty write buffer: ");
  1036. // c = __connect_nonblock();
  1037. // test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
  1038. // redisFree(c);
  1039. //
  1040. // test("redisBufferWrite against not yet connected fd: ");
  1041. // c = __connect_nonblock();
  1042. // redisCommand(c,"PING");
  1043. // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
  1044. // strncmp(c->error,"write:",6) == 0);
  1045. // redisFree(c);
  1046. //
  1047. // test("redisBufferWrite against closed fd: ");
  1048. // c = __connect_nonblock();
  1049. // redisCommand(c,"PING");
  1050. // redisDisconnect(c);
  1051. // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
  1052. // strncmp(c->error,"write:",6) == 0);
  1053. // redisFree(c);
  1054. //
  1055. // test("Process callbacks in the right sequence: ");
  1056. // c = __connect_nonblock();
  1057. // redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
  1058. // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
  1059. // redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");
  1060. //
  1061. // /* Write output buffer */
  1062. // wdone = 0;
  1063. // while(!wdone) {
  1064. // usleep(500);
  1065. // redisBufferWrite(c,&wdone);
  1066. // }
  1067. //
  1068. // /* Read until at least one callback is executed (the 3 replies will
  1069. // * arrive in a single packet, causing all callbacks to be executed in
  1070. // * a single pass). */
  1071. // while(__test_callback_flags == 0) {
  1072. // assert(redisBufferRead(c) == REDIS_OK);
  1073. // redisProcessCallbacks(c);
  1074. // }
  1075. // test_cond(__test_callback_flags == 0x010203);
  1076. // redisFree(c);
  1077. //
  1078. // test("redisDisconnect executes pending callbacks with NULL reply: ");
  1079. // c = __connect_nonblock();
  1080. // redisSetDisconnectCallback(c,__test_callback,(void*)1);
  1081. // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
  1082. // redisDisconnect(c);
  1083. // test_cond(__test_callback_flags == 0x0201);
  1084. // redisFree(c);
  1085. // }
  1086. int main(int argc, char **argv) {
  1087. struct config cfg = {
  1088. .tcp = {
  1089. .host = "127.0.0.1",
  1090. .port = 6379
  1091. },
  1092. .unix_sock = {
  1093. .path = "/tmp/redis.sock"
  1094. }
  1095. };
  1096. int throughput = 1;
  1097. int test_inherit_fd = 1;
  1098. int skips_as_fails = 0;
  1099. int test_unix_socket;
  1100. /* Parse command line options. */
  1101. argv++; argc--;
  1102. while (argc) {
  1103. if (argc >= 2 && !strcmp(argv[0],"-h")) {
  1104. argv++; argc--;
  1105. cfg.tcp.host = argv[0];
  1106. } else if (argc >= 2 && !strcmp(argv[0],"-p")) {
  1107. argv++; argc--;
  1108. cfg.tcp.port = atoi(argv[0]);
  1109. } else if (argc >= 2 && !strcmp(argv[0],"-s")) {
  1110. argv++; argc--;
  1111. cfg.unix_sock.path = argv[0];
  1112. } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
  1113. throughput = 0;
  1114. } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
  1115. test_inherit_fd = 0;
  1116. } else if (argc >= 1 && !strcmp(argv[0],"--skips-as-fails")) {
  1117. skips_as_fails = 1;
  1118. #ifdef HIREDIS_TEST_SSL
  1119. } else if (argc >= 2 && !strcmp(argv[0],"--ssl-port")) {
  1120. argv++; argc--;
  1121. cfg.ssl.port = atoi(argv[0]);
  1122. } else if (argc >= 2 && !strcmp(argv[0],"--ssl-host")) {
  1123. argv++; argc--;
  1124. cfg.ssl.host = argv[0];
  1125. } else if (argc >= 2 && !strcmp(argv[0],"--ssl-ca-cert")) {
  1126. argv++; argc--;
  1127. cfg.ssl.ca_cert = argv[0];
  1128. } else if (argc >= 2 && !strcmp(argv[0],"--ssl-cert")) {
  1129. argv++; argc--;
  1130. cfg.ssl.cert = argv[0];
  1131. } else if (argc >= 2 && !strcmp(argv[0],"--ssl-key")) {
  1132. argv++; argc--;
  1133. cfg.ssl.key = argv[0];
  1134. #endif
  1135. } else {
  1136. fprintf(stderr, "Invalid argument: %s\n", argv[0]);
  1137. exit(1);
  1138. }
  1139. argv++; argc--;
  1140. }
  1141. #ifndef _WIN32
  1142. /* Ignore broken pipe signal (for I/O error tests). */
  1143. signal(SIGPIPE, SIG_IGN);
  1144. test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0;
  1145. #else
  1146. /* Unix sockets don't exist in Windows */
  1147. test_unix_socket = 0;
  1148. #endif
  1149. test_allocator_injection();
  1150. test_format_commands();
  1151. test_reply_reader();
  1152. test_blocking_connection_errors();
  1153. test_free_null();
  1154. printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
  1155. cfg.type = CONN_TCP;
  1156. test_blocking_connection(cfg);
  1157. test_blocking_connection_timeouts(cfg);
  1158. test_blocking_io_errors(cfg);
  1159. test_invalid_timeout_errors(cfg);
  1160. test_append_formatted_commands(cfg);
  1161. if (throughput) test_throughput(cfg);
  1162. printf("\nTesting against Unix socket connection (%s): ", cfg.unix_sock.path);
  1163. if (test_unix_socket) {
  1164. printf("\n");
  1165. cfg.type = CONN_UNIX;
  1166. test_blocking_connection(cfg);
  1167. test_blocking_connection_timeouts(cfg);
  1168. test_blocking_io_errors(cfg);
  1169. if (throughput) test_throughput(cfg);
  1170. } else {
  1171. test_skipped();
  1172. }
  1173. #ifdef HIREDIS_TEST_SSL
  1174. if (cfg.ssl.port && cfg.ssl.host) {
  1175. redisInitOpenSSL();
  1176. _ssl_ctx = redisCreateSSLContext(cfg.ssl.ca_cert, NULL, cfg.ssl.cert, cfg.ssl.key, NULL, NULL);
  1177. assert(_ssl_ctx != NULL);
  1178. printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port);
  1179. cfg.type = CONN_SSL;
  1180. test_blocking_connection(cfg);
  1181. test_blocking_connection_timeouts(cfg);
  1182. test_blocking_io_errors(cfg);
  1183. test_invalid_timeout_errors(cfg);
  1184. test_append_formatted_commands(cfg);
  1185. if (throughput) test_throughput(cfg);
  1186. redisFreeSSLContext(_ssl_ctx);
  1187. _ssl_ctx = NULL;
  1188. }
  1189. #endif
  1190. if (test_inherit_fd) {
  1191. printf("\nTesting against inherited fd (%s): ", cfg.unix_sock.path);
  1192. if (test_unix_socket) {
  1193. printf("\n");
  1194. cfg.type = CONN_FD;
  1195. test_blocking_connection(cfg);
  1196. } else {
  1197. test_skipped();
  1198. }
  1199. }
  1200. if (fails || (skips_as_fails && skips)) {
  1201. printf("*** %d TESTS FAILED ***\n", fails);
  1202. if (skips) {
  1203. printf("*** %d TESTS SKIPPED ***\n", skips);
  1204. }
  1205. return 1;
  1206. }
  1207. printf("ALL TESTS PASSED (%d skipped)\n", skips);
  1208. return 0;
  1209. }