2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
16 #include "libmemcached/common.h"
17 #include "libmemcached/string.hpp"
19 static memcached_return_t
textual_value_fetch(memcached_instance_st
*instance
, char *buffer
,
20 memcached_result_st
*result
) {
22 ssize_t read_length
= 0;
25 WATCHPOINT_ASSERT(instance
->root
);
26 char *end_ptr
= buffer
+ MEMCACHED_DEFAULT_COMMAND_SIZE
;
28 memcached_result_reset(result
);
30 char *string_ptr
= buffer
;
31 string_ptr
+= 6; /* "VALUE " */
33 // Just used for cases of AES decrypt currently
34 memcached_return_t rc
= MEMCACHED_SUCCESS
;
38 char *key
= result
->item_key
;
39 result
->key_length
= 0;
41 for (ptrdiff_t prefix_length
= memcached_array_size(instance
->root
->_namespace
);
42 !(iscntrl(*string_ptr
) || isspace(*string_ptr
)); string_ptr
++)
44 if (prefix_length
== 0) {
51 result
->item_key
[result
->key_length
] = 0;
54 if (end_ptr
== string_ptr
) {
58 /* Flags fetch move past space */
60 if (end_ptr
== string_ptr
) {
64 for (next_ptr
= string_ptr
; isdigit(*string_ptr
); string_ptr
++) {
67 result
->item_flags
= (uint32_t) strtoul(next_ptr
, &string_ptr
, 10);
69 if (errno
!= 0 or end_ptr
== string_ptr
) {
73 /* Length fetch move past space*/
75 if (end_ptr
== string_ptr
) {
79 for (next_ptr
= string_ptr
; isdigit(*string_ptr
); string_ptr
++) {
82 value_length
= (size_t) strtoull(next_ptr
, &string_ptr
, 10);
84 if (errno
!= 0 or end_ptr
== string_ptr
) {
89 if (*string_ptr
== '\r') {
90 /* Skip past the \r\n */
94 for (next_ptr
= string_ptr
; isdigit(*string_ptr
); string_ptr
++) {
97 result
->item_cas
= strtoull(next_ptr
, &string_ptr
, 10);
100 if (errno
!= 0 or end_ptr
< string_ptr
) {
104 /* We add two bytes so that we can walk the \r\n */
105 if (memcached_failed(memcached_string_check(&result
->value
, value_length
+ 2))) {
106 return memcached_set_error(*instance
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
);
110 char *value_ptr
= memcached_string_value_mutable(&result
->value
);
112 We read the \r\n into the string since not doing so is more
113 cycles then the waster of memory to do so.
115 We are null terminating through, which will most likely make
116 some people lazy about using the return length.
118 size_t to_read
= (value_length
) + 2;
119 memcached_return_t rrc
= memcached_io_read(instance
, value_ptr
, to_read
, read_length
);
120 if (memcached_failed(rrc
) and rrc
== MEMCACHED_IN_PROGRESS
) {
121 memcached_quit_server(instance
, true);
122 return memcached_set_error(*instance
, MEMCACHED_IN_PROGRESS
, MEMCACHED_AT
);
123 } else if (memcached_failed(rrc
)) {
128 if (read_length
!= (ssize_t
)(value_length
+ 2)) {
132 /* This next bit blows the API, but this is internal....*/
135 char_ptr
= memcached_string_value_mutable(&result
->value
);
137 char_ptr
[value_length
] = 0;
138 char_ptr
[value_length
+ 1] = 0;
139 memcached_string_set_length(&result
->value
, value_length
);
142 if (memcached_is_encrypted(instance
->root
) and memcached_result_length(result
)) {
143 hashkit_string_st
*destination
;
145 if ((destination
= hashkit_decrypt(&instance
->root
->hashkit
, memcached_result_value(result
),
146 memcached_result_length(result
)))
149 rc
= memcached_set_error(*instance
->root
, MEMCACHED_FAILURE
, MEMCACHED_AT
,
150 memcached_literal_param("hashkit_decrypt() failed"));
152 memcached_result_reset_value(result
);
153 if (memcached_failed(memcached_result_set_value(result
, hashkit_string_c_str(destination
),
154 hashkit_string_length(destination
))))
156 rc
= memcached_set_error(*instance
->root
, MEMCACHED_FAILURE
, MEMCACHED_AT
,
157 memcached_literal_param("hashkit_decrypt() failed"));
161 if (memcached_failed(rc
)) {
162 memcached_result_reset(result
);
164 hashkit_string_free(destination
);
170 memcached_io_reset(instance
);
172 return MEMCACHED_PARTIAL_READ
;
175 static memcached_return_t
textual_read_one_response(memcached_instance_st
*instance
, char *buffer
,
176 const size_t buffer_length
,
177 memcached_result_st
*result
) {
179 memcached_return_t rc
= memcached_io_readline(instance
, buffer
, buffer_length
, total_read
);
181 if (memcached_failed(rc
)) {
189 if (buffer
[1] == 'A' and buffer
[2] == 'L' and buffer
[3] == 'U' and buffer
[4] == 'E') /* VALUE */
191 /* We add back in one because we will need to search for END */
192 memcached_server_response_increment(instance
);
193 return textual_value_fetch(instance
, buffer
, result
);
196 else if (buffer
[1] == 'E' and buffer
[2] == 'R' and buffer
[3] == 'S' and buffer
[4] == 'I'
197 and buffer
[5] == 'O' and buffer
[6] == 'N') /* VERSION */
199 /* Find the space, and then move one past it to copy version */
200 char *response_ptr
= index(buffer
, ' ');
204 long int version
= strtol(response_ptr
, &endptr
, 10);
205 if (errno
!= 0 or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
207 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
208 return memcached_set_error(
209 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
210 memcached_literal_param("strtol() failed to parse major version"));
212 instance
->major_version
= uint8_t(version
);
216 version
= strtol(endptr
, &endptr
, 10);
217 if (errno
!= 0 or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
218 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
219 return memcached_set_error(
220 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
221 memcached_literal_param("strtol() failed to parse minor version"));
223 instance
->minor_version
= uint8_t(version
);
227 version
= strtol(endptr
, &endptr
, 10);
228 if (errno
!= 0 or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
229 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
230 return memcached_set_error(
231 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
232 memcached_literal_param("strtol() failed to parse micro version"));
234 instance
->micro_version
= uint8_t(version
);
236 return MEMCACHED_SUCCESS
;
242 if (buffer
[1] == 'K') {
243 return MEMCACHED_SUCCESS
;
249 if (buffer
[1] == 'T' and buffer
[2] == 'A' and buffer
[3] == 'T') /* STORED STATS */ {
250 memcached_server_response_increment(instance
);
251 return MEMCACHED_STAT
;
254 else if (buffer
[1] == 'E' and buffer
[2] == 'R' and buffer
[3] == 'V' and buffer
[4] == 'E'
255 and buffer
[5] == 'R' and buffer
[6] == '_' and buffer
[7] == 'E' and buffer
[8] == 'R'
256 and buffer
[9] == 'R' and buffer
[10] == 'O' and buffer
[11] == 'R')
258 if (total_read
== memcached_literal_param_size("SERVER_ERROR")) {
259 return MEMCACHED_SERVER_ERROR
;
262 if (total_read
>= memcached_literal_param_size("SERVER_ERROR object too large for cache")
263 and (memcmp(buffer
, memcached_literal_param("SERVER_ERROR object too large for cache"))
266 return MEMCACHED_E2BIG
;
269 if (total_read
>= memcached_literal_param_size("SERVER_ERROR out of memory")
270 and ((memcmp(buffer
, memcached_literal_param("SERVER_ERROR out of memory")) == 0)
271 or (memcmp(buffer
, memcached_literal_param("SERVER_ERROR Out of memory")) == 0)))
273 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE
;
276 // Move past the basic error message and whitespace
277 char *startptr
= buffer
+ memcached_literal_param_size("SERVER_ERROR");
278 if (startptr
[0] == ' ') {
282 char *endptr
= startptr
;
283 while (*endptr
!= '\r' && *endptr
!= '\n') endptr
++;
285 return memcached_set_error(*instance
, MEMCACHED_SERVER_ERROR
, MEMCACHED_AT
, startptr
,
286 size_t(endptr
- startptr
));
289 else if (buffer
[1] == 'T' and buffer
[2] == 'O'
290 and buffer
[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
292 return MEMCACHED_STORED
;
298 if (buffer
[1] == 'E' and buffer
[2] == 'L' and buffer
[3] == 'E' and buffer
[4] == 'T'
299 and buffer
[5] == 'E' and buffer
[6] == 'D')
301 return MEMCACHED_DELETED
;
307 if (buffer
[1] == 'O' and buffer
[2] == 'T' and buffer
[3] == '_' and buffer
[4] == 'F'
308 and buffer
[5] == 'O' and buffer
[6] == 'U' and buffer
[7] == 'N' and buffer
[8] == 'D')
310 return MEMCACHED_NOTFOUND
;
313 else if (buffer
[1] == 'O' and buffer
[2] == 'T' and buffer
[3] == '_' and buffer
[4] == 'S'
314 and buffer
[5] == 'T' and buffer
[6] == 'O' and buffer
[7] == 'R' and buffer
[8] == 'E'
315 and buffer
[9] == 'D')
317 return MEMCACHED_NOTSTORED
;
321 case 'E': /* PROTOCOL ERROR or END */
324 if (buffer
[1] == 'N' and buffer
[2] == 'D') {
325 return MEMCACHED_END
;
329 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'
331 and buffer
[9] == 'E' and buffer
[10] == 'R' and buffer
[11] == 'R' and buffer
[12] == 'O' and buffer
[13] == 'R')
333 return MEMCACHED_PROTOCOL_ERROR
;
337 else if (buffer
[1] == 'R' and buffer
[2] == 'R' and buffer
[3] == 'O' and buffer
[4] == 'R')
339 return MEMCACHED_ERROR
;
342 else if (buffer
[1] == 'X' and buffer
[2] == 'I' and buffer
[3] == 'S' and buffer
[4] == 'T'
343 and buffer
[5] == 'S')
345 return MEMCACHED_DATA_EXISTS
;
349 case 'T': /* TOUCHED */
352 if (buffer
[1] == 'O' and buffer
[2] == 'U' and buffer
[3] == 'C' and buffer
[4] == 'H'
353 and buffer
[5] == 'E' and buffer
[6] == 'D')
355 return MEMCACHED_SUCCESS
;
362 if (buffer
[1] == 'T' and buffer
[2] == 'E' and buffer
[3] == 'M') {
363 /* We add back in one because we will need to search for END */
364 memcached_server_response_increment(instance
);
365 return MEMCACHED_ITEM
;
369 case 'C': /* CLIENT ERROR */
372 if (buffer
[1] == 'L' and buffer
[2] == 'I' and buffer
[3] == 'E' and buffer
[4] == 'N'
373 and buffer
[5] == 'T' and buffer
[6] == '_' and buffer
[7] == 'E' and buffer
[8] == 'R'
374 and buffer
[9] == 'R' and buffer
[10] == 'O' and buffer
[11] == 'R')
376 // Move past the basic error message and whitespace
377 char *startptr
= buffer
+ memcached_literal_param_size("CLIENT_ERROR");
378 if (startptr
[0] == ' ') {
382 char *endptr
= startptr
;
383 while (*endptr
!= '\r' && *endptr
!= '\n') endptr
++;
385 return memcached_set_error(*instance
, MEMCACHED_CLIENT_ERROR
, MEMCACHED_AT
, startptr
,
386 size_t(endptr
- startptr
));
390 case '0': /* INCR/DECR response */
391 case '1': /* INCR/DECR response */
392 case '2': /* INCR/DECR response */
393 case '3': /* INCR/DECR response */
394 case '4': /* INCR/DECR response */
395 case '5': /* INCR/DECR response */
396 case '6': /* INCR/DECR response */
397 case '7': /* INCR/DECR response */
398 case '8': /* INCR/DECR response */
399 case '9': /* INCR/DECR response */
402 unsigned long long int auto_return_value
= strtoull(buffer
, (char **) NULL
, 10);
404 if (auto_return_value
== ULLONG_MAX
and errno
== ERANGE
) {
405 result
->numeric_value
= UINT64_MAX
;
406 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
407 memcached_literal_param("Numeric response was out of range"));
408 } else if (errno
== EINVAL
) {
409 result
->numeric_value
= UINT64_MAX
;
410 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
411 memcached_literal_param("Numeric response was out of range"));
412 } else if (errno
!= 0) {
413 result
->numeric_value
= UINT64_MAX
;
414 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
415 memcached_literal_param("Numeric response was out of range"));
418 result
->numeric_value
= uint64_t(auto_return_value
);
420 WATCHPOINT_STRING(buffer
);
421 return MEMCACHED_SUCCESS
;
427 buffer
[total_read
] = 0;
429 if (total_read
>= sizeof("STORSTORED") -1)
431 fprintf(stderr
, "%s:%d '%s', %.*s\n", __FILE__
, __LINE__
,
432 buffer
, MEMCACHED_MAX_BUFFER
, instance
->read_buffer
);
433 assert(memcmp(buffer
,"STORSTORED", sizeof("STORSTORED") -1));
436 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
, buffer
,
440 static memcached_return_t
binary_read_one_response(memcached_instance_st
*instance
, char *buffer
,
441 const size_t buffer_length
,
442 memcached_result_st
*result
) {
443 memcached_return_t rc
;
444 protocol_binary_response_header header
;
446 assert(memcached_is_binary(instance
->root
));
448 if ((rc
= memcached_safe_read(instance
, &header
.bytes
, sizeof(header
.bytes
)))
449 != MEMCACHED_SUCCESS
) {
450 WATCHPOINT_ERROR(rc
);
454 if (header
.response
.magic
!= PROTOCOL_BINARY_RES
) {
455 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
459 ** Convert the header to host local endian!
461 header
.response
.keylen
= ntohs(header
.response
.keylen
);
462 header
.response
.status
= ntohs(header
.response
.status
);
463 header
.response
.bodylen
= ntohl(header
.response
.bodylen
);
464 header
.response
.cas
= memcached_ntohll(header
.response
.cas
);
465 uint32_t bodylen
= header
.response
.bodylen
;
467 if (header
.response
.status
== PROTOCOL_BINARY_RESPONSE_SUCCESS
468 or header
.response
.status
== PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE
)
470 switch (header
.response
.opcode
) {
471 case PROTOCOL_BINARY_CMD_GETKQ
:
473 * We didn't increment the response counter for the GETKQ packet
474 * (only the final NOOP), so we need to increment the counter again.
476 memcached_server_response_increment(instance
);
478 case PROTOCOL_BINARY_CMD_GETK
: {
479 uint16_t keylen
= header
.response
.keylen
;
480 memcached_result_reset(result
);
481 result
->item_cas
= header
.response
.cas
;
483 if ((rc
= memcached_safe_read(instance
, &result
->item_flags
, sizeof(result
->item_flags
)))
484 != MEMCACHED_SUCCESS
)
486 WATCHPOINT_ERROR(rc
);
487 return MEMCACHED_UNKNOWN_READ_FAILURE
;
490 result
->item_flags
= ntohl(result
->item_flags
);
491 bodylen
-= header
.response
.extlen
;
493 result
->key_length
= keylen
;
494 if (memcached_failed(rc
= memcached_safe_read(instance
, result
->item_key
, keylen
))) {
495 WATCHPOINT_ERROR(rc
);
496 return MEMCACHED_UNKNOWN_READ_FAILURE
;
499 // Only bother with doing this if key_length > 0
500 if (result
->key_length
) {
501 if (memcached_array_size(instance
->root
->_namespace
)
502 and memcached_array_size(instance
->root
->_namespace
) >= result
->key_length
)
504 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
507 if (memcached_array_size(instance
->root
->_namespace
)) {
508 result
->key_length
-= memcached_array_size(instance
->root
->_namespace
);
509 memmove(result
->item_key
,
510 result
->item_key
+ memcached_array_size(instance
->root
->_namespace
),
516 if (memcached_failed(memcached_string_check(&result
->value
, bodylen
))) {
517 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
520 char *vptr
= memcached_string_value_mutable(&result
->value
);
521 if (memcached_failed(rc
= memcached_safe_read(instance
, vptr
, bodylen
))) {
522 WATCHPOINT_ERROR(rc
);
523 return MEMCACHED_UNKNOWN_READ_FAILURE
;
526 memcached_string_set_length(&result
->value
, bodylen
);
529 case PROTOCOL_BINARY_CMD_INCREMENT
:
530 case PROTOCOL_BINARY_CMD_DECREMENT
: {
531 if (bodylen
!= sizeof(uint64_t)) {
532 result
->numeric_value
= UINT64_MAX
;
533 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
537 if ((rc
= memcached_safe_read(instance
, &val
, sizeof(val
))) != MEMCACHED_SUCCESS
) {
538 result
->numeric_value
= UINT64_MAX
;
539 return MEMCACHED_UNKNOWN_READ_FAILURE
;
542 result
->numeric_value
= memcached_ntohll(val
);
545 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS
: {
546 if (header
.response
.keylen
!= 0 || bodylen
+ 1 > buffer_length
) {
547 return MEMCACHED_UNKNOWN_READ_FAILURE
;
549 if ((rc
= memcached_safe_read(instance
, buffer
, bodylen
)) != MEMCACHED_SUCCESS
) {
550 return MEMCACHED_UNKNOWN_READ_FAILURE
;
555 case PROTOCOL_BINARY_CMD_VERSION
: {
556 char version_buffer
[32]; // @todo document this number
557 memset(version_buffer
, 0, sizeof(version_buffer
));
559 if (memcached_safe_read(instance
, version_buffer
, bodylen
) != MEMCACHED_SUCCESS
) {
560 return MEMCACHED_UNKNOWN_READ_FAILURE
;
565 long int version
= strtol(version_buffer
, &endptr
, 10);
566 if (errno
!= 0 or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
568 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
569 return memcached_set_error(
570 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
571 memcached_literal_param("strtol() failed to parse major version"));
573 instance
->major_version
= uint8_t(version
);
577 version
= strtol(endptr
, &endptr
, 10);
578 if (errno
!= 0 or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
579 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
580 return memcached_set_error(
581 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
582 memcached_literal_param("strtol() failed to parse minor version"));
584 instance
->minor_version
= uint8_t(version
);
588 version
= strtol(endptr
, &endptr
, 10);
589 if (errno
!= 0 or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
590 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
591 return memcached_set_error(
592 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
593 memcached_literal_param("strtol() failed to parse micro version"));
595 instance
->micro_version
= uint8_t(version
);
598 case PROTOCOL_BINARY_CMD_TOUCH
: {
599 rc
= MEMCACHED_SUCCESS
;
600 if (bodylen
== 4) // The four byte read is a bug?
602 char touch_buffer
[4]; // @todo document this number
603 rc
= memcached_safe_read(instance
, touch_buffer
, sizeof(touch_buffer
));
605 fprintf(stderr
, "%s:%d %d %d %d %d %.*s(%d)\n", __FILE__
, __LINE__
,
606 int(touch_buffer
[0]),
607 int(touch_buffer
[1]),
608 int(touch_buffer
[2]),
609 int(touch_buffer
[3]),
610 int(bodylen
), touch_buffer
, int(bodylen
));
613 return memcached_set_error(*instance
, rc
, MEMCACHED_AT
);
616 case PROTOCOL_BINARY_CMD_FLUSH
:
617 case PROTOCOL_BINARY_CMD_QUIT
:
618 case PROTOCOL_BINARY_CMD_SET
:
619 case PROTOCOL_BINARY_CMD_ADD
:
620 case PROTOCOL_BINARY_CMD_REPLACE
:
621 case PROTOCOL_BINARY_CMD_APPEND
:
622 case PROTOCOL_BINARY_CMD_PREPEND
:
623 case PROTOCOL_BINARY_CMD_DELETE
: {
624 WATCHPOINT_ASSERT(bodylen
== 0);
625 return MEMCACHED_SUCCESS
;
628 case PROTOCOL_BINARY_CMD_NOOP
: {
629 WATCHPOINT_ASSERT(bodylen
== 0);
630 return MEMCACHED_END
;
633 case PROTOCOL_BINARY_CMD_STAT
: {
635 return MEMCACHED_END
;
636 } else if (bodylen
+ 1 > buffer_length
) {
637 /* not enough space in buffer.. should not happen... */
638 return MEMCACHED_UNKNOWN_READ_FAILURE
;
640 size_t keylen
= header
.response
.keylen
;
641 memset(buffer
, 0, buffer_length
);
642 if ((rc
= memcached_safe_read(instance
, buffer
, keylen
)) != MEMCACHED_SUCCESS
643 || (rc
= memcached_safe_read(instance
, buffer
+ keylen
+ 1, bodylen
- keylen
))
644 != MEMCACHED_SUCCESS
)
646 WATCHPOINT_ERROR(rc
);
647 return MEMCACHED_UNKNOWN_READ_FAILURE
;
652 case PROTOCOL_BINARY_CMD_SASL_AUTH
:
653 case PROTOCOL_BINARY_CMD_SASL_STEP
: {
654 memcached_result_reset(result
);
655 result
->item_cas
= header
.response
.cas
;
657 if (memcached_string_check(&result
->value
, bodylen
) != MEMCACHED_SUCCESS
)
658 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
660 char *vptr
= memcached_string_value_mutable(&result
->value
);
661 if ((rc
= memcached_safe_read(instance
, vptr
, bodylen
)) != MEMCACHED_SUCCESS
) {
662 WATCHPOINT_ERROR(rc
);
663 return MEMCACHED_UNKNOWN_READ_FAILURE
;
666 memcached_string_set_length(&result
->value
, bodylen
);
669 /* Command not implemented yet! */
670 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
673 } else if (header
.response
.bodylen
) {
674 /* What should I do with the error message??? just discard it for now */
675 char hole
[SMALL_STRING_LEN
];
676 while (bodylen
> 0) {
677 size_t nr
= (bodylen
> SMALL_STRING_LEN
) ? SMALL_STRING_LEN
: bodylen
;
678 if ((rc
= memcached_safe_read(instance
, hole
, nr
)) != MEMCACHED_SUCCESS
) {
679 WATCHPOINT_ERROR(rc
);
680 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
682 bodylen
-= (uint32_t) nr
;
685 /* This might be an error from one of the quiet commands.. if
686 * so, just throw it away and get the next one. What about creating
687 * a callback to the user with the error information?
689 switch (header
.response
.opcode
) {
690 case PROTOCOL_BINARY_CMD_SETQ
:
691 case PROTOCOL_BINARY_CMD_ADDQ
:
692 case PROTOCOL_BINARY_CMD_REPLACEQ
:
693 case PROTOCOL_BINARY_CMD_APPENDQ
:
694 case PROTOCOL_BINARY_CMD_PREPENDQ
: return MEMCACHED_FETCH_NOTFINISHED
;
700 rc
= MEMCACHED_SUCCESS
;
701 if (header
.response
.status
!= 0) {
702 switch (header
.response
.status
) {
703 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
: rc
= MEMCACHED_NOTFOUND
; break;
705 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
: rc
= MEMCACHED_DATA_EXISTS
; break;
707 case PROTOCOL_BINARY_RESPONSE_NOT_STORED
: rc
= MEMCACHED_NOTSTORED
; break;
709 case PROTOCOL_BINARY_RESPONSE_E2BIG
: rc
= MEMCACHED_E2BIG
; break;
711 case PROTOCOL_BINARY_RESPONSE_ENOMEM
: rc
= MEMCACHED_MEMORY_ALLOCATION_FAILURE
; break;
713 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE
: rc
= MEMCACHED_AUTH_CONTINUE
; break;
715 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR
: rc
= MEMCACHED_AUTH_FAILURE
; break;
717 case PROTOCOL_BINARY_RESPONSE_EINVAL
:
718 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
:
720 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
728 static memcached_return_t
_read_one_response(memcached_instance_st
*instance
, char *buffer
,
729 const size_t buffer_length
,
730 memcached_result_st
*result
) {
731 memcached_server_response_decrement(instance
);
733 if (result
== NULL
) {
734 Memcached
*root
= (Memcached
*) instance
->root
;
735 result
= &root
->result
;
738 memcached_return_t rc
;
739 if (memcached_is_binary(instance
->root
)) {
741 rc
= binary_read_one_response(instance
, buffer
, buffer_length
, result
);
742 } while (rc
== MEMCACHED_FETCH_NOTFINISHED
);
744 rc
= textual_read_one_response(instance
, buffer
, buffer_length
, result
);
747 if (memcached_fatal(rc
) && rc
!= MEMCACHED_TIMEOUT
) {
748 memcached_io_reset(instance
);
754 memcached_return_t
memcached_read_one_response(memcached_instance_st
*instance
,
755 memcached_result_st
*result
) {
756 char buffer
[SMALL_STRING_LEN
];
758 if (memcached_is_udp(instance
->root
)) {
759 return memcached_set_error(*instance
, MEMCACHED_NOT_SUPPORTED
, MEMCACHED_AT
);
762 return _read_one_response(instance
, buffer
, sizeof(buffer
), result
);
765 memcached_return_t
memcached_response(memcached_instance_st
*instance
,
766 memcached_result_st
*result
) {
769 return memcached_response(instance
, buffer
, sizeof(buffer
), result
);
772 memcached_return_t
memcached_response(memcached_instance_st
*instance
, char *buffer
,
773 size_t buffer_length
, memcached_result_st
*result
) {
774 if (memcached_is_udp(instance
->root
)) {
775 return memcached_set_error(*instance
, MEMCACHED_NOT_SUPPORTED
, MEMCACHED_AT
);
778 /* We may have old commands in the buffer not sent, first purge */
779 if ((instance
->root
->flags
.no_block
) and (memcached_is_processing_input(instance
->root
) == false))
781 (void) memcached_io_write(instance
);
784 /* Before going into loop wait to see if we have any IO waiting for us */
786 memcached_return_t read_rc
= memcached_io_wait_for_read(instance
);
787 fprintf(stderr
, "%s:%d: %s\n", __FILE__
, __LINE__
, memcached_strerror(NULL
, read_rc
));
791 * The previous implementation purged all pending requests and just
792 * returned the last one. Purge all pending messages to ensure backwards
795 if (memcached_is_binary(instance
->root
) == false
796 and memcached_server_response_count(instance
) > 1) {
797 memcached_result_st junked_result
;
798 memcached_result_st
*junked_result_ptr
=
799 memcached_result_create(instance
->root
, &junked_result
);
801 assert(junked_result_ptr
);
803 while (memcached_server_response_count(instance
) > 1) {
804 memcached_return_t rc
=
805 _read_one_response(instance
, buffer
, buffer_length
, junked_result_ptr
);
807 // @TODO should we return an error on another but a bad read case?
808 if (memcached_fatal(rc
)) {
809 memcached_result_free(junked_result_ptr
);
813 memcached_result_free(junked_result_ptr
);
816 return _read_one_response(instance
, buffer
, buffer_length
, result
);