revert most of d7a0084bf99d618d1dc26a54fd413db7ae8b8e63
[awesomized/libmemcached] / src / libmemcached / response.cc
1 /*
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 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcached/common.h"
17 #include "libmemcached/string.hpp"
18
19 static memcached_return_t textual_value_fetch(memcached_instance_st *instance, char *buffer,
20 memcached_result_st *result) {
21 char *next_ptr;
22 ssize_t read_length = 0;
23 size_t value_length;
24
25 WATCHPOINT_ASSERT(instance->root);
26 char *end_ptr = buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
27
28 memcached_result_reset(result);
29
30 char *string_ptr = buffer;
31 string_ptr += 6; /* "VALUE " */
32
33 // Just used for cases of AES decrypt currently
34 memcached_return_t rc = MEMCACHED_SUCCESS;
35
36 /* We load the key */
37 {
38 char *key = result->item_key;
39 result->key_length = 0;
40
41 for (ptrdiff_t prefix_length = memcached_array_size(instance->root->_namespace);
42 !(iscntrl(*string_ptr) || isspace(*string_ptr)); string_ptr++)
43 {
44 if (prefix_length == 0) {
45 *key = *string_ptr;
46 key++;
47 result->key_length++;
48 } else
49 prefix_length--;
50 }
51 result->item_key[result->key_length] = 0;
52 }
53
54 if (end_ptr == string_ptr) {
55 goto read_error;
56 }
57
58 /* Flags fetch move past space */
59 string_ptr++;
60 if (end_ptr == string_ptr) {
61 goto read_error;
62 }
63
64 for (next_ptr = string_ptr; isdigit(*string_ptr); string_ptr++) {
65 };
66 errno = 0;
67 result->item_flags = (uint32_t) strtoul(next_ptr, &string_ptr, 10);
68
69 if (errno or end_ptr == string_ptr) {
70 goto read_error;
71 }
72
73 /* Length fetch move past space*/
74 string_ptr++;
75 if (end_ptr == string_ptr) {
76 goto read_error;
77 }
78
79 for (next_ptr = string_ptr; isdigit(*string_ptr); string_ptr++) {
80 };
81 errno = 0;
82 value_length = (size_t) strtoull(next_ptr, &string_ptr, 10);
83
84 if (errno or end_ptr == string_ptr) {
85 goto read_error;
86 }
87
88 /* Skip spaces */
89 if (*string_ptr == '\r') {
90 /* Skip past the \r\n */
91 string_ptr += 2;
92 } else {
93 string_ptr++;
94 for (next_ptr = string_ptr; isdigit(*string_ptr); string_ptr++) {
95 };
96 errno = 0;
97 result->item_cas = strtoull(next_ptr, &string_ptr, 10);
98 }
99
100 if (errno or end_ptr < string_ptr) {
101 goto read_error;
102 }
103
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);
107 }
108
109 {
110 char *value_ptr = memcached_string_value_mutable(&result->value);
111 /*
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.
114
115 We are null terminating through, which will most likely make
116 some people lazy about using the return length.
117 */
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)) {
124 return rrc;
125 }
126 }
127
128 if (read_length != (ssize_t)(value_length + 2)) {
129 goto read_error;
130 }
131
132 /* This next bit blows the API, but this is internal....*/
133 {
134 char *char_ptr;
135 char_ptr = memcached_string_value_mutable(&result->value);
136 ;
137 char_ptr[value_length] = 0;
138 char_ptr[value_length + 1] = 0;
139 memcached_string_set_length(&result->value, value_length);
140 }
141
142 if (memcached_is_encrypted(instance->root) and memcached_result_length(result)) {
143 hashkit_string_st *destination;
144
145 if ((destination = hashkit_decrypt(&instance->root->hashkit, memcached_result_value(result),
146 memcached_result_length(result)))
147 == NULL)
148 {
149 rc = memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
150 memcached_literal_param("hashkit_decrypt() failed"));
151 } else {
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))))
155 {
156 rc = memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
157 memcached_literal_param("hashkit_decrypt() failed"));
158 }
159 }
160
161 if (memcached_failed(rc)) {
162 memcached_result_reset(result);
163 }
164 hashkit_string_free(destination);
165 }
166
167 return rc;
168
169 read_error:
170 memcached_io_reset(instance);
171
172 return MEMCACHED_PARTIAL_READ;
173 }
174
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) {
178 size_t total_read;
179 memcached_return_t rc = memcached_io_readline(instance, buffer, buffer_length, total_read);
180
181 if (memcached_failed(rc)) {
182 return rc;
183 }
184 assert(total_read);
185
186 switch (buffer[0]) {
187 case 'V': {
188 // VALUE
189 if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
190 {
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);
194 }
195 // VERSION
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 */
198 {
199 /* Find the space, and then move one past it to copy version */
200 char *response_ptr = strchr(buffer, ' ');
201
202 char *endptr;
203 errno = 0;
204 long int version = strtol(response_ptr, &endptr, 10);
205 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX
206 or version == 0) {
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"));
211 }
212 instance->major_version = uint8_t(version);
213
214 endptr++;
215 errno = 0;
216 version = strtol(endptr, &endptr, 10);
217 if (errno 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"));
222 }
223 instance->minor_version = uint8_t(version);
224
225 endptr++;
226 errno = 0;
227 version = strtol(endptr, &endptr, 10);
228 if (errno 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"));
233 }
234 instance->micro_version = uint8_t(version);
235
236 return MEMCACHED_SUCCESS;
237 }
238 } break;
239
240 case 'O': {
241 // OK
242 if (buffer[1] == 'K') {
243 return MEMCACHED_SUCCESS;
244 }
245 } break;
246
247 case 'S': {
248 // STAT
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;
252 }
253 // SERVER_ERROR
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')
257 {
258 if (total_read == memcached_literal_param_size("SERVER_ERROR")) {
259 return MEMCACHED_SERVER_ERROR;
260 }
261
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"))
264 == 0))
265 {
266 return MEMCACHED_E2BIG;
267 }
268
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)))
272 {
273 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
274 }
275
276 // Move past the basic error message and whitespace
277 char *startptr = buffer + memcached_literal_param_size("SERVER_ERROR");
278 if (startptr[0] == ' ') {
279 startptr++;
280 }
281
282 char *endptr = startptr;
283 while (*endptr != '\r' && *endptr != '\n') endptr++;
284
285 return memcached_set_error(*instance, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr,
286 size_t(endptr - startptr));
287 }
288 // STORED
289 else if (buffer[1] == 'T' and buffer[2] == 'O'
290 and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
291 {
292 return MEMCACHED_STORED;
293 }
294 } break;
295
296 case 'D': {
297 // DELETED
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')
300 {
301 return MEMCACHED_DELETED;
302 }
303 } break;
304
305 case 'N': {
306 // NOT_FOUND
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')
309 {
310 return MEMCACHED_NOTFOUND;
311 }
312 // NOT_STORED
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')
316 {
317 return MEMCACHED_NOTSTORED;
318 }
319 } break;
320
321 case 'E': /* PROTOCOL ERROR or END */
322 {
323 // END
324 if (buffer[1] == 'N' and buffer[2] == 'D') {
325 return MEMCACHED_END;
326 }
327 #if 0
328 // PROTOCOL_ERROR
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'
330 and buffer[8] == '_'
331 and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
332 {
333 return MEMCACHED_PROTOCOL_ERROR;
334 }
335 #endif
336 // ERROR
337 else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
338 {
339 return MEMCACHED_ERROR;
340 }
341 // EXISTS
342 else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T'
343 and buffer[5] == 'S')
344 {
345 return MEMCACHED_DATA_EXISTS;
346 }
347 } break;
348
349 case 'T': /* TOUCHED */
350 {
351 // 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')
354 {
355 return MEMCACHED_SUCCESS;
356 }
357 } break;
358
359 case 'I': /* ITEM */
360 {
361 // ITEM
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;
366 }
367 } break;
368
369 case 'C': /* CLIENT ERROR */
370 {
371 // 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')
375 {
376 // Move past the basic error message and whitespace
377 char *startptr = buffer + memcached_literal_param_size("CLIENT_ERROR");
378 if (startptr[0] == ' ') {
379 startptr++;
380 }
381
382 char *endptr = startptr;
383 while (*endptr != '\r' && *endptr != '\n') endptr++;
384
385 return memcached_set_error(*instance, MEMCACHED_CLIENT_ERROR, MEMCACHED_AT, startptr,
386 size_t(endptr - startptr));
387 }
388 } break;
389
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 */
400 {
401 errno = 0;
402 unsigned long long int auto_return_value = strtoull(buffer, (char **) NULL, 10);
403
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) {
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"));
416 }
417
418 result->numeric_value = uint64_t(auto_return_value);
419
420 WATCHPOINT_STRING(buffer);
421 return MEMCACHED_SUCCESS;
422 }
423
424 default:
425 break;
426 }
427
428 buffer[total_read] = 0;
429 #if 0
430 if (total_read >= sizeof("STORSTORED") -1)
431 {
432 fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
433 buffer, MEMCACHED_MAX_BUFFER, instance->read_buffer);
434 assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
435 }
436 #endif
437 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, buffer,
438 total_read);
439 }
440
441 static memcached_return_t binary_read_one_response(memcached_instance_st *instance, char *buffer,
442 const size_t buffer_length,
443 memcached_result_st *result) {
444 memcached_return_t rc;
445 protocol_binary_response_header header;
446
447 assert(memcached_is_binary(instance->root));
448
449 if ((rc = memcached_safe_read(instance, &header.bytes, sizeof(header.bytes)))
450 != MEMCACHED_SUCCESS) {
451 WATCHPOINT_ERROR(rc);
452 return rc;
453 }
454
455 if (header.response.magic != PROTOCOL_BINARY_RES) {
456 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
457 }
458
459 /*
460 ** Convert the header to host local endian!
461 */
462 header.response.keylen = ntohs(header.response.keylen);
463 header.response.status = ntohs(header.response.status);
464 header.response.bodylen = ntohl(header.response.bodylen);
465 header.response.cas = memcached_ntohll(header.response.cas);
466 uint32_t bodylen = header.response.bodylen;
467
468 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS
469 or header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
470 {
471 switch (header.response.opcode) {
472 case PROTOCOL_BINARY_CMD_GETKQ:
473 /*
474 * We didn't increment the response counter for the GETKQ packet
475 * (only the final NOOP), so we need to increment the counter again.
476 */
477 memcached_server_response_increment(instance);
478 /* fall through */
479 case PROTOCOL_BINARY_CMD_GETK: {
480 uint16_t keylen = header.response.keylen;
481 memcached_result_reset(result);
482 result->item_cas = header.response.cas;
483
484 if ((rc = memcached_safe_read(instance, &result->item_flags, sizeof(result->item_flags)))
485 != MEMCACHED_SUCCESS)
486 {
487 WATCHPOINT_ERROR(rc);
488 return MEMCACHED_UNKNOWN_READ_FAILURE;
489 }
490
491 result->item_flags = ntohl(result->item_flags);
492 bodylen -= header.response.extlen;
493
494 result->key_length = keylen;
495 if (memcached_failed(rc = memcached_safe_read(instance, result->item_key, keylen))) {
496 WATCHPOINT_ERROR(rc);
497 return MEMCACHED_UNKNOWN_READ_FAILURE;
498 }
499
500 // Only bother with doing this if key_length > 0
501 if (result->key_length) {
502 if (memcached_array_size(instance->root->_namespace)
503 and memcached_array_size(instance->root->_namespace) >= result->key_length)
504 {
505 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
506 }
507
508 if (memcached_array_size(instance->root->_namespace)) {
509 result->key_length -= memcached_array_size(instance->root->_namespace);
510 memmove(result->item_key,
511 result->item_key + memcached_array_size(instance->root->_namespace),
512 result->key_length);
513 }
514 }
515
516 bodylen -= keylen;
517 if (memcached_failed(memcached_string_check(&result->value, bodylen))) {
518 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
519 }
520
521 char *vptr = memcached_string_value_mutable(&result->value);
522 if (memcached_failed(rc = memcached_safe_read(instance, vptr, bodylen))) {
523 WATCHPOINT_ERROR(rc);
524 return MEMCACHED_UNKNOWN_READ_FAILURE;
525 }
526
527 memcached_string_set_length(&result->value, bodylen);
528 } break;
529
530 case PROTOCOL_BINARY_CMD_INCREMENT:
531 case PROTOCOL_BINARY_CMD_DECREMENT: {
532 if (bodylen != sizeof(uint64_t)) {
533 result->numeric_value = UINT64_MAX;
534 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
535 }
536
537 uint64_t val;
538 if ((rc = memcached_safe_read(instance, &val, sizeof(val))) != MEMCACHED_SUCCESS) {
539 result->numeric_value = UINT64_MAX;
540 return MEMCACHED_UNKNOWN_READ_FAILURE;
541 }
542
543 result->numeric_value = memcached_ntohll(val);
544 } break;
545
546 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: {
547 if (header.response.keylen || bodylen + 1 > buffer_length) {
548 return MEMCACHED_UNKNOWN_READ_FAILURE;
549 } else {
550 if ((rc = memcached_safe_read(instance, buffer, bodylen)) != MEMCACHED_SUCCESS) {
551 return MEMCACHED_UNKNOWN_READ_FAILURE;
552 }
553 }
554 } break;
555
556 case PROTOCOL_BINARY_CMD_VERSION: {
557 char version_buffer[32]; // @todo document this number
558 memset(version_buffer, 0, sizeof(version_buffer));
559
560 if (memcached_safe_read(instance, version_buffer, bodylen) != MEMCACHED_SUCCESS) {
561 return MEMCACHED_UNKNOWN_READ_FAILURE;
562 }
563
564 char *endptr;
565 errno = 0;
566 long int version = strtol(version_buffer, &endptr, 10);
567 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX
568 or version == 0) {
569 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
570 return memcached_set_error(
571 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
572 memcached_literal_param("strtol() failed to parse major version"));
573 }
574 instance->major_version = uint8_t(version);
575
576 endptr++;
577 errno = 0;
578 version = strtol(endptr, &endptr, 10);
579 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
580 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
581 return memcached_set_error(
582 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
583 memcached_literal_param("strtol() failed to parse minor version"));
584 }
585 instance->minor_version = uint8_t(version);
586
587 endptr++;
588 errno = 0;
589 version = strtol(endptr, &endptr, 10);
590 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
591 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
592 return memcached_set_error(
593 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
594 memcached_literal_param("strtol() failed to parse micro version"));
595 }
596 instance->micro_version = uint8_t(version);
597 } break;
598
599 case PROTOCOL_BINARY_CMD_TOUCH: {
600 rc = MEMCACHED_SUCCESS;
601 if (bodylen == 4) // The four byte read is a bug?
602 {
603 char touch_buffer[4]; // @todo document this number
604 rc = memcached_safe_read(instance, touch_buffer, sizeof(touch_buffer));
605 #if 0
606 fprintf(stderr, "%s:%d %d %d %d %d %.*s(%d)\n", __FILE__, __LINE__,
607 int(touch_buffer[0]),
608 int(touch_buffer[1]),
609 int(touch_buffer[2]),
610 int(touch_buffer[3]),
611 int(bodylen), touch_buffer, int(bodylen));
612 #endif
613 }
614 return memcached_set_error(*instance, rc, MEMCACHED_AT);
615 }
616
617 case PROTOCOL_BINARY_CMD_FLUSH:
618 case PROTOCOL_BINARY_CMD_QUIT:
619 case PROTOCOL_BINARY_CMD_SET:
620 case PROTOCOL_BINARY_CMD_ADD:
621 case PROTOCOL_BINARY_CMD_REPLACE:
622 case PROTOCOL_BINARY_CMD_APPEND:
623 case PROTOCOL_BINARY_CMD_PREPEND:
624 case PROTOCOL_BINARY_CMD_DELETE: {
625 WATCHPOINT_ASSERT(bodylen == 0);
626 return MEMCACHED_SUCCESS;
627 }
628
629 case PROTOCOL_BINARY_CMD_NOOP: {
630 WATCHPOINT_ASSERT(bodylen == 0);
631 return MEMCACHED_END;
632 }
633
634 case PROTOCOL_BINARY_CMD_STAT: {
635 if (bodylen == 0) {
636 return MEMCACHED_END;
637 } else if (bodylen + 1 > buffer_length) {
638 /* not enough space in buffer.. should not happen... */
639 return MEMCACHED_UNKNOWN_READ_FAILURE;
640 } else {
641 size_t keylen = header.response.keylen;
642 memset(buffer, 0, buffer_length);
643 if ((rc = memcached_safe_read(instance, buffer, keylen)) != MEMCACHED_SUCCESS
644 || (rc = memcached_safe_read(instance, buffer + keylen + 1, bodylen - keylen))
645 != MEMCACHED_SUCCESS)
646 {
647 WATCHPOINT_ERROR(rc);
648 return MEMCACHED_UNKNOWN_READ_FAILURE;
649 }
650 }
651 } break;
652
653 case PROTOCOL_BINARY_CMD_SASL_AUTH:
654 case PROTOCOL_BINARY_CMD_SASL_STEP: {
655 memcached_result_reset(result);
656 result->item_cas = header.response.cas;
657
658 if (memcached_string_check(&result->value, bodylen) != MEMCACHED_SUCCESS)
659 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
660
661 char *vptr = memcached_string_value_mutable(&result->value);
662 if ((rc = memcached_safe_read(instance, vptr, bodylen)) != MEMCACHED_SUCCESS) {
663 WATCHPOINT_ERROR(rc);
664 return MEMCACHED_UNKNOWN_READ_FAILURE;
665 }
666
667 memcached_string_set_length(&result->value, bodylen);
668 } break;
669 default: {
670 /* Command not implemented yet! */
671 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
672 }
673 }
674 } else if (header.response.bodylen) {
675 /* What should I do with the error message??? just discard it for now */
676 char hole[SMALL_STRING_LEN];
677 while (bodylen > 0) {
678 size_t nr = (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
679 if ((rc = memcached_safe_read(instance, hole, nr)) != MEMCACHED_SUCCESS) {
680 WATCHPOINT_ERROR(rc);
681 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
682 }
683 bodylen -= (uint32_t) nr;
684 }
685
686 /* This might be an error from one of the quiet commands.. if
687 * so, just throw it away and get the next one. What about creating
688 * a callback to the user with the error information?
689 */
690 switch (header.response.opcode) {
691 case PROTOCOL_BINARY_CMD_SETQ:
692 case PROTOCOL_BINARY_CMD_ADDQ:
693 case PROTOCOL_BINARY_CMD_REPLACEQ:
694 case PROTOCOL_BINARY_CMD_APPENDQ:
695 case PROTOCOL_BINARY_CMD_PREPENDQ:
696 return MEMCACHED_FETCH_NOTFINISHED;
697
698 default:
699 break;
700 }
701 }
702
703 rc = MEMCACHED_SUCCESS;
704 if (header.response.status) {
705 switch (header.response.status) {
706 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
707 rc = MEMCACHED_NOTFOUND;
708 break;
709
710 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
711 rc = MEMCACHED_DATA_EXISTS;
712 break;
713
714 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
715 rc = MEMCACHED_NOTSTORED;
716 break;
717
718 case PROTOCOL_BINARY_RESPONSE_E2BIG:
719 rc = MEMCACHED_E2BIG;
720 break;
721
722 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
723 rc = MEMCACHED_MEMORY_ALLOCATION_FAILURE;
724 break;
725
726 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
727 rc = MEMCACHED_AUTH_CONTINUE;
728 break;
729
730 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
731 rc = MEMCACHED_AUTH_FAILURE;
732 break;
733
734 case PROTOCOL_BINARY_RESPONSE_EINVAL:
735 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
736 default:
737 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
738 break;
739 }
740 }
741
742 return rc;
743 }
744
745 static memcached_return_t _read_one_response(memcached_instance_st *instance, char *buffer,
746 const size_t buffer_length,
747 memcached_result_st *result) {
748 memcached_server_response_decrement(instance);
749
750 if (result == NULL) {
751 Memcached *root = (Memcached *) instance->root;
752 result = &root->result;
753 }
754
755 memcached_return_t rc;
756 if (memcached_is_binary(instance->root)) {
757 do {
758 rc = binary_read_one_response(instance, buffer, buffer_length, result);
759 } while (rc == MEMCACHED_FETCH_NOTFINISHED);
760 } else {
761 rc = textual_read_one_response(instance, buffer, buffer_length, result);
762 }
763
764 if (memcached_fatal(rc)) {
765 memcached_io_reset(instance);
766 }
767
768 return rc;
769 }
770
771 memcached_return_t memcached_read_one_response(memcached_instance_st *instance,
772 memcached_result_st *result) {
773 char buffer[SMALL_STRING_LEN];
774
775 if (memcached_is_udp(instance->root)) {
776 return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
777 }
778
779 return _read_one_response(instance, buffer, sizeof(buffer), result);
780 }
781
782 memcached_return_t memcached_response(memcached_instance_st *instance,
783 memcached_result_st *result) {
784 char buffer[1024];
785
786 return memcached_response(instance, buffer, sizeof(buffer), result);
787 }
788
789 memcached_return_t memcached_response(memcached_instance_st *instance, char *buffer,
790 size_t buffer_length, memcached_result_st *result) {
791 if (memcached_is_udp(instance->root)) {
792 return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
793 }
794
795 /* We may have old commands in the buffer not sent, first purge */
796 if ((instance->root->flags.no_block) and (memcached_is_processing_input(instance->root) == false))
797 {
798 (void) memcached_io_write(instance);
799 }
800
801 /* Before going into loop wait to see if we have any IO waiting for us */
802 if (0) {
803 memcached_return_t read_rc = memcached_io_wait_for_read(instance);
804 fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, memcached_strerror(NULL, read_rc));
805 }
806
807 /*
808 * The previous implementation purged all pending requests and just
809 * returned the last one. Purge all pending messages to ensure backwards
810 * compatibility.
811 */
812 if (memcached_is_binary(instance->root) == false
813 and memcached_server_response_count(instance) > 1) {
814 memcached_result_st junked_result;
815 memcached_result_st *junked_result_ptr =
816 memcached_result_create(instance->root, &junked_result);
817
818 assert(junked_result_ptr);
819
820 while (memcached_server_response_count(instance) > 1) {
821 memcached_return_t rc =
822 _read_one_response(instance, buffer, buffer_length, junked_result_ptr);
823
824 // @TODO should we return an error on another but a bad read case?
825 if (memcached_fatal(rc)) {
826 memcached_result_free(junked_result_ptr);
827 return rc;
828 }
829 }
830 memcached_result_free(junked_result_ptr);
831 }
832
833 return _read_one_response(instance, buffer, buffer_length, result);
834 }