Merge from trunk.
authorPadraig O'Sullivan <osullivan.padraig@gmail.com>
Sun, 27 Sep 2009 17:21:58 +0000 (13:21 -0400)
committerPadraig O'Sullivan <osullivan.padraig@gmail.com>
Sun, 27 Sep 2009 17:21:58 +0000 (13:21 -0400)
27 files changed:
.bzrignore
ChangeLog
clients/Makefile.am
clients/execute.c
clients/generator.c
clients/memcapable.c [new file with mode: 0644]
clients/memstat.c
configure.ac
docs/Makefile.am
docs/memcached_pool.pod
docs/memcapable.pod [new file with mode: 0644]
libmemcached/Makefile.am
libmemcached/byteorder.c
libmemcached/byteorder.h [new file with mode: 0644]
libmemcached/common.h
libmemcached/memcached.h
libmemcached/memcached/protocol_binary.h
libmemcached/memcached_constants.h
libmemcached/memcached_dump.c
libmemcached/memcached_get.c
libmemcached/memcached_pool.h
libmemcached/memcached_response.c
libmemcached/memcached_strerror.c
libmemcachedutil/memcached_pool.c
m4/memcached.m4
tests/Makefile.am
tests/function.c

index dc1898a6b765711c72159b16ac1655d604029731..2586c531ab2a7580e50aa51cf3449053d065d16b 100644 (file)
@@ -11,6 +11,7 @@ Makefile.in
 TAGS
 aclocal.m4
 autom4te.cache
+clients/memcapable
 clients/memcat
 clients/memcp
 clients/memdump
index c3d1a941b56a796c0fafc64cc05d6762dedc5feb..bb373dc8d66cfe9e411eae97d490c8e8c20ceb83 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,18 @@
-  0.32 Tue Sep 15 15:49:09 PDT 2009
+0.33 Wed Sep 23 10:11:58 PDT 2009
+  * Added memcapable to test servers for binary compatibility.
+  * Updated C++ interface. Added basic support for C++ exceptions. Added 
+    multiple constructors the memcached client object. The C++ interface
+    now takes parameters which are C++ types (such as std::string).
+  * Several bug fixes for binary protocol support.
+  * Fixed crashing issue with dumping from memcachd server (server internals
+    were changed without documenting change).
+
+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.
   * Added Twitter's memcached_server_error() functions.
   * Fix for OSX compiles in development builds.
-  * Updated C++ interface. Added basic support for C++ exceptions. Added 
-    multiple constructors the memcached client object. The C++ interface
-    now takes parameters which are C++ types (such as std::string).
+  * Updated C++ interface.
   * Updated memcached_mget and memcached_mget_by_key to take a size_t 
     as a parameter instead of an unsigned int for number_of_keys.
 
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
index e25483392e2876e51b61b202c684a437bd23f423..7255d37ede3522dcc15a97a239aed387c898d1fb 100644 (file)
@@ -46,7 +46,7 @@ unsigned int execute_get(memcached_st *memc, pairs_st *pairs, unsigned int numbe
     uint32_t flags;
     unsigned int fetch_key;
 
-    fetch_key= (unsigned int)random() % number_of;
+    fetch_key= (unsigned int)((unsigned int)random() % number_of);
 
     value= memcached_get(memc, pairs[fetch_key].key, pairs[fetch_key].key_length,
                          &value_length, &flags, &rc);
index 213246ba2206a3d84916c8503c43c089bc26e975..5bce0301ab6ae848f9315ea5b85c186210c8f30a 100644 (file)
@@ -47,7 +47,9 @@ void pairs_free(pairs_st *pairs)
 pairs_st *pairs_generate(uint64_t number_of, size_t value_length)
 {
   unsigned int x;
-  pairs_st *pairs= calloc((size_t)(number_of + 1), sizeof(pairs_st));
+  pairs_st *pairs;
+
+  pairs= (pairs_st*)calloc((size_t)number_of + 1, sizeof(pairs_st));
 
   if (!pairs)
     goto error;
diff --git a/clients/memcapable.c b/clients/memcapable.c
new file mode 100644 (file)
index 0000000..7a2b514
--- /dev/null
@@ -0,0 +1,1224 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#undef NDEBUG
+#include "config.h"
+#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 e6783b68c095c7a8de4b2f88309a2ce5f53aceed..847e7f0d0ee8eeb8763ca07158a34705ff67e57e 100644 (file)
@@ -168,8 +168,8 @@ static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat,
       }
       gettimeofday(&end_time, NULL);
 
-      elapsed_time= timedif(end_time, start_time);
-      elapsed_time /= (long)num_of_tests;
+      elapsed_time= (long) timedif(end_time, start_time);
+      elapsed_time /= (long) num_of_tests;
 
       if (elapsed_time > slowest_time)
       {
index 6ce3f3c9d07c81540c9f6ff2e634aba39f04c8e8..a8137eb0b066666761136a014e695e7186d653de 100644 (file)
@@ -6,7 +6,7 @@
 # the COPYING file in this directory for full text.
 
 AC_PREREQ(2.59)
-AC_INIT([libmemcached],[0.32],[http://tangent.org/552/libmemcached.html])
+AC_INIT([libmemcached],[0.33],[http://tangent.org/552/libmemcached.html])
 AC_CONFIG_SRCDIR([libmemcached/memcached.c])
 AC_CONFIG_AUX_DIR(config)
 AM_CONFIG_HEADER([config.h])
index 598eaf06f726f48c28a886901365e1952f76d839..772b03443025d8d17650ab7ea7ef297d83407baa 100644 (file)
@@ -19,6 +19,7 @@ EXTRA_DIST = libmemcached.pod\
        memcached_behavior.pod\
        memcached_callback.pod\
        memcached_server_st.pod\
+        memcapable.pod \
        memcat.pod\
        memcached_create.pod\
        memcached_pool.pod\
@@ -37,6 +38,7 @@ EXTRA_DIST = libmemcached.pod\
 
 man_MANS = libmemcached.3\
        libmemcached_examples.3\
+       memcapable.1\
        memcat.1\
        memcp.1\
        memerror.1\
@@ -105,6 +107,8 @@ man_MANS = libmemcached.3\
 
 if BUILD_LIBMEMCACHEDUTIL
 man_MANS+= libmemcachedutil.3 \
+       memcached_pool_behavior_set.3 \
+       memcached_pool_behavior_get.3 \
        memcached_pool_create.3 \
        memcached_pool_destroy.3 \
        memcached_pool_push.3 \
@@ -112,16 +116,16 @@ man_MANS+= libmemcachedutil.3 \
 endif
 
 
-libmemcached.3: libmemcached.pod 
+libmemcached.3: libmemcached.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/libmemcached.pod > libmemcached.3
 
-libmemcachedutil.3: libmemcachedutil.pod 
+libmemcachedutil.3: libmemcachedutil.pod
        ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/libmemcachedutil.pod > libmemcachedutil.3
 
 libmemcached_examples.3: libmemcached_examples.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/libmemcached_examples.pod > libmemcached_examples.3
 
-memcached_create.3: memcached_create.pod 
+memcached_create.3: memcached_create.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_create.pod > memcached_create.3
 
 memcached_free.3: memcached_create.pod
@@ -325,16 +329,22 @@ memcached_get_user_data.3: memcached_user_data.pod
 memcached_set_user_data.3: memcached_user_data.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_user_data.pod > memcached_set_user_data.3
 
-memcached_pool_create.3: memcached_pool.pod 
+memcached_pool_behavior_get.3: memcached_pool.pod
+       ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_behavior_get.3
+
+memcached_pool_behavior_set.3: memcached_pool.pod
+       ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_behavior_set.3
+
+memcached_pool_create.3: memcached_pool.pod
        ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_create.3
 
-memcached_pool_destroy.3: memcached_pool.pod 
+memcached_pool_destroy.3: memcached_pool.pod
        ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_destroy.3
 
-memcached_pool_pop.3: memcached_pool.pod 
+memcached_pool_pop.3: memcached_pool.pod
        ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_pop.3
 
-memcached_pool_push.3: memcached_pool.pod 
+memcached_pool_push.3: memcached_pool.pod
        ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_push.3
 
 memcp.1: memcp.pod
@@ -343,6 +353,9 @@ memcp.1: memcp.pod
 memslap.1: memslap.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memslap.pod > memslap.1
 
+memcapable.1: memcapable.pod
+       ${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memcapable.pod > memcapable.1
+
 memcat.1: memcat.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memcat.pod > memcat.1
 
@@ -361,7 +374,7 @@ memdump.1: memdump.pod
 memflush.1: memflush.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memflush.pod > memflush.1
 
-test: 
+test:
        podchecker $(top_srcdir)/docs/libmemcached.pod
        podchecker $(top_srcdir)/docs/memcached_flush.pod
        podchecker $(top_srcdir)/docs/memcached_stats.pod
@@ -390,6 +403,7 @@ test:
        podchecker $(top_srcdir)/docs/memcached_pool.pod
        podchecker $(top_srcdir)/docs/memcached_memory_allocators.pod
        podchecker $(top_srcdir)/docs/memcached_user_data.pod
+       podchecker $(top_srcdir)/docs/memcapable.pod
 
 html-local:
        pod2htmltree "/libmemcached" .
index 2b464ac226dd8352eae83a2984638728fe2a1a8f..ac85d3b9fdfbd9d30e922aaf082b5fc05f2552b4 100644 (file)
@@ -18,6 +18,12 @@ C Client Library for memcached (libmemcachedutil, -lmemcachedutil)
 
   memcached_st *memcached_create (memcached_st *ptr);
 
+  memcached_return memcached_pool_behavior_set(memcached_pool_st *pool, 
+                                               memcached_behavior flag, 
+                                               uint64_t data)
+  memcached_return memcached_pool_behavior_get(memcached_pool_st *pool, 
+                                               memcached_behavior flag,
+                                               uint64_t *value)
 
 =head1 DESCRIPTION
 
@@ -46,6 +52,10 @@ to exceed the maximum size.
 
 memcached_pool_push() is used to return a connection structure back to the pool.
 
+memcached_pool_behavior_set() and memcached_pool_behagior_get() is
+used to get/set behavior flags on all connections in the pool.
+
+
 =head1 RETURN
 
 memcached_pool_create() returns a pointer to the newly created
@@ -61,6 +71,9 @@ from the pool (or NULL if an allocation cannot be satisfied).
 
 memcached_pool_push() returns MEMCACHED_SUCCESS upon success.
 
+memcached_pool_behavior_get() and memcached_pool_behavior_get()
+returns MEMCACHED_SUCCESS upon success.
+
 =head1 HOME
 
 To find out more information please check:
@@ -72,6 +85,6 @@ Trond Norbye, E<lt>trond.norbye@sun.comE<gt>
 
 =head1 SEE ALSO
 
-memcached(1) libmemcached(3) memcached_create(3) memcached_free(3) libmemcachedutil(3)
+memcached(1) libmemcached(3) memcached_create(3) memcached_free(3) libmemcachedutil(3) memcached_behavior_get(3) memcached_behavior_set(3)
 
 =cut
diff --git a/docs/memcapable.pod b/docs/memcapable.pod
new file mode 100644 (file)
index 0000000..6555988
--- /dev/null
@@ -0,0 +1,61 @@
+=head1 NAME
+
+memcapable - Check memcached server capabilites
+
+=head1 SYNOPSIS
+
+  memcat [-h hostname] [-p port] [-c] [-v] [-t n]
+
+=head1 DESCRIPTION
+
+B<memcapable> connects to the specified memcached server and tries to
+determine its capabilities by running the various commands and verifying
+the response.
+
+=head1 OPTIONS
+
+The following options are supported:
+
+=over 3
+
+=item -h hostname
+
+Specify the hostname to connect to. The default is I<localhost>
+
+=item -p port
+
+Specify the port number to connect to. The default is I<11211>
+
+=item -c
+
+Generate a coredump when it detects an error from the server.
+
+=item -v
+
+Print out the comparison when it detects an error from the server.
+
+=item -t n
+
+Set the timeout from an IO operation to/from the server to I<n> seconds.
+
+=back
+
+=head1 LIMITATIONS
+
+The current version of memcapable will only verify the binary protocol.
+
+=head1 HOME
+
+To find out more information please check:
+L<http://tangent.org/552/libmemcached.html>
+
+=head1 AUTHOR
+
+Trond Norbye, E<lt>trond.norbye@sun.comE<gt>
+
+=head1 SEE ALSO
+
+memcached(1) libmemcached(3)
+
+=cut
+
index f192eda45b5d6f035cbb5d5ba0ebf33b7cb7c6ac..ad0dbf44409cd3e0f522e12ae87a4a317916d83d 100644 (file)
@@ -1,8 +1,8 @@
 EXTRA_DIST = libmemcached_probes.d memcached/README.txt libmemcached.ver \
              memcached_configure.h.in
 
-EXTRA_HEADERS = 
-BUILT_SOURCES= 
+EXTRA_HEADERS =
+BUILT_SOURCES=
 
 noinst_HEADERS = libmemcached_probes.h \
                  memcached_io.h \
@@ -74,14 +74,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 3fc9382640d03b61f443defaa668ba50b878205d..5068f98b6294ba0e5d802c8584900fe7adc8424f 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 #endif
 
 #define MEMCACHED_VERSION_STRING_LENGTH 24
-#define LIBMEMCACHED_VERSION_STRING "0.32"
+#define LIBMEMCACHED_VERSION_STRING "0.33"
 
 struct memcached_analysis_st {
   uint32_t average_item_size;
index 08df72e8b10fe0c505086443c8409f49ecd6a118..cb81331574a57b987287fd1aea228e317174fc37 100644 (file)
@@ -48,313 +48,370 @@ 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_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_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 3bb157c50a02e90627b7fa5164c37b039e6f091e..1705bb6e57883ed71f0972b6e015faa68e81051a 100644 (file)
@@ -62,6 +62,7 @@ typedef enum {
   MEMCACHED_INVALID_HOST_PROTOCOL,
   MEMCACHED_SERVER_MARKED_DEAD,
   MEMCACHED_UNKNOWN_STAT_KEY,
+  MEMCACHED_E2BIG,
   MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */
 } memcached_return;
 
index 393e140a557e50602876038b05b7500b25193d6f..5872aa40de4b9f953d1ff7492553fa8c4e1605d4 100644 (file)
@@ -55,6 +55,18 @@ static memcached_return ascii_dump(memcached_st *ptr, memcached_dump_func *callb
         }
         else if (rc == MEMCACHED_END)
           break;
+        else if (rc == MEMCACHED_SERVER_ERROR || rc == MEMCACHED_CLIENT_ERROR)
+        {
+          /* If we try to request stats cachedump for a slab class that is too big
+           * the server will return an incorrect error message:
+           * "MEMCACHED_SERVER_ERROR failed to allocate memory"
+           * This isn't really a fatal error, so let's just skip it. I want to
+           * fix the return value from the memcached server to a CLIENT_ERROR,
+           * so let's add support for that as well right now.
+           */
+          rc= MEMCACHED_END;
+          break;
+        }
         else
           goto error;
       }
index ee4f68e7ecb0c006c4025f346340ba0941b41ca6..176be6ad5af2d388b10dc3978c673a409750d7bb 100644 (file)
@@ -14,6 +14,14 @@ char *memcached_get(memcached_st *ptr, const char *key,
                               flags, error);
 }
 
+static memcached_return memcached_mget_by_key_real(memcached_st *ptr, 
+                                       const char *master_key, 
+                                       size_t master_key_length,
+                                       const char **keys, 
+                                       size_t *key_length, 
+                                       size_t number_of_keys,
+                                      bool mget_mode);
+
 char *memcached_get_by_key(memcached_st *ptr, 
                            const char *master_key, 
                            size_t master_key_length, 
@@ -34,10 +42,10 @@ char *memcached_get_by_key(memcached_st *ptr,
   }
 
   /* Request the key */
-  *error= memcached_mget_by_key(ptr, 
+  *error= memcached_mget_by_key_real(ptr, 
                                 master_key, 
                                 master_key_length, 
-                                (const char **)&key, &key_length, 1);
+                                (const char **)&key, &key_length, 1, false);
 
   value= memcached_fetch(ptr, NULL, NULL, 
                          value_length, flags, error);
@@ -112,14 +120,16 @@ static memcached_return binary_mget_by_key(memcached_st *ptr,
                                            unsigned int master_server_key,
                                            bool is_master_key_set,
                                            const char **keys, size_t *key_length,
-                                           size_t number_of_keys);
+                                           size_t number_of_keys,
+                                          bool mget_mode);
 
-memcached_return memcached_mget_by_key(memcached_st *ptr, 
+static memcached_return memcached_mget_by_key_real(memcached_st *ptr, 
                                        const char *master_key, 
                                        size_t master_key_length,
                                        const char **keys, 
                                        size_t *key_length, 
-                                       size_t number_of_keys)
+                                       size_t number_of_keys,
+                                      bool mget_mode)
 {
   unsigned int x;
   memcached_return rc= MEMCACHED_NOTFOUND;
@@ -173,7 +183,7 @@ memcached_return memcached_mget_by_key(memcached_st *ptr,
   
   if (ptr->flags & MEM_BINARY_PROTOCOL)
     return binary_mget_by_key(ptr, master_server_key, is_master_key_set, keys, 
-                              key_length, number_of_keys);
+                              key_length, number_of_keys, mget_mode);
 
   if (ptr->flags & MEM_SUPPORT_CAS)
   {
@@ -256,11 +266,22 @@ memcached_return memcached_mget_by_key(memcached_st *ptr,
   return rc;
 }
 
+memcached_return memcached_mget_by_key(memcached_st *ptr, 
+                                       const char *master_key, 
+                                       size_t master_key_length,
+                                       const char **keys, 
+                                       size_t *key_length, 
+                                       size_t number_of_keys)
+{
+  return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys, 
+                                    key_length, number_of_keys, true);
+}
+
 static memcached_return simple_binary_mget(memcached_st *ptr,
                                            unsigned int master_server_key,
                                            bool is_master_key_set,
                                            const char **keys, size_t *key_length, 
-                                           size_t number_of_keys)
+                                           size_t number_of_keys, bool mget_mode)
 {
   memcached_return rc= MEMCACHED_NOTFOUND;
   uint32_t x;
@@ -289,10 +310,10 @@ static memcached_return simple_binary_mget(memcached_st *ptr,
      
     protocol_binary_request_getk request= {.bytes= {0}};
     request.message.header.request.magic= PROTOCOL_BINARY_REQ;
-    if (number_of_keys == 1)
-      request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
-    else
+    if (mget_mode)
       request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ;
+    else
+      request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
 
     memcached_return vk;
     vk= memcached_validate_key_length(key_length[x],
@@ -323,7 +344,7 @@ static memcached_return simple_binary_mget(memcached_st *ptr,
       rc= MEMCACHED_SOME_ERRORS;
   }
 
-  if (number_of_keys > 1
+  if (mget_mode
   {
     /*
      * Send a noop command to flush the buffers
@@ -361,7 +382,7 @@ static memcached_return simple_binary_mget(memcached_st *ptr,
 static memcached_return replication_binary_mget(memcached_st *ptr,
                                              uint32_t* hash, bool* dead_servers,
                                              const char **keys, size_t *key_length,
-                                             size_t number_of_keys)
+                                             size_t number_of_keys, bool mget_mode)
 {
   memcached_return rc= MEMCACHED_NOTFOUND;
   uint32_t x;
@@ -398,10 +419,10 @@ static memcached_return replication_binary_mget(memcached_st *ptr,
 
       protocol_binary_request_getk request= {.bytes= {0}};
       request.message.header.request.magic= PROTOCOL_BINARY_REQ;
-      if (number_of_keys == 1)
-        request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
-      else
+      if (mget_mode)
         request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ;
+      else
+        request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
 
       request.message.header.request.keylen= htons((uint16_t)key_length[x]);
       request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
@@ -420,7 +441,7 @@ static memcached_return replication_binary_mget(memcached_st *ptr,
       memcached_server_response_increment(&ptr->hosts[server]);
     }
 
-    if (number_of_keys > 1)
+    if (mget_mode)
     {
       /*
        * Send a noop command to flush the buffers
@@ -460,14 +481,14 @@ static memcached_return binary_mget_by_key(memcached_st *ptr,
                                            unsigned int master_server_key,
                                            bool is_master_key_set,
                                            const char **keys, size_t *key_length,
-                                           size_t number_of_keys)
+                                           size_t number_of_keys, bool mget_mode)
 {
   memcached_return rc;
 
   if (ptr->number_of_replicas == 0) 
   {
     rc= simple_binary_mget(ptr, master_server_key, is_master_key_set,
-                           keys, key_length, number_of_keys);
+                           keys, key_length, number_of_keys, mget_mode);
   } 
   else 
   {
@@ -492,7 +513,7 @@ static memcached_return binary_mget_by_key(memcached_st *ptr,
         hash[x]= memcached_generate_hash(ptr, keys[x], key_length[x]);
 
     rc= replication_binary_mget(ptr, hash, dead_servers, keys, 
-                                key_length, number_of_keys);
+                                key_length, number_of_keys, mget_mode);
 
     ptr->call_free(ptr, hash);
     ptr->call_free(ptr, dead_servers);
index 7efe65e9e9674835c0c77db08bbd26bd082146a0..03fc0e3a78d9b82fadcd1b18e74feb7d600a4b49 100644 (file)
@@ -31,6 +31,11 @@ LIBMEMCACHED_API
 memcached_return memcached_pool_push(memcached_pool_st* pool, 
                                      memcached_st* mmc);
 
+LIBMEMCACHED_API
+memcached_return memcached_pool_behavior_set(memcached_pool_st *ptr, memcached_behavior flag, uint64_t data);
+LIBMEMCACHED_API
+memcached_return memcached_pool_behavior_get(memcached_pool_st *ptr, memcached_behavior flag, uint64_t *value);
+
 #ifdef __cplusplus
 }
 #endif
index f617f27f9a14379b8c6b4276e2b7318ead302a69..ea13bb151ce09065cd37f4d958206a7140d1465b 100644 (file)
@@ -493,11 +493,17 @@ static memcached_return binary_read_one_response(memcached_server_st *ptr,
     case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
       rc= MEMCACHED_DATA_EXISTS;
       break;
+    case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
+      rc= MEMCACHED_NOTSTORED;
+      break;
     case PROTOCOL_BINARY_RESPONSE_E2BIG:
+      rc= MEMCACHED_E2BIG;
+      break;
+    case PROTOCOL_BINARY_RESPONSE_ENOMEM:
+      rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+      break;
     case PROTOCOL_BINARY_RESPONSE_EINVAL:
-    case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
     case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
-    case PROTOCOL_BINARY_RESPONSE_ENOMEM:
     default:
       /* @todo fix the error mappings */
       rc= MEMCACHED_PROTOCOL_ERROR;
index 8b75edd40be73ceb72488fef6ecfcbafe7b0b0b1..5a8b36958dbbc237129f86bcefe09282034e39de 100644 (file)
@@ -78,6 +78,8 @@ const char *memcached_strerror(memcached_st *ptr __attribute__((unused)), memcac
     return "SERVER IS MARKED DEAD";
   case MEMCACHED_UNKNOWN_STAT_KEY:
     return "ENCOUNTERED AN UNKNOWN STAT KEY";
+  case MEMCACHED_E2BIG:
+    return "ITEM TOO BIG";
   case MEMCACHED_MAXIMUM_RETURN:
     return "Gibberish returned!";
   default:
index 708b7ce2a793ffa930336eb7102f119d99846cbb..afc2fee3c26304cd24302d84074ee4009bc57f2e 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
 #include "libmemcached/common.h"
 #include "libmemcached/memcached_pool.h"
 
@@ -13,6 +14,7 @@ struct memcached_pool_st
   int firstfree;
   uint32_t size;
   uint32_t current_size;
+  char* version;
 };
 
 static memcached_return mutex_enter(pthread_mutex_t *mutex) 
@@ -158,6 +160,18 @@ memcached_return memcached_pool_push(memcached_pool_st* pool,
   if (rc != MEMCACHED_SUCCESS)
     return rc;
 
+  char* version = memcached_get_user_data(mmc);
+  /* Someone updated the behavior on the object.. */
+  if (version != pool->version) 
+  {
+    memcached_free(mmc);
+    memset(mmc, 0, sizeof(*mmc));
+    if (memcached_clone(mmc, pool->master) == NULL)
+    {
+      rc= MEMCACHED_SOME_ERRORS;
+    }
+  }
+
   pool->mmc[++pool->firstfree]= mmc;
 
   if (pool->firstfree == 0 && pool->current_size == pool->size) 
@@ -166,5 +180,67 @@ memcached_return memcached_pool_push(memcached_pool_st* pool,
     pthread_cond_broadcast(&pool->cond);
   }
 
+  memcached_return rval= mutex_exit(&pool->mutex);
+  if (rc == MEMCACHED_SOME_ERRORS)
+    return rc;
+
+  return rval;
+}
+
+
+memcached_return memcached_pool_behavior_set(memcached_pool_st *pool, 
+                                             memcached_behavior flag, 
+                                             uint64_t data)
+{
+
+  memcached_return rc= mutex_enter(&pool->mutex);
+  if (rc != MEMCACHED_SUCCESS)
+     return rc;
+
+  /* update the master */
+  rc= memcached_behavior_set(pool->master, flag, data);
+  if (rc != MEMCACHED_SUCCESS)
+  {
+    mutex_exit(&pool->mutex);
+    return rc;
+  }
+
+  ++pool->version;
+  memcached_set_user_data(pool->master, pool->version);
+  /* update the clones */
+  for (int xx= 0; xx <= pool->firstfree; ++xx) 
+  {
+    rc= memcached_behavior_set(pool->mmc[xx], flag, data);
+    if (rc == MEMCACHED_SUCCESS)
+      memcached_set_user_data(pool->mmc[xx], pool->version);
+    else
+    {
+      memcached_free(pool->mmc[xx]);
+      memset(pool->mmc[xx], 0, sizeof(*pool->mmc[xx]));
+      if (memcached_clone(pool->mmc[xx], pool->master) == NULL)
+      {
+        /* I'm not sure what to do in this case.. this would happen
+           if we fail to push the server list inside the client..
+           I should add a testcase for this, but I believe the following
+           would work, except that you would add a hole in the pool list..
+           in theory you could end up with an empty pool....
+        */
+        free(pool->mmc[xx]);
+        pool->mmc[xx]= NULL;
+      }
+    }
+  }
+
+  return mutex_exit(&pool->mutex);
+}
+
+memcached_return memcached_pool_behavior_get(memcached_pool_st *pool, 
+                                             memcached_behavior flag,
+                                             uint64_t *value)
+{
+  memcached_return rc= mutex_enter(&pool->mutex);
+  if (rc != MEMCACHED_SUCCESS)
+    return rc;
+  *value= memcached_behavior_get(pool->master, flag);
   return mutex_exit(&pool->mutex);
 }
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
index d3b67f0ff4b320f59b6af25bfce78df785e3cafd..1dce9ff89f90fddce49f9f26da2e41b34bec9418 100644 (file)
@@ -287,17 +287,25 @@ static test_return  connection_test(memcached_st *memc)
 static test_return  error_test(memcached_st *memc)
 {
   memcached_return rc;
-  uint32_t values[] = { 851992627U, 2337886783U, 3196981036U, 4001849190U, 982370485U, 1263635348U, 4242906218U, 3829656100U, 1891735253U, 
-                        334139633U, 2257084983U, 3088286104U, 13199785U, 2542027183U, 1097051614U, 199566778U, 2748246961U, 2465192557U, 
-                        1664094137U, 2405439045U, 1842224848U, 692413798U, 3479807801U, 919913813U, 4269430871U, 610793021U, 527273862U, 
-                        1437122909U, 2300930706U, 2943759320U, 674306647U, 2400528935U, 54481931U, 4186304426U, 1741088401U, 2979625118U, 
-                        4159057246U };
-
-  assert(MEMCACHED_MAXIMUM_RETURN == 37); // You have updated the memcache_error messages but not updated docs/tests.
+  uint32_t values[] = { 851992627U, 2337886783U, 3196981036U, 4001849190U, 
+                        982370485U, 1263635348U, 4242906218U, 3829656100U, 
+                        1891735253U, 334139633U, 2257084983U, 3088286104U, 
+                        13199785U, 2542027183U, 1097051614U, 199566778U,
+                        2748246961U, 2465192557U, 1664094137U, 2405439045U, 
+                        1842224848U, 692413798U, 3479807801U, 919913813U,
+                        4269430871U, 610793021U, 527273862U, 1437122909U,
+                        2300930706U, 2943759320U, 674306647U, 2400528935U,
+                        54481931U, 4186304426U, 1741088401U, 2979625118U, 
+                        4159057246U, 3425930182U};
+
+  // You have updated the memcache_error messages but not updated docs/tests.
+  assert(MEMCACHED_MAXIMUM_RETURN == 38); 
   for (rc= MEMCACHED_SUCCESS; rc < MEMCACHED_MAXIMUM_RETURN; rc++)
   {
     uint32_t hash_val;
-    hash_val= memcached_generate_hash_value(memcached_strerror(memc, rc), strlen(memcached_strerror(memc, rc)), MEMCACHED_HASH_JENKINS);
+    const char *msg=  memcached_strerror(memc, rc);
+    hash_val= memcached_generate_hash_value(msg, strlen(msg), 
+                                            MEMCACHED_HASH_JENKINS);
     assert(values[rc] == hash_val);
   }
 
@@ -1042,6 +1050,70 @@ static test_return get_test5(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
+static test_return  mget_end(memcached_st *memc)
+{
+  const char *keys[]= { "foo", "foo2" };
+  size_t lengths[]= { 3, 4 };
+  const char *values[]= { "fjord", "41" };
+
+  memcached_return rc;
+
+  // Set foo and foo2
+  for (int i= 0; i < 2; i++)
+  {
+    rc= memcached_set(memc, keys[i], lengths[i], values[i], strlen(values[i]),
+                     (time_t)0, (uint32_t)0);
+    assert(rc == MEMCACHED_SUCCESS);
+  }
+
+  char *string;
+  size_t string_length;
+  uint32_t flags;
+
+  // retrieve both via mget
+  rc= memcached_mget(memc, keys, lengths, 2);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  char key[MEMCACHED_MAX_KEY];
+  size_t key_length;
+
+  // this should get both
+  for (int i = 0; i < 2; i++) 
+  {
+    string= memcached_fetch(memc, key, &key_length, &string_length,
+                            &flags, &rc);
+    assert(rc == MEMCACHED_SUCCESS);
+    int val = 0;
+    if (key_length == 4)
+      val= 1;
+    assert(string_length == strlen(values[val]));
+    assert(strncmp(values[val], string, string_length) == 0);
+    free(string);
+  }
+
+  // this should indicate end
+  string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
+  assert(rc == MEMCACHED_END);
+
+  // now get just one
+  rc= memcached_mget(memc, keys, lengths, 1);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
+  assert(key_length == lengths[0]);
+  assert(strncmp(keys[0], key, key_length) == 0);
+  assert(string_length == strlen(values[0]));
+  assert(strncmp(values[0], string, string_length) == 0);
+  assert(rc == MEMCACHED_SUCCESS);
+  free(string);
+
+  // this should indicate end
+  string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
+  assert(rc == MEMCACHED_END);
+
+  return TEST_SUCCESS;
+}
+
 /* Do not copy the style of this code, I just access hosts to testthis function */
 static test_return  stats_servername_test(memcached_st *memc)
 {
@@ -3592,6 +3664,29 @@ static test_return connection_pool_test(memcached_st *memc)
   for (int x= 0; x < 10; ++x)
     assert(memcached_pool_push(pool, mmc[x]) == MEMCACHED_SUCCESS);
 
+
+  /* verify that I can set behaviors on the pool when I don't have all 
+   * of the connections in the pool. It should however be enabled
+   * when I push the item into the pool
+   */
+  mmc[0]= memcached_pool_pop(pool, false, &rc);
+  assert(mmc[0] != NULL);
+
+  rc= memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK, 9999);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  mmc[1]= memcached_pool_pop(pool, false, &rc);
+  assert(mmc[1] != NULL);
+
+  assert(memcached_behavior_get(mmc[1], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK) == 9999);
+  assert(memcached_pool_push(pool, mmc[1]) == MEMCACHED_SUCCESS);
+  assert(memcached_pool_push(pool, mmc[0]) == MEMCACHED_SUCCESS);
+
+  mmc[0]= memcached_pool_pop(pool, false, &rc);
+  assert(memcached_behavior_get(mmc[0], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK) == 9999);
+  assert(memcached_pool_push(pool, mmc[0]) == MEMCACHED_SUCCESS);
+
+
   assert(memcached_pool_destroy(pool) == memc);
   return TEST_SUCCESS;
 }
@@ -4385,6 +4480,28 @@ static test_return jenkins_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
+static test_return regression_bug_434484(memcached_st *memc)
+{
+  if (pre_binary(memc) != TEST_SUCCESS) 
+    return TEST_SUCCESS;
+   
+  memcached_return ret;
+  const char *key= "regression_bug_434484";
+  size_t keylen= strlen(key);
+
+  ret= memcached_append(memc, key, keylen, key, keylen, 0, 0);
+  assert(ret == MEMCACHED_NOTSTORED);
+
+  size_t size= 2048 * 1024;
+  void *data= malloc(size);
+  assert(data != NULL);
+  ret= memcached_set(memc, key, keylen, data, size, 0, 0);
+  assert(ret == MEMCACHED_E2BIG);
+  free(data);
+
+  return TEST_SUCCESS;
+}
+
 test_st udp_setup_server_tests[] ={
   {"set_udp_behavior_test", 0, set_udp_behavior_test},
   {"add_tcp_server_udp_client_test", 0, add_tcp_server_udp_client_test},
@@ -4447,6 +4564,7 @@ test_st tests[] ={
   {"mget_result", 1, mget_result_test },
   {"mget_result_alloc", 1, mget_result_alloc_test },
   {"mget_result_function", 1, mget_result_function },
+  {"mget_end", 0, mget_end },
   {"get_stats", 0, get_stats },
   {"add_host_test", 0, add_host_test },
   {"add_host_test_1", 0, add_host_test1 },
@@ -4537,6 +4655,17 @@ test_st replication_tests[]= {
   {0, 0, 0}
 };
 
+/*
+ * The following test suite is used to verify that we don't introduce
+ * regression bugs. If you want more information about the bug / test,
+ * you should look in the bug report at 
+ *   http://bugs.launchpad.net/libmemcached
+ */
+test_st regression_tests[]= {
+  {"lp:434484", 1, regression_bug_434484 },
+  {0, 0, 0}
+};
+
 test_st generate_tests[] ={
   {"generate_pairs", 1, generate_pairs },
   {"generate_data", 1, generate_data },
@@ -4645,6 +4774,7 @@ collection_st collection[] ={
   {"test_hashes", 0, 0, hash_tests},
   {"replication", pre_replication, 0, replication_tests},
   {"replication_noblock", pre_replication_noblock, 0, replication_tests},
+  {"regression", 0, 0, regression_tests},
   {0, 0, 0, 0}
 };