Merge trunk
authorTrond Norbye <trond.norbye@sun.com>
Thu, 17 Sep 2009 21:35:44 +0000 (23:35 +0200)
committerTrond Norbye <trond.norbye@sun.com>
Thu, 17 Sep 2009 21:35:44 +0000 (23:35 +0200)
.bzrignore
ChangeLog
clients/Makefile.am
clients/memcapable.c [new file with mode: 0644]
libmemcached/Makefile.am
libmemcached/byteorder.c
libmemcached/byteorder.h [new file with mode: 0644]
libmemcached/common.h
libmemcached/memcached/protocol_binary.h
m4/memcached.m4
tests/Makefile.am

index f1beb3e190f7cd975be5edfb40243d4661340e05..73d93db3e789967e35c7eff8a7b13b759e670bfd 100644 (file)
@@ -14,6 +14,7 @@ Makefile.in
 TAGS
 aclocal.m4
 autom4te.cache
+clients/memcapable
 clients/memcat
 clients/memcp
 clients/memdump
index 6c9641fc8928e67426ac3424c0a7cc58286109de..958cc0fda94a8fac84d22e85ebf0a089c55b9ca2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+0.33
+  * Added memcapable to test servers for binary compatibility.
+
   0.32 Tue Sep 15 15:49:09 PDT 2009
   * Change of behavior where linger is only modified for no-block and then
     it is set to zero.
index 7eba92863adf6d1dca8011bfd53198fb49f10258..e5a1f0c0afff8d7e5eb9eea1a84a2117a500fc7e 100644 (file)
@@ -1,11 +1,11 @@
 LDADDS = libutilities.la $(top_builddir)/libmemcached/libmemcached.la
 
-bin_PROGRAMS = memcat memdump memcp memstat memrm memflush memslap memerror
+bin_PROGRAMS = memcat memdump memcp memstat memrm memflush memslap memerror memcapable
 
 noinst_HEADERS = client_options.h \
                utilities.h \
                generator.h \
-               execute.h 
+               execute.h
 
 noinst_LTLIBRARIES= libutilities.la libgenexec.la
 
@@ -35,7 +35,12 @@ memerror_LDADD = $(LDADDS)
 
 memslap_SOURCES = memslap.c
 memslap_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS)
-memslap_LDADD = $(PTHREAD_LIBS) libgenexec.la $(LDADDS) 
+memslap_LDADD = $(PTHREAD_LIBS) libgenexec.la $(LDADDS)
+
+memcapable_SOURCES = memcapable.c
+if BUILD_BYTEORDER
+memcapable_LDADD=$(top_builddir)/libmemcached/libbyteorder.la
+endif
 
 test-start-server:
        memflush --servers=localhost
diff --git a/clients/memcapable.c b/clients/memcapable.c
new file mode 100644 (file)
index 0000000..0519f38
--- /dev/null
@@ -0,0 +1,1223 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#undef NDEBUG
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <libmemcached/memcached/protocol_binary.h>
+#include <libmemcached/byteorder.h>
+
+#ifdef linux
+/* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to
+ * optimize the conversion functions, but the prototypes generate warnings
+ * from gcc. The conversion methods isn't the bottleneck for my app, so
+ * just remove the warnings by undef'ing the optimization ..
+ */
+#undef ntohs
+#undef ntohl
+#endif
+
+/* Should we generate coredumps when we enounter an error (-c) */
+static bool do_core= false;
+/* connection to the server */
+static int sock;
+/* Should the output from test failures be verbose or quiet? */
+static bool verbose= false;
+
+/* The number of seconds to wait for an IO-operation */
+static int timeout= 2;
+
+/*
+ * Instead of having to cast between the different datatypes we create
+ * a union of all of the different types of pacages we want to send.
+ * A lot of the different commands use the same packet layout, so I'll
+ * just define the different types I need. The typedefs only contain
+ * the header of the message, so we need some space for keys and body
+ * To avoid to have to do multiple writes, lets add a chunk of memory
+ * to use. 1k should be more than enough for header, key and body.
+ */
+typedef union
+{
+  protocol_binary_request_no_extras plain;
+  protocol_binary_request_flush flush;
+  protocol_binary_request_incr incr;
+  protocol_binary_request_set set;
+  char bytes[1024];
+} command;
+
+typedef union
+{
+  protocol_binary_response_no_extras plain;
+  protocol_binary_response_incr incr;
+  protocol_binary_response_decr decr;
+  char bytes[1024];
+} response;
+
+enum test_return
+{
+  TEST_SKIP, TEST_PASS, TEST_PASS_RECONNECT, TEST_FAIL
+};
+
+/**
+ * Try to get an addrinfo struct for a given port on a given host
+ */
+static struct addrinfo *lookuphost(const char *hostname, const char *port)
+{
+  struct addrinfo *ai= 0;
+  struct addrinfo hints= {.ai_family=AF_UNSPEC,
+    .ai_protocol=IPPROTO_TCP,
+    .ai_socktype=SOCK_STREAM};
+  int error= getaddrinfo(hostname, port, &hints, &ai);
+
+  if (error != 0)
+  {
+    if (error != EAI_SYSTEM)
+      fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
+    else
+      perror("getaddrinfo()");
+  }
+
+  return ai;
+}
+
+/**
+ * Set the socket in nonblocking mode
+ * @return -1 if failure, the socket otherwise
+ */
+static int set_noblock(void)
+{
+  int flags= fcntl(sock, F_GETFL, 0);
+  if (flags == -1)
+  {
+    perror("Failed to get socket flags");
+    close(sock);
+    return -1;
+  }
+
+  if ((flags & O_NONBLOCK) != O_NONBLOCK)
+  {
+    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
+    {
+      perror("Failed to set socket to nonblocking mode");
+      close(sock);
+      return -1;
+    }
+  }
+
+  return sock;
+}
+
+/**
+ * Try to open a connection to the server
+ * @param hostname the name of the server to connect to
+ * @param port the port number (or service) to connect to
+ * @return positive integer if success, -1 otherwise
+ */
+static int connect_server(const char *hostname, const char *port)
+{
+  struct addrinfo *ai= lookuphost(hostname, port);
+  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
+      {
+        sock= set_noblock();
+      }
+    } else
+      fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
+
+    freeaddrinfo(ai);
+  }
+
+  return sock;
+}
+
+static ssize_t timeout_io_op(int fd, short direction, void *buf, size_t len)
+{
+  ssize_t ret;
+
+  if (direction == POLLOUT)
+    ret= write(fd, buf, len);
+  else
+    ret= read(fd, buf, len);
+
+  if (ret == -1 && errno == EWOULDBLOCK) {
+    struct pollfd fds = {
+      .events= direction,
+      .fd= fd
+    };
+    int err= poll(&fds, 1, timeout * 1000);
+
+    if (err == 1)
+    {
+      if (direction == POLLOUT)
+        ret= write(fd, buf, len);
+      else
+        ret= read(fd, buf, len);
+    }
+    else if (err == 0)
+    {
+      errno = ETIMEDOUT;
+    }
+    else
+    {
+      perror("Failed to poll");
+      return -1;
+    }
+  }
+
+  return ret;
+}
+
+/**
+ * Ensure that an expression is true. If it isn't print out a message similar
+ * to assert() and create a coredump if the user wants that. If not an error
+ * message is returned.
+ *
+ */
+static enum test_return ensure(bool val, const char *expression, const char *file, int line)
+{
+  if (!val)
+  {
+    if (verbose)
+      fprintf(stderr, "%s:%u: %s\n", file, line, expression);
+
+    if (do_core)
+      abort();
+
+    return TEST_FAIL;
+  }
+
+  return TEST_PASS;
+}
+
+#define verify(expression) do { if (ensure(expression, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
+#define execute(expression) do { if (ensure(expression == TEST_PASS, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
+
+/**
+ * Send a chunk of memory over the socket (retry if the call is iterrupted
+ */
+static enum test_return retry_write(const void* buf, size_t len)
+{
+  size_t offset= 0;
+  const char* ptr= buf;
+
+  do
+  {
+    size_t num_bytes= len - offset;
+    ssize_t nw= timeout_io_op(sock, POLLOUT, (void*)(ptr + offset), num_bytes);
+    if (nw == -1)
+      verify(errno == EINTR || errno == EAGAIN);
+    else
+      offset+= (size_t)nw;
+  } while (offset < len);
+
+  return TEST_PASS;
+}
+
+/**
+ * Resend a packet to the server (All fields in the command header should
+ * be in network byte order)
+ */
+static enum test_return resend_packet(command *cmd)
+{
+  size_t length= sizeof (protocol_binary_request_no_extras) +
+          ntohl(cmd->plain.message.header.request.bodylen);
+
+  execute(retry_write(cmd, length));
+  return TEST_PASS;
+}
+
+/**
+ * Send a command to the server. The command header needs to be updated
+ * to network byte order
+ */
+static enum test_return send_packet(command *cmd)
+{
+  /* Fix the byteorder of the header */
+  cmd->plain.message.header.request.keylen=
+          ntohs(cmd->plain.message.header.request.keylen);
+  cmd->plain.message.header.request.bodylen=
+          ntohl(cmd->plain.message.header.request.bodylen);
+  cmd->plain.message.header.request.cas=
+          ntohll(cmd->plain.message.header.request.cas);
+
+  execute(resend_packet(cmd));
+  return TEST_PASS;
+}
+
+/**
+ * Read a fixed length chunk of data from the server
+ */
+static enum test_return retry_read(void *buf, size_t len)
+{
+  size_t offset= 0;
+  do
+  {
+    ssize_t nr= timeout_io_op(sock, POLLIN, ((char*) buf) + offset, len - offset);
+    switch (nr) {
+    case -1 :
+      verify(errno == EINTR || errno == EAGAIN);
+      break;
+    case 0:
+      return TEST_FAIL;
+    default:
+      offset+= (size_t)nr;
+    }
+  } while (offset < len);
+
+  return TEST_PASS;
+}
+
+/**
+ * Receive a response from the server and conver the fields in the header
+ * to local byte order
+ */
+static enum test_return recv_packet(response *rsp)
+{
+  execute(retry_read(rsp, sizeof (protocol_binary_response_no_extras)));
+
+  /* Fix the byte order in the packet header */
+  rsp->plain.message.header.response.keylen=
+          ntohs(rsp->plain.message.header.response.keylen);
+  rsp->plain.message.header.response.status=
+          ntohs(rsp->plain.message.header.response.status);
+  rsp->plain.message.header.response.bodylen=
+          ntohl(rsp->plain.message.header.response.bodylen);
+  rsp->plain.message.header.response.cas=
+          ntohll(rsp->plain.message.header.response.cas);
+
+  size_t bodysz= rsp->plain.message.header.response.bodylen;
+  if (bodysz > 0)
+    execute(retry_read(rsp->bytes + sizeof (protocol_binary_response_no_extras), bodysz));
+
+  return TEST_PASS;
+}
+
+/**
+ * Create a storage command (add, set, replace etc)
+ *
+ * @param cmd destination buffer
+ * @param cc the storage command to create
+ * @param key the key to store
+ * @param keylen the length of the key
+ * @param dta the data to store with the key
+ * @param dtalen the length of the data to store with the key
+ * @param flags the flags to store along with the key
+ * @param exp the expiry time for the key
+ */
+static void storage_command(command *cmd,
+                            uint8_t cc,
+                            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= &cmd->set;
+
+  memset(request, 0, sizeof (*request));
+  request->message.header.request.magic= PROTOCOL_BINARY_REQ;
+  request->message.header.request.opcode= cc;
+  request->message.header.request.keylen= (uint16_t)keylen;
+  request->message.header.request.extlen= 8;
+  request->message.header.request.bodylen= (uint32_t)(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(cmd->bytes + key_offset, key, keylen);
+  if (dta != NULL)
+    memcpy(cmd->bytes + key_offset + keylen, dta, dtalen);
+}
+
+/**
+ * Create a basic command to send to the server
+ * @param cmd destination buffer
+ * @param cc the command to create
+ * @param key the key to store
+ * @param keylen the length of the key
+ * @param dta the data to store with the key
+ * @param dtalen the length of the data to store with the key
+ */
+static void raw_command(command *cmd,
+                        uint8_t cc,
+                        const void* key,
+                        size_t keylen,
+                        const void* dta,
+                        size_t dtalen)
+{
+  /* all of the storage commands use the same command layout */
+  memset(cmd, 0, sizeof (*cmd));
+  cmd->plain.message.header.request.magic= PROTOCOL_BINARY_REQ;
+  cmd->plain.message.header.request.opcode= cc;
+  cmd->plain.message.header.request.keylen= (uint16_t)keylen;
+  cmd->plain.message.header.request.bodylen= (uint32_t)(keylen + dtalen);
+  cmd->plain.message.header.request.opaque= 0xdeadbeef;
+
+  off_t key_offset= sizeof (protocol_binary_request_no_extras);
+
+  if (key != NULL)
+    memcpy(cmd->bytes + key_offset, key, keylen);
+
+  if (dta != NULL)
+    memcpy(cmd->bytes + key_offset + keylen, dta, dtalen);
+}
+
+/**
+ * Create the flush command
+ * @param cmd destination buffer
+ * @param cc the command to create (FLUSH/FLUSHQ)
+ * @param exptime when to flush
+ * @param use_extra to force using of the extra field?
+ */
+static void flush_command(command *cmd,
+                          uint8_t cc, uint32_t exptime, bool use_extra)
+{
+  memset(cmd, 0, sizeof (cmd->flush));
+  cmd->flush.message.header.request.magic= PROTOCOL_BINARY_REQ;
+  cmd->flush.message.header.request.opcode= cc;
+  cmd->flush.message.header.request.opaque= 0xdeadbeef;
+
+  if (exptime != 0 || use_extra)
+  {
+    cmd->flush.message.header.request.extlen= 4;
+    cmd->flush.message.body.expiration= htonl(exptime);
+    cmd->flush.message.header.request.bodylen= 4;
+  }
+}
+
+/**
+ * Create a incr/decr command
+ * @param cc the cmd to create (FLUSH/FLUSHQ)
+ * @param key the key to operate on
+ * @param keylen the number of bytes in the key
+ * @param delta the number to add/subtract
+ * @param initial the initial value if the key doesn't exist
+ * @param exp when the key should expire if it isn't set
+ */
+static void arithmetic_command(command *cmd,
+                               uint8_t cc,
+                               const void* key,
+                               size_t keylen,
+                               uint64_t delta,
+                               uint64_t initial,
+                               uint32_t exp)
+{
+  memset(cmd, 0, sizeof (cmd->incr));
+  cmd->incr.message.header.request.magic= PROTOCOL_BINARY_REQ;
+  cmd->incr.message.header.request.opcode= cc;
+  cmd->incr.message.header.request.keylen= (uint16_t)keylen;
+  cmd->incr.message.header.request.extlen= 20;
+  cmd->incr.message.header.request.bodylen= (uint32_t)(keylen + 20);
+  cmd->incr.message.header.request.opaque= 0xdeadbeef;
+  cmd->incr.message.body.delta= htonll(delta);
+  cmd->incr.message.body.initial= htonll(initial);
+  cmd->incr.message.body.expiration= htonl(exp);
+
+  off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20;
+  memcpy(cmd->bytes + key_offset, key, keylen);
+}
+
+/**
+ * Validate the response header from the server
+ * @param rsp the response to check
+ * @param cc the expected command
+ * @param status the expected status
+ */
+static enum test_return validate_response_header(response *rsp,
+                                                 uint8_t cc, uint16_t status)
+{
+  verify(rsp->plain.message.header.response.magic == PROTOCOL_BINARY_RES);
+  verify(rsp->plain.message.header.response.opcode == cc);
+  verify(rsp->plain.message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
+  verify(rsp->plain.message.header.response.status == status);
+  verify(rsp->plain.message.header.response.opaque == 0xdeadbeef);
+
+  if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS)
+  {
+    switch (cc) {
+    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:
+      verify("Quiet command shouldn't return on success" == NULL);
+    default:
+      break;
+    }
+
+    switch (cc) {
+    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:
+      verify(rsp->plain.message.header.response.keylen == 0);
+      verify(rsp->plain.message.header.response.extlen == 0);
+      verify(rsp->plain.message.header.response.bodylen == 0);
+      verify(rsp->plain.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:
+      verify(rsp->plain.message.header.response.keylen == 0);
+      verify(rsp->plain.message.header.response.extlen == 0);
+      verify(rsp->plain.message.header.response.bodylen == 0);
+      verify(rsp->plain.message.header.response.cas == 0);
+      break;
+
+    case PROTOCOL_BINARY_CMD_DECREMENT:
+    case PROTOCOL_BINARY_CMD_INCREMENT:
+      verify(rsp->plain.message.header.response.keylen == 0);
+      verify(rsp->plain.message.header.response.extlen == 0);
+      verify(rsp->plain.message.header.response.bodylen == 8);
+      verify(rsp->plain.message.header.response.cas != 0);
+      break;
+
+    case PROTOCOL_BINARY_CMD_STAT:
+      verify(rsp->plain.message.header.response.extlen == 0);
+      /* key and value exists in all packets except in the terminating */
+      verify(rsp->plain.message.header.response.cas == 0);
+      break;
+
+    case PROTOCOL_BINARY_CMD_VERSION:
+      verify(rsp->plain.message.header.response.keylen == 0);
+      verify(rsp->plain.message.header.response.extlen == 0);
+      verify(rsp->plain.message.header.response.bodylen != 0);
+      verify(rsp->plain.message.header.response.cas == 0);
+      break;
+
+    case PROTOCOL_BINARY_CMD_GET:
+    case PROTOCOL_BINARY_CMD_GETQ:
+      verify(rsp->plain.message.header.response.keylen == 0);
+      verify(rsp->plain.message.header.response.extlen == 4);
+      verify(rsp->plain.message.header.response.cas != 0);
+      break;
+
+    case PROTOCOL_BINARY_CMD_GETK:
+    case PROTOCOL_BINARY_CMD_GETKQ:
+      verify(rsp->plain.message.header.response.keylen != 0);
+      verify(rsp->plain.message.header.response.extlen == 4);
+      verify(rsp->plain.message.header.response.cas != 0);
+      break;
+
+    default:
+      /* Undefined command code */
+      break;
+    }
+  }
+  else
+  {
+    verify(rsp->plain.message.header.response.cas == 0);
+    verify(rsp->plain.message.header.response.extlen == 0);
+    if (cc != PROTOCOL_BINARY_CMD_GETK)
+    {
+      verify(rsp->plain.message.header.response.keylen == 0);
+    }
+  }
+
+  return TEST_PASS;
+}
+
+static enum test_return test_binary_noop(void)
+{
+  command cmd;
+  response rsp;
+  raw_command(&cmd, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0);
+  execute(send_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_NOOP,
+                                  PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  return TEST_PASS;
+}
+
+static enum test_return test_binary_quit_impl(uint8_t cc)
+{
+  command cmd;
+  response rsp;
+  raw_command(&cmd, cc, NULL, 0, NULL, 0);
+
+  execute(send_packet(&cmd));
+  if (cc == PROTOCOL_BINARY_CMD_QUIT)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_QUIT,
+                                    PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  }
+
+  /* Socket should be closed now, read should return 0 */
+  verify(timeout_io_op(sock, POLLIN, rsp.bytes, sizeof(rsp.bytes)) == 0);
+
+  return TEST_PASS_RECONNECT;
+}
+
+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 cc)
+{
+  command cmd;
+  response rsp;
+
+  uint64_t value= 0xdeadbeefdeadcafe;
+  storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
+
+  /* set should always work */
+  for (int ii= 0; ii < 10; ii++)
+  {
+    if (ii == 0)
+      execute(send_packet(&cmd));
+    else
+      execute(resend_packet(&cmd));
+
+    if (cc == PROTOCOL_BINARY_CMD_SET)
+    {
+      execute(recv_packet(&rsp));
+      verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+    }
+    else
+      execute(test_binary_noop());
+  }
+
+  /* try to set with the correct CAS value */
+  cmd.plain.message.header.request.cas=
+          htonll(rsp.plain.message.header.response.cas);
+  execute(resend_packet(&cmd));
+  if (cc == PROTOCOL_BINARY_CMD_SET)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  }
+  else
+    execute(test_binary_noop());
+
+  /* try to set with an incorrect CAS value */
+  cmd.plain.message.header.request.cas=
+          htonll(rsp.plain.message.header.response.cas - 1);
+  execute(resend_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
+
+  return test_binary_noop();
+}
+
+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 cc)
+{
+  command cmd;
+  response rsp;
+  uint64_t value= 0xdeadbeefdeadcafe;
+  storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
+
+  /* first add should work, rest of them should fail (even with cas
+     as wildcard */
+  for (int ii=0; ii < 10; ii++)
+  {
+    if (ii == 0)
+      execute(send_packet(&cmd));
+    else
+      execute(resend_packet(&cmd));
+
+    if (cc == PROTOCOL_BINARY_CMD_ADD || ii > 0)
+    {
+      uint16_t expected_result;
+      if (ii == 0)
+        expected_result= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+      else
+        expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+
+      execute(recv_packet(&rsp));
+      verify(validate_response_header(&rsp, cc, expected_result));
+    }
+    else
+      execute(test_binary_noop());
+  }
+
+  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 set_item(const char *key, const char *value)
+{
+  command cmd;
+  response rsp;
+  storage_command(&cmd, PROTOCOL_BINARY_CMD_SET, key, strlen(key),
+                  value, strlen(value), 0, 0);
+  execute(send_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
+                                  PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  return TEST_PASS;
+}
+
+static enum test_return test_binary_replace_impl(const char* key, uint8_t cc)
+{
+  command cmd;
+  response rsp;
+  uint64_t value= 0xdeadbeefdeadcafe;
+  storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
+
+  /* first replace should fail, successive should succeed (when the
+     item is added! */
+  for (int ii= 0; ii < 10; ii++)
+  {
+    if (ii == 0)
+      execute(send_packet(&cmd));
+    else
+      execute(resend_packet(&cmd));
+
+    if (cc == PROTOCOL_BINARY_CMD_REPLACE || ii == 0)
+    {
+      uint16_t expected_result;
+      if (ii == 0)
+        expected_result=PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+      else
+        expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+      execute(recv_packet(&rsp));
+      verify(validate_response_header(&rsp, cc, expected_result));
+
+      if (ii == 0)
+        execute(set_item(key, key));
+    }
+    else
+      execute(test_binary_noop());
+  }
+
+  /* verify that replace with CAS value works! */
+  cmd.plain.message.header.request.cas=
+          htonll(rsp.plain.message.header.response.cas);
+  execute(resend_packet(&cmd));
+
+  if (cc == PROTOCOL_BINARY_CMD_REPLACE)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  }
+  else
+    execute(test_binary_noop());
+
+  /* try to set with an incorrect CAS value */
+  cmd.plain.message.header.request.cas=
+          htonll(rsp.plain.message.header.response.cas - 1);
+  execute(resend_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
+
+  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 cc)
+{
+  command cmd;
+  response rsp;
+  raw_command(&cmd, cc, key, strlen(key), NULL, 0);
+
+  /* The delete shouldn't work the first time, because the item isn't there */
+  execute(send_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
+  execute(set_item(key, key));
+
+  /* The item should be present now, resend*/
+  execute(resend_packet(&cmd));
+  if (cc == PROTOCOL_BINARY_CMD_DELETE)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  }
+
+  execute(test_binary_noop());
+
+  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 cc)
+{
+  command cmd;
+  response rsp;
+
+  raw_command(&cmd, cc, key, strlen(key), NULL, 0);
+  execute(send_packet(&cmd));
+
+  if (cc == PROTOCOL_BINARY_CMD_GET || cc == PROTOCOL_BINARY_CMD_GETK)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
+  }
+  else
+    execute(test_binary_noop());
+
+  execute(set_item(key, key));
+  execute(resend_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, cc, 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(void)
+{
+  return test_binary_get_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
+}
+
+static enum test_return test_binary_getkq(void)
+{
+  return test_binary_get_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
+}
+
+static enum test_return test_binary_incr_impl(const char* key, uint8_t cc)
+{
+  command cmd;
+  response rsp;
+  arithmetic_command(&cmd, cc, key, strlen(key), 1, 0, 0);
+
+  uint64_t ii;
+  for (ii= 0; ii < 10; ++ii)
+  {
+    if (ii == 0)
+      execute(send_packet(&cmd));
+    else
+      execute(resend_packet(&cmd));
+
+    if (cc == PROTOCOL_BINARY_CMD_INCREMENT)
+    {
+      execute(recv_packet(&rsp));
+      verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+      verify(ntohll(rsp.incr.message.body.value) == ii);
+    }
+    else
+      execute(test_binary_noop());
+  }
+
+  /* @todo add incorrect CAS */
+  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 cc)
+{
+  command cmd;
+  response rsp;
+  arithmetic_command(&cmd, cc, key, strlen(key), 1, 9, 0);
+
+  int ii;
+  for (ii= 9; ii > -1; --ii)
+  {
+    if (ii == 9)
+      execute(send_packet(&cmd));
+    else
+      execute(resend_packet(&cmd));
+
+    if (cc == PROTOCOL_BINARY_CMD_DECREMENT)
+    {
+      execute(recv_packet(&rsp));
+      verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+      verify(ntohll(rsp.decr.message.body.value) == (uint64_t)ii);
+    }
+    else
+      execute(test_binary_noop());
+  }
+
+  /* decr 0 should not wrap */
+  execute(resend_packet(&cmd));
+  if (cc == PROTOCOL_BINARY_CMD_DECREMENT)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+    verify(ntohll(rsp.decr.message.body.value) == 0);
+  }
+  else
+  {
+    /* @todo get the value and verify! */
+
+  }
+
+  /* @todo add incorrect cas */
+  execute(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)
+{
+  command cmd;
+  response rsp;
+  raw_command(&cmd, PROTOCOL_BINARY_CMD_VERSION, NULL, 0, NULL, 0);
+
+  execute(send_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_VERSION,
+                                  PROTOCOL_BINARY_RESPONSE_SUCCESS));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_binary_flush_impl(const char *key, uint8_t cc)
+{
+  command cmd;
+  response rsp;
+
+  for (int ii= 0; ii < 2; ++ii)
+  {
+    execute(set_item(key, key));
+    flush_command(&cmd, cc, 0, ii == 0);
+    execute(send_packet(&cmd));
+
+    if (cc == PROTOCOL_BINARY_CMD_FLUSH)
+    {
+      execute(recv_packet(&rsp));
+      verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+    }
+    else
+      execute(test_binary_noop());
+
+    raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
+    execute(send_packet(&cmd));
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, 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 cc)
+{
+  command cmd;
+  response rsp;
+  const char *value;
+
+  if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
+    value="hello";
+  else
+    value=" world";
+
+  execute(set_item(key, value));
+
+  if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
+    value=" world";
+  else
+    value="hello";
+
+  raw_command(&cmd, cc, key, strlen(key), value, strlen(value));
+  execute(send_packet(&cmd));
+  if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_PREPEND)
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  }
+  else
+    execute(test_binary_noop());
+
+  raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
+  execute(send_packet(&cmd));
+  execute(recv_packet(&rsp));
+  verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET,
+                                  PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  verify(rsp.plain.message.header.response.bodylen - 4 == 11);
+  verify(memcmp(rsp.bytes + 28, "hello world", 11) == 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)
+{
+  command cmd;
+  response rsp;
+
+  raw_command(&cmd, PROTOCOL_BINARY_CMD_STAT, NULL, 0, NULL, 0);
+  execute(send_packet(&cmd));
+
+  do
+  {
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_STAT,
+                                    PROTOCOL_BINARY_RESPONSE_SUCCESS));
+  } while (rsp.plain.message.header.response.keylen != 0);
+
+  return TEST_PASS;
+}
+
+static enum test_return test_binary_illegal(void)
+{
+  command cmd;
+  response rsp;
+  uint8_t cc= 0x1b;
+
+  while (cc != 0x00)
+  {
+    raw_command(&cmd, cc, NULL, 0, NULL, 0);
+    execute(send_packet(&cmd));
+    execute(recv_packet(&rsp));
+    verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND));
+    ++cc;
+  }
+
+  return TEST_PASS;
+}
+
+typedef enum test_return(*TEST_FUNC)(void);
+
+struct testcase
+{
+  const char *description;
+  TEST_FUNC function;
+};
+
+struct testcase testcases[]= {
+  { "noop", test_binary_noop},
+  { "quit", test_binary_quit},
+  { "quitq", test_binary_quitq},
+  { "set", test_binary_set},
+  { "setq", test_binary_setq},
+  { "flush", test_binary_flush},
+  { "flushq", test_binary_flushq},
+  { "add", test_binary_add},
+  { "addq", test_binary_addq},
+  { "replace", test_binary_replace},
+  { "replaceq", test_binary_replaceq},
+  { "delete", test_binary_delete},
+  { "deleteq", test_binary_deleteq},
+  { "get", test_binary_get},
+  { "getq", test_binary_getq},
+  { "getk", test_binary_getk},
+  { "getkq", test_binary_getkq},
+  { "incr", test_binary_incr},
+  { "incrq", test_binary_incrq},
+  { "decr", test_binary_decr},
+  { "decrq", test_binary_decrq},
+  { "version", test_binary_version},
+  { "append", test_binary_append},
+  { "appendq", test_binary_appendq},
+  { "prepend", test_binary_prepend},
+  { "prependq", test_binary_prependq},
+  { "stat", test_binary_stat},
+  { "illegal", test_binary_illegal},
+  { NULL, NULL}
+};
+
+int main(int argc, char **argv)
+{
+  static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"};
+  int total= 0;
+  int failed= 0;
+  const char *hostname= "localhost";
+  const char *port= "11211";
+  int cmd;
+
+  while ((cmd= getopt(argc, argv, "t:vch:p:?")) != EOF)
+  {
+    switch (cmd) {
+    case 't':
+      timeout= atoi(optarg);
+      if (timeout == 0)
+      {
+        fprintf(stderr, "Invalid timeout. Please specify a number for -t\n");
+        return 1;
+      }
+      break;
+    case 'v': verbose= true;
+      break;
+    case 'c': do_core= true;
+      break;
+    case 'h': hostname= optarg;
+      break;
+    case 'p': port= optarg;
+      break;
+    default:
+      fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n]\n"
+              "\t-c\tGenerate coredump if a test fails\n"
+              "\t-v\tVerbose test output (print out the assertion)\n"
+              "\t-c n\tSet the timeout for io-operations to n seconds\n",
+              argv[0]);
+      return 1;
+    }
+  }
+
+  sock= connect_server(hostname, port);
+  if (sock == -1)
+  {
+    fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
+            hostname, port, strerror(errno));
+    return 1;
+  }
+
+  for (int ii= 0; testcases[ii].description != NULL; ++ii)
+  {
+    ++total;
+    fprintf(stdout, "%s\t\t", testcases[ii].description);
+    fflush(stdout);
+
+    bool reconnect= false;
+    enum test_return ret= testcases[ii].function();
+    fprintf(stderr, "%s\n", status_msg[ret]);
+    if (ret == TEST_FAIL)
+    {
+      reconnect= true;
+      ++failed;
+    }
+    else if (ret == TEST_PASS_RECONNECT)
+      reconnect= true;
+
+    if (reconnect)
+    {
+      (void) close(sock);
+      if ((sock=connect_server(hostname, port)) == -1)
+      {
+        fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
+                hostname, port, strerror(errno));
+        fprintf(stderr, "%d of %d tests failed\n", failed, total);
+        return 1;
+      }
+    }
+  }
+
+  (void) close(sock);
+  if (failed == 0)
+    fprintf(stdout, "All tests passed\n");
+  else
+    fprintf(stderr, "%d of %d tests failed\n", failed, total);
+
+  return (failed == 0) ? 0 : 1;
+}
index 165122d0cd9b941f57eec548f2ca9c1d6cde308d..2ae51ec55f233e5ebbf17dd59d85d8606828c5fd 100644 (file)
@@ -3,8 +3,8 @@ EXTRA_DIST = libmemcached_probes.d memcached/README.txt libmemcached.ver \
 
 SUBDIRS = protocol
 
-EXTRA_HEADERS = 
-BUILT_SOURCES= 
+EXTRA_HEADERS =
+BUILT_SOURCES=
 
 noinst_HEADERS = libmemcached_probes.h \
                  memcached_io.h \
@@ -79,14 +79,17 @@ if INCLUDE_HSIEH_SRC
 libmemcached_la_SOURCES += hsieh_hash.c
 endif
 
-if BUILD_BYTEORDER
-libmemcached_la_SOURCES += byteorder.c
-endif
-
 libmemcached_la_DEPENDENCIES= libmemcachedcallbacks.la
 libmemcached_la_LIBADD= $(LIBM) libmemcachedcallbacks.la
 libmemcached_la_LDFLAGS = -version-info $(MEMCACHED_LIBRARY_VERSION) $(LD_VERSION_SCRIPT) $(LIBM)
 
+if BUILD_BYTEORDER
+noinst_LTLIBRARIES += libbyteorder.la
+libbyteorder_la_SOURCES= byteorder.c byteorder.h
+libmemcached_la_LIBADD += libbyteorder.la
+libmemcached_la_DEPENDENCIES+= libbyteorder.la
+endif
+
 if HAVE_DTRACE
 BUILT_SOURCES+= dtrace_probes.h
 libmemcached_la_SOURCES += libmemcached_probes.d
index 28889aaee457158fce825a881bc02c9bb1d527db..5b1cf88bc8d0090a2ac9840783791c9cec861a20 100644 (file)
@@ -1,14 +1,14 @@
-#include "common.h"
+#include "byteorder.h"
 
 /* Byte swap a 64-bit number. */
-static inline uint64_t swap64(uint64_t in) 
+static inline uint64_t swap64(uint64_t in)
 {
 #ifndef BYTEORDER_BIG_ENDIAN
   /* Little endian, flip the bytes around until someone makes a faster/better
    * way to do this. */
   uint64_t rv= 0;
   uint8_t x= 0;
-  for(x= 0; x < 8; x++) 
+  for(x= 0; x < 8; x++)
   {
     rv= (rv << 8) | (in & 0xff);
     in >>= 8;
diff --git a/libmemcached/byteorder.h b/libmemcached/byteorder.h
new file mode 100644 (file)
index 0000000..0964b61
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef LIBMEMCACHED_BYTEORDER_H
+#define LIBMEMCACHED_BYTEORDER_H
+
+#include "config.h"
+
+/* Define this here, which will turn on the visibilty controls while we're
+ * building libmemcached.
+ */
+#define BUILDING_LIBMEMCACHED 1
+
+#include "libmemcached/memcached.h"
+
+#ifndef HAVE_HTONLL
+LIBMEMCACHED_LOCAL
+extern uint64_t ntohll(uint64_t);
+LIBMEMCACHED_LOCAL
+extern uint64_t htonll(uint64_t);
+#endif
+
+#endif /* LIBMEMCACHED_BYTEORDER_H */
index d37fede9b4eb618bc6c60bc0899c51858086532a..96b834632ccfaefa23d3ce5f1bd97acf1e84703d 100644 (file)
@@ -48,6 +48,7 @@
 #include "libmemcached/memcached_internal.h"
 #include "libmemcached/libmemcached_probes.h"
 #include "libmemcached/memcached/protocol_binary.h"
+#include "libmemcached/byteorder.h"
 
 /* string value */
 struct memcached_continuum_item_st {
@@ -142,21 +143,14 @@ memcached_return memcached_key_test(const char **keys, size_t *key_length,
 LIBMEMCACHED_LOCAL
 uint32_t generate_hash(memcached_st *ptr, const char *key, size_t key_length);
 
-#ifndef HAVE_HTONLL
-LIBMEMCACHED_LOCAL
-extern uint64_t ntohll(uint64_t);
-LIBMEMCACHED_LOCAL
-extern uint64_t htonll(uint64_t);
-#endif
-
 LIBMEMCACHED_LOCAL
 memcached_return memcached_purge(memcached_server_st *ptr);
 
-static inline memcached_return memcached_validate_key_length(size_t key_length, 
+static inline memcached_return memcached_validate_key_length(size_t key_length,
                                                              bool binary) {
   unlikely (key_length == 0)
     return MEMCACHED_BAD_KEY_PROVIDED;
-  
+
   if (binary)
   {
     unlikely (key_length > 0xffff)
@@ -164,7 +158,7 @@ static inline memcached_return memcached_validate_key_length(size_t key_length,
   }
   else
   {
-    unlikely (key_length >= MEMCACHED_MAX_KEY) 
+    unlikely (key_length >= MEMCACHED_MAX_KEY)
       return MEMCACHED_BAD_KEY_PROVIDED;
   }
 
index 74cdca52d61d3a284536fa744bb6169d13a61e5c..c88883088e63770a1aa8357365065983834ccd06 100644 (file)
@@ -48,316 +48,371 @@ extern "C"
 {
 #endif
 
-  /**
-   * Definition of the legal "magic" values used in a packet.
-   * See section 3.1 Magic byte
-   */
-  typedef enum {
-    PROTOCOL_BINARY_REQ = 0x80,
-    PROTOCOL_BINARY_RES = 0x81
-  } protocol_binary_magic;
-
-  /**
-   * Definition of the valid response status numbers.
-   * See section 3.2 Response Status
-   */
-  typedef enum {
-    PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00,
-    PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01,
-    PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02,
-    PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03,
-    PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
-    PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
-    PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
-    PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82,
-
-
-    PROTOCOL_BINARY_RESPONSE_EIO = 0xff
-  } protocol_binary_response_status;
-
-  /**
-   * Defintion of the different command opcodes.
-   * See section 3.3 Command Opcodes
-   */
-  typedef enum {
-    PROTOCOL_BINARY_CMD_GET = 0x00,
-    PROTOCOL_BINARY_CMD_SET = 0x01,
-    PROTOCOL_BINARY_CMD_ADD = 0x02,
-    PROTOCOL_BINARY_CMD_REPLACE = 0x03,
-    PROTOCOL_BINARY_CMD_DELETE = 0x04,
-    PROTOCOL_BINARY_CMD_INCREMENT = 0x05,
-    PROTOCOL_BINARY_CMD_DECREMENT = 0x06,
-    PROTOCOL_BINARY_CMD_QUIT = 0x07,
-    PROTOCOL_BINARY_CMD_FLUSH = 0x08,
-    PROTOCOL_BINARY_CMD_GETQ = 0x09,
-    PROTOCOL_BINARY_CMD_NOOP = 0x0a,
-    PROTOCOL_BINARY_CMD_VERSION = 0x0b,
-    PROTOCOL_BINARY_CMD_GETK = 0x0c,
-    PROTOCOL_BINARY_CMD_GETKQ = 0x0d,
-    PROTOCOL_BINARY_CMD_APPEND = 0x0e,
-    PROTOCOL_BINARY_CMD_PREPEND = 0x0f,
-    PROTOCOL_BINARY_CMD_STAT = 0x10,
-    PROTOCOL_BINARY_CMD_SETQ = 0x11,
-    PROTOCOL_BINARY_CMD_ADDQ = 0x12,
-    PROTOCOL_BINARY_CMD_REPLACEQ = 0x13,
-    PROTOCOL_BINARY_CMD_DELETEQ = 0x14,
-    PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15,
-    PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16,
-    PROTOCOL_BINARY_CMD_QUITQ = 0x17,
-    PROTOCOL_BINARY_CMD_FLUSHQ = 0x18,
-    PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
-    PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a
-  } protocol_binary_command;
-
-  /**
-   * Definition of the data types in the packet
-   * See section 3.4 Data Types
-   */
-  typedef enum {
-    PROTOCOL_BINARY_RAW_BYTES = 0x00
-  } protocol_binary_datatypes;
-
-  /**
-   * Definition of the header structure for a request packet.
-   * See section 2
-   */
-  typedef union {
-    struct {
-      uint8_t magic;
-      uint8_t opcode;
-      uint16_t keylen;
-      uint8_t extlen;
-      uint8_t datatype;
-      uint16_t reserved;
-      uint32_t bodylen;
-      uint32_t opaque;
-      uint64_t cas;
-    } request;
-    uint8_t bytes[24];
-  } protocol_binary_request_header;
-
-  /**
-   * Definition of the header structure for a response packet.
-   * See section 2
-   */
-  typedef union {
-    struct {
-      uint8_t magic;
-      uint8_t opcode;
-      uint16_t keylen;
-      uint8_t extlen;
-      uint8_t datatype;
-      uint16_t status;
-      uint32_t bodylen;
-      uint32_t opaque;
-      uint64_t cas;
-    } response;
-    uint8_t bytes[24];
-  } protocol_binary_response_header;
-
-  /**
-   * Definition of a request-packet containing no extras
-   */
-  typedef union {
-    struct {
-      protocol_binary_request_header header;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_request_header)];
-  } protocol_binary_request_no_extras;
-
-  /**
-   * Definition of a response-packet containing no extras
-   */
-  typedef union {
-    struct {
-      protocol_binary_response_header header;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_response_header)];
-  } protocol_binary_response_no_extras;
-
-  /**
-   * Definition of the packet used by the get, getq, getk and getkq command.
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_get;
-  typedef protocol_binary_request_no_extras protocol_binary_request_getq;
-  typedef protocol_binary_request_no_extras protocol_binary_request_getk;
-  typedef protocol_binary_request_no_extras protocol_binary_request_getkq;
-
-  /**
-   * Definition of the packet returned from a successful get, getq, getk and
-   * getkq.
-   * See section 4
-   */
-  typedef union {
-    struct {
-      protocol_binary_response_header header;
-      struct {
-        uint32_t flags;
-      } body;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_response_header) + 4];
-  } protocol_binary_response_get;
-
-  typedef protocol_binary_response_get protocol_binary_response_getq;
-  typedef protocol_binary_response_get protocol_binary_response_getk;
-  typedef protocol_binary_response_get protocol_binary_response_getkq;
-
-  /**
-   * Definition of the packet used by the delete command
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_delete;
-
-  /**
-   * Definition of the packet returned by the delete command
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_delete;
-
-  /**
-   * Definition of the packet used by the flush command
-   * See section 4
-   * Please note that the expiration field is optional, so remember to see
-   * check the header.bodysize to see if it is present.
-   */
-  typedef union {
-    struct {
-      protocol_binary_request_header header;
-      struct {
-        uint32_t expiration;
-      } body;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
-  } protocol_binary_request_flush;
-
-  /**
-   * Definition of the packet returned by the flush command
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_flush;
-
-  /**
-   * Definition of the packet used by set, add and replace
-   * See section 4
-   */
-  typedef union {
-    struct {
-      protocol_binary_request_header header;
-      struct {
-        uint32_t flags;
-        uint32_t expiration;
-      } body;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_request_header) + 8];
-  } protocol_binary_request_set;
-  typedef protocol_binary_request_set protocol_binary_request_add;
-  typedef protocol_binary_request_set protocol_binary_request_replace;
-
-  /**
-   * Definition of the packet returned by set, add and replace
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_set;
-  typedef protocol_binary_response_no_extras protocol_binary_response_add;
-  typedef protocol_binary_response_no_extras protocol_binary_response_replace;
-
-  /**
-   * Definition of the noop packet
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_noop;
-
-  /**
-   * Definition of the packet returned by the noop command
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_noop;
-
-  /**
-   * Definition of the structure used by the increment and decrement
-   * command.
-   * See section 4
-   */
-  typedef union {
-    struct {
-      protocol_binary_request_header header;
-      struct {
-        uint64_t delta;
-        uint64_t initial;
-        uint32_t expiration;
-      } body;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_request_header) + 20];
-  } protocol_binary_request_incr;
-  typedef protocol_binary_request_incr protocol_binary_request_decr;
-
-  /**
-   * Definition of the response from an incr or decr command
-   * command.
-   * See section 4
-   */
-  typedef union {
-    struct {
-      protocol_binary_response_header header;
-      struct {
-        uint64_t value;
-      } body;
-    } message;
-    uint8_t bytes[sizeof(protocol_binary_response_header) + 8];
-  } protocol_binary_response_incr;
-  typedef protocol_binary_response_incr protocol_binary_response_decr;
-
-  /**
-   * Definition of the quit
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_quit;
-
-  /**
-   * Definition of the packet returned by the quit command
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_quit;
-
-  /**
-   * Definition of the packet used by append and prepend command
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_append;
-  typedef protocol_binary_request_no_extras protocol_binary_request_prepend;
-
-  /**
-   * Definition of the packet returned from a successful append or prepend
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_append;
-  typedef protocol_binary_response_no_extras protocol_binary_response_prepend;
-
-  /**
-   * Definition of the packet used by the version command
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_version;
-
-  /**
-   * Definition of the packet returned from a successful version command
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_version;
-
-
-  /**
-   * Definition of the packet used by the stats command.
-   * See section 4
-   */
-  typedef protocol_binary_request_no_extras protocol_binary_request_stats;
-
-  /**
-   * Definition of the packet returned from a successful stats command
-   * See section 4
-   */
-  typedef protocol_binary_response_no_extras protocol_binary_response_stats;
+    /**
+     * Definition of the legal "magic" values used in a packet.
+     * See section 3.1 Magic byte
+     */
+    typedef enum {
+        PROTOCOL_BINARY_REQ = 0x80,
+        PROTOCOL_BINARY_RES = 0x81
+    } protocol_binary_magic;
+
+    /**
+     * Definition of the valid response status numbers.
+     * See section 3.2 Response Status
+     */
+    typedef enum {
+        PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00,
+        PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01,
+        PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02,
+        PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03,
+        PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
+        PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
+        PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06,
+        PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
+        PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82,
+        PROTOCOL_BINARY_RESPONSE_EIO = 0xff
+    } protocol_binary_response_status;
+
+    /**
+     * Defintion of the different command opcodes.
+     * See section 3.3 Command Opcodes
+     */
+    typedef enum {
+        PROTOCOL_BINARY_CMD_GET = 0x00,
+        PROTOCOL_BINARY_CMD_SET = 0x01,
+        PROTOCOL_BINARY_CMD_ADD = 0x02,
+        PROTOCOL_BINARY_CMD_REPLACE = 0x03,
+        PROTOCOL_BINARY_CMD_DELETE = 0x04,
+        PROTOCOL_BINARY_CMD_INCREMENT = 0x05,
+        PROTOCOL_BINARY_CMD_DECREMENT = 0x06,
+        PROTOCOL_BINARY_CMD_QUIT = 0x07,
+        PROTOCOL_BINARY_CMD_FLUSH = 0x08,
+        PROTOCOL_BINARY_CMD_GETQ = 0x09,
+        PROTOCOL_BINARY_CMD_NOOP = 0x0a,
+        PROTOCOL_BINARY_CMD_VERSION = 0x0b,
+        PROTOCOL_BINARY_CMD_GETK = 0x0c,
+        PROTOCOL_BINARY_CMD_GETKQ = 0x0d,
+        PROTOCOL_BINARY_CMD_APPEND = 0x0e,
+        PROTOCOL_BINARY_CMD_PREPEND = 0x0f,
+        PROTOCOL_BINARY_CMD_STAT = 0x10,
+        PROTOCOL_BINARY_CMD_SETQ = 0x11,
+        PROTOCOL_BINARY_CMD_ADDQ = 0x12,
+        PROTOCOL_BINARY_CMD_REPLACEQ = 0x13,
+        PROTOCOL_BINARY_CMD_DELETEQ = 0x14,
+        PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15,
+        PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16,
+        PROTOCOL_BINARY_CMD_QUITQ = 0x17,
+        PROTOCOL_BINARY_CMD_FLUSHQ = 0x18,
+        PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
+        PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a,
+
+        /* These commands are used for range operations and exist within
+         * this header for use in other projects.  Range operations are
+         * not expected to be implemented in the memcached server itself.
+         */
+        PROTOCOL_BINARY_CMD_RGET      = 0x30,
+        PROTOCOL_BINARY_CMD_RSET      = 0x31,
+        PROTOCOL_BINARY_CMD_RSETQ     = 0x32,
+        PROTOCOL_BINARY_CMD_RAPPEND   = 0x33,
+        PROTOCOL_BINARY_CMD_RAPPENDQ  = 0x34,
+        PROTOCOL_BINARY_CMD_RPREPEND  = 0x35,
+        PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36,
+        PROTOCOL_BINARY_CMD_RDELETE   = 0x37,
+        PROTOCOL_BINARY_CMD_RDELETEQ  = 0x38,
+        PROTOCOL_BINARY_CMD_RINCR     = 0x39,
+        PROTOCOL_BINARY_CMD_RINCRQ    = 0x3a,
+        PROTOCOL_BINARY_CMD_RDECR     = 0x3b,
+        PROTOCOL_BINARY_CMD_RDECRQ    = 0x3c
+        /* End Range operations */
+
+    } protocol_binary_command;
+
+    /**
+     * Definition of the data types in the packet
+     * See section 3.4 Data Types
+     */
+    typedef enum {
+        PROTOCOL_BINARY_RAW_BYTES = 0x00
+    } protocol_binary_datatypes;
+
+    /**
+     * Definition of the header structure for a request packet.
+     * See section 2
+     */
+    typedef union {
+        struct {
+            uint8_t magic;
+            uint8_t opcode;
+            uint16_t keylen;
+            uint8_t extlen;
+            uint8_t datatype;
+            uint16_t reserved;
+            uint32_t bodylen;
+            uint32_t opaque;
+            uint64_t cas;
+        } request;
+        uint8_t bytes[24];
+    } protocol_binary_request_header;
+
+    /**
+     * Definition of the header structure for a response packet.
+     * See section 2
+     */
+    typedef union {
+        struct {
+            uint8_t magic;
+            uint8_t opcode;
+            uint16_t keylen;
+            uint8_t extlen;
+            uint8_t datatype;
+            uint16_t status;
+            uint32_t bodylen;
+            uint32_t opaque;
+            uint64_t cas;
+        } response;
+        uint8_t bytes[24];
+    } protocol_binary_response_header;
+
+    /**
+     * Definition of a request-packet containing no extras
+     */
+    typedef union {
+        struct {
+            protocol_binary_request_header header;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_request_header)];
+    } protocol_binary_request_no_extras;
+
+    /**
+     * Definition of a response-packet containing no extras
+     */
+    typedef union {
+        struct {
+            protocol_binary_response_header header;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_response_header)];
+    } protocol_binary_response_no_extras;
+
+    /**
+     * Definition of the packet used by the get, getq, getk and getkq command.
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_get;
+    typedef protocol_binary_request_no_extras protocol_binary_request_getq;
+    typedef protocol_binary_request_no_extras protocol_binary_request_getk;
+    typedef protocol_binary_request_no_extras protocol_binary_request_getkq;
+
+    /**
+     * Definition of the packet returned from a successful get, getq, getk and
+     * getkq.
+     * See section 4
+     */
+    typedef union {
+        struct {
+            protocol_binary_response_header header;
+            struct {
+                uint32_t flags;
+            } body;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_response_header) + 4];
+    } protocol_binary_response_get;
+
+    typedef protocol_binary_response_get protocol_binary_response_getq;
+    typedef protocol_binary_response_get protocol_binary_response_getk;
+    typedef protocol_binary_response_get protocol_binary_response_getkq;
+
+    /**
+     * Definition of the packet used by the delete command
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_delete;
+
+    /**
+     * Definition of the packet returned by the delete command
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_delete;
+
+    /**
+     * Definition of the packet used by the flush command
+     * See section 4
+     * Please note that the expiration field is optional, so remember to see
+     * check the header.bodysize to see if it is present.
+     */
+    typedef union {
+        struct {
+            protocol_binary_request_header header;
+            struct {
+                uint32_t expiration;
+            } body;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
+    } protocol_binary_request_flush;
+
+    /**
+     * Definition of the packet returned by the flush command
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_flush;
+
+    /**
+     * Definition of the packet used by set, add and replace
+     * See section 4
+     */
+    typedef union {
+        struct {
+            protocol_binary_request_header header;
+            struct {
+                uint32_t flags;
+                uint32_t expiration;
+            } body;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_request_header) + 8];
+    } protocol_binary_request_set;
+    typedef protocol_binary_request_set protocol_binary_request_add;
+    typedef protocol_binary_request_set protocol_binary_request_replace;
+
+    /**
+     * Definition of the packet returned by set, add and replace
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_set;
+    typedef protocol_binary_response_no_extras protocol_binary_response_add;
+    typedef protocol_binary_response_no_extras protocol_binary_response_replace;
+
+    /**
+     * Definition of the noop packet
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_noop;
+
+    /**
+     * Definition of the packet returned by the noop command
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_noop;
+
+    /**
+     * Definition of the structure used by the increment and decrement
+     * command.
+     * See section 4
+     */
+    typedef union {
+        struct {
+            protocol_binary_request_header header;
+            struct {
+                uint64_t delta;
+                uint64_t initial;
+                uint32_t expiration;
+            } body;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_request_header) + 20];
+    } protocol_binary_request_incr;
+    typedef protocol_binary_request_incr protocol_binary_request_decr;
+
+    /**
+     * Definition of the response from an incr or decr command
+     * command.
+     * See section 4
+     */
+    typedef union {
+        struct {
+            protocol_binary_response_header header;
+            struct {
+                uint64_t value;
+            } body;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_response_header) + 8];
+    } protocol_binary_response_incr;
+    typedef protocol_binary_response_incr protocol_binary_response_decr;
+
+    /**
+     * Definition of the quit
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_quit;
+
+    /**
+     * Definition of the packet returned by the quit command
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_quit;
+
+    /**
+     * Definition of the packet used by append and prepend command
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_append;
+    typedef protocol_binary_request_no_extras protocol_binary_request_prepend;
+
+    /**
+     * Definition of the packet returned from a successful append or prepend
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_append;
+    typedef protocol_binary_response_no_extras protocol_binary_response_prepend;
+
+    /**
+     * Definition of the packet used by the version command
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_version;
+
+    /**
+     * Definition of the packet returned from a successful version command
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_version;
+
+
+    /**
+     * Definition of the packet used by the stats command.
+     * See section 4
+     */
+    typedef protocol_binary_request_no_extras protocol_binary_request_stats;
+
+    /**
+     * Definition of the packet returned from a successful stats command
+     * See section 4
+     */
+    typedef protocol_binary_response_no_extras protocol_binary_response_stats;
+
+    /**
+     * Definition of a request for a range operation.
+     * See http://code.google.com/p/memcached/wiki/RangeOps
+     *
+     * These types are used for range operations and exist within
+     * this header for use in other projects.  Range operations are
+     * not expected to be implemented in the memcached server itself.
+     */
+    typedef union {
+        struct {
+            protocol_binary_response_header header;
+            struct {
+                uint16_t size;
+                uint8_t  reserved;
+                uint8_t  flags;
+                uint32_t max_results;
+            } body;
+        } message;
+        uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
+    } protocol_binary_request_rangeop;
+
+    typedef protocol_binary_request_rangeop protocol_binary_request_rget;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rset;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rsetq;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rappend;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rappendq;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rprepend;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rprependq;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rdelete;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rincr;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rincrq;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rdecr;
+    typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq;
+
 #ifdef __cplusplus
 }
 #endif
index b5bc304ebdd2eae38bcc0634b1a914d07f2f13c6..010fb42900de16b05c07b0572a062b4e74d29ed6 100644 (file)
@@ -17,7 +17,7 @@ AC_DEFUN([WITH_MEMCACHED],
          [
            ac_cv_with_memcached=$withval
            MEMC_BINARY=$withval
-         ], 
+         ],
          [
            AC_PATH_PROG([MEMC_BINARY], [$ac_cv_with_memcached], "no")
            AS_IF([test "x$MEMC_BINARY" = "xno"],
@@ -25,6 +25,7 @@ AC_DEFUN([WITH_MEMCACHED],
          ])
     ])
 
-  AC_DEFINE_UNQUOTED([MEMCACHED_BINARY], "$MEMC_BINARY", 
+  AC_DEFINE_UNQUOTED([MEMCACHED_BINARY], "$MEMC_BINARY",
             [Name of the memcached binary used in make test])
+  AC_SUBST(MEMC_BINARY)
 ])
index c840f68cc9d3124282bbf441af6719c541d624cf..d9a1f7e6bbc7edcf0e6ab88ea7b158b5ee1ac908 100644 (file)
@@ -16,7 +16,7 @@ EXTRA_DIST = output.res output2.res\
                t/memslap.test\
                t/memstat.test
 
-LIBS = 
+LIBS =
 
 noinst_HEADERS = test.h server.h ketama_test_cases.h
 noinst_PROGRAMS = testapp testplus udptest atomsmasher startservers
@@ -55,7 +55,7 @@ client-record:
 record-extended:
        ./testapp extended > output2.res
 
-test: testapp testplus library_test
+test: testapp testplus library_test memcapable
        echo "Tests completed"
 
 library_test:
@@ -64,8 +64,14 @@ library_test:
 #      ./testplus > output_plus.cmp
 #      diff output_plus.res output_plus.cmp
 
+memcapable:
+       @MEMC_BINARY@ -d -P /tmp/Xumemc.pid -p 12555
+       @$(top_builddir)/clients/memcapable -p 12555 || echo "Your memcached server does not support all commands"
+       @cat /tmp/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
+       @rm /tmp/Xumemc.pid
+
 clients:
-       memcached -d -P /tmp/Xumemc.pid -p 12555
+       @MEMC_BINARY@ -d -P /tmp/Xumemc.pid -p 12555
        export MEMCACHED_SERVERS="localhost:12555"
        sh t/memcat.test > r/memcat.cmp
        diff r/memcat.res r/memcat.cmp