2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - 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-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
16 #include "libmemcached/common.h"
17 #include "libmemcached/string.hpp"
19 static inline memcached_return_t
ascii_read_inc(memcached_instance_st
*instance
, const char *buffer
,
20 memcached_result_st
*result
) {
23 unsigned long long int auto_return_value
= strtoull(buffer
, (char **) NULL
, 10);
26 result
->numeric_value
= UINT64_MAX
;
27 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
28 memcached_literal_param("Numeric response was out of range"));
31 result
->numeric_value
= uint64_t(auto_return_value
);
32 return MEMCACHED_SUCCESS
;
36 static inline memcached_return_t
ascii_read_ull(size_t &value
, char **string_ptr
, char *end_ptr
) {
37 char *next_ptr
= *string_ptr
;
40 value
= strtoull(next_ptr
, string_ptr
, 10);
43 return MEMCACHED_ERRNO
;
45 if (*string_ptr
>= end_ptr
) {
46 return MEMCACHED_PARTIAL_READ
;
48 return MEMCACHED_SUCCESS
;
51 static inline memcached_return_t
ascii_read_key(char *buf
, size_t len
, char **str_ptr
, char *end_ptr
) {
53 while (**str_ptr
!= ' ' && **str_ptr
!= '\r') {
54 *(tmp_ptr
++) = *((*str_ptr
)++);
55 if (*str_ptr
== end_ptr
|| size_t(tmp_ptr
- buf
) >= len
) {
56 return MEMCACHED_PARTIAL_READ
;
60 return MEMCACHED_SUCCESS
;
63 static inline memcached_return_t
ascii_read_val(memcached_instance_st
*instance
, memcached_result_st
*result
,
65 /* We add two bytes so that we can walk the \r\n */
66 if (memcached_failed(memcached_string_check(&result
->value
, to_read
))) {
67 return memcached_set_error(*instance
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
);
71 char *value_ptr
= memcached_string_value_mutable(&result
->value
);
73 We read the \r\n into the string since not doing so is more
74 cycles then the waster of memory to do so.
76 We are null terminating through, which will most likely make
77 some people lazy about using the return length.
79 memcached_return_t read_rc
= memcached_io_read(instance
, value_ptr
, to_read
, to_read
);
80 if (memcached_failed(read_rc
)) {
81 if (read_rc
== MEMCACHED_IN_PROGRESS
) {
82 memcached_quit_server(instance
, true);
83 return memcached_set_error(*instance
, MEMCACHED_IN_PROGRESS
, MEMCACHED_AT
);
88 /* This next bit blows the API, but this is internal....*/
90 char *char_ptr
= memcached_string_value_mutable(&result
->value
);
91 char_ptr
[--to_read
] = 0;
92 char_ptr
[--to_read
] = 0;
93 memcached_string_set_length(&result
->value
, to_read
);
96 return MEMCACHED_SUCCESS
;
100 static memcached_return_t
result_decrypt(memcached_instance_st
*instance
, memcached_result_st
*result
) {
101 memcached_return_t rc
= MEMCACHED_SUCCESS
;
103 if (memcached_result_length(result
)) {
104 hashkit_string_st
*destination
= hashkit_decrypt(&instance
->root
->hashkit
, memcached_result_value(result
),
105 memcached_result_length(result
));
107 return memcached_set_error(*instance
->root
, MEMCACHED_FAILURE
, MEMCACHED_AT
,
108 memcached_literal_param("hashkit_decrypt() failed"));
111 memcached_result_reset_value(result
);
113 rc
= memcached_result_set_value(result
, hashkit_string_c_str(destination
), hashkit_string_length(destination
));
114 if (memcached_failed(rc
)) {
115 rc
= memcached_set_error(*instance
->root
, MEMCACHED_FAILURE
, MEMCACHED_AT
,
116 memcached_literal_param("hashkit_decrypt() failed"));
119 hashkit_string_free(destination
);
125 static memcached_return_t
meta_fetch_flags(memcached_instance_st
*instance
, char *str_ptr
, char *end_ptr
,
126 memcached_result_st
*result
, char opaque
[MEMCACHED_MAX_KEY
]) {
130 while (str_ptr
< end_ptr
&& *str_ptr
!= '\r') {
131 switch (*str_ptr
++) {
136 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &str_ptr
, end_ptr
)) {
139 result
->item_cas
= ull_val
;
143 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &str_ptr
, end_ptr
)) {
146 result
->item_flags
= ull_val
;
150 str_ptr
+= memcached_array_size(instance
->root
->_namespace
);
151 if (str_ptr
>= end_ptr
) {
155 if (MEMCACHED_SUCCESS
!= ascii_read_key(result
->item_key
, sizeof(result
->item_key
), &str_ptr
, end_ptr
)) {
158 result
->key_length
= str_ptr
- tmp_ptr
;
162 if (ascii_read_ull(ull_val
, &str_ptr
, end_ptr
)) {
165 /* legacy result does not support last_accessed */
170 if (MEMCACHED_SUCCESS
!= ascii_read_key(opaque
, MEMCACHED_MAX_KEY
, &str_ptr
, end_ptr
)) {
173 /* legacy result does not support opaque */
177 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &str_ptr
, end_ptr
)) {
180 /* legacy result does not support size */
184 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &str_ptr
, end_ptr
)) {
187 result
->item_expiration
= time_t(ull_val
);
193 /* legacy result does not support re-cache semantics */
198 return MEMCACHED_SUCCESS
;
200 memcached_io_reset(instance
);
201 return MEMCACHED_PARTIAL_READ
;
204 static memcached_return_t
meta_va_fetch(memcached_instance_st
*instance
, char *buffer
,
205 memcached_result_st
*result
) {
206 char opaque
[MEMCACHED_MAX_KEY
] = "";
207 char *str_ptr
= buffer
+ sizeof("VA");
208 char *end_ptr
= buffer
+ MEMCACHED_DEFAULT_COMMAND_SIZE
;
209 size_t ull_val
, val_len
;
210 memcached_return_t rc
;
212 while (isspace(*str_ptr
) && str_ptr
< end_ptr
) {
215 if (str_ptr
== end_ptr
) {
219 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &str_ptr
, end_ptr
)) {
224 rc
= meta_fetch_flags(instance
, str_ptr
, end_ptr
, result
, opaque
);
225 if (memcached_success(rc
)) {
226 auto read_len
= ssize_t(val_len
+ 2);
227 rc
= ascii_read_val(instance
, result
, read_len
);
228 if (memcached_success(rc
)) {
229 if (read_len
!= ssize_t(val_len
)) {
233 /* meta INC/DEC response */
234 if ('+' == *opaque
) {
235 rc
= ascii_read_inc(instance
, result
->value
.string
, result
);
236 } else if (memcached_is_encrypted(instance
->root
)) {
237 rc
= result_decrypt(instance
, result
);
238 if (memcached_failed(rc
)) {
239 memcached_result_reset(result
);
248 memcached_io_reset(instance
);
249 return MEMCACHED_PARTIAL_READ
;
252 static memcached_return_t
textual_value_fetch(memcached_instance_st
*instance
, char *buffer
,
253 memcached_result_st
*result
) {
254 ssize_t read_length
= 0;
255 size_t value_length
, ull_val
;
257 WATCHPOINT_ASSERT(instance
->root
);
258 char *end_ptr
= buffer
+ MEMCACHED_DEFAULT_COMMAND_SIZE
;
260 memcached_result_reset(result
);
262 char *string_ptr
= buffer
;
263 string_ptr
+= 6; /* "VALUE " */
265 // Just used for cases of AES decrypt currently
266 memcached_return_t rc
= MEMCACHED_SUCCESS
;
268 /* We load the key */
270 char *key
= result
->item_key
;
271 result
->key_length
= 0;
273 for (ptrdiff_t prefix_length
= memcached_array_size(instance
->root
->_namespace
);
274 !(iscntrl(*string_ptr
) || isspace(*string_ptr
)); string_ptr
++)
276 if (prefix_length
== 0) {
279 result
->key_length
++;
283 result
->item_key
[result
->key_length
] = 0;
286 if (end_ptr
== string_ptr
) {
290 /* Flags fetch move past space */
292 if (end_ptr
== string_ptr
) {
296 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &string_ptr
, end_ptr
)) {
299 result
->item_flags
= ull_val
;
301 /* Length fetch move past space*/
303 if (end_ptr
== string_ptr
) {
307 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &string_ptr
, end_ptr
)) {
310 value_length
= ull_val
;
313 if (*string_ptr
== '\r') {
314 /* Skip past the \r\n */
318 if (MEMCACHED_SUCCESS
!= ascii_read_ull(ull_val
, &string_ptr
, end_ptr
)) {
321 result
->item_cas
= ull_val
;
324 if (end_ptr
< string_ptr
) {
328 read_length
= ssize_t(value_length
+ 2);
329 rc
= ascii_read_val(instance
, result
, read_length
);
330 if (MEMCACHED_SUCCESS
!= rc
) {
334 if (read_length
!= (ssize_t
)(value_length
)) {
338 if (memcached_is_encrypted(instance
->root
)) {
339 rc
= result_decrypt(instance
, result
);
340 if (memcached_failed(rc
)) {
341 memcached_result_reset(result
);
348 memcached_io_reset(instance
);
349 return MEMCACHED_PARTIAL_READ
;
352 static memcached_return_t
textual_read_one_response(memcached_instance_st
*instance
, char *buffer
,
353 const size_t buffer_length
,
354 memcached_result_st
*result
) {
356 memcached_return_t rc
= memcached_io_readline(instance
, buffer
, buffer_length
, total_read
);
358 if (memcached_failed(rc
)) {
366 if (buffer
[1] == 'A' and buffer
[2] == 'L' and buffer
[3] == 'U' and buffer
[4] == 'E') /* VALUE */
368 /* We add back in one because we will need to search for END */
369 memcached_server_response_increment(instance
);
370 return textual_value_fetch(instance
, buffer
, result
);
373 else if (buffer
[1] == 'A' && buffer
[2] == ' ') {
374 return meta_va_fetch(instance
, buffer
, result
);
377 else if (buffer
[1] == 'E' and buffer
[2] == 'R' and buffer
[3] == 'S' and buffer
[4] == 'I'
378 and buffer
[5] == 'O' and buffer
[6] == 'N') /* VERSION */
380 /* Find the space, and then move one past it to copy version */
381 char *response_ptr
= strchr(buffer
, ' ');
385 long int version
= strtol(response_ptr
, &endptr
, 10);
386 if (errno
or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
388 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
389 return memcached_set_error(
390 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
391 memcached_literal_param("strtol() failed to parse major version"));
393 instance
->major_version
= uint8_t(version
);
397 version
= strtol(endptr
, &endptr
, 10);
398 if (errno
or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
399 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
400 return memcached_set_error(
401 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
402 memcached_literal_param("strtol() failed to parse minor version"));
404 instance
->minor_version
= uint8_t(version
);
408 version
= strtol(endptr
, &endptr
, 10);
409 if (errno
or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
410 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
411 return memcached_set_error(
412 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
413 memcached_literal_param("strtol() failed to parse micro version"));
415 instance
->micro_version
= uint8_t(version
);
417 return MEMCACHED_SUCCESS
;
423 if (buffer
[1] == 'K') {
424 return MEMCACHED_SUCCESS
;
430 if (buffer
[1] == 'T' and buffer
[2] == 'A' and buffer
[3] == 'T') /* STORED STATS */ {
431 memcached_server_response_increment(instance
);
432 return MEMCACHED_STAT
;
435 else if (buffer
[1] == 'E' and buffer
[2] == 'R' and buffer
[3] == 'V' and buffer
[4] == 'E'
436 and buffer
[5] == 'R' and buffer
[6] == '_' and buffer
[7] == 'E' and buffer
[8] == 'R'
437 and buffer
[9] == 'R' and buffer
[10] == 'O' and buffer
[11] == 'R')
439 if (total_read
== memcached_literal_param_size("SERVER_ERROR")) {
440 return MEMCACHED_SERVER_ERROR
;
443 if (total_read
>= memcached_literal_param_size("SERVER_ERROR object too large for cache")
444 and (memcmp(buffer
, memcached_literal_param("SERVER_ERROR object too large for cache"))
447 return MEMCACHED_E2BIG
;
450 if (total_read
>= memcached_literal_param_size("SERVER_ERROR out of memory")
451 and ((memcmp(buffer
, memcached_literal_param("SERVER_ERROR out of memory")) == 0)
452 or (memcmp(buffer
, memcached_literal_param("SERVER_ERROR Out of memory")) == 0)))
454 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE
;
457 // Move past the basic error message and whitespace
458 char *startptr
= buffer
+ memcached_literal_param_size("SERVER_ERROR");
459 if (startptr
[0] == ' ') {
463 char *endptr
= startptr
;
464 while (*endptr
!= '\r' && *endptr
!= '\n') endptr
++;
466 return memcached_set_error(*instance
, MEMCACHED_SERVER_ERROR
, MEMCACHED_AT
, startptr
,
467 size_t(endptr
- startptr
));
470 else if (buffer
[1] == 'T' and buffer
[2] == 'O'
471 and buffer
[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
473 return MEMCACHED_STORED
;
479 if (buffer
[1] == 'E' and buffer
[2] == 'L' and buffer
[3] == 'E' and buffer
[4] == 'T'
480 and buffer
[5] == 'E' and buffer
[6] == 'D')
482 return MEMCACHED_DELETED
;
488 if (buffer
[1] == 'F') {
489 return MEMCACHED_NOTFOUND
;
491 else if (buffer
[1] == 'O' and buffer
[2] == 'T' and buffer
[3] == '_' and buffer
[4] == 'F'
492 and buffer
[5] == 'O' and buffer
[6] == 'U' and buffer
[7] == 'N' and buffer
[8] == 'D')
494 return MEMCACHED_NOTFOUND
;
497 else if (buffer
[1] == 'S') {
498 return MEMCACHED_NOTSTORED
;
500 else if (buffer
[1] == 'O' and buffer
[2] == 'T' and buffer
[3] == '_' and buffer
[4] == 'S'
501 and buffer
[5] == 'T' and buffer
[6] == 'O' and buffer
[7] == 'R' and buffer
[8] == 'E'
502 and buffer
[9] == 'D')
504 return MEMCACHED_NOTSTORED
;
508 case 'M': /* META NOOP */
509 if (buffer
[1] == 'N') {
510 return MEMCACHED_END
;
514 case 'E': /* PROTOCOL ERROR or END */
517 if (buffer
[1] == 'N') {
518 if (buffer
[2] == 'D') {
519 return MEMCACHED_END
;
521 return MEMCACHED_NOTFOUND
;
524 else if (buffer
[1] == 'R' and buffer
[2] == 'R' and buffer
[3] == 'O' and buffer
[4] == 'R')
526 return MEMCACHED_ERROR
;
529 else if (buffer
[1] == 'X')
531 return MEMCACHED_DATA_EXISTS
;
535 case 'T': /* TOUCHED */
538 if (buffer
[1] == 'O' and buffer
[2] == 'U' and buffer
[3] == 'C' and buffer
[4] == 'H'
539 and buffer
[5] == 'E' and buffer
[6] == 'D')
541 return MEMCACHED_SUCCESS
;
548 if (buffer
[1] == 'T' and buffer
[2] == 'E' and buffer
[3] == 'M') {
549 /* We add back in one because we will need to search for END */
550 memcached_server_response_increment(instance
);
551 return MEMCACHED_ITEM
;
555 case 'C': /* CLIENT ERROR */
558 if (buffer
[1] == 'L' and buffer
[2] == 'I' and buffer
[3] == 'E' and buffer
[4] == 'N'
559 and buffer
[5] == 'T' and buffer
[6] == '_' and buffer
[7] == 'E' and buffer
[8] == 'R'
560 and buffer
[9] == 'R' and buffer
[10] == 'O' and buffer
[11] == 'R')
562 // Move past the basic error message and whitespace
563 char *startptr
= buffer
+ memcached_literal_param_size("CLIENT_ERROR");
564 if (startptr
[0] == ' ') {
568 char *endptr
= startptr
;
569 while (*endptr
!= '\r' && *endptr
!= '\n') endptr
++;
571 return memcached_set_error(*instance
, MEMCACHED_CLIENT_ERROR
, MEMCACHED_AT
, startptr
,
572 size_t(endptr
- startptr
));
576 case '0': /* INCR/DECR response */
577 case '1': /* INCR/DECR response */
578 case '2': /* INCR/DECR response */
579 case '3': /* INCR/DECR response */
580 case '4': /* INCR/DECR response */
581 case '5': /* INCR/DECR response */
582 case '6': /* INCR/DECR response */
583 case '7': /* INCR/DECR response */
584 case '8': /* INCR/DECR response */
585 case '9': /* INCR/DECR response */
586 return ascii_read_inc(instance
, buffer
, result
);
592 buffer
[total_read
] = 0;
594 if (total_read
>= sizeof("STORSTORED") -1)
596 fprintf(stderr
, "%s:%d '%s', %.*s\n", __FILE__
, __LINE__
,
597 buffer
, MEMCACHED_MAX_BUFFER
, instance
->read_buffer
);
598 assert(memcmp(buffer
,"STORSTORED", sizeof("STORSTORED") -1));
601 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
, buffer
,
605 static memcached_return_t
binary_read_one_response(memcached_instance_st
*instance
, char *buffer
,
606 const size_t buffer_length
,
607 memcached_result_st
*result
) {
608 memcached_return_t rc
;
609 protocol_binary_response_header header
;
611 assert(memcached_is_binary(instance
->root
));
613 if ((rc
= memcached_safe_read(instance
, &header
.bytes
, sizeof(header
.bytes
)))
614 != MEMCACHED_SUCCESS
) {
615 WATCHPOINT_ERROR(rc
);
619 if (header
.response
.magic
!= PROTOCOL_BINARY_RES
) {
620 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
624 ** Convert the header to host local endian!
626 header
.response
.keylen
= ntohs(header
.response
.keylen
);
627 header
.response
.status
= ntohs(header
.response
.status
);
628 header
.response
.bodylen
= ntohl(header
.response
.bodylen
);
629 header
.response
.cas
= memcached_ntohll(header
.response
.cas
);
630 uint32_t bodylen
= header
.response
.bodylen
;
632 if (header
.response
.status
== PROTOCOL_BINARY_RESPONSE_SUCCESS
633 or header
.response
.status
== PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE
)
635 switch (header
.response
.opcode
) {
636 case PROTOCOL_BINARY_CMD_GETKQ
:
638 * We didn't increment the response counter for the GETKQ packet
639 * (only the final NOOP), so we need to increment the counter again.
641 memcached_server_response_increment(instance
);
643 case PROTOCOL_BINARY_CMD_GETK
: {
644 uint16_t keylen
= header
.response
.keylen
;
645 memcached_result_reset(result
);
646 result
->item_cas
= header
.response
.cas
;
648 if ((rc
= memcached_safe_read(instance
, &result
->item_flags
, sizeof(result
->item_flags
)))
649 != MEMCACHED_SUCCESS
)
651 WATCHPOINT_ERROR(rc
);
652 return MEMCACHED_UNKNOWN_READ_FAILURE
;
655 result
->item_flags
= ntohl(result
->item_flags
);
656 bodylen
-= header
.response
.extlen
;
658 result
->key_length
= keylen
;
659 if (memcached_failed(rc
= memcached_safe_read(instance
, result
->item_key
, keylen
))) {
660 WATCHPOINT_ERROR(rc
);
661 return MEMCACHED_UNKNOWN_READ_FAILURE
;
664 // Only bother with doing this if key_length > 0
665 if (result
->key_length
) {
666 if (memcached_array_size(instance
->root
->_namespace
)
667 and memcached_array_size(instance
->root
->_namespace
) >= result
->key_length
)
669 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
672 if (memcached_array_size(instance
->root
->_namespace
)) {
673 result
->key_length
-= memcached_array_size(instance
->root
->_namespace
);
674 memmove(result
->item_key
,
675 result
->item_key
+ memcached_array_size(instance
->root
->_namespace
),
681 if (memcached_failed(memcached_string_check(&result
->value
, bodylen
))) {
682 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
685 char *vptr
= memcached_string_value_mutable(&result
->value
);
686 if (memcached_failed(rc
= memcached_safe_read(instance
, vptr
, bodylen
))) {
687 WATCHPOINT_ERROR(rc
);
688 return MEMCACHED_UNKNOWN_READ_FAILURE
;
691 memcached_string_set_length(&result
->value
, bodylen
);
694 case PROTOCOL_BINARY_CMD_INCREMENT
:
695 case PROTOCOL_BINARY_CMD_DECREMENT
: {
696 if (bodylen
!= sizeof(uint64_t)) {
697 result
->numeric_value
= UINT64_MAX
;
698 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
702 if ((rc
= memcached_safe_read(instance
, &val
, sizeof(val
))) != MEMCACHED_SUCCESS
) {
703 result
->numeric_value
= UINT64_MAX
;
704 return MEMCACHED_UNKNOWN_READ_FAILURE
;
707 result
->numeric_value
= memcached_ntohll(val
);
710 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS
: {
711 if (header
.response
.keylen
|| bodylen
+ 1 > buffer_length
) {
712 return MEMCACHED_UNKNOWN_READ_FAILURE
;
714 if ((rc
= memcached_safe_read(instance
, buffer
, bodylen
)) != MEMCACHED_SUCCESS
) {
715 return MEMCACHED_UNKNOWN_READ_FAILURE
;
720 case PROTOCOL_BINARY_CMD_VERSION
: {
721 char version_buffer
[32]; // @todo document this number
722 memset(version_buffer
, 0, sizeof(version_buffer
));
724 if (memcached_safe_read(instance
, version_buffer
, bodylen
) != MEMCACHED_SUCCESS
) {
725 return MEMCACHED_UNKNOWN_READ_FAILURE
;
730 long int version
= strtol(version_buffer
, &endptr
, 10);
731 if (errno
or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
733 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
734 return memcached_set_error(
735 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
736 memcached_literal_param("strtol() failed to parse major version"));
738 instance
->major_version
= uint8_t(version
);
742 version
= strtol(endptr
, &endptr
, 10);
743 if (errno
or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
744 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
745 return memcached_set_error(
746 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
747 memcached_literal_param("strtol() failed to parse minor version"));
749 instance
->minor_version
= uint8_t(version
);
753 version
= strtol(endptr
, &endptr
, 10);
754 if (errno
or version
== LONG_MIN
or version
== LONG_MAX
or version
> UINT8_MAX
) {
755 instance
->major_version
= instance
->minor_version
= instance
->micro_version
= UINT8_MAX
;
756 return memcached_set_error(
757 *instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
,
758 memcached_literal_param("strtol() failed to parse micro version"));
760 instance
->micro_version
= uint8_t(version
);
763 case PROTOCOL_BINARY_CMD_TOUCH
: {
764 rc
= MEMCACHED_SUCCESS
;
765 if (bodylen
== 4) // The four byte read is a bug?
767 char touch_buffer
[4]; // @todo document this number
768 rc
= memcached_safe_read(instance
, touch_buffer
, sizeof(touch_buffer
));
770 fprintf(stderr
, "%s:%d %d %d %d %d %.*s(%d)\n", __FILE__
, __LINE__
,
771 int(touch_buffer
[0]),
772 int(touch_buffer
[1]),
773 int(touch_buffer
[2]),
774 int(touch_buffer
[3]),
775 int(bodylen
), touch_buffer
, int(bodylen
));
778 return memcached_set_error(*instance
, rc
, MEMCACHED_AT
);
781 case PROTOCOL_BINARY_CMD_FLUSH
:
782 case PROTOCOL_BINARY_CMD_QUIT
:
783 case PROTOCOL_BINARY_CMD_SET
:
784 case PROTOCOL_BINARY_CMD_ADD
:
785 case PROTOCOL_BINARY_CMD_REPLACE
:
786 case PROTOCOL_BINARY_CMD_APPEND
:
787 case PROTOCOL_BINARY_CMD_PREPEND
:
788 case PROTOCOL_BINARY_CMD_DELETE
: {
789 WATCHPOINT_ASSERT(bodylen
== 0);
790 return MEMCACHED_SUCCESS
;
793 case PROTOCOL_BINARY_CMD_NOOP
: {
794 WATCHPOINT_ASSERT(bodylen
== 0);
795 return MEMCACHED_END
;
798 case PROTOCOL_BINARY_CMD_STAT
: {
800 return MEMCACHED_END
;
801 } else if (bodylen
+ 1 > buffer_length
) {
802 /* not enough space in buffer.. should not happen... */
803 return MEMCACHED_UNKNOWN_READ_FAILURE
;
805 size_t keylen
= header
.response
.keylen
;
806 memset(buffer
, 0, buffer_length
);
807 if ((rc
= memcached_safe_read(instance
, buffer
, keylen
)) != MEMCACHED_SUCCESS
808 || (rc
= memcached_safe_read(instance
, buffer
+ keylen
+ 1, bodylen
- keylen
))
809 != MEMCACHED_SUCCESS
)
811 WATCHPOINT_ERROR(rc
);
812 return MEMCACHED_UNKNOWN_READ_FAILURE
;
817 case PROTOCOL_BINARY_CMD_SASL_AUTH
:
818 case PROTOCOL_BINARY_CMD_SASL_STEP
: {
819 memcached_result_reset(result
);
820 result
->item_cas
= header
.response
.cas
;
822 if (memcached_string_check(&result
->value
, bodylen
) != MEMCACHED_SUCCESS
)
823 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
825 char *vptr
= memcached_string_value_mutable(&result
->value
);
826 if ((rc
= memcached_safe_read(instance
, vptr
, bodylen
)) != MEMCACHED_SUCCESS
) {
827 WATCHPOINT_ERROR(rc
);
828 return MEMCACHED_UNKNOWN_READ_FAILURE
;
831 memcached_string_set_length(&result
->value
, bodylen
);
834 /* Command not implemented yet! */
835 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
838 } else if (header
.response
.bodylen
) {
839 /* What should I do with the error message??? just discard it for now */
840 char hole
[SMALL_STRING_LEN
];
841 while (bodylen
> 0) {
842 size_t nr
= (bodylen
> SMALL_STRING_LEN
) ? SMALL_STRING_LEN
: bodylen
;
843 if ((rc
= memcached_safe_read(instance
, hole
, nr
)) != MEMCACHED_SUCCESS
) {
844 WATCHPOINT_ERROR(rc
);
845 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
847 bodylen
-= (uint32_t) nr
;
850 /* This might be an error from one of the quiet commands.. if
851 * so, just throw it away and get the next one. What about creating
852 * a callback to the user with the error information?
854 switch (header
.response
.opcode
) {
855 case PROTOCOL_BINARY_CMD_SETQ
:
856 case PROTOCOL_BINARY_CMD_ADDQ
:
857 case PROTOCOL_BINARY_CMD_REPLACEQ
:
858 case PROTOCOL_BINARY_CMD_APPENDQ
:
859 case PROTOCOL_BINARY_CMD_PREPENDQ
:
860 return MEMCACHED_FETCH_NOTFINISHED
;
867 rc
= MEMCACHED_SUCCESS
;
868 if (header
.response
.status
) {
869 switch (header
.response
.status
) {
870 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
:
871 rc
= MEMCACHED_NOTFOUND
;
874 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
:
875 rc
= MEMCACHED_DATA_EXISTS
;
878 case PROTOCOL_BINARY_RESPONSE_NOT_STORED
:
879 rc
= MEMCACHED_NOTSTORED
;
882 case PROTOCOL_BINARY_RESPONSE_E2BIG
:
883 rc
= MEMCACHED_E2BIG
;
886 case PROTOCOL_BINARY_RESPONSE_ENOMEM
:
887 rc
= MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
890 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE
:
891 rc
= MEMCACHED_AUTH_CONTINUE
;
894 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR
:
895 rc
= MEMCACHED_AUTH_FAILURE
;
898 case PROTOCOL_BINARY_RESPONSE_EINVAL
:
899 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
:
901 return memcached_set_error(*instance
, MEMCACHED_UNKNOWN_READ_FAILURE
, MEMCACHED_AT
);
909 static memcached_return_t
_read_one_response(memcached_instance_st
*instance
, char *buffer
,
910 const size_t buffer_length
,
911 memcached_result_st
*result
) {
912 memcached_server_response_decrement(instance
);
914 if (result
== NULL
) {
915 Memcached
*root
= (Memcached
*) instance
->root
;
916 result
= &root
->result
;
919 memcached_return_t rc
;
920 if (memcached_is_binary(instance
->root
)) {
922 rc
= binary_read_one_response(instance
, buffer
, buffer_length
, result
);
923 } while (rc
== MEMCACHED_FETCH_NOTFINISHED
);
925 rc
= textual_read_one_response(instance
, buffer
, buffer_length
, result
);
928 if (memcached_fatal(rc
) && rc
!= MEMCACHED_TIMEOUT
) {
929 memcached_io_reset(instance
);
935 memcached_return_t
memcached_read_one_response(memcached_instance_st
*instance
,
936 memcached_result_st
*result
) {
937 char buffer
[SMALL_STRING_LEN
];
939 if (memcached_is_udp(instance
->root
)) {
940 return memcached_set_error(*instance
, MEMCACHED_NOT_SUPPORTED
, MEMCACHED_AT
);
943 return _read_one_response(instance
, buffer
, sizeof(buffer
), result
);
946 memcached_return_t
memcached_response(memcached_instance_st
*instance
,
947 memcached_result_st
*result
) {
950 return memcached_response(instance
, buffer
, sizeof(buffer
), result
);
953 memcached_return_t
memcached_response(memcached_instance_st
*instance
, char *buffer
,
954 size_t buffer_length
, memcached_result_st
*result
) {
955 if (memcached_is_udp(instance
->root
)) {
956 return memcached_set_error(*instance
, MEMCACHED_NOT_SUPPORTED
, MEMCACHED_AT
);
959 /* We may have old commands in the buffer not sent, first purge */
960 if ((instance
->root
->flags
.no_block
) and (memcached_is_processing_input(instance
->root
) == false))
962 (void) memcached_io_write(instance
);
965 /* Before going into loop wait to see if we have any IO waiting for us */
967 memcached_return_t read_rc
= memcached_io_wait_for_read(instance
);
968 fprintf(stderr
, "%s:%d: %s\n", __FILE__
, __LINE__
, memcached_strerror(NULL
, read_rc
));
972 * The previous implementation purged all pending requests and just
973 * returned the last one. Purge all pending messages to ensure backwards
976 if (memcached_is_binary(instance
->root
) == false
977 and memcached_server_response_count(instance
) > 1) {
978 memcached_result_st junked_result
;
979 memcached_result_st
*junked_result_ptr
=
980 memcached_result_create(instance
->root
, &junked_result
);
982 assert(junked_result_ptr
);
984 while (memcached_server_response_count(instance
) > 1) {
985 memcached_return_t rc
=
986 _read_one_response(instance
, buffer
, buffer_length
, junked_result_ptr
);
988 // @TODO should we return an error on another but a bad read case?
989 if (memcached_fatal(rc
)) {
990 memcached_result_free(junked_result_ptr
);
994 memcached_result_free(junked_result_ptr
);
997 return _read_one_response(instance
, buffer
, buffer_length
, result
);