xref: /memcached-1.4.29/testapp.c (revision 7edb1a03)
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #undef NDEBUG
3 #include <pthread.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <sys/wait.h>
7 #include <netdb.h>
8 #include <arpa/inet.h>
9 #include <netinet/in.h>
10 #include <netinet/tcp.h>
11 #include <signal.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <assert.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <netinet/in.h>
19 #include <fcntl.h>
20 
21 #include "config.h"
22 #include "cache.h"
23 #include "util.h"
24 #include "protocol_binary.h"
25 
26 #define TMP_TEMPLATE "/tmp/test_file.XXXXXXX"
27 
28 enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
29 
30 static pid_t server_pid;
31 static in_port_t port;
32 static int sock;
33 static bool allow_closed_read = false;
34 
cache_create_test(void)35 static enum test_return cache_create_test(void)
36 {
37     cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
38                                   NULL, NULL);
39     assert(cache != NULL);
40     cache_destroy(cache);
41     return TEST_PASS;
42 }
43 
44 const uint64_t constructor_pattern = 0xdeadcafebabebeef;
45 
cache_constructor(void * buffer,void * notused1,int notused2)46 static int cache_constructor(void *buffer, void *notused1, int notused2) {
47     uint64_t *ptr = buffer;
48     *ptr = constructor_pattern;
49     return 0;
50 }
51 
cache_constructor_test(void)52 static enum test_return cache_constructor_test(void)
53 {
54     cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
55                                   cache_constructor, NULL);
56     assert(cache != NULL);
57     uint64_t *ptr = cache_alloc(cache);
58     uint64_t pattern = *ptr;
59     cache_free(cache, ptr);
60     cache_destroy(cache);
61     return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
62 }
63 
cache_fail_constructor(void * buffer,void * notused1,int notused2)64 static int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
65     return 1;
66 }
67 
cache_fail_constructor_test(void)68 static enum test_return cache_fail_constructor_test(void)
69 {
70     enum test_return ret = TEST_PASS;
71 
72     cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
73                                   cache_fail_constructor, NULL);
74     assert(cache != NULL);
75     uint64_t *ptr = cache_alloc(cache);
76     if (ptr != NULL) {
77         ret = TEST_FAIL;
78     }
79     cache_destroy(cache);
80     return ret;
81 }
82 
83 static void *destruct_data = 0;
84 
cache_destructor(void * buffer,void * notused)85 static void cache_destructor(void *buffer, void *notused) {
86     destruct_data = buffer;
87 }
88 
cache_destructor_test(void)89 static enum test_return cache_destructor_test(void)
90 {
91     cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
92                                   NULL, cache_destructor);
93     assert(cache != NULL);
94     char *ptr = cache_alloc(cache);
95     cache_free(cache, ptr);
96     cache_destroy(cache);
97 
98     return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
99 }
100 
cache_reuse_test(void)101 static enum test_return cache_reuse_test(void)
102 {
103     int ii;
104     cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
105                                   NULL, NULL);
106     char *ptr = cache_alloc(cache);
107     cache_free(cache, ptr);
108     for (ii = 0; ii < 100; ++ii) {
109         char *p = cache_alloc(cache);
110         assert(p == ptr);
111         cache_free(cache, ptr);
112     }
113     cache_destroy(cache);
114     return TEST_PASS;
115 }
116 
117 
cache_bulkalloc(size_t datasize)118 static enum test_return cache_bulkalloc(size_t datasize)
119 {
120     cache_t *cache = cache_create("test", datasize, sizeof(char*),
121                                   NULL, NULL);
122 #define ITERATIONS 1024
123     void *ptr[ITERATIONS];
124 
125     for (int ii = 0; ii < ITERATIONS; ++ii) {
126         ptr[ii] = cache_alloc(cache);
127         assert(ptr[ii] != 0);
128         memset(ptr[ii], 0xff, datasize);
129     }
130 
131     for (int ii = 0; ii < ITERATIONS; ++ii) {
132         cache_free(cache, ptr[ii]);
133     }
134 
135 #undef ITERATIONS
136     cache_destroy(cache);
137     return TEST_PASS;
138 }
139 
test_issue_161(void)140 static enum test_return test_issue_161(void)
141 {
142     enum test_return ret = cache_bulkalloc(1);
143     if (ret == TEST_PASS) {
144         ret = cache_bulkalloc(512);
145     }
146 
147     return ret;
148 }
149 
cache_redzone_test(void)150 static enum test_return cache_redzone_test(void)
151 {
152 #ifndef HAVE_UMEM_H
153     cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
154                                   NULL, NULL);
155 
156     /* Ignore SIGABORT */
157     struct sigaction old_action;
158     struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0};
159     sigemptyset(&action.sa_mask);
160     sigaction(SIGABRT, &action, &old_action);
161 
162     /* check memory debug.. */
163     char *p = cache_alloc(cache);
164     char old = *(p - 1);
165     *(p - 1) = 0;
166     cache_free(cache, p);
167     assert(cache_error == -1);
168     *(p - 1) = old;
169 
170     p[sizeof(uint32_t)] = 0;
171     cache_free(cache, p);
172     assert(cache_error == 1);
173 
174     /* restore signal handler */
175     sigaction(SIGABRT, &old_action, NULL);
176 
177     cache_destroy(cache);
178 
179     return TEST_PASS;
180 #else
181     return TEST_SKIP;
182 #endif
183 }
184 
test_safe_strtoul(void)185 static enum test_return test_safe_strtoul(void) {
186     uint32_t val;
187     assert(safe_strtoul("123", &val));
188     assert(val == 123);
189     assert(safe_strtoul("+123", &val));
190     assert(val == 123);
191     assert(!safe_strtoul("", &val));  // empty
192     assert(!safe_strtoul("123BOGUS", &val));  // non-numeric
193     assert(!safe_strtoul(" issue221", &val));  // non-numeric
194     /* Not sure what it does, but this works with ICC :/
195        assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
196     */
197 
198     // extremes:
199     assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
200     assert(val == 4294967295L);
201     /* This actually works on 64-bit ubuntu
202        assert(!safe_strtoul("4294967296", &val)); // 2**32
203     */
204     assert(!safe_strtoul("-1", &val));  // negative
205     return TEST_PASS;
206 }
207 
208 
test_safe_strtoull(void)209 static enum test_return test_safe_strtoull(void) {
210     uint64_t val;
211     assert(safe_strtoull("123", &val));
212     assert(val == 123);
213     assert(safe_strtoull("+123", &val));
214     assert(val == 123);
215     assert(!safe_strtoull("", &val));  // empty
216     assert(!safe_strtoull("123BOGUS", &val));  // non-numeric
217     assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
218     assert(!safe_strtoull(" issue221", &val));  // non-numeric
219 
220     // extremes:
221     assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
222     assert(val == 18446744073709551615ULL);
223     assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
224     assert(!safe_strtoull("-1", &val));  // negative
225     return TEST_PASS;
226 }
227 
test_safe_strtoll(void)228 static enum test_return test_safe_strtoll(void) {
229     int64_t val;
230     assert(safe_strtoll("123", &val));
231     assert(val == 123);
232     assert(safe_strtoll("+123", &val));
233     assert(val == 123);
234     assert(safe_strtoll("-123", &val));
235     assert(val == -123);
236     assert(!safe_strtoll("", &val));  // empty
237     assert(!safe_strtoll("123BOGUS", &val));  // non-numeric
238     assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
239     assert(!safe_strtoll(" issue221", &val));  // non-numeric
240 
241     // extremes:
242     assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
243     assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
244     assert(val == 9223372036854775807LL);
245     /*
246       assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
247       assert(val == -9223372036854775808LL);
248     */
249     assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
250 
251     // We'll allow space to terminate the string.  And leading space.
252     assert(safe_strtoll(" 123 foo", &val));
253     assert(val == 123);
254     return TEST_PASS;
255 }
256 
test_safe_strtol(void)257 static enum test_return test_safe_strtol(void) {
258     int32_t val;
259     assert(safe_strtol("123", &val));
260     assert(val == 123);
261     assert(safe_strtol("+123", &val));
262     assert(val == 123);
263     assert(safe_strtol("-123", &val));
264     assert(val == -123);
265     assert(!safe_strtol("", &val));  // empty
266     assert(!safe_strtol("123BOGUS", &val));  // non-numeric
267     assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
268     assert(!safe_strtol(" issue221", &val));  // non-numeric
269 
270     // extremes:
271     /* This actually works on 64-bit ubuntu
272        assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
273     */
274     assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
275     assert(val == 2147483647L);
276     /* This actually works on 64-bit ubuntu
277        assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
278     */
279 
280     // We'll allow space to terminate the string.  And leading space.
281     assert(safe_strtol(" 123 foo", &val));
282     assert(val == 123);
283     return TEST_PASS;
284 }
285 
286 /**
287  * Function to start the server and let it listen on a random port
288  *
289  * @param port_out where to store the TCP port number the server is
290  *                 listening on
291  * @param daemon set to true if you want to run the memcached server
292  *               as a daemon process
293  * @return the pid of the memcached server
294  */
start_server(in_port_t * port_out,bool daemon,int timeout)295 static pid_t start_server(in_port_t *port_out, bool daemon, int timeout) {
296     char environment[80];
297     snprintf(environment, sizeof(environment),
298              "MEMCACHED_PORT_FILENAME=/tmp/ports.%lu", (long)getpid());
299     char *filename= environment + strlen("MEMCACHED_PORT_FILENAME=");
300     char pid_file[80];
301     snprintf(pid_file, sizeof(pid_file), "/tmp/pid.%lu", (long)getpid());
302 
303     remove(filename);
304     remove(pid_file);
305 
306 #ifdef __sun
307     /* I want to name the corefiles differently so that they don't
308        overwrite each other
309     */
310     char coreadm[128];
311     snprintf(coreadm, sizeof(coreadm),
312              "coreadm -p core.%%f.%%p %lu", (unsigned long)getpid());
313     system(coreadm);
314 #endif
315 
316     pid_t pid = fork();
317     assert(pid != -1);
318 
319     if (pid == 0) {
320         /* Child */
321         char *argv[20];
322         int arg = 0;
323         char tmo[24];
324         snprintf(tmo, sizeof(tmo), "%u", timeout);
325 
326         putenv(environment);
327 #ifdef __sun
328         putenv("LD_PRELOAD=watchmalloc.so.1");
329         putenv("MALLOC_DEBUG=WATCH");
330 #endif
331 
332         if (!daemon) {
333             argv[arg++] = "./timedrun";
334             argv[arg++] = tmo;
335         }
336         argv[arg++] = "./memcached-debug";
337         argv[arg++] = "-A";
338         argv[arg++] = "-p";
339         argv[arg++] = "-1";
340         argv[arg++] = "-U";
341         argv[arg++] = "0";
342         /* Handle rpmbuild and the like doing this as root */
343         if (getuid() == 0) {
344             argv[arg++] = "-u";
345             argv[arg++] = "root";
346         }
347         if (daemon) {
348             argv[arg++] = "-d";
349             argv[arg++] = "-P";
350             argv[arg++] = pid_file;
351         }
352 #ifdef MESSAGE_DEBUG
353          argv[arg++] = "-vvv";
354 #endif
355         argv[arg++] = NULL;
356         assert(execv(argv[0], argv) != -1);
357     }
358 
359     /* Yeah just let us "busy-wait" for the file to be created ;-) */
360     while (access(filename, F_OK) == -1) {
361         usleep(10);
362     }
363 
364     FILE *fp = fopen(filename, "r");
365     if (fp == NULL) {
366         fprintf(stderr, "Failed to open the file containing port numbers: %s\n",
367                 strerror(errno));
368         assert(false);
369     }
370 
371     *port_out = (in_port_t)-1;
372     char buffer[80];
373     while ((fgets(buffer, sizeof(buffer), fp)) != NULL) {
374         if (strncmp(buffer, "TCP INET: ", 10) == 0) {
375             int32_t val;
376             assert(safe_strtol(buffer + 10, &val));
377             *port_out = (in_port_t)val;
378         }
379     }
380     fclose(fp);
381     assert(remove(filename) == 0);
382 
383     if (daemon) {
384         /* loop and wait for the pid file.. There is a potential race
385          * condition that the server just created the file but isn't
386          * finished writing the content, so we loop a few times
387          * reading as well */
388         while (access(pid_file, F_OK) == -1) {
389             usleep(10);
390         }
391 
392         fp = fopen(pid_file, "r");
393         if (fp == NULL) {
394             fprintf(stderr, "Failed to open pid file: %s\n",
395                     strerror(errno));
396             assert(false);
397         }
398 
399         /* Avoid race by retrying 20 times */
400         for (int x = 0; x < 20 && fgets(buffer, sizeof(buffer), fp) == NULL; x++) {
401             usleep(10);
402         }
403         fclose(fp);
404 
405         int32_t val;
406         assert(safe_strtol(buffer, &val));
407         pid = (pid_t)val;
408     }
409 
410     return pid;
411 }
412 
test_issue_44(void)413 static enum test_return test_issue_44(void) {
414     in_port_t port;
415     pid_t pid = start_server(&port, true, 15);
416     assert(kill(pid, SIGHUP) == 0);
417     sleep(1);
418     assert(kill(pid, SIGTERM) == 0);
419 
420     return TEST_PASS;
421 }
422 
lookuphost(const char * hostname,in_port_t port)423 static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
424 {
425     struct addrinfo *ai = 0;
426     struct addrinfo hints = { .ai_family = AF_UNSPEC,
427                               .ai_protocol = IPPROTO_TCP,
428                               .ai_socktype = SOCK_STREAM };
429     char service[NI_MAXSERV];
430     int error;
431 
432     (void)snprintf(service, NI_MAXSERV, "%d", port);
433     if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
434        if (error != EAI_SYSTEM) {
435           fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
436        } else {
437           perror("getaddrinfo()");
438        }
439     }
440 
441     return ai;
442 }
443 
connect_server(const char * hostname,in_port_t port,bool nonblock)444 static int connect_server(const char *hostname, in_port_t port, bool nonblock)
445 {
446     struct addrinfo *ai = lookuphost(hostname, port);
447     int sock = -1;
448     if (ai != NULL) {
449        if ((sock = socket(ai->ai_family, ai->ai_socktype,
450                           ai->ai_protocol)) != -1) {
451           if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
452              fprintf(stderr, "Failed to connect socket: %s\n",
453                      strerror(errno));
454              close(sock);
455              sock = -1;
456           } else if (nonblock) {
457               int flags = fcntl(sock, F_GETFL, 0);
458               if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
459                   fprintf(stderr, "Failed to enable nonblocking mode: %s\n",
460                           strerror(errno));
461                   close(sock);
462                   sock = -1;
463               }
464           }
465        } else {
466           fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
467        }
468 
469        freeaddrinfo(ai);
470     }
471     return sock;
472 }
473 
test_vperror(void)474 static enum test_return test_vperror(void) {
475     int rv = 0;
476     int oldstderr = dup(STDERR_FILENO);
477     char tmpl[sizeof(TMP_TEMPLATE)+1];
478     strncpy(tmpl, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
479 
480     int newfile = mkstemp(tmpl);
481     assert(newfile > 0);
482     rv = dup2(newfile, STDERR_FILENO);
483     assert(rv == STDERR_FILENO);
484     rv = close(newfile);
485     assert(rv == 0);
486 
487     errno = EIO;
488     vperror("Old McDonald had a farm.  %s", "EI EIO");
489 
490     /* Restore stderr */
491     rv = dup2(oldstderr, STDERR_FILENO);
492     assert(rv == STDERR_FILENO);
493 
494 
495     /* Go read the file */
496     char buf[80] = { 0 };
497     FILE *efile = fopen(tmpl, "r");
498     assert(efile);
499     char *prv = fgets(buf, sizeof(buf), efile);
500     assert(prv);
501     fclose(efile);
502 
503     unlink(tmpl);
504 
505     char expected[80] = { 0 };
506     snprintf(expected, sizeof(expected),
507              "Old McDonald had a farm.  EI EIO: %s\n", strerror(EIO));
508 
509     /*
510     fprintf(stderr,
511             "\nExpected:  ``%s''"
512             "\nGot:       ``%s''\n", expected, buf);
513     */
514 
515     return strcmp(expected, buf) == 0 ? TEST_PASS : TEST_FAIL;
516 }
517 
send_ascii_command(const char * buf)518 static void send_ascii_command(const char *buf) {
519     off_t offset = 0;
520     const char* ptr = buf;
521     size_t len = strlen(buf);
522 
523     do {
524         ssize_t nw = write(sock, ptr + offset, len - offset);
525         if (nw == -1) {
526             if (errno != EINTR) {
527                 fprintf(stderr, "Failed to write: %s\n", strerror(errno));
528                 abort();
529             }
530         } else {
531             offset += nw;
532         }
533     } while (offset < len);
534 }
535 
536 /*
537  * This is a dead slow single byte read, but it should only read out
538  * _one_ response and I don't have an input buffer... The current
539  * implementation only supports single-line responses, so if you want to use
540  * it for get commands you need to implement that first ;-)
541  */
read_ascii_response(char * buffer,size_t size)542 static void read_ascii_response(char *buffer, size_t size) {
543     off_t offset = 0;
544     bool need_more = true;
545     do {
546         ssize_t nr = read(sock, buffer + offset, 1);
547         if (nr == -1) {
548             if (errno != EINTR) {
549                 fprintf(stderr, "Failed to read: %s\n", strerror(errno));
550                 abort();
551             }
552         } else {
553             assert(nr == 1);
554             if (buffer[offset] == '\n') {
555                 need_more = false;
556                 buffer[offset + 1] = '\0';
557             }
558             offset += nr;
559             assert(offset + 1 < size);
560         }
561     } while (need_more);
562 }
563 
test_issue_92(void)564 static enum test_return test_issue_92(void) {
565     char buffer[1024];
566 
567     close(sock);
568     sock = connect_server("127.0.0.1", port, false);
569 
570     send_ascii_command("stats cachedump 1 0 0\r\n");
571     read_ascii_response(buffer, sizeof(buffer));
572     assert(strncmp(buffer, "END", strlen("END")) == 0);
573 
574     send_ascii_command("stats cachedump 200 0 0\r\n");
575     read_ascii_response(buffer, sizeof(buffer));
576     assert(strncmp(buffer, "CLIENT_ERROR", strlen("CLIENT_ERROR")) == 0);
577 
578     close(sock);
579     sock = connect_server("127.0.0.1", port, false);
580     return TEST_PASS;
581 }
582 
test_issue_102(void)583 static enum test_return test_issue_102(void) {
584     char buffer[4096];
585     memset(buffer, ' ', sizeof(buffer));
586     buffer[sizeof(buffer) - 1] = '\0';
587 
588     close(sock);
589     sock = connect_server("127.0.0.1", port, false);
590 
591     send_ascii_command(buffer);
592     /* verify that the server closed the connection */
593     assert(read(sock, buffer, sizeof(buffer)) == 0);
594     close(sock);
595     sock = connect_server("127.0.0.1", port, false);
596 
597     snprintf(buffer, sizeof(buffer), "gets ");
598     size_t offset = 5;
599     while (offset < 4000) {
600         offset += snprintf(buffer + offset, sizeof(buffer) - offset,
601                            "%010u ", (unsigned int)offset);
602     }
603 
604     send_ascii_command(buffer);
605     usleep(250);
606 
607     send_ascii_command("\r\n");
608     char rsp[80];
609     read_ascii_response(rsp, sizeof(rsp));
610     assert(strncmp(rsp, "END", strlen("END")) == 0);
611     buffer[3]= ' ';
612     send_ascii_command(buffer);
613     usleep(250);
614     send_ascii_command("\r\n");
615     read_ascii_response(rsp, sizeof(rsp));
616     assert(strncmp(rsp, "END", strlen("END")) == 0);
617 
618     memset(buffer, ' ', sizeof(buffer));
619     int len = snprintf(buffer + 101, sizeof(buffer) - 101, "gets foo");
620     buffer[101 + len] = ' ';
621     buffer[sizeof(buffer) - 1] = '\0';
622     send_ascii_command(buffer);
623     /* verify that the server closed the connection */
624     assert(read(sock, buffer, sizeof(buffer)) == 0);
625 
626     close(sock);
627     sock = connect_server("127.0.0.1", port, false);
628 
629     return TEST_PASS;
630 }
631 
start_memcached_server(void)632 static enum test_return start_memcached_server(void) {
633     server_pid = start_server(&port, false, 600);
634     sock = connect_server("127.0.0.1", port, false);
635     return TEST_PASS;
636 }
637 
stop_memcached_server(void)638 static enum test_return stop_memcached_server(void) {
639     close(sock);
640     if (server_pid != -1) {
641         assert(kill(server_pid, SIGTERM) == 0);
642     }
643 
644     return TEST_PASS;
645 }
646 
shutdown_memcached_server(void)647 static enum test_return shutdown_memcached_server(void) {
648     char buffer[1024];
649 
650     close(sock);
651     sock = connect_server("127.0.0.1", port, false);
652 
653     send_ascii_command("shutdown\r\n");
654     /* verify that the server closed the connection */
655     assert(read(sock, buffer, sizeof(buffer)) == 0);
656 
657     close(sock);
658 
659     /* We set server_pid to -1 so that we don't later call kill() */
660     if (kill(server_pid, 0) == 0) {
661         server_pid = -1;
662     }
663 
664     return TEST_PASS;
665 }
666 
safe_send(const void * buf,size_t len,bool hickup)667 static void safe_send(const void* buf, size_t len, bool hickup)
668 {
669     off_t offset = 0;
670     const char* ptr = buf;
671 #ifdef MESSAGE_DEBUG
672     uint8_t val = *ptr;
673     assert(val == (uint8_t)0x80);
674     fprintf(stderr, "About to send %lu bytes:", (unsigned long)len);
675     for (int ii = 0; ii < len; ++ii) {
676         if (ii % 4 == 0) {
677             fprintf(stderr, "\n   ");
678         }
679         val = *(ptr + ii);
680         fprintf(stderr, " 0x%02x", val);
681     }
682     fprintf(stderr, "\n");
683     usleep(500);
684 #endif
685 
686     do {
687         size_t num_bytes = len - offset;
688         if (hickup) {
689             if (num_bytes > 1024) {
690                 num_bytes = (rand() % 1023) + 1;
691             }
692         }
693 
694         ssize_t nw = write(sock, ptr + offset, num_bytes);
695         if (nw == -1) {
696             if (errno != EINTR) {
697                 fprintf(stderr, "Failed to write: %s\n", strerror(errno));
698                 abort();
699             }
700         } else {
701             if (hickup) {
702                 usleep(100);
703             }
704             offset += nw;
705         }
706     } while (offset < len);
707 }
708 
safe_recv(void * buf,size_t len)709 static bool safe_recv(void *buf, size_t len) {
710     if (len == 0) {
711         return true;
712     }
713     off_t offset = 0;
714     do {
715         ssize_t nr = read(sock, ((char*)buf) + offset, len - offset);
716         if (nr == -1) {
717             if (errno != EINTR) {
718                 fprintf(stderr, "Failed to read: %s\n", strerror(errno));
719                 abort();
720             }
721         } else {
722             if (nr == 0 && allow_closed_read) {
723                 return false;
724             }
725             assert(nr != 0);
726             offset += nr;
727         }
728     } while (offset < len);
729 
730     return true;
731 }
732 
safe_recv_packet(void * buf,size_t size)733 static bool safe_recv_packet(void *buf, size_t size) {
734     protocol_binary_response_no_extras *response = buf;
735     assert(size > sizeof(*response));
736     if (!safe_recv(response, sizeof(*response))) {
737         return false;
738     }
739     response->message.header.response.keylen = ntohs(response->message.header.response.keylen);
740     response->message.header.response.status = ntohs(response->message.header.response.status);
741     response->message.header.response.bodylen = ntohl(response->message.header.response.bodylen);
742 
743     size_t len = sizeof(*response);
744 
745     char *ptr = buf;
746     ptr += len;
747     if (!safe_recv(ptr, response->message.header.response.bodylen)) {
748         return false;
749     }
750 
751 #ifdef MESSAGE_DEBUG
752     usleep(500);
753     ptr = buf;
754     len += response->message.header.response.bodylen;
755     uint8_t val = *ptr;
756     assert(val == (uint8_t)0x81);
757     fprintf(stderr, "Received %lu bytes:", (unsigned long)len);
758     for (int ii = 0; ii < len; ++ii) {
759         if (ii % 4 == 0) {
760             fprintf(stderr, "\n   ");
761         }
762         val = *(ptr + ii);
763         fprintf(stderr, " 0x%02x", val);
764     }
765     fprintf(stderr, "\n");
766 #endif
767     return true;
768 }
769 
storage_command(char * buf,size_t bufsz,uint8_t cmd,const void * key,size_t keylen,const void * dta,size_t dtalen,uint32_t flags,uint32_t exp)770 static off_t storage_command(char*buf,
771                              size_t bufsz,
772                              uint8_t cmd,
773                              const void* key,
774                              size_t keylen,
775                              const void* dta,
776                              size_t dtalen,
777                              uint32_t flags,
778                              uint32_t exp) {
779     /* all of the storage commands use the same command layout */
780     protocol_binary_request_set *request = (void*)buf;
781     assert(bufsz > sizeof(*request) + keylen + dtalen);
782 
783     memset(request, 0, sizeof(*request));
784     request->message.header.request.magic = PROTOCOL_BINARY_REQ;
785     request->message.header.request.opcode = cmd;
786     request->message.header.request.keylen = htons(keylen);
787     request->message.header.request.extlen = 8;
788     request->message.header.request.bodylen = htonl(keylen + 8 + dtalen);
789     request->message.header.request.opaque = 0xdeadbeef;
790     request->message.body.flags = flags;
791     request->message.body.expiration = exp;
792 
793     off_t key_offset = sizeof(protocol_binary_request_no_extras) + 8;
794 
795     memcpy(buf + key_offset, key, keylen);
796     if (dta != NULL) {
797         memcpy(buf + key_offset + keylen, dta, dtalen);
798     }
799 
800     return key_offset + keylen + dtalen;
801 }
802 
ext_command(char * buf,size_t bufsz,uint8_t cmd,const void * ext,size_t extlen,const void * key,size_t keylen,const void * dta,size_t dtalen)803 static off_t ext_command(char* buf,
804                          size_t bufsz,
805                          uint8_t cmd,
806                          const void* ext,
807                          size_t extlen,
808                          const void* key,
809                          size_t keylen,
810                          const void* dta,
811                          size_t dtalen) {
812     protocol_binary_request_no_extras *request = (void*)buf;
813     assert(bufsz > sizeof(*request) + extlen + keylen + dtalen);
814 
815     memset(request, 0, sizeof(*request));
816     request->message.header.request.magic = PROTOCOL_BINARY_REQ;
817     request->message.header.request.opcode = cmd;
818     request->message.header.request.extlen = extlen;
819     request->message.header.request.keylen = htons(keylen);
820     request->message.header.request.bodylen = htonl(extlen + keylen + dtalen);
821     request->message.header.request.opaque = 0xdeadbeef;
822 
823     off_t ext_offset = sizeof(protocol_binary_request_no_extras);
824     off_t key_offset = ext_offset + extlen;
825     off_t dta_offset = key_offset + keylen;
826 
827     if (ext != NULL) {
828         memcpy(buf + ext_offset, ext, extlen);
829     }
830     if (key != NULL) {
831         memcpy(buf + key_offset, key, keylen);
832     }
833     if (dta != NULL) {
834         memcpy(buf + dta_offset, dta, dtalen);
835     }
836 
837     return sizeof(*request) + extlen + keylen + dtalen;
838 }
839 
raw_command(char * buf,size_t bufsz,uint8_t cmd,const void * key,size_t keylen,const void * dta,size_t dtalen)840 static off_t raw_command(char* buf,
841                          size_t bufsz,
842                          uint8_t cmd,
843                          const void* key,
844                          size_t keylen,
845                          const void* dta,
846                          size_t dtalen) {
847     /* all of the storage commands use the same command layout */
848     return ext_command(buf, bufsz, cmd, NULL, 0, key, keylen, dta, dtalen);
849 }
850 
flush_command(char * buf,size_t bufsz,uint8_t cmd,uint32_t exptime,bool use_extra)851 static off_t flush_command(char* buf, size_t bufsz, uint8_t cmd, uint32_t exptime, bool use_extra) {
852     protocol_binary_request_flush *request = (void*)buf;
853     assert(bufsz > sizeof(*request));
854 
855     memset(request, 0, sizeof(*request));
856     request->message.header.request.magic = PROTOCOL_BINARY_REQ;
857     request->message.header.request.opcode = cmd;
858 
859     off_t size = sizeof(protocol_binary_request_no_extras);
860     if (use_extra) {
861         request->message.header.request.extlen = 4;
862         request->message.body.expiration = htonl(exptime);
863         request->message.header.request.bodylen = htonl(4);
864         size += 4;
865     }
866 
867     request->message.header.request.opaque = 0xdeadbeef;
868 
869     return size;
870 }
871 
872 
touch_command(char * buf,size_t bufsz,uint8_t cmd,const void * key,size_t keylen,uint32_t exptime)873 static off_t touch_command(char* buf,
874                            size_t bufsz,
875                            uint8_t cmd,
876                            const void* key,
877                            size_t keylen,
878                            uint32_t exptime) {
879     protocol_binary_request_touch *request = (void*)buf;
880     assert(bufsz > sizeof(*request));
881 
882     memset(request, 0, sizeof(*request));
883     request->message.header.request.magic = PROTOCOL_BINARY_REQ;
884     request->message.header.request.opcode = cmd;
885 
886     request->message.header.request.keylen = htons(keylen);
887     request->message.header.request.extlen = 4;
888     request->message.body.expiration = htonl(exptime);
889     request->message.header.request.bodylen = htonl(keylen + 4);
890 
891     request->message.header.request.opaque = 0xdeadbeef;
892 
893     off_t key_offset = sizeof(protocol_binary_request_no_extras) + 4;
894 
895     memcpy(buf + key_offset, key, keylen);
896     return sizeof(protocol_binary_request_no_extras) + 4 + keylen;
897 }
898 
arithmetic_command(char * buf,size_t bufsz,uint8_t cmd,const void * key,size_t keylen,uint64_t delta,uint64_t initial,uint32_t exp)899 static off_t arithmetic_command(char* buf,
900                                 size_t bufsz,
901                                 uint8_t cmd,
902                                 const void* key,
903                                 size_t keylen,
904                                 uint64_t delta,
905                                 uint64_t initial,
906                                 uint32_t exp) {
907     protocol_binary_request_incr *request = (void*)buf;
908     assert(bufsz > sizeof(*request) + keylen);
909 
910     memset(request, 0, sizeof(*request));
911     request->message.header.request.magic = PROTOCOL_BINARY_REQ;
912     request->message.header.request.opcode = cmd;
913     request->message.header.request.keylen = htons(keylen);
914     request->message.header.request.extlen = 20;
915     request->message.header.request.bodylen = htonl(keylen + 20);
916     request->message.header.request.opaque = 0xdeadbeef;
917     request->message.body.delta = htonll(delta);
918     request->message.body.initial = htonll(initial);
919     request->message.body.expiration = htonl(exp);
920 
921     off_t key_offset = sizeof(protocol_binary_request_no_extras) + 20;
922 
923     memcpy(buf + key_offset, key, keylen);
924     return key_offset + keylen;
925 }
926 
validate_response_header(protocol_binary_response_no_extras * response,uint8_t cmd,uint16_t status)927 static void validate_response_header(protocol_binary_response_no_extras *response,
928                                      uint8_t cmd, uint16_t status)
929 {
930     assert(response->message.header.response.magic == PROTOCOL_BINARY_RES);
931     assert(response->message.header.response.opcode == cmd);
932     assert(response->message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
933     assert(response->message.header.response.status == status);
934     assert(response->message.header.response.opaque == 0xdeadbeef);
935 
936     if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
937         switch (cmd) {
938         case PROTOCOL_BINARY_CMD_ADDQ:
939         case PROTOCOL_BINARY_CMD_APPENDQ:
940         case PROTOCOL_BINARY_CMD_DECREMENTQ:
941         case PROTOCOL_BINARY_CMD_DELETEQ:
942         case PROTOCOL_BINARY_CMD_FLUSHQ:
943         case PROTOCOL_BINARY_CMD_INCREMENTQ:
944         case PROTOCOL_BINARY_CMD_PREPENDQ:
945         case PROTOCOL_BINARY_CMD_QUITQ:
946         case PROTOCOL_BINARY_CMD_REPLACEQ:
947         case PROTOCOL_BINARY_CMD_SETQ:
948             assert("Quiet command shouldn't return on success" == NULL);
949         default:
950             break;
951         }
952 
953         switch (cmd) {
954         case PROTOCOL_BINARY_CMD_ADD:
955         case PROTOCOL_BINARY_CMD_REPLACE:
956         case PROTOCOL_BINARY_CMD_SET:
957         case PROTOCOL_BINARY_CMD_APPEND:
958         case PROTOCOL_BINARY_CMD_PREPEND:
959             assert(response->message.header.response.keylen == 0);
960             assert(response->message.header.response.extlen == 0);
961             assert(response->message.header.response.bodylen == 0);
962             assert(response->message.header.response.cas != 0);
963             break;
964         case PROTOCOL_BINARY_CMD_FLUSH:
965         case PROTOCOL_BINARY_CMD_NOOP:
966         case PROTOCOL_BINARY_CMD_QUIT:
967         case PROTOCOL_BINARY_CMD_DELETE:
968             assert(response->message.header.response.keylen == 0);
969             assert(response->message.header.response.extlen == 0);
970             assert(response->message.header.response.bodylen == 0);
971             assert(response->message.header.response.cas == 0);
972             break;
973 
974         case PROTOCOL_BINARY_CMD_DECREMENT:
975         case PROTOCOL_BINARY_CMD_INCREMENT:
976             assert(response->message.header.response.keylen == 0);
977             assert(response->message.header.response.extlen == 0);
978             assert(response->message.header.response.bodylen == 8);
979             assert(response->message.header.response.cas != 0);
980             break;
981 
982         case PROTOCOL_BINARY_CMD_STAT:
983             assert(response->message.header.response.extlen == 0);
984             /* key and value exists in all packets except in the terminating */
985             assert(response->message.header.response.cas == 0);
986             break;
987 
988         case PROTOCOL_BINARY_CMD_VERSION:
989             assert(response->message.header.response.keylen == 0);
990             assert(response->message.header.response.extlen == 0);
991             assert(response->message.header.response.bodylen != 0);
992             assert(response->message.header.response.cas == 0);
993             break;
994 
995         case PROTOCOL_BINARY_CMD_GET:
996         case PROTOCOL_BINARY_CMD_GETQ:
997         case PROTOCOL_BINARY_CMD_GAT:
998         case PROTOCOL_BINARY_CMD_GATQ:
999             assert(response->message.header.response.keylen == 0);
1000             assert(response->message.header.response.extlen == 4);
1001             assert(response->message.header.response.cas != 0);
1002             break;
1003 
1004         case PROTOCOL_BINARY_CMD_GETK:
1005         case PROTOCOL_BINARY_CMD_GETKQ:
1006         case PROTOCOL_BINARY_CMD_GATK:
1007         case PROTOCOL_BINARY_CMD_GATKQ:
1008             assert(response->message.header.response.keylen != 0);
1009             assert(response->message.header.response.extlen == 4);
1010             assert(response->message.header.response.cas != 0);
1011             break;
1012 
1013         default:
1014             /* Undefined command code */
1015             break;
1016         }
1017     } else {
1018         assert(response->message.header.response.cas == 0);
1019         assert(response->message.header.response.extlen == 0);
1020         if (cmd != PROTOCOL_BINARY_CMD_GETK &&
1021             cmd != PROTOCOL_BINARY_CMD_GATK) {
1022             assert(response->message.header.response.keylen == 0);
1023         }
1024     }
1025 }
1026 
test_binary_noop(void)1027 static enum test_return test_binary_noop(void) {
1028     union {
1029         protocol_binary_request_no_extras request;
1030         protocol_binary_response_no_extras response;
1031         char bytes[1024];
1032     } buffer;
1033 
1034     size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1035                              PROTOCOL_BINARY_CMD_NOOP,
1036                              NULL, 0, NULL, 0);
1037 
1038     safe_send(buffer.bytes, len, false);
1039     safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1040     validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_NOOP,
1041                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1042 
1043     return TEST_PASS;
1044 }
1045 
test_binary_quit_impl(uint8_t cmd)1046 static enum test_return test_binary_quit_impl(uint8_t cmd) {
1047     union {
1048         protocol_binary_request_no_extras request;
1049         protocol_binary_response_no_extras response;
1050         char bytes[1024];
1051     } buffer;
1052     size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1053                              cmd, NULL, 0, NULL, 0);
1054 
1055     safe_send(buffer.bytes, len, false);
1056     if (cmd == PROTOCOL_BINARY_CMD_QUIT) {
1057         safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1058         validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_QUIT,
1059                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1060     }
1061 
1062     /* Socket should be closed now, read should return 0 */
1063     assert(read(sock, buffer.bytes, sizeof(buffer.bytes)) == 0);
1064     close(sock);
1065     sock = connect_server("127.0.0.1", port, false);
1066 
1067     return TEST_PASS;
1068 }
1069 
test_binary_quit(void)1070 static enum test_return test_binary_quit(void) {
1071     return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
1072 }
1073 
test_binary_quitq(void)1074 static enum test_return test_binary_quitq(void) {
1075     return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
1076 }
1077 
test_binary_set_impl(const char * key,uint8_t cmd)1078 static enum test_return test_binary_set_impl(const char *key, uint8_t cmd) {
1079     union {
1080         protocol_binary_request_no_extras request;
1081         protocol_binary_response_no_extras response;
1082         char bytes[1024];
1083     } send, receive;
1084     uint64_t value = 0xdeadbeefdeadcafe;
1085     size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1086                                  key, strlen(key), &value, sizeof(value),
1087                                  0, 0);
1088 
1089     /* Set should work over and over again */
1090     int ii;
1091     for (ii = 0; ii < 10; ++ii) {
1092         safe_send(send.bytes, len, false);
1093         if (cmd == PROTOCOL_BINARY_CMD_SET) {
1094             safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1095             validate_response_header(&receive.response, cmd,
1096                                      PROTOCOL_BINARY_RESPONSE_SUCCESS);
1097         }
1098     }
1099 
1100     if (cmd == PROTOCOL_BINARY_CMD_SETQ) {
1101         return test_binary_noop();
1102     }
1103 
1104     send.request.message.header.request.cas = receive.response.message.header.response.cas;
1105     safe_send(send.bytes, len, false);
1106     if (cmd == PROTOCOL_BINARY_CMD_SET) {
1107         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1108         validate_response_header(&receive.response, cmd,
1109                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1110         assert(receive.response.message.header.response.cas != send.request.message.header.request.cas);
1111     } else {
1112         return test_binary_noop();
1113     }
1114 
1115     return TEST_PASS;
1116 }
1117 
test_binary_set(void)1118 static enum test_return test_binary_set(void) {
1119     return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
1120 }
1121 
test_binary_setq(void)1122 static enum test_return test_binary_setq(void) {
1123     return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
1124 }
1125 
1126 
test_binary_add_impl(const char * key,uint8_t cmd)1127 static enum test_return test_binary_add_impl(const char *key, uint8_t cmd) {
1128     uint64_t value = 0xdeadbeefdeadcafe;
1129     union {
1130         protocol_binary_request_no_extras request;
1131         protocol_binary_response_no_extras response;
1132         char bytes[1024];
1133     } send, receive;
1134     size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd, key,
1135                                  strlen(key), &value, sizeof(value),
1136                                  0, 0);
1137 
1138     /* Add should only work the first time */
1139     int ii;
1140     for (ii = 0; ii < 10; ++ii) {
1141         safe_send(send.bytes, len, false);
1142         if (ii == 0) {
1143             if (cmd == PROTOCOL_BINARY_CMD_ADD) {
1144                 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1145                 validate_response_header(&receive.response, cmd,
1146                                          PROTOCOL_BINARY_RESPONSE_SUCCESS);
1147             }
1148         } else {
1149             safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1150             validate_response_header(&receive.response, cmd,
1151                                      PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
1152         }
1153     }
1154 
1155     return TEST_PASS;
1156 }
1157 
test_binary_add(void)1158 static enum test_return test_binary_add(void) {
1159     return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
1160 }
1161 
test_binary_addq(void)1162 static enum test_return test_binary_addq(void) {
1163     return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
1164 }
1165 
test_binary_replace_impl(const char * key,uint8_t cmd)1166 static enum test_return test_binary_replace_impl(const char* key, uint8_t cmd) {
1167     uint64_t value = 0xdeadbeefdeadcafe;
1168     union {
1169         protocol_binary_request_no_extras request;
1170         protocol_binary_response_no_extras response;
1171         char bytes[1024];
1172     } send, receive;
1173     size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1174                                  key, strlen(key), &value, sizeof(value),
1175                                  0, 0);
1176     safe_send(send.bytes, len, false);
1177     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1178     validate_response_header(&receive.response, cmd,
1179                              PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1180     len = storage_command(send.bytes, sizeof(send.bytes),
1181                           PROTOCOL_BINARY_CMD_ADD,
1182                           key, strlen(key), &value, sizeof(value), 0, 0);
1183     safe_send(send.bytes, len, false);
1184     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1185     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1186                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1187 
1188     len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1189                           key, strlen(key), &value, sizeof(value), 0, 0);
1190     int ii;
1191     for (ii = 0; ii < 10; ++ii) {
1192         safe_send(send.bytes, len, false);
1193         if (cmd == PROTOCOL_BINARY_CMD_REPLACE) {
1194             safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1195             validate_response_header(&receive.response,
1196                                      PROTOCOL_BINARY_CMD_REPLACE,
1197                                      PROTOCOL_BINARY_RESPONSE_SUCCESS);
1198         }
1199     }
1200 
1201     if (cmd == PROTOCOL_BINARY_CMD_REPLACEQ) {
1202         test_binary_noop();
1203     }
1204 
1205     return TEST_PASS;
1206 }
1207 
test_binary_replace(void)1208 static enum test_return test_binary_replace(void) {
1209     return test_binary_replace_impl("test_binary_replace",
1210                                     PROTOCOL_BINARY_CMD_REPLACE);
1211 }
1212 
test_binary_replaceq(void)1213 static enum test_return test_binary_replaceq(void) {
1214     return test_binary_replace_impl("test_binary_replaceq",
1215                                     PROTOCOL_BINARY_CMD_REPLACEQ);
1216 }
1217 
test_binary_delete_impl(const char * key,uint8_t cmd)1218 static enum test_return test_binary_delete_impl(const char *key, uint8_t cmd) {
1219     union {
1220         protocol_binary_request_no_extras request;
1221         protocol_binary_response_no_extras response;
1222         char bytes[1024];
1223     } send, receive;
1224     size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1225                              key, strlen(key), NULL, 0);
1226 
1227     safe_send(send.bytes, len, false);
1228     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1229     validate_response_header(&receive.response, cmd,
1230                              PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1231     len = storage_command(send.bytes, sizeof(send.bytes),
1232                           PROTOCOL_BINARY_CMD_ADD,
1233                           key, strlen(key), NULL, 0, 0, 0);
1234     safe_send(send.bytes, len, false);
1235     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1236     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1237                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1238 
1239     len = raw_command(send.bytes, sizeof(send.bytes),
1240                       cmd, key, strlen(key), NULL, 0);
1241     safe_send(send.bytes, len, false);
1242 
1243     if (cmd == PROTOCOL_BINARY_CMD_DELETE) {
1244         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1245         validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_DELETE,
1246                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1247     }
1248 
1249     safe_send(send.bytes, len, false);
1250     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1251     validate_response_header(&receive.response, cmd,
1252                              PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1253 
1254     return TEST_PASS;
1255 }
1256 
test_binary_delete(void)1257 static enum test_return test_binary_delete(void) {
1258     return test_binary_delete_impl("test_binary_delete",
1259                                    PROTOCOL_BINARY_CMD_DELETE);
1260 }
1261 
test_binary_deleteq(void)1262 static enum test_return test_binary_deleteq(void) {
1263     return test_binary_delete_impl("test_binary_deleteq",
1264                                    PROTOCOL_BINARY_CMD_DELETEQ);
1265 }
1266 
test_binary_get_impl(const char * key,uint8_t cmd)1267 static enum test_return test_binary_get_impl(const char *key, uint8_t cmd) {
1268     union {
1269         protocol_binary_request_no_extras request;
1270         protocol_binary_response_no_extras response;
1271         char bytes[1024];
1272     } send, receive;
1273 
1274     uint32_t expiration = htonl(3600);
1275     size_t extlen = 0;
1276     if (cmd == PROTOCOL_BINARY_CMD_GAT || cmd == PROTOCOL_BINARY_CMD_GATK)
1277         extlen = sizeof(expiration);
1278 
1279     size_t len = ext_command(send.bytes, sizeof(send.bytes), cmd,
1280                              extlen ? &expiration : NULL, extlen,
1281                              key, strlen(key), NULL, 0);
1282 
1283     safe_send(send.bytes, len, false);
1284     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1285     validate_response_header(&receive.response, cmd,
1286                              PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1287 
1288     len = storage_command(send.bytes, sizeof(send.bytes),
1289                           PROTOCOL_BINARY_CMD_ADD,
1290                           key, strlen(key), NULL, 0,
1291                           0, 0);
1292     safe_send(send.bytes, len, false);
1293     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1294     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1295                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1296 
1297     /* run a little pipeline test ;-) */
1298     len = 0;
1299     int ii;
1300     for (ii = 0; ii < 10; ++ii) {
1301         union {
1302             protocol_binary_request_no_extras request;
1303             char bytes[1024];
1304         } temp;
1305         size_t l = ext_command(temp.bytes, sizeof(temp.bytes), cmd,
1306                                extlen ? &expiration : NULL, extlen,
1307                                key, strlen(key), NULL, 0);
1308         memcpy(send.bytes + len, temp.bytes, l);
1309         len += l;
1310     }
1311 
1312     safe_send(send.bytes, len, false);
1313     for (ii = 0; ii < 10; ++ii) {
1314         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1315         validate_response_header(&receive.response, cmd,
1316                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1317     }
1318 
1319     return TEST_PASS;
1320 }
1321 
test_binary_get(void)1322 static enum test_return test_binary_get(void) {
1323     return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
1324 }
1325 
test_binary_getk(void)1326 static enum test_return test_binary_getk(void) {
1327     return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
1328 }
1329 
test_binary_gat(void)1330 static enum test_return test_binary_gat(void) {
1331     return test_binary_get_impl("test_binary_gat", PROTOCOL_BINARY_CMD_GAT);
1332 }
1333 
test_binary_gatk(void)1334 static enum test_return test_binary_gatk(void) {
1335     return test_binary_get_impl("test_binary_gatk", PROTOCOL_BINARY_CMD_GATK);
1336 }
1337 
test_binary_getq_impl(const char * key,uint8_t cmd)1338 static enum test_return test_binary_getq_impl(const char *key, uint8_t cmd) {
1339     const char *missing = "test_binary_getq_missing";
1340     union {
1341         protocol_binary_request_no_extras request;
1342         protocol_binary_response_no_extras response;
1343         char bytes[1024];
1344     } send, temp, receive;
1345 
1346     uint32_t expiration = htonl(3600);
1347     size_t extlen = 0;
1348     if (cmd == PROTOCOL_BINARY_CMD_GATQ || cmd == PROTOCOL_BINARY_CMD_GATKQ)
1349         extlen = sizeof(expiration);
1350 
1351     size_t len = storage_command(send.bytes, sizeof(send.bytes),
1352                                  PROTOCOL_BINARY_CMD_ADD,
1353                                  key, strlen(key), NULL, 0,
1354                                  0, 0);
1355     size_t len2 = ext_command(temp.bytes, sizeof(temp.bytes), cmd,
1356                               extlen ? &expiration : NULL, extlen,
1357                               missing, strlen(missing), NULL, 0);
1358     /* I need to change the first opaque so that I can separate the two
1359      * return packets */
1360     temp.request.message.header.request.opaque = 0xfeedface;
1361     memcpy(send.bytes + len, temp.bytes, len2);
1362     len += len2;
1363 
1364     len2 = ext_command(temp.bytes, sizeof(temp.bytes), cmd,
1365                        extlen ? &expiration : NULL, extlen,
1366                        key, strlen(key), NULL, 0);
1367     memcpy(send.bytes + len, temp.bytes, len2);
1368     len += len2;
1369 
1370     safe_send(send.bytes, len, false);
1371     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1372     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1373                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1374     /* The first GETQ shouldn't return anything */
1375     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1376     validate_response_header(&receive.response, cmd,
1377                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1378 
1379     return TEST_PASS;
1380 }
1381 
test_binary_getq(void)1382 static enum test_return test_binary_getq(void) {
1383     return test_binary_getq_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
1384 }
1385 
test_binary_getkq(void)1386 static enum test_return test_binary_getkq(void) {
1387     return test_binary_getq_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
1388 }
1389 
test_binary_gatq(void)1390 static enum test_return test_binary_gatq(void) {
1391     return test_binary_getq_impl("test_binary_gatq", PROTOCOL_BINARY_CMD_GATQ);
1392 }
1393 
test_binary_gatkq(void)1394 static enum test_return test_binary_gatkq(void) {
1395     return test_binary_getq_impl("test_binary_gatkq", PROTOCOL_BINARY_CMD_GATKQ);
1396 }
1397 
test_binary_incr_impl(const char * key,uint8_t cmd)1398 static enum test_return test_binary_incr_impl(const char* key, uint8_t cmd) {
1399     union {
1400         protocol_binary_request_no_extras request;
1401         protocol_binary_response_no_extras response_header;
1402         protocol_binary_response_incr response;
1403         char bytes[1024];
1404     } send, receive;
1405     size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
1406                                     key, strlen(key), 1, 0, 0);
1407 
1408     int ii;
1409     for (ii = 0; ii < 10; ++ii) {
1410         safe_send(send.bytes, len, false);
1411         if (cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
1412             safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1413             validate_response_header(&receive.response_header, cmd,
1414                                      PROTOCOL_BINARY_RESPONSE_SUCCESS);
1415             assert(ntohll(receive.response.message.body.value) == ii);
1416         }
1417     }
1418 
1419     if (cmd == PROTOCOL_BINARY_CMD_INCREMENTQ) {
1420         test_binary_noop();
1421     }
1422     return TEST_PASS;
1423 }
1424 
test_binary_incr(void)1425 static enum test_return test_binary_incr(void) {
1426     return test_binary_incr_impl("test_binary_incr",
1427                                  PROTOCOL_BINARY_CMD_INCREMENT);
1428 }
1429 
test_binary_incrq(void)1430 static enum test_return test_binary_incrq(void) {
1431     return test_binary_incr_impl("test_binary_incrq",
1432                                  PROTOCOL_BINARY_CMD_INCREMENTQ);
1433 }
1434 
test_binary_decr_impl(const char * key,uint8_t cmd)1435 static enum test_return test_binary_decr_impl(const char* key, uint8_t cmd) {
1436     union {
1437         protocol_binary_request_no_extras request;
1438         protocol_binary_response_no_extras response_header;
1439         protocol_binary_response_decr response;
1440         char bytes[1024];
1441     } send, receive;
1442     size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
1443                                     key, strlen(key), 1, 9, 0);
1444 
1445     int ii;
1446     for (ii = 9; ii >= 0; --ii) {
1447         safe_send(send.bytes, len, false);
1448         if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
1449             safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1450             validate_response_header(&receive.response_header, cmd,
1451                                      PROTOCOL_BINARY_RESPONSE_SUCCESS);
1452             assert(ntohll(receive.response.message.body.value) == ii);
1453         }
1454     }
1455 
1456     /* decr on 0 should not wrap */
1457     safe_send(send.bytes, len, false);
1458     if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
1459         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1460         validate_response_header(&receive.response_header, cmd,
1461                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1462         assert(ntohll(receive.response.message.body.value) == 0);
1463     } else {
1464         test_binary_noop();
1465     }
1466 
1467     return TEST_PASS;
1468 }
1469 
test_binary_decr(void)1470 static enum test_return test_binary_decr(void) {
1471     return test_binary_decr_impl("test_binary_decr",
1472                                  PROTOCOL_BINARY_CMD_DECREMENT);
1473 }
1474 
test_binary_decrq(void)1475 static enum test_return test_binary_decrq(void) {
1476     return test_binary_decr_impl("test_binary_decrq",
1477                                  PROTOCOL_BINARY_CMD_DECREMENTQ);
1478 }
1479 
test_binary_version(void)1480 static enum test_return test_binary_version(void) {
1481     union {
1482         protocol_binary_request_no_extras request;
1483         protocol_binary_response_no_extras response;
1484         char bytes[1024];
1485     } buffer;
1486 
1487     size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1488                              PROTOCOL_BINARY_CMD_VERSION,
1489                              NULL, 0, NULL, 0);
1490 
1491     safe_send(buffer.bytes, len, false);
1492     safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1493     validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_VERSION,
1494                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1495 
1496     return TEST_PASS;
1497 }
1498 
test_binary_flush_impl(const char * key,uint8_t cmd)1499 static enum test_return test_binary_flush_impl(const char *key, uint8_t cmd) {
1500     union {
1501         protocol_binary_request_no_extras request;
1502         protocol_binary_response_no_extras response;
1503         char bytes[1024];
1504     } send, receive;
1505 
1506     size_t len = storage_command(send.bytes, sizeof(send.bytes),
1507                                  PROTOCOL_BINARY_CMD_ADD,
1508                                  key, strlen(key), NULL, 0, 0, 0);
1509     safe_send(send.bytes, len, false);
1510     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1511     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1512                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1513 
1514     len = flush_command(send.bytes, sizeof(send.bytes), cmd, 2, true);
1515     safe_send(send.bytes, len, false);
1516     if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
1517         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1518         validate_response_header(&receive.response, cmd,
1519                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1520     }
1521 
1522     len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GET,
1523                       key, strlen(key), NULL, 0);
1524     safe_send(send.bytes, len, false);
1525     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1526     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1527                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1528 
1529     sleep(2);
1530     safe_send(send.bytes, len, false);
1531     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1532     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1533                              PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1534 
1535     int ii;
1536     for (ii = 0; ii < 2; ++ii) {
1537         len = storage_command(send.bytes, sizeof(send.bytes),
1538                               PROTOCOL_BINARY_CMD_ADD,
1539                               key, strlen(key), NULL, 0, 0, 0);
1540         safe_send(send.bytes, len, false);
1541         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1542         validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1543                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1544 
1545         len = flush_command(send.bytes, sizeof(send.bytes), cmd, 0, ii == 0);
1546         safe_send(send.bytes, len, false);
1547         if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
1548             safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1549             validate_response_header(&receive.response, cmd,
1550                                      PROTOCOL_BINARY_RESPONSE_SUCCESS);
1551         }
1552 
1553         len = raw_command(send.bytes, sizeof(send.bytes),
1554                           PROTOCOL_BINARY_CMD_GET,
1555                           key, strlen(key), NULL, 0);
1556         safe_send(send.bytes, len, false);
1557         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1558         validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1559                                  PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1560     }
1561 
1562     return TEST_PASS;
1563 }
1564 
test_binary_flush(void)1565 static enum test_return test_binary_flush(void) {
1566     return test_binary_flush_impl("test_binary_flush",
1567                                   PROTOCOL_BINARY_CMD_FLUSH);
1568 }
1569 
test_binary_flushq(void)1570 static enum test_return test_binary_flushq(void) {
1571     return test_binary_flush_impl("test_binary_flushq",
1572                                   PROTOCOL_BINARY_CMD_FLUSHQ);
1573 }
1574 
test_binary_concat_impl(const char * key,uint8_t cmd)1575 static enum test_return test_binary_concat_impl(const char *key, uint8_t cmd) {
1576     union {
1577         protocol_binary_request_no_extras request;
1578         protocol_binary_response_no_extras response;
1579         char bytes[1024];
1580     } send, receive;
1581     const char *value = "world";
1582 
1583     size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1584                               key, strlen(key), value, strlen(value));
1585 
1586 
1587     safe_send(send.bytes, len, false);
1588     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1589     validate_response_header(&receive.response, cmd,
1590                              PROTOCOL_BINARY_RESPONSE_NOT_STORED);
1591 
1592     len = storage_command(send.bytes, sizeof(send.bytes),
1593                           PROTOCOL_BINARY_CMD_ADD,
1594                           key, strlen(key), value, strlen(value), 0, 0);
1595     safe_send(send.bytes, len, false);
1596     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1597     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1598                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1599 
1600     len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1601                       key, strlen(key), value, strlen(value));
1602     safe_send(send.bytes, len, false);
1603 
1604     if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_PREPEND) {
1605         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1606         validate_response_header(&receive.response, cmd,
1607                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1608     } else {
1609         len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_NOOP,
1610                           NULL, 0, NULL, 0);
1611         safe_send(send.bytes, len, false);
1612         safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1613         validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_NOOP,
1614                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1615     }
1616 
1617     len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GETK,
1618                       key, strlen(key), NULL, 0);
1619 
1620     safe_send(send.bytes, len, false);
1621     safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1622     validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GETK,
1623                              PROTOCOL_BINARY_RESPONSE_SUCCESS);
1624 
1625     assert(receive.response.message.header.response.keylen == strlen(key));
1626     assert(receive.response.message.header.response.bodylen == (strlen(key) + 2*strlen(value) + 4));
1627 
1628     char *ptr = receive.bytes;
1629     ptr += sizeof(receive.response);
1630     ptr += 4;
1631 
1632     assert(memcmp(ptr, key, strlen(key)) == 0);
1633     ptr += strlen(key);
1634     assert(memcmp(ptr, value, strlen(value)) == 0);
1635     ptr += strlen(value);
1636     assert(memcmp(ptr, value, strlen(value)) == 0);
1637 
1638     return TEST_PASS;
1639 }
1640 
test_binary_append(void)1641 static enum test_return test_binary_append(void) {
1642     return test_binary_concat_impl("test_binary_append",
1643                                    PROTOCOL_BINARY_CMD_APPEND);
1644 }
1645 
test_binary_prepend(void)1646 static enum test_return test_binary_prepend(void) {
1647     return test_binary_concat_impl("test_binary_prepend",
1648                                    PROTOCOL_BINARY_CMD_PREPEND);
1649 }
1650 
test_binary_appendq(void)1651 static enum test_return test_binary_appendq(void) {
1652     return test_binary_concat_impl("test_binary_appendq",
1653                                    PROTOCOL_BINARY_CMD_APPENDQ);
1654 }
1655 
test_binary_prependq(void)1656 static enum test_return test_binary_prependq(void) {
1657     return test_binary_concat_impl("test_binary_prependq",
1658                                    PROTOCOL_BINARY_CMD_PREPENDQ);
1659 }
1660 
test_binary_stat(void)1661 static enum test_return test_binary_stat(void) {
1662     union {
1663         protocol_binary_request_no_extras request;
1664         protocol_binary_response_no_extras response;
1665         char bytes[1024];
1666     } buffer;
1667 
1668     size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1669                              PROTOCOL_BINARY_CMD_STAT,
1670                              NULL, 0, NULL, 0);
1671 
1672     safe_send(buffer.bytes, len, false);
1673     do {
1674         safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1675         validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_STAT,
1676                                  PROTOCOL_BINARY_RESPONSE_SUCCESS);
1677     } while (buffer.response.message.header.response.keylen != 0);
1678 
1679     return TEST_PASS;
1680 }
1681 
test_binary_illegal(void)1682 static enum test_return test_binary_illegal(void) {
1683     uint8_t cmd = 0x25;
1684     while (cmd != 0x00) {
1685         union {
1686             protocol_binary_request_no_extras request;
1687             protocol_binary_response_no_extras response;
1688             char bytes[1024];
1689         } buffer;
1690         size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1691                                  cmd, NULL, 0, NULL, 0);
1692         safe_send(buffer.bytes, len, false);
1693         safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1694         validate_response_header(&buffer.response, cmd,
1695                                  PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
1696         ++cmd;
1697     }
1698 
1699     return TEST_PASS;
1700 }
1701 
1702 volatile bool hickup_thread_running;
1703 
binary_hickup_recv_verification_thread(void * arg)1704 static void *binary_hickup_recv_verification_thread(void *arg) {
1705     protocol_binary_response_no_extras *response = malloc(65*1024);
1706     if (response != NULL) {
1707         while (safe_recv_packet(response, 65*1024)) {
1708             /* Just validate the packet format */
1709             validate_response_header(response,
1710                                      response->message.header.response.opcode,
1711                                      response->message.header.response.status);
1712         }
1713         free(response);
1714     }
1715     hickup_thread_running = false;
1716     allow_closed_read = false;
1717     return NULL;
1718 }
1719 
test_binary_pipeline_hickup_chunk(void * buffer,size_t buffersize)1720 static enum test_return test_binary_pipeline_hickup_chunk(void *buffer, size_t buffersize) {
1721     off_t offset = 0;
1722     char *key[256];
1723     uint64_t value = 0xfeedfacedeadbeef;
1724 
1725     while (hickup_thread_running &&
1726            offset + sizeof(protocol_binary_request_no_extras) < buffersize) {
1727         union {
1728             protocol_binary_request_no_extras request;
1729             char bytes[65 * 1024];
1730         } command;
1731         uint8_t cmd = (uint8_t)(rand() & 0xff);
1732         size_t len;
1733         size_t keylen = (rand() % 250) + 1;
1734 
1735         switch (cmd) {
1736         case PROTOCOL_BINARY_CMD_ADD:
1737         case PROTOCOL_BINARY_CMD_ADDQ:
1738         case PROTOCOL_BINARY_CMD_REPLACE:
1739         case PROTOCOL_BINARY_CMD_REPLACEQ:
1740         case PROTOCOL_BINARY_CMD_SET:
1741         case PROTOCOL_BINARY_CMD_SETQ:
1742             len = storage_command(command.bytes, sizeof(command.bytes), cmd,
1743                                   key, keylen , &value, sizeof(value),
1744                                   0, 0);
1745             break;
1746         case PROTOCOL_BINARY_CMD_APPEND:
1747         case PROTOCOL_BINARY_CMD_APPENDQ:
1748         case PROTOCOL_BINARY_CMD_PREPEND:
1749         case PROTOCOL_BINARY_CMD_PREPENDQ:
1750             len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1751                               key, keylen, &value, sizeof(value));
1752             break;
1753         case PROTOCOL_BINARY_CMD_FLUSH:
1754         case PROTOCOL_BINARY_CMD_FLUSHQ:
1755             len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1756                               NULL, 0, NULL, 0);
1757             break;
1758         case PROTOCOL_BINARY_CMD_NOOP:
1759             len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1760                               NULL, 0, NULL, 0);
1761             break;
1762         case PROTOCOL_BINARY_CMD_DELETE:
1763         case PROTOCOL_BINARY_CMD_DELETEQ:
1764             len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1765                              key, keylen, NULL, 0);
1766             break;
1767         case PROTOCOL_BINARY_CMD_DECREMENT:
1768         case PROTOCOL_BINARY_CMD_DECREMENTQ:
1769         case PROTOCOL_BINARY_CMD_INCREMENT:
1770         case PROTOCOL_BINARY_CMD_INCREMENTQ:
1771             len = arithmetic_command(command.bytes, sizeof(command.bytes), cmd,
1772                                      key, keylen, 1, 0, 0);
1773             break;
1774         case PROTOCOL_BINARY_CMD_VERSION:
1775             len = raw_command(command.bytes, sizeof(command.bytes),
1776                              PROTOCOL_BINARY_CMD_VERSION,
1777                              NULL, 0, NULL, 0);
1778             break;
1779         case PROTOCOL_BINARY_CMD_GET:
1780         case PROTOCOL_BINARY_CMD_GETK:
1781         case PROTOCOL_BINARY_CMD_GETKQ:
1782         case PROTOCOL_BINARY_CMD_GETQ:
1783             len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1784                              key, keylen, NULL, 0);
1785             break;
1786 
1787         case PROTOCOL_BINARY_CMD_TOUCH:
1788         case PROTOCOL_BINARY_CMD_GAT:
1789         case PROTOCOL_BINARY_CMD_GATQ:
1790         case PROTOCOL_BINARY_CMD_GATK:
1791         case PROTOCOL_BINARY_CMD_GATKQ:
1792             len = touch_command(command.bytes, sizeof(command.bytes), cmd,
1793                                 key, keylen, 10);
1794             break;
1795 
1796         case PROTOCOL_BINARY_CMD_STAT:
1797             len = raw_command(command.bytes, sizeof(command.bytes),
1798                               PROTOCOL_BINARY_CMD_STAT,
1799                               NULL, 0, NULL, 0);
1800             break;
1801 
1802         case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
1803         case PROTOCOL_BINARY_CMD_SASL_AUTH:
1804         case PROTOCOL_BINARY_CMD_SASL_STEP:
1805             /* Ignoring SASL */
1806         case PROTOCOL_BINARY_CMD_QUITQ:
1807         case PROTOCOL_BINARY_CMD_QUIT:
1808             /* I don't want to pass on the quit commands ;-) */
1809             cmd |= 0xf0;
1810             /* FALLTHROUGH */
1811         default:
1812             len = raw_command(command.bytes, sizeof(command.bytes),
1813                               cmd, NULL, 0, NULL, 0);
1814         }
1815 
1816         if ((len + offset) < buffersize) {
1817             memcpy(((char*)buffer) + offset, command.bytes, len);
1818             offset += len;
1819         } else {
1820             break;
1821         }
1822     }
1823     safe_send(buffer, offset, true);
1824 
1825     return TEST_PASS;
1826 }
1827 
test_binary_pipeline_hickup(void)1828 static enum test_return test_binary_pipeline_hickup(void)
1829 {
1830     size_t buffersize = 65 * 1024;
1831     void *buffer = malloc(buffersize);
1832     int ii;
1833 
1834     pthread_t tid;
1835     int ret;
1836     allow_closed_read = true;
1837     hickup_thread_running = true;
1838     if ((ret = pthread_create(&tid, NULL,
1839                               binary_hickup_recv_verification_thread, NULL)) != 0) {
1840         fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
1841         return TEST_FAIL;
1842     }
1843 
1844     /* Allow the thread to start */
1845     usleep(250);
1846 
1847     srand((int)time(NULL));
1848     for (ii = 0; ii < 2; ++ii) {
1849         test_binary_pipeline_hickup_chunk(buffer, buffersize);
1850     }
1851 
1852     /* send quitq to shut down the read thread ;-) */
1853     size_t len = raw_command(buffer, buffersize, PROTOCOL_BINARY_CMD_QUITQ,
1854                              NULL, 0, NULL, 0);
1855     safe_send(buffer, len, false);
1856 
1857     pthread_join(tid, NULL);
1858     free(buffer);
1859     return TEST_PASS;
1860 }
1861 
1862 
test_issue_101(void)1863 static enum test_return test_issue_101(void) {
1864     enum { max = 2 };
1865     enum test_return ret = TEST_PASS;
1866     int fds[max];
1867     int ii = 0;
1868     pid_t child = 0;
1869 
1870     if (getenv("SKIP_TEST_101") != NULL) {
1871         return TEST_SKIP;
1872     }
1873 
1874     const char *command = "stats\r\nstats\r\nstats\r\nstats\r\nstats\r\n";
1875     size_t cmdlen = strlen(command);
1876 
1877     server_pid = start_server(&port, false, 1000);
1878 
1879     for (ii = 0; ii < max; ++ii) {
1880         fds[ii] = connect_server("127.0.0.1", port, true);
1881         assert(fds[ii] > 0);
1882     }
1883 
1884     /* Send command on the connection until it blocks */
1885     for (ii = 0; ii < max; ++ii) {
1886         bool more = true;
1887         do {
1888             ssize_t err = write(fds[ii], command, cmdlen);
1889             if (err == -1) {
1890                 switch (errno) {
1891                 case EINTR:
1892                     break;
1893                 case ENOMEM:
1894                 case EWOULDBLOCK:
1895                     more = false;
1896                     break;
1897                 default:
1898                     ret = TEST_FAIL;
1899                     goto cleanup;
1900                 }
1901             }
1902         } while (more);
1903     }
1904 
1905     child = fork();
1906     if (child == (pid_t)-1) {
1907         abort();
1908     } else if (child > 0) {
1909         int stat;
1910         pid_t c;
1911         while ((c = waitpid(child, &stat, 0)) == (pid_t)-1 && errno == EINTR);
1912         assert(c == child);
1913         assert(stat == 0);
1914     } else {
1915         sock = connect_server("127.0.0.1", port, false);
1916         ret = test_binary_noop();
1917         close(sock);
1918         exit(0);
1919     }
1920 
1921  cleanup:
1922     /* close all connections */
1923     for (ii = 0; ii < max; ++ii) {
1924         close(fds[ii]);
1925     }
1926 
1927     assert(kill(server_pid, SIGTERM) == 0);
1928 
1929     return ret;
1930 }
1931 
1932 typedef enum test_return (*TEST_FUNC)(void);
1933 struct testcase {
1934     const char *description;
1935     TEST_FUNC function;
1936 };
1937 
1938 struct testcase testcases[] = {
1939     { "cache_create", cache_create_test },
1940     { "cache_constructor", cache_constructor_test },
1941     { "cache_constructor_fail", cache_fail_constructor_test },
1942     { "cache_destructor", cache_destructor_test },
1943     { "cache_reuse", cache_reuse_test },
1944     { "cache_redzone", cache_redzone_test },
1945     { "issue_161", test_issue_161 },
1946     { "strtol", test_safe_strtol },
1947     { "strtoll", test_safe_strtoll },
1948     { "strtoul", test_safe_strtoul },
1949     { "strtoull", test_safe_strtoull },
1950     { "issue_44", test_issue_44 },
1951     { "vperror", test_vperror },
1952     { "issue_101", test_issue_101 },
1953     /* The following tests all run towards the same server */
1954     { "start_server", start_memcached_server },
1955     { "issue_92", test_issue_92 },
1956     { "issue_102", test_issue_102 },
1957     { "binary_noop", test_binary_noop },
1958     { "binary_quit", test_binary_quit },
1959     { "binary_quitq", test_binary_quitq },
1960     { "binary_set", test_binary_set },
1961     { "binary_setq", test_binary_setq },
1962     { "binary_add", test_binary_add },
1963     { "binary_addq", test_binary_addq },
1964     { "binary_replace", test_binary_replace },
1965     { "binary_replaceq", test_binary_replaceq },
1966     { "binary_delete", test_binary_delete },
1967     { "binary_deleteq", test_binary_deleteq },
1968     { "binary_get", test_binary_get },
1969     { "binary_getq", test_binary_getq },
1970     { "binary_getk", test_binary_getk },
1971     { "binary_getkq", test_binary_getkq },
1972     { "binary_gat", test_binary_gat },
1973     { "binary_gatq", test_binary_gatq },
1974     { "binary_gatk", test_binary_gatk },
1975     { "binary_gatkq", test_binary_gatkq },
1976     { "binary_incr", test_binary_incr },
1977     { "binary_incrq", test_binary_incrq },
1978     { "binary_decr", test_binary_decr },
1979     { "binary_decrq", test_binary_decrq },
1980     { "binary_version", test_binary_version },
1981     { "binary_flush", test_binary_flush },
1982     { "binary_flushq", test_binary_flushq },
1983     { "binary_append", test_binary_append },
1984     { "binary_appendq", test_binary_appendq },
1985     { "binary_prepend", test_binary_prepend },
1986     { "binary_prependq", test_binary_prependq },
1987     { "binary_stat", test_binary_stat },
1988     { "binary_illegal", test_binary_illegal },
1989     { "binary_pipeline_hickup", test_binary_pipeline_hickup },
1990     { "shutdown", shutdown_memcached_server },
1991     { "stop_server", stop_memcached_server },
1992     { NULL, NULL }
1993 };
1994 
main(int argc,char ** argv)1995 int main(int argc, char **argv)
1996 {
1997     int exitcode = 0;
1998     int ii = 0, num_cases = 0;
1999 
2000     for (num_cases = 0; testcases[num_cases].description; num_cases++) {
2001         /* Just counting */
2002     }
2003 
2004     printf("1..%d\n", num_cases);
2005 
2006     for (ii = 0; testcases[ii].description != NULL; ++ii) {
2007         fflush(stdout);
2008 #ifndef DEBUG
2009         /* the test program shouldn't run longer than 10 minutes... */
2010         alarm(600);
2011 #endif
2012         enum test_return ret = testcases[ii].function();
2013         if (ret == TEST_SKIP) {
2014             fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
2015         } else if (ret == TEST_PASS) {
2016             fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description);
2017         } else {
2018             fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description);
2019             exitcode = 1;
2020         }
2021         fflush(stdout);
2022     }
2023 
2024     return exitcode;
2025 }
2026