/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
- *
+ *
* Libmemcached library
*
* Copyright (C) 2011 Data Differential, http://datadifferential.com/
#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,
- memcached_result_st *result);
-static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
- char *buffer, size_t buffer_length,
- memcached_result_st *result);
-
-memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
- char *buffer, size_t buffer_length,
- memcached_result_st *result)
-{
- memcached_server_response_decrement(ptr);
-
- if (result == NULL)
- {
- memcached_st *root= (memcached_st *)ptr->root;
- result = &root->result;
- }
-
- 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 or
- rc == MEMCACHED_PROTOCOL_ERROR or
- rc == MEMCACHED_CLIENT_ERROR or
- rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
- memcached_io_reset(ptr);
-
- return rc;
-}
-
-memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
- char *buffer, size_t buffer_length,
- memcached_result_st *result)
-{
- /* We may have old commands in the buffer not set, first purge */
- if ((ptr->root->flags.no_block) && (memcached_is_processing_input(ptr->root) == false))
- {
- (void)memcached_io_write(ptr, NULL, 0, true);
- }
-
- /*
- * The previous implementation purged all pending requests and just
- * returned the last one. Purge all pending messages to ensure backwards
- * compatibility.
- */
- if (ptr->root->flags.binary_protocol == false)
- {
- while (memcached_server_response_count(ptr) > 1)
- {
- memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result);
-
- unlikely (rc != MEMCACHED_END &&
- rc != MEMCACHED_STORED &&
- rc != MEMCACHED_SUCCESS &&
- rc != MEMCACHED_STAT &&
- rc != MEMCACHED_DELETED &&
- rc != MEMCACHED_NOTFOUND &&
- rc != MEMCACHED_NOTSTORED &&
- rc != MEMCACHED_DATA_EXISTS)
- return rc;
- }
- }
-
- return memcached_read_one_response(ptr, buffer, buffer_length, result);
-}
-
-static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr,
+static memcached_return_t textual_value_fetch(memcached_instance_st* instance,
char *buffer,
memcached_result_st *result)
{
- char *string_ptr;
- char *end_ptr;
char *next_ptr;
- size_t value_length;
- size_t to_read;
ssize_t read_length= 0;
+ size_t value_length;
- if (ptr->root->flags.use_udp)
- return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
-
- WATCHPOINT_ASSERT(ptr->root);
- end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
+ WATCHPOINT_ASSERT(instance->root);
+ char *end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
memcached_result_reset(result);
- string_ptr= buffer;
+ char *string_ptr= buffer;
string_ptr+= 6; /* "VALUE " */
+ // Just used for cases of AES decrypt currently
+ memcached_return_t rc= MEMCACHED_SUCCESS;
+
/* We load the key */
{
- char *key;
- size_t prefix_length;
-
- key= result->item_key;
+ char *key= result->item_key;
result->key_length= 0;
- for (prefix_length= memcached_array_size(ptr->root->_namespace); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
+ for (ptrdiff_t prefix_length= memcached_array_size(instance->root->_namespace); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
{
if (prefix_length == 0)
{
}
if (end_ptr == string_ptr)
+ {
goto read_error;
+ }
/* Flags fetch move past space */
string_ptr++;
if (end_ptr == string_ptr)
+ {
goto read_error;
+ }
for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
+ errno= 0;
result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10);
- if (end_ptr == string_ptr)
+ if (errno != 0 or end_ptr == string_ptr)
+ {
goto read_error;
+ }
/* Length fetch move past space*/
string_ptr++;
if (end_ptr == string_ptr)
+ {
goto read_error;
+ }
for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
+ errno= 0;
value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);
- if (end_ptr == string_ptr)
+ if (errno != 0 or end_ptr == string_ptr)
+ {
goto read_error;
+ }
/* Skip spaces */
if (*string_ptr == '\r')
{
string_ptr++;
for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
+ errno= 0;
result->item_cas= strtoull(next_ptr, &string_ptr, 10);
}
- if (end_ptr < string_ptr)
+ if (errno != 0 or end_ptr < string_ptr)
+ {
goto read_error;
+ }
/* We add two bytes so that we can walk the \r\n */
if (memcached_failed(memcached_string_check(&result->value, value_length +2)))
{
- value_length= 0;
- return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
+ return memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
}
{
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);
+ size_t to_read= (value_length) + 2;
+ memcached_return_t rrc= memcached_io_read(instance, 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);
+ memcached_quit_server(instance, true);
+ return memcached_set_error(*instance, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
}
else if (memcached_failed(rrc))
{
memcached_string_set_length(&result->value, value_length);
}
- return MEMCACHED_SUCCESS;
+ if (memcached_is_encrypted(instance->root) and memcached_result_length(result))
+ {
+ hashkit_string_st *destination;
+
+ if ((destination= hashkit_decrypt(&instance->root->hashkit,
+ memcached_result_value(result), memcached_result_length(result))) == NULL)
+ {
+ rc= memcached_set_error(*instance->root, MEMCACHED_FAILURE,
+ MEMCACHED_AT, memcached_literal_param("hashkit_decrypt() failed"));
+ }
+ else
+ {
+ memcached_result_reset_value(result);
+ if (memcached_failed(memcached_result_set_value(result, hashkit_string_c_str(destination), hashkit_string_length(destination))))
+ {
+ rc= memcached_set_error(*instance->root, MEMCACHED_FAILURE,
+ MEMCACHED_AT, memcached_literal_param("hashkit_decrypt() failed"));
+ }
+ }
+
+ if (memcached_failed(rc))
+ {
+ memcached_result_reset(result);
+ }
+ hashkit_string_free(destination);
+ }
+
+ return rc;
read_error:
- memcached_io_reset(ptr);
+ memcached_io_reset(instance);
return MEMCACHED_PARTIAL_READ;
}
-static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
- char *buffer, size_t buffer_length,
+static memcached_return_t textual_read_one_response(memcached_instance_st* instance,
+ char *buffer, const size_t buffer_length,
memcached_result_st *result)
{
size_t total_read;
- memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
+ memcached_return_t rc= memcached_io_readline(instance, buffer, buffer_length, total_read);
if (memcached_failed(rc))
{
return rc;
}
+ assert(total_read);
switch(buffer[0])
{
- case 'V': /* VALUE || VERSION */
- if (buffer[1] == 'A') /* VALUE */
- {
- /* We add back in one because we will need to search for END */
- memcached_server_response_increment(ptr);
- return textual_value_fetch(ptr, buffer, result);
- }
- else if (buffer[1] == 'E') /* VERSION */
+ case 'V':
{
- return MEMCACHED_SUCCESS;
+ // VALUE
+ if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
+ {
+ /* We add back in one because we will need to search for END */
+ memcached_server_response_increment(instance);
+ return textual_value_fetch(instance, buffer, result);
+ }
+ // VERSION
+ else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'S' and buffer[4] == 'I' and buffer[5] == 'O' and buffer[6] == 'N') /* VERSION */
+ {
+ /* Find the space, and then move one past it to copy version */
+ char *response_ptr= index(buffer, ' ');
+
+ char *endptr;
+ errno= 0;
+ long int version= strtol(response_ptr, &endptr, 10);
+ if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX or version == 0)
+ {
+ instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse major version"));
+ }
+ instance->major_version= uint8_t(version);
+
+ endptr++;
+ errno= 0;
+ version= strtol(endptr, &endptr, 10);
+ if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX)
+ {
+ instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse minor version"));
+ }
+ instance->minor_version= uint8_t(version);
+
+ endptr++;
+ errno= 0;
+ version= strtol(endptr, &endptr, 10);
+ if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX)
+ {
+ instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse micro version"));
+ }
+ instance->micro_version= uint8_t(version);
+
+ return MEMCACHED_SUCCESS;
+ }
}
- else
+ break;
+
+ case 'O':
{
- WATCHPOINT_STRING(buffer);
- return MEMCACHED_UNKNOWN_READ_FAILURE;
+ // OK
+ if (buffer[1] == 'K')
+ {
+ return MEMCACHED_SUCCESS;
+ }
}
- case 'O': /* OK */
- return MEMCACHED_SUCCESS;
- case 'S': /* STORED STATS SERVER_ERROR */
+ break;
+
+ case 'S':
{
- if (buffer[2] == 'A') /* STORED STATS */
+ // STAT
+ if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */
{
- memcached_server_response_increment(ptr);
+ memcached_server_response_increment(instance);
return MEMCACHED_STAT;
}
- else if (buffer[1] == 'E') /* SERVER_ERROR */
+ // SERVER_ERROR
+ else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E' and buffer[5] == 'R'
+ and buffer[6] == '_'
+ and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R' )
{
if (total_read == memcached_literal_param_size("SERVER_ERROR"))
{
return MEMCACHED_SERVER_ERROR;
}
- if (total_read > memcached_literal_param_size("SERVER_ERROR object too large for cache") and
+ 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;
}
+ if (total_read >= memcached_literal_param_size("SERVER_ERROR out of memory storing object") and
+ (memcmp(buffer, memcached_literal_param("SERVER_ERROR out of memory storing object")) == 0))
+ {
+ return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
+ }
+
// Move past the basic error message and whitespace
char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
if (startptr[0] == ' ')
char *endptr= startptr;
while (*endptr != '\r' && *endptr != '\n') endptr++;
- return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
+ return memcached_set_error(*instance, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
}
- else if (buffer[1] == 'T')
+ // STORED
+ else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
{
return MEMCACHED_STORED;
}
- else
+ }
+ break;
+
+ case 'D':
+ {
+ // DELETED
+ if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T' and buffer[5] == 'E' and buffer[6] == 'D')
{
- WATCHPOINT_STRING(buffer);
- return MEMCACHED_UNKNOWN_READ_FAILURE;
+ return MEMCACHED_DELETED;
}
}
- case 'D': /* DELETED */
- return MEMCACHED_DELETED;
+ break;
- case 'N': /* NOT_FOUND */
+ case 'N':
{
- if (buffer[4] == 'F')
+ // NOT_FOUND
+ if (buffer[1] == 'O' and buffer[2] == 'T'
+ and buffer[3] == '_'
+ and buffer[4] == 'F' and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
+ {
return MEMCACHED_NOTFOUND;
- else if (buffer[4] == 'S')
- return MEMCACHED_NOTSTORED;
- else
+ }
+ // NOT_STORED
+ else if (buffer[1] == 'O' and buffer[2] == 'T'
+ and buffer[3] == '_'
+ and buffer[4] == 'S' and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E' and buffer[9] == 'D')
{
- WATCHPOINT_STRING(buffer);
- return MEMCACHED_UNKNOWN_READ_FAILURE;
+ return MEMCACHED_NOTSTORED;
}
}
+ break;
+
case 'E': /* PROTOCOL ERROR or END */
{
- if (buffer[1] == 'N')
+ // END
+ if (buffer[1] == 'N' and buffer[2] == 'D')
+ {
return MEMCACHED_END;
- else if (buffer[1] == 'R')
+ }
+#if 0
+ // PROTOCOL_ERROR
+ else if (buffer[1] == 'R' and buffer[2] == 'O' and buffer[3] == 'T' and buffer[4] == 'O' and buffer[5] == 'C' and buffer[6] == 'O' and buffer[7] == 'L'
+ and buffer[8] == '_'
+ and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
+ {
return MEMCACHED_PROTOCOL_ERROR;
- else if (buffer[1] == 'X')
+ }
+#endif
+ // ERROR
+ else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
+ {
+ return MEMCACHED_ERROR;
+ }
+ // EXISTS
+ else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T' and buffer[5] == 'S')
+ {
return MEMCACHED_DATA_EXISTS;
- else
+ }
+ }
+ break;
+
+ case 'T': /* TOUCHED */
+ {
+ // 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')
{
- WATCHPOINT_STRING(buffer);
- return MEMCACHED_UNKNOWN_READ_FAILURE;
+ return MEMCACHED_SUCCESS;
}
+ }
+ break;
+ case 'I': /* ITEM */
+ {
+ // ITEM
+ if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M')
+ {
+ /* We add back in one because we will need to search for END */
+ memcached_server_response_increment(instance);
+ return MEMCACHED_ITEM;
+ }
}
- 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;
+ break;
+
case 'C': /* CLIENT ERROR */
- return MEMCACHED_CLIENT_ERROR;
- default:
{
- unsigned long long auto_return_value;
+ // CLIENT_ERROR
+ if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N' and buffer[5] == 'T'
+ and buffer[6] == '_'
+ and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
+ {
+ // Move past the basic error message and whitespace
+ char *startptr= buffer + memcached_literal_param_size("CLIENT_ERROR");
+ if (startptr[0] == ' ')
+ {
+ startptr++;
+ }
- if (sscanf(buffer, "%llu", &auto_return_value) == 1)
- return MEMCACHED_SUCCESS;
+ char *endptr= startptr;
+ while (*endptr != '\r' && *endptr != '\n') endptr++;
+
+ return memcached_set_error(*instance, MEMCACHED_CLIENT_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
+ }
+ }
+ break;
+
+ case '0': /* INCR/DECR response */
+ case '1': /* INCR/DECR response */
+ case '2': /* INCR/DECR response */
+ case '3': /* INCR/DECR response */
+ case '4': /* INCR/DECR response */
+ case '5': /* INCR/DECR response */
+ case '6': /* INCR/DECR response */
+ case '7': /* INCR/DECR response */
+ case '8': /* INCR/DECR response */
+ case '9': /* INCR/DECR response */
+ {
+ errno= 0;
+ unsigned long long int auto_return_value= strtoull(buffer, (char **)NULL, 10);
+
+ if (auto_return_value == ULLONG_MAX and errno == ERANGE)
+ {
+ result->numeric_value= UINT64_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+ memcached_literal_param("Numeric response was out of range"));
+ }
+ else if (errno == EINVAL)
+ {
+ result->numeric_value= UINT64_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+ memcached_literal_param("Numeric response was out of range"));
+ }
+ else if (errno != 0)
+ {
+ result->numeric_value= UINT64_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+ memcached_literal_param("Numeric response was out of range"));
+ }
+
+ result->numeric_value= uint64_t(auto_return_value);
WATCHPOINT_STRING(buffer);
- return MEMCACHED_UNKNOWN_READ_FAILURE;
+ return MEMCACHED_SUCCESS;
}
+
+ default:
+ break;
}
- /* NOTREACHED */
+ buffer[total_read]= 0;
+#if 0
+ if (total_read >= sizeof("STORSTORED") -1)
+ {
+ fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
+ buffer, MEMCACHED_MAX_BUFFER, instance->read_buffer);
+ assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
+ }
+#endif
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+ buffer, total_read);
}
-static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
- char *buffer, size_t buffer_length,
+static memcached_return_t binary_read_one_response(memcached_instance_st* instance,
+ char *buffer, const size_t buffer_length,
memcached_result_st *result)
{
memcached_return_t rc;
protocol_binary_response_header header;
- if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
+ if ((rc= memcached_safe_read(instance, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
{
WATCHPOINT_ERROR(rc);
return rc;
if (header.response.magic != PROTOCOL_BINARY_RES)
{
- return MEMCACHED_PROTOCOL_ERROR;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
}
/*
* We didn't increment the response counter for the GETKQ packet
* (only the final NOOP), so we need to increment the counter again.
*/
- memcached_server_response_increment(ptr);
+ memcached_server_response_increment(instance);
/* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_GETK:
{
memcached_result_reset(result);
result->item_cas= header.response.cas;
- if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
+ if ((rc= memcached_safe_read(instance, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
{
WATCHPOINT_ERROR(rc);
return MEMCACHED_UNKNOWN_READ_FAILURE;
bodylen -= header.response.extlen;
result->key_length= keylen;
- if (memcached_failed(rc= memcached_safe_read(ptr, result->item_key, keylen)))
+ if (memcached_failed(rc= memcached_safe_read(instance, 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)
+ if (memcached_array_size(instance->root->_namespace) and memcached_array_size(instance->root->_namespace) >= result->key_length)
{
- return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
}
- if (memcached_array_size(ptr->root->_namespace))
+ if (memcached_array_size(instance->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);
+ result->key_length-= memcached_array_size(instance->root->_namespace);
+ memmove(result->item_key, result->item_key +memcached_array_size(instance->root->_namespace), result->key_length);
}
}
}
char *vptr= memcached_string_value_mutable(&result->value);
- if (memcached_failed(rc= memcached_safe_read(ptr, vptr, bodylen)))
+ if (memcached_failed(rc= memcached_safe_read(instance, vptr, bodylen)))
{
WATCHPOINT_ERROR(rc);
return MEMCACHED_UNKNOWN_READ_FAILURE;
case PROTOCOL_BINARY_CMD_INCREMENT:
case PROTOCOL_BINARY_CMD_DECREMENT:
{
- if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
+ if (bodylen != sizeof(uint64_t))
{
- return MEMCACHED_PROTOCOL_ERROR;
+ result->numeric_value= UINT64_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
}
- WATCHPOINT_ASSERT(bodylen == buffer_length);
uint64_t val;
- if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
+ if ((rc= memcached_safe_read(instance, &val, sizeof(val))) != MEMCACHED_SUCCESS)
{
- WATCHPOINT_ERROR(rc);
+ result->numeric_value= UINT64_MAX;
return MEMCACHED_UNKNOWN_READ_FAILURE;
}
- val= memcached_ntohll(val);
- memcpy(buffer, &val, sizeof(val));
+ result->numeric_value= memcached_ntohll(val);
}
break;
case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
- case PROTOCOL_BINARY_CMD_VERSION:
{
- memset(buffer, 0, buffer_length);
- if (bodylen >= buffer_length)
+ if (header.response.keylen != 0 || bodylen + 1 > buffer_length)
{
- /* not enough space in buffer.. should not happen... */
return MEMCACHED_UNKNOWN_READ_FAILURE;
}
- else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
+ else
+ {
+ if ((rc= memcached_safe_read(instance, buffer, bodylen)) != MEMCACHED_SUCCESS)
+ {
+ return MEMCACHED_UNKNOWN_READ_FAILURE;
+ }
+ }
+ }
+ break;
+
+ case PROTOCOL_BINARY_CMD_VERSION:
+ {
+ char version_buffer[32]; // @todo document this number
+ memset(version_buffer, 0, sizeof(version_buffer));
+
+ if (memcached_safe_read(instance, version_buffer, bodylen) != MEMCACHED_SUCCESS)
{
- WATCHPOINT_ERROR(rc);
return MEMCACHED_UNKNOWN_READ_FAILURE;
}
+
+ char *endptr;
+ errno= 0;
+ long int version= strtol(version_buffer, &endptr, 10);
+ if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX or version == 0)
+ {
+ instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse major version"));
+ }
+ instance->major_version= uint8_t(version);
+
+ endptr++;
+ errno= 0;
+ version= strtol(endptr, &endptr, 10);
+ if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX)
+ {
+ instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse minor version"));
+ }
+ instance->minor_version= uint8_t(version);
+
+ endptr++;
+ errno= 0;
+ version= strtol(endptr, &endptr, 10);
+ if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX)
+ {
+ instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse micro version"));
+ }
+ instance->micro_version= uint8_t(version);
}
break;
+
+ case PROTOCOL_BINARY_CMD_TOUCH:
+ {
+ rc= MEMCACHED_SUCCESS;
+ if (bodylen == 4) // The four byte read is a bug?
+ {
+ char touch_buffer[4]; // @todo document this number
+ rc= memcached_safe_read(instance, touch_buffer, sizeof(touch_buffer));
+#if 0
+ fprintf(stderr, "%s:%d %d %d %d %d %.*s(%d)\n", __FILE__, __LINE__,
+ int(touch_buffer[0]),
+ int(touch_buffer[1]),
+ int(touch_buffer[2]),
+ int(touch_buffer[3]),
+ int(bodylen), touch_buffer, int(bodylen));
+#endif
+ }
+ return memcached_set_error(*instance, rc, MEMCACHED_AT);
+ }
+
case PROTOCOL_BINARY_CMD_FLUSH:
case PROTOCOL_BINARY_CMD_QUIT:
case PROTOCOL_BINARY_CMD_SET:
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)
{
size_t keylen= header.response.keylen;
memset(buffer, 0, buffer_length);
- if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
- (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
+ if ((rc= memcached_safe_read(instance, buffer, keylen)) != MEMCACHED_SUCCESS ||
+ (rc= memcached_safe_read(instance, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
{
WATCHPOINT_ERROR(rc);
return MEMCACHED_UNKNOWN_READ_FAILURE;
return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
char *vptr= memcached_string_value_mutable(&result->value);
- if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
+ if ((rc= memcached_safe_read(instance, vptr, bodylen)) != MEMCACHED_SUCCESS)
{
WATCHPOINT_ERROR(rc);
return MEMCACHED_UNKNOWN_READ_FAILURE;
default:
{
/* Command not implemented yet! */
- WATCHPOINT_ASSERT(0);
- return MEMCACHED_PROTOCOL_ERROR;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
}
}
}
while (bodylen > 0)
{
size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
- if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
+ if ((rc= memcached_safe_read(instance, hole, nr)) != MEMCACHED_SUCCESS)
{
WATCHPOINT_ERROR(rc);
- return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
}
bodylen-= (uint32_t) nr;
}
case PROTOCOL_BINARY_CMD_REPLACEQ:
case PROTOCOL_BINARY_CMD_APPENDQ:
case PROTOCOL_BINARY_CMD_PREPENDQ:
- return binary_read_one_response(ptr, buffer, buffer_length, result);
+ return binary_read_one_response(instance, buffer, buffer_length, result);
default:
break;
case PROTOCOL_BINARY_RESPONSE_EINVAL:
case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
default:
- /* @todo fix the error mappings */
- rc= MEMCACHED_PROTOCOL_ERROR;
+ return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
break;
}
}
return rc;
}
+
+static memcached_return_t _read_one_response(memcached_instance_st* instance,
+ char *buffer, const size_t buffer_length,
+ memcached_result_st *result)
+{
+ memcached_server_response_decrement(instance);
+
+ if (result == NULL)
+ {
+ Memcached *root= (Memcached *)instance->root;
+ result = &root->result;
+ }
+
+ memcached_return_t rc;
+ if (memcached_is_binary(instance->root))
+ {
+ rc= binary_read_one_response(instance, buffer, buffer_length, result);
+ }
+ else
+ {
+ rc= textual_read_one_response(instance, buffer, buffer_length, result);
+ }
+
+ if (memcached_fatal(rc))
+ {
+ memcached_io_reset(instance);
+ }
+
+ return rc;
+}
+
+memcached_return_t memcached_read_one_response(memcached_instance_st* instance,
+ memcached_result_st *result)
+{
+ char buffer[SMALL_STRING_LEN];
+
+ if (memcached_is_udp(instance->root))
+ {
+ return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
+ }
+
+
+ return _read_one_response(instance, buffer, sizeof(buffer), result);
+}
+
+memcached_return_t memcached_response(memcached_instance_st* instance,
+ memcached_result_st *result)
+{
+ char buffer[1024];
+
+ return memcached_response(instance, buffer, sizeof(buffer), result);
+}
+
+memcached_return_t memcached_response(memcached_instance_st* instance,
+ char *buffer, size_t buffer_length,
+ memcached_result_st *result)
+{
+ if (memcached_is_udp(instance->root))
+ {
+ return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
+ }
+
+ /* We may have old commands in the buffer not set, first purge */
+ if ((instance->root->flags.no_block) and (memcached_is_processing_input(instance->root) == false))
+ {
+ (void)memcached_io_write(instance);
+ }
+
+ /*
+ * The previous implementation purged all pending requests and just
+ * returned the last one. Purge all pending messages to ensure backwards
+ * compatibility.
+ */
+ if (memcached_is_binary(instance->root) == false and memcached_server_response_count(instance) > 1)
+ {
+ memcached_result_st junked_result;
+ memcached_result_st *junked_result_ptr= memcached_result_create(instance->root, &junked_result);
+
+ assert(junked_result_ptr);
+
+ while (memcached_server_response_count(instance) > 1)
+ {
+ memcached_return_t rc= _read_one_response(instance, buffer, buffer_length, junked_result_ptr);
+
+ // @TODO should we return an error on another but a bad read case?
+ if (memcached_fatal(rc))
+ {
+ memcached_result_free(junked_result_ptr);
+ return rc;
+ }
+ }
+ memcached_result_free(junked_result_ptr);
+ }
+
+ return _read_one_response(instance, buffer, buffer_length, result);
+}