Merge in touch.
[awesomized/libmemcached] / libmemcached / response.cc
index 4035a5b952670f4c3829f919b9e13f2f209a5b56..061f7401fee84a19dbd462a0564601ebce5a7276 100644 (file)
@@ -1,5 +1,5 @@
 /*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
- * 
+ *
  *  Libmemcached library
  *
  *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
@@ -36,6 +36,7 @@
  */
 
 #include <libmemcached/common.h>
+#include <libmemcached/string.hpp>
 
 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
                                                     char *buffer, size_t buffer_length,
@@ -58,15 +59,21 @@ memcached_return_t memcached_read_one_response(memcached_server_write_instance_s
 
   memcached_return_t rc;
   if (ptr->root->flags.binary_protocol)
+  {
     rc= binary_read_one_response(ptr, buffer, buffer_length, result);
+  }
   else
+  {
     rc= textual_read_one_response(ptr, buffer, buffer_length, result);
+  }
 
-  unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE ||
-           rc == MEMCACHED_PROTOCOL_ERROR ||
-           rc == MEMCACHED_CLIENT_ERROR ||
+  unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE or
+           rc == MEMCACHED_PROTOCOL_ERROR or
+           rc == MEMCACHED_CLIENT_ERROR or
            rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
+  {
     memcached_io_reset(ptr);
+  }
 
   return rc;
 }
@@ -111,18 +118,17 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
                                               char *buffer,
                                               memcached_result_st *result)
 {
-  memcached_return_t rc= MEMCACHED_SUCCESS;
   char *string_ptr;
   char *end_ptr;
   char *next_ptr;
   size_t value_length;
   size_t to_read;
-  char *value_ptr;
   ssize_t read_length= 0;
-  memcached_return_t rrc;
 
   if (ptr->root->flags.use_udp)
-    return MEMCACHED_NOT_SUPPORTED;
+  {
+    return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
+  }
 
   WATCHPOINT_ASSERT(ptr->root);
   end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
@@ -141,7 +147,7 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
     key= result->item_key;
     result->key_length= 0;
 
-    for (prefix_length= memcached_array_size(ptr->root->prefix_key); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
+    for (prefix_length= memcached_array_size(ptr->root->_namespace); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
     {
       if (prefix_length == 0)
       {
@@ -197,25 +203,32 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
     goto read_error;
 
   /* We add two bytes so that we can walk the \r\n */
-  rc= memcached_string_check(&result->value, value_length+2);
-  if (rc != MEMCACHED_SUCCESS)
+  if (memcached_failed(memcached_string_check(&result->value, value_length +2)))
   {
-    value_length= 0;
-    return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+    return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
   }
 
-  value_ptr= memcached_string_value_mutable(&result->value);
-  /*
-    We read the \r\n into the string since not doing so is more
-    cycles then the waster of memory to do so.
-
-    We are null terminating through, which will most likely make
-    some people lazy about using the return length.
-  */
-  to_read= (value_length) + 2;
-  rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
-  if (rrc != MEMCACHED_SUCCESS)
-    return rrc;
+  {
+    char *value_ptr= memcached_string_value_mutable(&result->value);
+    /*
+      We read the \r\n into the string since not doing so is more
+      cycles then the waster of memory to do so.
+
+      We are null terminating through, which will most likely make
+      some people lazy about using the return length.
+    */
+    to_read= (value_length) + 2;
+    memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
+    if (memcached_failed(rrc) and rrc == MEMCACHED_IN_PROGRESS)
+    {
+      memcached_quit_server(ptr, true);
+      return memcached_set_error(*ptr, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
+    }
+    else if (memcached_failed(rrc))
+    {
+      return rrc;
+    }
+  }
 
   if (read_length != (ssize_t)(value_length + 2))
   {
@@ -227,7 +240,7 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
     char *char_ptr;
     char_ptr= memcached_string_value_mutable(&result->value);;
     char_ptr[value_length]= 0;
-    char_ptr[value_length + 1]= 0;
+    char_ptr[value_length +1]= 0;
     memcached_string_set_length(&result->value, value_length);
   }
 
@@ -243,9 +256,13 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
                                                     char *buffer, size_t buffer_length,
                                                     memcached_result_st *result)
 {
-  memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length);
-  if (rc != MEMCACHED_SUCCESS)
+  size_t total_read;
+  memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
+
+  if (memcached_failed(rc))
+  {
     return rc;
+  }
 
   switch(buffer[0])
   {
@@ -267,6 +284,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
     }
   case 'O': /* OK */
     return MEMCACHED_SUCCESS;
+
   case 'S': /* STORED STATS SERVER_ERROR */
     {
       if (buffer[2] == 'A') /* STORED STATS */
@@ -276,36 +294,33 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
       }
       else if (buffer[1] == 'E') /* SERVER_ERROR */
       {
-        char *rel_ptr;
-        char *startptr= buffer + 13, *endptr= startptr;
+        if (total_read == memcached_literal_param_size("SERVER_ERROR"))
+        {
+          return MEMCACHED_SERVER_ERROR;
+        }
 
-        while (*endptr != '\r' && *endptr != '\n') endptr++;
+        if (total_read > memcached_literal_param_size("SERVER_ERROR object too large for cache") and
+            (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
+        {
+          return MEMCACHED_E2BIG;
+        }
 
-        /*
-          Yes, we could make this "efficent" but to do that we would need
-          to maintain more state for the size of the buffer. Why waste
-          memory in the struct, which is important, for something that
-          rarely should happen?
-        */
-        rel_ptr= (char *)libmemcached_realloc(ptr->root,
-                                              ptr->cached_server_error,
-                                              (size_t) (endptr - startptr + 1));
-
-        if (rel_ptr == NULL)
+        // Move past the basic error message and whitespace
+        char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
+        if (startptr[0] == ' ')
         {
-          /* If we happened to have some memory, we just null it since we don't know the size */
-          if (ptr->cached_server_error)
-            ptr->cached_server_error[0]= 0;
-          return MEMCACHED_SERVER_ERROR;
+          startptr++;
         }
-        ptr->cached_server_error= rel_ptr;
 
-        memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr));
-        ptr->cached_server_error[endptr - startptr]= 0;
-        return MEMCACHED_SERVER_ERROR;
+        char *endptr= startptr;
+        while (*endptr != '\r' && *endptr != '\n') endptr++;
+
+        return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
       }
       else if (buffer[1] == 'T')
+      {
         return MEMCACHED_STORED;
+      }
       else
       {
         WATCHPOINT_STRING(buffer);
@@ -314,12 +329,17 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
     }
   case 'D': /* DELETED */
     return MEMCACHED_DELETED;
+
   case 'N': /* NOT_FOUND */
     {
       if (buffer[4] == 'F')
+      {
         return MEMCACHED_NOTFOUND;
+      }
       else if (buffer[4] == 'S')
+      {
         return MEMCACHED_NOTSTORED;
+      }
       else
       {
         WATCHPOINT_STRING(buffer);
@@ -329,11 +349,17 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
   case 'E': /* PROTOCOL ERROR or END */
     {
       if (buffer[1] == 'N')
+      {
         return MEMCACHED_END;
+      }
       else if (buffer[1] == 'R')
+      {
         return MEMCACHED_PROTOCOL_ERROR;
+      }
       else if (buffer[1] == 'X')
+      {
         return MEMCACHED_DATA_EXISTS;
+      }
       else
       {
         WATCHPOINT_STRING(buffer);
@@ -341,12 +367,25 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
       }
 
     }
+  case 'T': /* TOUCHED */
+    {
+      if (buffer[1] == 'O' and buffer[2] == 'U' 
+          and buffer[3] == 'C' and buffer[4] == 'H' 
+          and buffer[5] == 'E' and buffer[6] == 'D')
+      {
+        return MEMCACHED_SUCCESS;
+      }
+    }
+    return MEMCACHED_UNKNOWN_READ_FAILURE;
+
   case 'I': /* CLIENT ERROR */
     /* We add back in one because we will need to search for END */
     memcached_server_response_increment(ptr);
     return MEMCACHED_ITEM;
+
   case 'C': /* CLIENT ERROR */
     return MEMCACHED_CLIENT_ERROR;
+
   default:
     {
       unsigned long long auto_return_value;
@@ -386,10 +425,10 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
   header.response.keylen= ntohs(header.response.keylen);
   header.response.status= ntohs(header.response.status);
   header.response.bodylen= ntohl(header.response.bodylen);
-  header.response.cas= ntohll(header.response.cas);
+  header.response.cas= memcached_ntohll(header.response.cas);
   uint32_t bodylen= header.response.bodylen;
 
-  if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS ||
+  if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS or
       header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
   {
     switch (header.response.opcode)
@@ -417,19 +456,35 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
         bodylen -= header.response.extlen;
 
         result->key_length= keylen;
-        if ((rc= memcached_safe_read(ptr, result->item_key, keylen)) != MEMCACHED_SUCCESS)
+        if (memcached_failed(rc= memcached_safe_read(ptr, result->item_key, keylen)))
         {
           WATCHPOINT_ERROR(rc);
           return MEMCACHED_UNKNOWN_READ_FAILURE;
         }
 
+        // Only bother with doing this if key_length > 0
+        if (result->key_length)
+        {
+          if (memcached_array_size(ptr->root->_namespace) and memcached_array_size(ptr->root->_namespace) >= result->key_length)
+          {
+            return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
+          }
+
+          if (memcached_array_size(ptr->root->_namespace))
+          {
+            result->key_length-= memcached_array_size(ptr->root->_namespace);
+            memmove(result->item_key, result->item_key +memcached_array_size(ptr->root->_namespace), result->key_length);
+          }
+        }
+
         bodylen -= keylen;
-        if (memcached_string_check(&result->value,
-                                   bodylen) != MEMCACHED_SUCCESS)
+        if (memcached_failed(memcached_string_check(&result->value, bodylen)))
+        {
           return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+        }
 
         char *vptr= memcached_string_value_mutable(&result->value);
-        if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
+        if (memcached_failed(rc= memcached_safe_read(ptr, vptr, bodylen)))
         {
           WATCHPOINT_ERROR(rc);
           return MEMCACHED_UNKNOWN_READ_FAILURE;
@@ -438,11 +493,14 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
         memcached_string_set_length(&result->value, bodylen);
       }
       break;
+
     case PROTOCOL_BINARY_CMD_INCREMENT:
     case PROTOCOL_BINARY_CMD_DECREMENT:
       {
         if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
+        {
           return MEMCACHED_PROTOCOL_ERROR;
+        }
 
         WATCHPOINT_ASSERT(bodylen == buffer_length);
         uint64_t val;
@@ -452,10 +510,11 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
           return MEMCACHED_UNKNOWN_READ_FAILURE;
         }
 
-        val= ntohll(val);
+        val= memcached_ntohll(val);
         memcpy(buffer, &val, sizeof(val));
       }
       break;
+
     case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
     case PROTOCOL_BINARY_CMD_VERSION:
       {
@@ -480,15 +539,18 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
     case PROTOCOL_BINARY_CMD_APPEND:
     case PROTOCOL_BINARY_CMD_PREPEND:
     case PROTOCOL_BINARY_CMD_DELETE:
+    case PROTOCOL_BINARY_CMD_TOUCH:
       {
         WATCHPOINT_ASSERT(bodylen == 0);
         return MEMCACHED_SUCCESS;
       }
+
     case PROTOCOL_BINARY_CMD_NOOP:
       {
         WATCHPOINT_ASSERT(bodylen == 0);
         return MEMCACHED_END;
       }
+
     case PROTOCOL_BINARY_CMD_STAT:
       {
         if (bodylen == 0)
@@ -552,7 +614,7 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
       if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
       {
         WATCHPOINT_ERROR(rc);
-        return MEMCACHED_UNKNOWN_READ_FAILURE;
+        return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
       }
       bodylen-= (uint32_t) nr;
     }
@@ -569,36 +631,45 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
     case PROTOCOL_BINARY_CMD_APPENDQ:
     case PROTOCOL_BINARY_CMD_PREPENDQ:
       return binary_read_one_response(ptr, buffer, buffer_length, result);
+
     default:
       break;
     }
   }
 
   rc= MEMCACHED_SUCCESS;
-  unlikely(header.response.status != 0)
+  if (header.response.status != 0)
+  {
     switch (header.response.status)
     {
     case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
       rc= MEMCACHED_NOTFOUND;
       break;
+
     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_AUTH_CONTINUE:
       rc= MEMCACHED_AUTH_CONTINUE;
       break;
+
     case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
       rc= MEMCACHED_AUTH_FAILURE;
       break;
+
     case PROTOCOL_BINARY_RESPONSE_EINVAL:
     case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
     default:
@@ -606,6 +677,7 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
       rc= MEMCACHED_PROTOCOL_ERROR;
       break;
     }
+  }
 
   return rc;
 }