+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#undef NDEBUG
-#include <pthread.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-
-#include "config.h"
-#include "cache.h"
-#include "util.h"
-#include "protocol_binary.h"
-
-#define TMP_TEMPLATE "/tmp/test_file.XXXXXXX"
-
-enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
-
-static pid_t server_pid;
-static in_port_t port;
-static int sock;
-static bool allow_closed_read = false;
-
-static enum test_return cache_create_test(void)
-{
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, NULL);
- assert(cache != NULL);
- cache_destroy(cache);
- return TEST_PASS;
-}
-
-const uint64_t constructor_pattern = 0xdeadcafebabebeef;
-
-static int cache_constructor(void *buffer, void *notused1, int notused2) {
- uint64_t *ptr = buffer;
- *ptr = constructor_pattern;
- return 0;
-}
-
-static enum test_return cache_constructor_test(void)
-{
- cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
- cache_constructor, NULL);
- assert(cache != NULL);
- uint64_t *ptr = cache_alloc(cache);
- uint64_t pattern = *ptr;
- cache_free(cache, ptr);
- cache_destroy(cache);
- return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
-}
-
-static int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
- return 1;
-}
-
-static enum test_return cache_fail_constructor_test(void)
-{
- enum test_return ret = TEST_PASS;
-
- cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
- cache_fail_constructor, NULL);
- assert(cache != NULL);
- uint64_t *ptr = cache_alloc(cache);
- if (ptr != NULL) {
- ret = TEST_FAIL;
- }
- cache_destroy(cache);
- return ret;
-}
-
-static void *destruct_data = 0;
-
-static void cache_destructor(void *buffer, void *notused) {
- destruct_data = buffer;
-}
-
-static enum test_return cache_destructor_test(void)
-{
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, cache_destructor);
- assert(cache != NULL);
- char *ptr = cache_alloc(cache);
- cache_free(cache, ptr);
- cache_destroy(cache);
-
- return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
-}
-
-static enum test_return cache_reuse_test(void)
-{
- int ii;
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, NULL);
- char *ptr = cache_alloc(cache);
- cache_free(cache, ptr);
- for (ii = 0; ii < 100; ++ii) {
- char *p = cache_alloc(cache);
- assert(p == ptr);
- cache_free(cache, ptr);
- }
- cache_destroy(cache);
- return TEST_PASS;
-}
-
-
-static enum test_return cache_bulkalloc(size_t datasize)
-{
- cache_t *cache = cache_create("test", datasize, sizeof(char*),
- NULL, NULL);
-#define ITERATIONS 1024
- void *ptr[ITERATIONS];
-
- for (int ii = 0; ii < ITERATIONS; ++ii) {
- ptr[ii] = cache_alloc(cache);
- assert(ptr[ii] != 0);
- memset(ptr[ii], 0xff, datasize);
- }
-
- for (int ii = 0; ii < ITERATIONS; ++ii) {
- cache_free(cache, ptr[ii]);
- }
-
-#undef ITERATIONS
- cache_destroy(cache);
- return TEST_PASS;
-}
-
-static enum test_return test_issue_161(void)
-{
- enum test_return ret = cache_bulkalloc(1);
- if (ret == TEST_PASS) {
- ret = cache_bulkalloc(512);
- }
-
- return ret;
-}
-
-static enum test_return cache_redzone_test(void)
-{
-#ifndef HAVE_UMEM_H
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, NULL);
-
- /* Ignore SIGABORT */
- struct sigaction old_action;
- struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0};
- sigemptyset(&action.sa_mask);
- sigaction(SIGABRT, &action, &old_action);
-
- /* check memory debug.. */
- char *p = cache_alloc(cache);
- char old = *(p - 1);
- *(p - 1) = 0;
- cache_free(cache, p);
- assert(cache_error == -1);
- *(p - 1) = old;
-
- p[sizeof(uint32_t)] = 0;
- cache_free(cache, p);
- assert(cache_error == 1);
-
- /* restore signal handler */
- sigaction(SIGABRT, &old_action, NULL);
-
- cache_destroy(cache);
-
- return TEST_PASS;
-#else
- return TEST_SKIP;
-#endif
-}
-
-static enum test_return test_safe_strtoul(void) {
- uint32_t val;
- assert(safe_strtoul("123", &val));
- assert(val == 123);
- assert(safe_strtoul("+123", &val));
- assert(val == 123);
- assert(!safe_strtoul("", &val)); // empty
- assert(!safe_strtoul("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoul(" issue221", &val)); // non-numeric
- /* Not sure what it does, but this works with ICC :/
- assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
- */
-
- // extremes:
- assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
- assert(val == 4294967295L);
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtoul("4294967296", &val)); // 2**32
- */
- assert(!safe_strtoul("-1", &val)); // negative
- return TEST_PASS;
-}
-
-
-static enum test_return test_safe_strtoull(void) {
- uint64_t val;
- assert(safe_strtoull("123", &val));
- assert(val == 123);
- assert(safe_strtoull("+123", &val));
- assert(val == 123);
- assert(!safe_strtoull("", &val)); // empty
- assert(!safe_strtoull("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
- assert(!safe_strtoull(" issue221", &val)); // non-numeric
-
- // extremes:
- assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
- assert(val == 18446744073709551615ULL);
- assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
- assert(!safe_strtoull("-1", &val)); // negative
- return TEST_PASS;
-}
-
-static enum test_return test_safe_strtoll(void) {
- int64_t val;
- assert(safe_strtoll("123", &val));
- assert(val == 123);
- assert(safe_strtoll("+123", &val));
- assert(val == 123);
- assert(safe_strtoll("-123", &val));
- assert(val == -123);
- assert(!safe_strtoll("", &val)); // empty
- assert(!safe_strtoll("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
- assert(!safe_strtoll(" issue221", &val)); // non-numeric
-
- // extremes:
- assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
- assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
- assert(val == 9223372036854775807LL);
- /*
- assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
- assert(val == -9223372036854775808LL);
- */
- assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
-
- // We'll allow space to terminate the string. And leading space.
- assert(safe_strtoll(" 123 foo", &val));
- assert(val == 123);
- return TEST_PASS;
-}
-
-static enum test_return test_safe_strtol(void) {
- int32_t val;
- assert(safe_strtol("123", &val));
- assert(val == 123);
- assert(safe_strtol("+123", &val));
- assert(val == 123);
- assert(safe_strtol("-123", &val));
- assert(val == -123);
- assert(!safe_strtol("", &val)); // empty
- assert(!safe_strtol("123BOGUS", &val)); // non-numeric
- assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
- assert(!safe_strtol(" issue221", &val)); // non-numeric
-
- // extremes:
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
- */
- assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
- assert(val == 2147483647L);
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
- */
-
- // We'll allow space to terminate the string. And leading space.
- assert(safe_strtol(" 123 foo", &val));
- assert(val == 123);
- return TEST_PASS;
-}
-
-/**
- * Function to start the server and let it listen on a random port
- *
- * @param port_out where to store the TCP port number the server is
- * listening on
- * @param daemon set to true if you want to run the memcached server
- * as a daemon process
- * @return the pid of the memcached server
- */
-static pid_t start_server(in_port_t *port_out, bool daemon, int timeout) {
- char environment[80];
- snprintf(environment, sizeof(environment),
- "MEMCACHED_PORT_FILENAME=/tmp/ports.%lu", (long)getpid());
- char *filename= environment + strlen("MEMCACHED_PORT_FILENAME=");
- char pid_file[80];
- snprintf(pid_file, sizeof(pid_file), "/tmp/pid.%lu", (long)getpid());
-
- remove(filename);
- remove(pid_file);
-
-#ifdef __sun
- /* I want to name the corefiles differently so that they don't
- overwrite each other
- */
- char coreadm[128];
- snprintf(coreadm, sizeof(coreadm),
- "coreadm -p core.%%f.%%p %lu", (unsigned long)getpid());
- system(coreadm);
-#endif
-
- pid_t pid = fork();
- assert(pid != -1);
-
- if (pid == 0) {
- /* Child */
- char *argv[20];
- int arg = 0;
- char tmo[24];
- snprintf(tmo, sizeof(tmo), "%u", timeout);
-
- putenv(environment);
-#ifdef __sun
- putenv("LD_PRELOAD=watchmalloc.so.1");
- putenv("MALLOC_DEBUG=WATCH");
-#endif
-
- if (!daemon) {
- argv[arg++] = "./timedrun";
- argv[arg++] = tmo;
- }
- argv[arg++] = "./memcached-debug";
- argv[arg++] = "-p";
- argv[arg++] = "-1";
- argv[arg++] = "-U";
- argv[arg++] = "0";
- /* Handle rpmbuild and the like doing this as root */
- if (getuid() == 0) {
- argv[arg++] = "-u";
- argv[arg++] = "root";
- }
- if (daemon) {
- argv[arg++] = "-d";
- argv[arg++] = "-P";
- argv[arg++] = pid_file;
- }
-#ifdef MESSAGE_DEBUG
- argv[arg++] = "-vvv";
-#endif
- argv[arg++] = NULL;
- assert(execv(argv[0], argv) != -1);
- }
-
- /* Yeah just let us "busy-wait" for the file to be created ;-) */
- while (access(filename, F_OK) == -1) {
- usleep(10);
- }
-
- FILE *fp = fopen(filename, "r");
- if (fp == NULL) {
- fprintf(stderr, "Failed to open the file containing port numbers: %s\n",
- strerror(errno));
- assert(false);
- }
-
- *port_out = (in_port_t)-1;
- char buffer[80];
- while ((fgets(buffer, sizeof(buffer), fp)) != NULL) {
- if (strncmp(buffer, "TCP INET: ", 10) == 0) {
- int32_t val;
- assert(safe_strtol(buffer + 10, &val));
- *port_out = (in_port_t)val;
- }
- }
- fclose(fp);
- assert(remove(filename) == 0);
-
- if (daemon) {
- /* loop and wait for the pid file.. There is a potential race
- * condition that the server just created the file but isn't
- * finished writing the content, so we loop a few times
- * reading as well */
- while (access(pid_file, F_OK) == -1) {
- usleep(10);
- }
-
- fp = fopen(pid_file, "r");
- if (fp == NULL) {
- fprintf(stderr, "Failed to open pid file: %s\n",
- strerror(errno));
- assert(false);
- }
-
- /* Avoid race by retrying 20 times */
- for (int x = 0; x < 20 && fgets(buffer, sizeof(buffer), fp) == NULL; x++) {
- usleep(10);
- }
- fclose(fp);
-
- int32_t val;
- assert(safe_strtol(buffer, &val));
- pid = (pid_t)val;
- }
-
- return pid;
-}
-
-static enum test_return test_issue_44(void) {
- in_port_t port;
- pid_t pid = start_server(&port, true, 15);
- assert(kill(pid, SIGHUP) == 0);
- sleep(1);
- assert(kill(pid, SIGTERM) == 0);
-
- return TEST_PASS;
-}
-
-static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
-{
- struct addrinfo *ai = 0;
- struct addrinfo hints = { .ai_family = AF_UNSPEC,
- .ai_protocol = IPPROTO_TCP,
- .ai_socktype = SOCK_STREAM };
- char service[NI_MAXSERV];
- int error;
-
- (void)snprintf(service, NI_MAXSERV, "%d", port);
- if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
- if (error != EAI_SYSTEM) {
- fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
- } else {
- perror("getaddrinfo()");
- }
- }
-
- return ai;
-}
-
-static int connect_server(const char *hostname, in_port_t port, bool nonblock)
-{
- struct addrinfo *ai = lookuphost(hostname, port);
- int sock = -1;
- if (ai != NULL) {
- if ((sock = socket(ai->ai_family, ai->ai_socktype,
- ai->ai_protocol)) != -1) {
- if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
- fprintf(stderr, "Failed to connect socket: %s\n",
- strerror(errno));
- close(sock);
- sock = -1;
- } else if (nonblock) {
- int flags = fcntl(sock, F_GETFL, 0);
- if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
- fprintf(stderr, "Failed to enable nonblocking mode: %s\n",
- strerror(errno));
- close(sock);
- sock = -1;
- }
- }
- } else {
- fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
- }
-
- freeaddrinfo(ai);
- }
- return sock;
-}
-
-static enum test_return test_vperror(void) {
- int rv = 0;
- int oldstderr = dup(STDERR_FILENO);
- char tmpl[sizeof(TMP_TEMPLATE)+1];
- strncpy(tmpl, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
-
- int newfile = mkstemp(tmpl);
- assert(newfile > 0);
- rv = dup2(newfile, STDERR_FILENO);
- assert(rv == STDERR_FILENO);
- rv = close(newfile);
- assert(rv == 0);
-
- errno = EIO;
- vperror("Old McDonald had a farm. %s", "EI EIO");
-
- /* Restore stderr */
- rv = dup2(oldstderr, STDERR_FILENO);
- assert(rv == STDERR_FILENO);
-
-
- /* Go read the file */
- char buf[80] = { 0 };
- FILE *efile = fopen(tmpl, "r");
- assert(efile);
- char *prv = fgets(buf, sizeof(buf), efile);
- assert(prv);
- fclose(efile);
-
- unlink(tmpl);
-
- char expected[80] = { 0 };
- snprintf(expected, sizeof(expected),
- "Old McDonald had a farm. EI EIO: %s\n", strerror(EIO));
-
- /*
- fprintf(stderr,
- "\nExpected: ``%s''"
- "\nGot: ``%s''\n", expected, buf);
- */
-
- return strcmp(expected, buf) == 0 ? TEST_PASS : TEST_FAIL;
-}
-
-static void send_ascii_command(const char *buf) {
- off_t offset = 0;
- const char* ptr = buf;
- size_t len = strlen(buf);
-
- do {
- ssize_t nw = write(sock, ptr + offset, len - offset);
- if (nw == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to write: %s\n", strerror(errno));
- abort();
- }
- } else {
- offset += nw;
- }
- } while (offset < len);
-}
-
-/*
- * This is a dead slow single byte read, but it should only read out
- * _one_ response and I don't have an input buffer... The current
- * implementation only supports single-line responses, so if you want to use
- * it for get commands you need to implement that first ;-)
- */
-static void read_ascii_response(char *buffer, size_t size) {
- off_t offset = 0;
- bool need_more = true;
- do {
- ssize_t nr = read(sock, buffer + offset, 1);
- if (nr == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to read: %s\n", strerror(errno));
- abort();
- }
- } else {
- assert(nr == 1);
- if (buffer[offset] == '\n') {
- need_more = false;
- buffer[offset + 1] = '\0';
- }
- offset += nr;
- assert(offset + 1 < size);
- }
- } while (need_more);
-}
-
-static enum test_return test_issue_92(void) {
- char buffer[1024];
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- send_ascii_command("stats cachedump 1 0 0\r\n");
- read_ascii_response(buffer, sizeof(buffer));
- assert(strncmp(buffer, "END", strlen("END")) == 0);
-
- send_ascii_command("stats cachedump 200 0 0\r\n");
- read_ascii_response(buffer, sizeof(buffer));
- assert(strncmp(buffer, "CLIENT_ERROR", strlen("CLIENT_ERROR")) == 0);
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
- return TEST_PASS;
-}
-
-static enum test_return test_issue_102(void) {
- char buffer[4096];
- memset(buffer, ' ', sizeof(buffer));
- buffer[sizeof(buffer) - 1] = '\0';
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- send_ascii_command(buffer);
- /* verify that the server closed the connection */
- assert(read(sock, buffer, sizeof(buffer)) == 0);
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- snprintf(buffer, sizeof(buffer), "gets ");
- size_t offset = 5;
- while (offset < 4000) {
- offset += snprintf(buffer + offset, sizeof(buffer) - offset,
- "%010u ", (unsigned int)offset);
- }
-
- send_ascii_command(buffer);
- usleep(250);
-
- send_ascii_command("\r\n");
- char rsp[80];
- read_ascii_response(rsp, sizeof(rsp));
- assert(strncmp(rsp, "END", strlen("END")) == 0);
- buffer[3]= ' ';
- send_ascii_command(buffer);
- usleep(250);
- send_ascii_command("\r\n");
- read_ascii_response(rsp, sizeof(rsp));
- assert(strncmp(rsp, "END", strlen("END")) == 0);
-
- memset(buffer, ' ', sizeof(buffer));
- int len = snprintf(buffer + 101, sizeof(buffer) - 101, "gets foo");
- buffer[101 + len] = ' ';
- buffer[sizeof(buffer) - 1] = '\0';
- send_ascii_command(buffer);
- /* verify that the server closed the connection */
- assert(read(sock, buffer, sizeof(buffer)) == 0);
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- return TEST_PASS;
-}
-
-static enum test_return start_memcached_server(void) {
- server_pid = start_server(&port, false, 600);
- sock = connect_server("127.0.0.1", port, false);
- return TEST_PASS;
-}
-
-static enum test_return stop_memcached_server(void) {
- close(sock);
- assert(kill(server_pid, SIGTERM) == 0);
- return TEST_PASS;
-}
-
-static void safe_send(const void* buf, size_t len, bool hickup)
-{
- off_t offset = 0;
- const char* ptr = buf;
-#ifdef MESSAGE_DEBUG
- uint8_t val = *ptr;
- assert(val == (uint8_t)0x80);
- fprintf(stderr, "About to send %lu bytes:", (unsigned long)len);
- for (int ii = 0; ii < len; ++ii) {
- if (ii % 4 == 0) {
- fprintf(stderr, "\n ");
- }
- val = *(ptr + ii);
- fprintf(stderr, " 0x%02x", val);
- }
- fprintf(stderr, "\n");
- usleep(500);
-#endif
-
- do {
- size_t num_bytes = len - offset;
- if (hickup) {
- if (num_bytes > 1024) {
- num_bytes = (rand() % 1023) + 1;
- }
- }
-
- ssize_t nw = write(sock, ptr + offset, num_bytes);
- if (nw == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to write: %s\n", strerror(errno));
- abort();
- }
- } else {
- if (hickup) {
- usleep(100);
- }
- offset += nw;
- }
- } while (offset < len);
-}
-
-static bool safe_recv(void *buf, size_t len) {
- if (len == 0) {
- return true;
- }
- off_t offset = 0;
- do {
- ssize_t nr = read(sock, ((char*)buf) + offset, len - offset);
- if (nr == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to read: %s\n", strerror(errno));
- abort();
- }
- } else {
- if (nr == 0 && allow_closed_read) {
- return false;
- }
- assert(nr != 0);
- offset += nr;
- }
- } while (offset < len);
-
- return true;
-}
-
-static bool safe_recv_packet(void *buf, size_t size) {
- protocol_binary_response_no_extras *response = buf;
- assert(size > sizeof(*response));
- if (!safe_recv(response, sizeof(*response))) {
- return false;
- }
- response->message.header.response.keylen = ntohs(response->message.header.response.keylen);
- response->message.header.response.status = ntohs(response->message.header.response.status);
- response->message.header.response.bodylen = ntohl(response->message.header.response.bodylen);
-
- size_t len = sizeof(*response);
-
- char *ptr = buf;
- ptr += len;
- if (!safe_recv(ptr, response->message.header.response.bodylen)) {
- return false;
- }
-
-#ifdef MESSAGE_DEBUG
- usleep(500);
- ptr = buf;
- len += response->message.header.response.bodylen;
- uint8_t val = *ptr;
- assert(val == (uint8_t)0x81);
- fprintf(stderr, "Received %lu bytes:", (unsigned long)len);
- for (int ii = 0; ii < len; ++ii) {
- if (ii % 4 == 0) {
- fprintf(stderr, "\n ");
- }
- val = *(ptr + ii);
- fprintf(stderr, " 0x%02x", val);
- }
- fprintf(stderr, "\n");
-#endif
- return true;
-}
-
-static off_t 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) {
- /* all of the storage commands use the same command layout */
- protocol_binary_request_set *request = (void*)buf;
- assert(bufsz > sizeof(*request) + keylen + dtalen);
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.extlen = 8;
- request->message.header.request.bodylen = htonl(keylen + 8 + dtalen);
- request->message.header.request.opaque = 0xdeadbeef;
- request->message.body.flags = flags;
- request->message.body.expiration = exp;
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras) + 8;
-
- memcpy(buf + key_offset, key, keylen);
- if (dta != NULL) {
- memcpy(buf + key_offset + keylen, dta, dtalen);
- }
-
- return key_offset + keylen + dtalen;
-}
-
-static off_t raw_command(char* buf,
- size_t bufsz,
- uint8_t cmd,
- const void* key,
- size_t keylen,
- const void* dta,
- size_t dtalen) {
- /* all of the storage commands use the same command layout */
- protocol_binary_request_no_extras *request = (void*)buf;
- assert(bufsz > sizeof(*request) + keylen + dtalen);
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.bodylen = htonl(keylen + dtalen);
- request->message.header.request.opaque = 0xdeadbeef;
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras);
-
- if (key != NULL) {
- memcpy(buf + key_offset, key, keylen);
- }
- if (dta != NULL) {
- memcpy(buf + key_offset + keylen, dta, dtalen);
- }
-
- return sizeof(*request) + keylen + dtalen;
-}
-
-static off_t flush_command(char* buf, size_t bufsz, uint8_t cmd, uint32_t exptime, bool use_extra) {
- protocol_binary_request_flush *request = (void*)buf;
- assert(bufsz > sizeof(*request));
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
-
- off_t size = sizeof(protocol_binary_request_no_extras);
- if (use_extra) {
- request->message.header.request.extlen = 4;
- request->message.body.expiration = htonl(exptime);
- request->message.header.request.bodylen = htonl(4);
- size += 4;
- }
-
- request->message.header.request.opaque = 0xdeadbeef;
-
- return size;
-}
-
-
-static off_t touch_command(char* buf,
- size_t bufsz,
- uint8_t cmd,
- const void* key,
- size_t keylen,
- uint32_t exptime) {
- protocol_binary_request_touch *request = (void*)buf;
- assert(bufsz > sizeof(*request));
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
-
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.extlen = 4;
- request->message.body.expiration = htonl(exptime);
- request->message.header.request.bodylen = htonl(keylen + 4);
-
- request->message.header.request.opaque = 0xdeadbeef;
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras) + 4;
-
- memcpy(buf + key_offset, key, keylen);
- return sizeof(protocol_binary_request_no_extras) + 4 + keylen;
-}
-
-static off_t 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) {
- protocol_binary_request_incr *request = (void*)buf;
- assert(bufsz > sizeof(*request) + keylen);
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.extlen = 20;
- request->message.header.request.bodylen = htonl(keylen + 20);
- request->message.header.request.opaque = 0xdeadbeef;
- request->message.body.delta = htonll(delta);
- request->message.body.initial = htonll(initial);
- request->message.body.expiration = htonl(exp);
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras) + 20;
-
- memcpy(buf + key_offset, key, keylen);
- return key_offset + keylen;
-}
-
-static void validate_response_header(protocol_binary_response_no_extras *response,
- uint8_t cmd, uint16_t status)
-{
- assert(response->message.header.response.magic == PROTOCOL_BINARY_RES);
- assert(response->message.header.response.opcode == cmd);
- assert(response->message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
- assert(response->message.header.response.status == status);
- assert(response->message.header.response.opaque == 0xdeadbeef);
-
- if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
- switch (cmd) {
- case PROTOCOL_BINARY_CMD_ADDQ:
- case PROTOCOL_BINARY_CMD_APPENDQ:
- case PROTOCOL_BINARY_CMD_DECREMENTQ:
- case PROTOCOL_BINARY_CMD_DELETEQ:
- case PROTOCOL_BINARY_CMD_FLUSHQ:
- case PROTOCOL_BINARY_CMD_INCREMENTQ:
- case PROTOCOL_BINARY_CMD_PREPENDQ:
- case PROTOCOL_BINARY_CMD_QUITQ:
- case PROTOCOL_BINARY_CMD_REPLACEQ:
- case PROTOCOL_BINARY_CMD_SETQ:
- assert("Quiet command shouldn't return on success" == NULL);
- default:
- break;
- }
-
- switch (cmd) {
- case PROTOCOL_BINARY_CMD_ADD:
- case PROTOCOL_BINARY_CMD_REPLACE:
- case PROTOCOL_BINARY_CMD_SET:
- case PROTOCOL_BINARY_CMD_APPEND:
- case PROTOCOL_BINARY_CMD_PREPEND:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen == 0);
- assert(response->message.header.response.cas != 0);
- break;
- case PROTOCOL_BINARY_CMD_FLUSH:
- case PROTOCOL_BINARY_CMD_NOOP:
- case PROTOCOL_BINARY_CMD_QUIT:
- case PROTOCOL_BINARY_CMD_DELETE:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen == 0);
- assert(response->message.header.response.cas == 0);
- break;
-
- case PROTOCOL_BINARY_CMD_DECREMENT:
- case PROTOCOL_BINARY_CMD_INCREMENT:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen == 8);
- assert(response->message.header.response.cas != 0);
- break;
-
- case PROTOCOL_BINARY_CMD_STAT:
- assert(response->message.header.response.extlen == 0);
- /* key and value exists in all packets except in the terminating */
- assert(response->message.header.response.cas == 0);
- break;
-
- case PROTOCOL_BINARY_CMD_VERSION:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen != 0);
- assert(response->message.header.response.cas == 0);
- break;
-
- case PROTOCOL_BINARY_CMD_GET:
- case PROTOCOL_BINARY_CMD_GETQ:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 4);
- assert(response->message.header.response.cas != 0);
- break;
-
- case PROTOCOL_BINARY_CMD_GETK:
- case PROTOCOL_BINARY_CMD_GETKQ:
- assert(response->message.header.response.keylen != 0);
- assert(response->message.header.response.extlen == 4);
- assert(response->message.header.response.cas != 0);
- break;
-
- default:
- /* Undefined command code */
- break;
- }
- } else {
- assert(response->message.header.response.cas == 0);
- assert(response->message.header.response.extlen == 0);
- if (cmd != PROTOCOL_BINARY_CMD_GETK &&
- cmd != PROTOCOL_BINARY_CMD_GATK) {
- assert(response->message.header.response.keylen == 0);
- }
- }
-}
-
-static enum test_return test_binary_noop(void) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
-
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- PROTOCOL_BINARY_CMD_NOOP,
- NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_NOOP,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_quit_impl(uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- cmd, NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_QUIT) {
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_QUIT,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- /* Socket should be closed now, read should return 0 */
- assert(read(sock, buffer.bytes, sizeof(buffer.bytes)) == 0);
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_quit(void) {
- return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
-}
-
-static enum test_return test_binary_quitq(void) {
- return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
-}
-
-static enum test_return test_binary_set_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- uint64_t value = 0xdeadbeefdeadcafe;
- size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), &value, sizeof(value),
- 0, 0);
-
- /* Set should work over and over again */
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_SET) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
- }
-
- if (cmd == PROTOCOL_BINARY_CMD_SETQ) {
- return test_binary_noop();
- }
-
- send.request.message.header.request.cas = receive.response.message.header.response.cas;
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_SET) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(receive.response.message.header.response.cas != send.request.message.header.request.cas);
- } else {
- return test_binary_noop();
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_set(void) {
- return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
-}
-
-static enum test_return test_binary_setq(void) {
- return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
-}
-
-
-static enum test_return test_binary_add_impl(const char *key, uint8_t cmd) {
- uint64_t value = 0xdeadbeefdeadcafe;
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd, key,
- strlen(key), &value, sizeof(value),
- 0, 0);
-
- /* Add should only work the first time */
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (ii == 0) {
- if (cmd == PROTOCOL_BINARY_CMD_ADD) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
- } else {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
- }
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_add(void) {
- return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
-}
-
-static enum test_return test_binary_addq(void) {
- return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
-}
-
-static enum test_return test_binary_replace_impl(const char* key, uint8_t cmd) {
- uint64_t value = 0xdeadbeefdeadcafe;
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), &value, sizeof(value),
- 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), &value, sizeof(value), 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = storage_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), &value, sizeof(value), 0, 0);
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_REPLACE) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response,
- PROTOCOL_BINARY_CMD_REPLACE,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
- }
-
- if (cmd == PROTOCOL_BINARY_CMD_REPLACEQ) {
- test_binary_noop();
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_replace(void) {
- return test_binary_replace_impl("test_binary_replace",
- PROTOCOL_BINARY_CMD_REPLACE);
-}
-
-static enum test_return test_binary_replaceq(void) {
- return test_binary_replace_impl("test_binary_replaceq",
- PROTOCOL_BINARY_CMD_REPLACEQ);
-}
-
-static enum test_return test_binary_delete_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), NULL, 0);
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0, 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = raw_command(send.bytes, sizeof(send.bytes),
- cmd, key, strlen(key), NULL, 0);
- safe_send(send.bytes, len, false);
-
- if (cmd == PROTOCOL_BINARY_CMD_DELETE) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_DELETE,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_delete(void) {
- return test_binary_delete_impl("test_binary_delete",
- PROTOCOL_BINARY_CMD_DELETE);
-}
-
-static enum test_return test_binary_deleteq(void) {
- return test_binary_delete_impl("test_binary_deleteq",
- PROTOCOL_BINARY_CMD_DELETEQ);
-}
-
-static enum test_return test_binary_get_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), NULL, 0);
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0,
- 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- /* run a little pipeline test ;-) */
- len = 0;
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- union {
- protocol_binary_request_no_extras request;
- char bytes[1024];
- } temp;
- size_t l = raw_command(temp.bytes, sizeof(temp.bytes),
- cmd, key, strlen(key), NULL, 0);
- memcpy(send.bytes + len, temp.bytes, l);
- len += l;
- }
-
- safe_send(send.bytes, len, false);
- for (ii = 0; ii < 10; ++ii) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_get(void) {
- return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
-}
-
-static enum test_return test_binary_getk(void) {
- return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
-}
-
-static enum test_return test_binary_getq_impl(const char *key, uint8_t cmd) {
- const char *missing = "test_binary_getq_missing";
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, temp, receive;
- size_t len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0,
- 0, 0);
- size_t len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
- missing, strlen(missing), NULL, 0);
- /* I need to change the first opaque so that I can separate the two
- * return packets */
- temp.request.message.header.request.opaque = 0xfeedface;
- memcpy(send.bytes + len, temp.bytes, len2);
- len += len2;
-
- len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
- key, strlen(key), NULL, 0);
- memcpy(send.bytes + len, temp.bytes, len2);
- len += len2;
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- /* The first GETQ shouldn't return anything */
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_getq(void) {
- return test_binary_getq_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
-}
-
-static enum test_return test_binary_getkq(void) {
- return test_binary_getq_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
-}
-
-static enum test_return test_binary_incr_impl(const char* key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response_header;
- protocol_binary_response_incr response;
- char bytes[1024];
- } send, receive;
- size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), 1, 0, 0);
-
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response_header, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(ntohll(receive.response.message.body.value) == ii);
- }
- }
-
- if (cmd == PROTOCOL_BINARY_CMD_INCREMENTQ) {
- test_binary_noop();
- }
- return TEST_PASS;
-}
-
-static enum test_return test_binary_incr(void) {
- return test_binary_incr_impl("test_binary_incr",
- PROTOCOL_BINARY_CMD_INCREMENT);
-}
-
-static enum test_return test_binary_incrq(void) {
- return test_binary_incr_impl("test_binary_incrq",
- PROTOCOL_BINARY_CMD_INCREMENTQ);
-}
-
-static enum test_return test_binary_decr_impl(const char* key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response_header;
- protocol_binary_response_decr response;
- char bytes[1024];
- } send, receive;
- size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), 1, 9, 0);
-
- int ii;
- for (ii = 9; ii >= 0; --ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response_header, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(ntohll(receive.response.message.body.value) == ii);
- }
- }
-
- /* decr on 0 should not wrap */
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response_header, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(ntohll(receive.response.message.body.value) == 0);
- } else {
- test_binary_noop();
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_decr(void) {
- return test_binary_decr_impl("test_binary_decr",
- PROTOCOL_BINARY_CMD_DECREMENT);
-}
-
-static enum test_return test_binary_decrq(void) {
- return test_binary_decr_impl("test_binary_decrq",
- PROTOCOL_BINARY_CMD_DECREMENTQ);
-}
-
-static enum test_return test_binary_version(void) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
-
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- PROTOCOL_BINARY_CMD_VERSION,
- NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_VERSION,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_flush_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
-
- size_t len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0, 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = flush_command(send.bytes, sizeof(send.bytes), cmd, 2, true);
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GET,
- key, strlen(key), NULL, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- sleep(2);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-
- int ii;
- for (ii = 0; ii < 2; ++ii) {
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0, 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = flush_command(send.bytes, sizeof(send.bytes), cmd, 0, ii == 0);
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- len = raw_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_GET,
- key, strlen(key), NULL, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_flush(void) {
- return test_binary_flush_impl("test_binary_flush",
- PROTOCOL_BINARY_CMD_FLUSH);
-}
-
-static enum test_return test_binary_flushq(void) {
- return test_binary_flush_impl("test_binary_flushq",
- PROTOCOL_BINARY_CMD_FLUSHQ);
-}
-
-static enum test_return test_binary_concat_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- const char *value = "world";
-
- size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), value, strlen(value));
-
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_NOT_STORED);
-
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), value, strlen(value), 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), value, strlen(value));
- safe_send(send.bytes, len, false);
-
- if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_PREPEND) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- } else {
- len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_NOOP,
- NULL, 0, NULL, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_NOOP,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GETK,
- key, strlen(key), NULL, 0);
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GETK,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- assert(receive.response.message.header.response.keylen == strlen(key));
- assert(receive.response.message.header.response.bodylen == (strlen(key) + 2*strlen(value) + 4));
-
- char *ptr = receive.bytes;
- ptr += sizeof(receive.response);
- ptr += 4;
-
- assert(memcmp(ptr, key, strlen(key)) == 0);
- ptr += strlen(key);
- assert(memcmp(ptr, value, strlen(value)) == 0);
- ptr += strlen(value);
- assert(memcmp(ptr, value, strlen(value)) == 0);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_append(void) {
- return test_binary_concat_impl("test_binary_append",
- PROTOCOL_BINARY_CMD_APPEND);
-}
-
-static enum test_return test_binary_prepend(void) {
- return test_binary_concat_impl("test_binary_prepend",
- PROTOCOL_BINARY_CMD_PREPEND);
-}
-
-static enum test_return test_binary_appendq(void) {
- return test_binary_concat_impl("test_binary_appendq",
- PROTOCOL_BINARY_CMD_APPENDQ);
-}
-
-static enum test_return test_binary_prependq(void) {
- return test_binary_concat_impl("test_binary_prependq",
- PROTOCOL_BINARY_CMD_PREPENDQ);
-}
-
-static enum test_return test_binary_stat(void) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
-
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- PROTOCOL_BINARY_CMD_STAT,
- NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- do {
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_STAT,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- } while (buffer.response.message.header.response.keylen != 0);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_illegal(void) {
- uint8_t cmd = 0x25;
- while (cmd != 0x00) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- cmd, NULL, 0, NULL, 0);
- safe_send(buffer.bytes, len, false);
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, cmd,
- PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
- ++cmd;
- }
-
- return TEST_PASS;
-}
-
-volatile bool hickup_thread_running;
-
-static void *binary_hickup_recv_verification_thread(void *arg) {
- protocol_binary_response_no_extras *response = malloc(65*1024);
- if (response != NULL) {
- while (safe_recv_packet(response, 65*1024)) {
- /* Just validate the packet format */
- validate_response_header(response,
- response->message.header.response.opcode,
- response->message.header.response.status);
- }
- free(response);
- }
- hickup_thread_running = false;
- allow_closed_read = false;
- return NULL;
-}
-
-static enum test_return test_binary_pipeline_hickup_chunk(void *buffer, size_t buffersize) {
- off_t offset = 0;
- char *key[256];
- uint64_t value = 0xfeedfacedeadbeef;
-
- while (hickup_thread_running &&
- offset + sizeof(protocol_binary_request_no_extras) < buffersize) {
- union {
- protocol_binary_request_no_extras request;
- char bytes[65 * 1024];
- } command;
- uint8_t cmd = (uint8_t)(rand() & 0xff);
- size_t len;
- size_t keylen = (rand() % 250) + 1;
-
- switch (cmd) {
- case PROTOCOL_BINARY_CMD_ADD:
- case PROTOCOL_BINARY_CMD_ADDQ:
- case PROTOCOL_BINARY_CMD_REPLACE:
- case PROTOCOL_BINARY_CMD_REPLACEQ:
- case PROTOCOL_BINARY_CMD_SET:
- case PROTOCOL_BINARY_CMD_SETQ:
- len = storage_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen , &value, sizeof(value),
- 0, 0);
- break;
- case PROTOCOL_BINARY_CMD_APPEND:
- case PROTOCOL_BINARY_CMD_APPENDQ:
- case PROTOCOL_BINARY_CMD_PREPEND:
- case PROTOCOL_BINARY_CMD_PREPENDQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, &value, sizeof(value));
- break;
- case PROTOCOL_BINARY_CMD_FLUSH:
- case PROTOCOL_BINARY_CMD_FLUSHQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- NULL, 0, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_NOOP:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- NULL, 0, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_DELETE:
- case PROTOCOL_BINARY_CMD_DELETEQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_DECREMENT:
- case PROTOCOL_BINARY_CMD_DECREMENTQ:
- case PROTOCOL_BINARY_CMD_INCREMENT:
- case PROTOCOL_BINARY_CMD_INCREMENTQ:
- len = arithmetic_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, 1, 0, 0);
- break;
- case PROTOCOL_BINARY_CMD_VERSION:
- len = raw_command(command.bytes, sizeof(command.bytes),
- PROTOCOL_BINARY_CMD_VERSION,
- NULL, 0, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_GET:
- case PROTOCOL_BINARY_CMD_GETK:
- case PROTOCOL_BINARY_CMD_GETKQ:
- case PROTOCOL_BINARY_CMD_GETQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, NULL, 0);
- break;
-
- case PROTOCOL_BINARY_CMD_TOUCH:
- case PROTOCOL_BINARY_CMD_GAT:
- case PROTOCOL_BINARY_CMD_GATQ:
- case PROTOCOL_BINARY_CMD_GATK:
- case PROTOCOL_BINARY_CMD_GATKQ:
- len = touch_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, 10);
- break;
-
- case PROTOCOL_BINARY_CMD_STAT:
- len = raw_command(command.bytes, sizeof(command.bytes),
- PROTOCOL_BINARY_CMD_STAT,
- NULL, 0, NULL, 0);
- break;
-
- case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
- case PROTOCOL_BINARY_CMD_SASL_AUTH:
- case PROTOCOL_BINARY_CMD_SASL_STEP:
- /* Ignoring SASL */
- case PROTOCOL_BINARY_CMD_QUITQ:
- case PROTOCOL_BINARY_CMD_QUIT:
- /* I don't want to pass on the quit commands ;-) */
- cmd |= 0xf0;
- /* FALLTHROUGH */
- default:
- len = raw_command(command.bytes, sizeof(command.bytes),
- cmd, NULL, 0, NULL, 0);
- }
-
- if ((len + offset) < buffersize) {
- memcpy(((char*)buffer) + offset, command.bytes, len);
- offset += len;
- } else {
- break;
- }
- }
- safe_send(buffer, offset, true);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_pipeline_hickup(void)
-{
- size_t buffersize = 65 * 1024;
- void *buffer = malloc(buffersize);
- int ii;
-
- pthread_t tid;
- int ret;
- allow_closed_read = true;
- hickup_thread_running = true;
- if ((ret = pthread_create(&tid, NULL,
- binary_hickup_recv_verification_thread, NULL)) != 0) {
- fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
- return TEST_FAIL;
- }
-
- /* Allow the thread to start */
- usleep(250);
-
- srand((int)time(NULL));
- for (ii = 0; ii < 2; ++ii) {
- test_binary_pipeline_hickup_chunk(buffer, buffersize);
- }
-
- /* send quitq to shut down the read thread ;-) */
- size_t len = raw_command(buffer, buffersize, PROTOCOL_BINARY_CMD_QUITQ,
- NULL, 0, NULL, 0);
- safe_send(buffer, len, false);
-
- pthread_join(tid, NULL);
- free(buffer);
- return TEST_PASS;
-}
-
-
-static enum test_return test_issue_101(void) {
- const int max = 2;
- enum test_return ret = TEST_PASS;
- int fds[max];
- int ii = 0;
- pid_t child = 0;
-
- if (getenv("SKIP_TEST_101") != NULL) {
- return TEST_SKIP;
- }
-
- const char *command = "stats\r\nstats\r\nstats\r\nstats\r\nstats\r\n";
- size_t cmdlen = strlen(command);
-
- server_pid = start_server(&port, false, 1000);
-
- for (ii = 0; ii < max; ++ii) {
- fds[ii] = connect_server("127.0.0.1", port, true);
- assert(fds[ii] > 0);
- }
-
- /* Send command on the connection until it blocks */
- for (ii = 0; ii < max; ++ii) {
- bool more = true;
- do {
- ssize_t err = write(fds[ii], command, cmdlen);
- if (err == -1) {
- switch (errno) {
- case EINTR:
- break;
- case ENOMEM:
- case EWOULDBLOCK:
- more = false;
- break;
- default:
- ret = TEST_FAIL;
- goto cleanup;
- }
- }
- } while (more);
- }
-
- child = fork();
- if (child == (pid_t)-1) {
- abort();
- } else if (child > 0) {
- int stat;
- pid_t c;
- while ((c = waitpid(child, &stat, 0)) == (pid_t)-1 && errno == EINTR);
- assert(c == child);
- assert(stat == 0);
- } else {
- sock = connect_server("127.0.0.1", port, false);
- ret = test_binary_noop();
- close(sock);
- exit(0);
- }
-
- cleanup:
- /* close all connections */
- for (ii = 0; ii < max; ++ii) {
- close(fds[ii]);
- }
-
- assert(kill(server_pid, SIGTERM) == 0);
-
- return ret;
-}
-
-typedef enum test_return (*TEST_FUNC)(void);
-struct testcase {
- const char *description;
- TEST_FUNC function;
-};
-
-struct testcase testcases[] = {
- { "cache_create", cache_create_test },
- { "cache_constructor", cache_constructor_test },
- { "cache_constructor_fail", cache_fail_constructor_test },
- { "cache_destructor", cache_destructor_test },
- { "cache_reuse", cache_reuse_test },
- { "cache_redzone", cache_redzone_test },
- { "issue_161", test_issue_161 },
- { "strtol", test_safe_strtol },
- { "strtoll", test_safe_strtoll },
- { "strtoul", test_safe_strtoul },
- { "strtoull", test_safe_strtoull },
- { "issue_44", test_issue_44 },
- { "vperror", test_vperror },
- { "issue_101", test_issue_101 },
- /* The following tests all run towards the same server */
- { "start_server", start_memcached_server },
- { "issue_92", test_issue_92 },
- { "issue_102", test_issue_102 },
- { "binary_noop", test_binary_noop },
- { "binary_quit", test_binary_quit },
- { "binary_quitq", test_binary_quitq },
- { "binary_set", test_binary_set },
- { "binary_setq", test_binary_setq },
- { "binary_add", test_binary_add },
- { "binary_addq", test_binary_addq },
- { "binary_replace", test_binary_replace },
- { "binary_replaceq", test_binary_replaceq },
- { "binary_delete", test_binary_delete },
- { "binary_deleteq", test_binary_deleteq },
- { "binary_get", test_binary_get },
- { "binary_getq", test_binary_getq },
- { "binary_getk", test_binary_getk },
- { "binary_getkq", test_binary_getkq },
- { "binary_incr", test_binary_incr },
- { "binary_incrq", test_binary_incrq },
- { "binary_decr", test_binary_decr },
- { "binary_decrq", test_binary_decrq },
- { "binary_version", test_binary_version },
- { "binary_flush", test_binary_flush },
- { "binary_flushq", test_binary_flushq },
- { "binary_append", test_binary_append },
- { "binary_appendq", test_binary_appendq },
- { "binary_prepend", test_binary_prepend },
- { "binary_prependq", test_binary_prependq },
- { "binary_stat", test_binary_stat },
- { "binary_illegal", test_binary_illegal },
- { "binary_pipeline_hickup", test_binary_pipeline_hickup },
- { "stop_server", stop_memcached_server },
- { NULL, NULL }
-};
-
-int main(int argc, char **argv)
-{
- int exitcode = 0;
- int ii = 0, num_cases = 0;
-
- for (num_cases = 0; testcases[num_cases].description; num_cases++) {
- /* Just counting */
- }
-
- printf("1..%d\n", num_cases);
-
- for (ii = 0; testcases[ii].description != NULL; ++ii) {
- fflush(stdout);
-#ifndef DEBUG
- /* the test program shouldn't run longer than 10 minutes... */
- alarm(600);
-#endif
- enum test_return ret = testcases[ii].function();
- if (ret == TEST_SKIP) {
- fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
- } else if (ret == TEST_PASS) {
- fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description);
- } else {
- fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description);
- exitcode = 1;
- }
- fflush(stdout);
- }
-
- return exitcode;
-}