This fixes memcached_dump(), and creates a couple of additional tests for memcached_s...
[awesomized/libmemcached] / libmemcached / response.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2009 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38 #include <libmemcached/common.h>
39 #include <libmemcached/string.hpp>
40
41 static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr,
42 char *buffer,
43 memcached_result_st *result)
44 {
45 char *next_ptr;
46 ssize_t read_length= 0;
47 size_t value_length;
48
49 WATCHPOINT_ASSERT(ptr->root);
50 char *end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
51
52 memcached_result_reset(result);
53
54 char *string_ptr= buffer;
55 string_ptr+= 6; /* "VALUE " */
56
57
58 /* We load the key */
59 {
60 char *key;
61 size_t prefix_length;
62
63 key= result->item_key;
64 result->key_length= 0;
65
66 for (prefix_length= memcached_array_size(ptr->root->_namespace); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
67 {
68 if (prefix_length == 0)
69 {
70 *key= *string_ptr;
71 key++;
72 result->key_length++;
73 }
74 else
75 prefix_length--;
76 }
77 result->item_key[result->key_length]= 0;
78 }
79
80 if (end_ptr == string_ptr)
81 {
82 goto read_error;
83 }
84
85 /* Flags fetch move past space */
86 string_ptr++;
87 if (end_ptr == string_ptr)
88 {
89 goto read_error;
90 }
91
92 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
93 result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10);
94
95 if (end_ptr == string_ptr)
96 {
97 goto read_error;
98 }
99
100 /* Length fetch move past space*/
101 string_ptr++;
102 if (end_ptr == string_ptr)
103 {
104 goto read_error;
105 }
106
107 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
108 value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);
109
110 if (end_ptr == string_ptr)
111 {
112 goto read_error;
113 }
114
115 /* Skip spaces */
116 if (*string_ptr == '\r')
117 {
118 /* Skip past the \r\n */
119 string_ptr+= 2;
120 }
121 else
122 {
123 string_ptr++;
124 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
125 result->item_cas= strtoull(next_ptr, &string_ptr, 10);
126 }
127
128 if (end_ptr < string_ptr)
129 {
130 goto read_error;
131 }
132
133 /* We add two bytes so that we can walk the \r\n */
134 if (memcached_failed(memcached_string_check(&result->value, value_length +2)))
135 {
136 return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
137 }
138
139 {
140 char *value_ptr= memcached_string_value_mutable(&result->value);
141 /*
142 We read the \r\n into the string since not doing so is more
143 cycles then the waster of memory to do so.
144
145 We are null terminating through, which will most likely make
146 some people lazy about using the return length.
147 */
148 size_t to_read= (value_length) + 2;
149 memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, read_length);
150 if (memcached_failed(rrc) and rrc == MEMCACHED_IN_PROGRESS)
151 {
152 memcached_quit_server(ptr, true);
153 return memcached_set_error(*ptr, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
154 }
155 else if (memcached_failed(rrc))
156 {
157 return rrc;
158 }
159 }
160
161 if (read_length != (ssize_t)(value_length + 2))
162 {
163 goto read_error;
164 }
165
166 /* This next bit blows the API, but this is internal....*/
167 {
168 char *char_ptr;
169 char_ptr= memcached_string_value_mutable(&result->value);;
170 char_ptr[value_length]= 0;
171 char_ptr[value_length +1]= 0;
172 memcached_string_set_length(&result->value, value_length);
173 }
174
175 return MEMCACHED_SUCCESS;
176
177 read_error:
178 memcached_io_reset(ptr);
179
180 return MEMCACHED_PARTIAL_READ;
181 }
182
183 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
184 char *buffer, const size_t buffer_length,
185 memcached_result_st *result,
186 uint64_t& numeric_value)
187 {
188 numeric_value= UINT64_MAX;
189 size_t total_read;
190 memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
191
192 if (memcached_failed(rc))
193 {
194 return rc;
195 }
196 assert(total_read);
197
198 switch(buffer[0])
199 {
200 case 'V':
201 {
202 // VALUE
203 if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
204 {
205 /* We add back in one because we will need to search for END */
206 memcached_server_response_increment(ptr);
207 return textual_value_fetch(ptr, buffer, result);
208 }
209 // VERSION
210 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 */
211 {
212 return MEMCACHED_SUCCESS;
213 }
214 }
215 break;
216
217 case 'O':
218 {
219 // OK
220 if (buffer[1] == 'K')
221 {
222 return MEMCACHED_SUCCESS;
223 }
224 }
225 break;
226
227 case 'S':
228 {
229 // STAT
230 if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */
231 {
232 memcached_server_response_increment(ptr);
233 return MEMCACHED_STAT;
234 }
235 // SERVER_ERROR
236 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E' and buffer[5] == 'R'
237 and buffer[6] == '_'
238 and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R' )
239 {
240 if (total_read == memcached_literal_param_size("SERVER_ERROR"))
241 {
242 return MEMCACHED_SERVER_ERROR;
243 }
244
245 if (total_read >= memcached_literal_param_size("SERVER_ERROR object too large for cache") and
246 (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
247 {
248 return MEMCACHED_E2BIG;
249 }
250
251 if (total_read >= memcached_literal_param_size("SERVER_ERROR out of memory storing object") and
252 (memcmp(buffer, memcached_literal_param("SERVER_ERROR out of memory storing object")) == 0))
253 {
254 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
255 }
256
257 // Move past the basic error message and whitespace
258 char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
259 if (startptr[0] == ' ')
260 {
261 startptr++;
262 }
263
264 char *endptr= startptr;
265 while (*endptr != '\r' && *endptr != '\n') endptr++;
266
267 return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
268 }
269 // STORED
270 else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
271 {
272 return MEMCACHED_STORED;
273 }
274 }
275 break;
276
277 case 'D':
278 {
279 // DELETED
280 if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T' and buffer[5] == 'E' and buffer[6] == 'D')
281 {
282 return MEMCACHED_DELETED;
283 }
284 }
285 break;
286
287 case 'N':
288 {
289 // NOT_FOUND
290 if (buffer[1] == 'O' and buffer[2] == 'T'
291 and buffer[3] == '_'
292 and buffer[4] == 'F' and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
293 {
294 return MEMCACHED_NOTFOUND;
295 }
296 // NOT_STORED
297 else if (buffer[1] == 'O' and buffer[2] == 'T'
298 and buffer[3] == '_'
299 and buffer[4] == 'S' and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E' and buffer[9] == 'D')
300 {
301 return MEMCACHED_NOTSTORED;
302 }
303 }
304 break;
305
306 case 'E': /* PROTOCOL ERROR or END */
307 {
308 // END
309 if (buffer[1] == 'N' and buffer[2] == 'D')
310 {
311 return MEMCACHED_END;
312 }
313 #if 0
314 // PROTOCOL_ERROR
315 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'
316 and buffer[8] == '_'
317 and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
318 {
319 return MEMCACHED_PROTOCOL_ERROR;
320 }
321 #endif
322 // ERROR
323 else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
324 {
325 return MEMCACHED_ERROR;
326 }
327 // EXISTS
328 else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T' and buffer[5] == 'S')
329 {
330 return MEMCACHED_DATA_EXISTS;
331 }
332 }
333 break;
334
335 case 'T': /* TOUCHED */
336 {
337 // TOUCHED
338 if (buffer[1] == 'O' and buffer[2] == 'U' and buffer[3] == 'C' and buffer[4] == 'H' and buffer[5] == 'E' and buffer[6] == 'D')
339 {
340 return MEMCACHED_SUCCESS;
341 }
342 }
343 break;
344
345 case 'I': /* ITEM */
346 {
347 // ITEM
348 if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M')
349 {
350 /* We add back in one because we will need to search for END */
351 memcached_server_response_increment(ptr);
352 return MEMCACHED_ITEM;
353 }
354 }
355 break;
356
357 case 'C': /* CLIENT ERROR */
358 {
359 // CLIENT_ERROR
360 if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N' and buffer[5] == 'T'
361 and buffer[6] == '_'
362 and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
363 {
364 return MEMCACHED_CLIENT_ERROR;
365 }
366 }
367 break;
368
369 case '0': /* INCR/DECR response */
370 case '1': /* INCR/DECR response */
371 case '2': /* INCR/DECR response */
372 case '3': /* INCR/DECR response */
373 case '4': /* INCR/DECR response */
374 case '5': /* INCR/DECR response */
375 case '6': /* INCR/DECR response */
376 case '7': /* INCR/DECR response */
377 case '8': /* INCR/DECR response */
378 case '9': /* INCR/DECR response */
379 {
380 unsigned long long int auto_return_value= strtoull(buffer, (char **)NULL, 10);
381
382 if (auto_return_value == ULLONG_MAX and errno == ERANGE)
383 {
384 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
385 memcached_literal_param("Numeric response was out of range"));
386 }
387 else if (errno == EINVAL)
388 {
389 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
390 memcached_literal_param("Numeric response was out of range"));
391 }
392
393 numeric_value= uint64_t(auto_return_value);
394
395 WATCHPOINT_STRING(buffer);
396 return MEMCACHED_SUCCESS;
397 }
398
399 default:
400 break;
401 }
402
403 buffer[total_read]= 0;
404 #if 0
405 if (total_read >= sizeof("STORSTORED") -1)
406 {
407 fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
408 buffer, MEMCACHED_MAX_BUFFER, ptr->read_buffer);
409 assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
410 }
411 #endif
412 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
413 buffer, total_read);
414 }
415
416 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
417 char *buffer, const size_t buffer_length,
418 memcached_result_st *result)
419 {
420 memcached_return_t rc;
421 protocol_binary_response_header header;
422
423 if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
424 {
425 WATCHPOINT_ERROR(rc);
426 return rc;
427 }
428
429 if (header.response.magic != PROTOCOL_BINARY_RES)
430 {
431 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
432 }
433
434 /*
435 ** Convert the header to host local endian!
436 */
437 header.response.keylen= ntohs(header.response.keylen);
438 header.response.status= ntohs(header.response.status);
439 header.response.bodylen= ntohl(header.response.bodylen);
440 header.response.cas= memcached_ntohll(header.response.cas);
441 uint32_t bodylen= header.response.bodylen;
442
443 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS or
444 header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
445 {
446 switch (header.response.opcode)
447 {
448 case PROTOCOL_BINARY_CMD_GETKQ:
449 /*
450 * We didn't increment the response counter for the GETKQ packet
451 * (only the final NOOP), so we need to increment the counter again.
452 */
453 memcached_server_response_increment(ptr);
454 /* FALLTHROUGH */
455 case PROTOCOL_BINARY_CMD_GETK:
456 {
457 uint16_t keylen= header.response.keylen;
458 memcached_result_reset(result);
459 result->item_cas= header.response.cas;
460
461 if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
462 {
463 WATCHPOINT_ERROR(rc);
464 return MEMCACHED_UNKNOWN_READ_FAILURE;
465 }
466
467 result->item_flags= ntohl(result->item_flags);
468 bodylen -= header.response.extlen;
469
470 result->key_length= keylen;
471 if (memcached_failed(rc= memcached_safe_read(ptr, result->item_key, keylen)))
472 {
473 WATCHPOINT_ERROR(rc);
474 return MEMCACHED_UNKNOWN_READ_FAILURE;
475 }
476
477 // Only bother with doing this if key_length > 0
478 if (result->key_length)
479 {
480 if (memcached_array_size(ptr->root->_namespace) and memcached_array_size(ptr->root->_namespace) >= result->key_length)
481 {
482 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
483 }
484
485 if (memcached_array_size(ptr->root->_namespace))
486 {
487 result->key_length-= memcached_array_size(ptr->root->_namespace);
488 memmove(result->item_key, result->item_key +memcached_array_size(ptr->root->_namespace), result->key_length);
489 }
490 }
491
492 bodylen -= keylen;
493 if (memcached_failed(memcached_string_check(&result->value, bodylen)))
494 {
495 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
496 }
497
498 char *vptr= memcached_string_value_mutable(&result->value);
499 if (memcached_failed(rc= memcached_safe_read(ptr, vptr, bodylen)))
500 {
501 WATCHPOINT_ERROR(rc);
502 return MEMCACHED_UNKNOWN_READ_FAILURE;
503 }
504
505 memcached_string_set_length(&result->value, bodylen);
506 }
507 break;
508
509 case PROTOCOL_BINARY_CMD_INCREMENT:
510 case PROTOCOL_BINARY_CMD_DECREMENT:
511 {
512 if (bodylen != sizeof(uint64_t) or buffer_length != sizeof(uint64_t))
513 {
514 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
515 }
516
517 WATCHPOINT_ASSERT(bodylen == buffer_length);
518 uint64_t val;
519 if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
520 {
521 WATCHPOINT_ERROR(rc);
522 return MEMCACHED_UNKNOWN_READ_FAILURE;
523 }
524
525 val= memcached_ntohll(val);
526 memcpy(buffer, &val, sizeof(val));
527 }
528 break;
529
530 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
531 case PROTOCOL_BINARY_CMD_VERSION:
532 {
533 memset(buffer, 0, buffer_length);
534 if (bodylen >= buffer_length)
535 {
536 /* not enough space in buffer.. should not happen... */
537 return MEMCACHED_UNKNOWN_READ_FAILURE;
538 }
539 else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
540 {
541 WATCHPOINT_ERROR(rc);
542 return MEMCACHED_UNKNOWN_READ_FAILURE;
543 }
544 }
545 break;
546 case PROTOCOL_BINARY_CMD_FLUSH:
547 case PROTOCOL_BINARY_CMD_QUIT:
548 case PROTOCOL_BINARY_CMD_SET:
549 case PROTOCOL_BINARY_CMD_ADD:
550 case PROTOCOL_BINARY_CMD_REPLACE:
551 case PROTOCOL_BINARY_CMD_APPEND:
552 case PROTOCOL_BINARY_CMD_PREPEND:
553 case PROTOCOL_BINARY_CMD_DELETE:
554 case PROTOCOL_BINARY_CMD_TOUCH:
555 {
556 WATCHPOINT_ASSERT(bodylen == 0);
557 return MEMCACHED_SUCCESS;
558 }
559
560 case PROTOCOL_BINARY_CMD_NOOP:
561 {
562 WATCHPOINT_ASSERT(bodylen == 0);
563 return MEMCACHED_END;
564 }
565
566 case PROTOCOL_BINARY_CMD_STAT:
567 {
568 if (bodylen == 0)
569 {
570 return MEMCACHED_END;
571 }
572 else if (bodylen + 1 > buffer_length)
573 {
574 /* not enough space in buffer.. should not happen... */
575 return MEMCACHED_UNKNOWN_READ_FAILURE;
576 }
577 else
578 {
579 size_t keylen= header.response.keylen;
580 memset(buffer, 0, buffer_length);
581 if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
582 (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
583 {
584 WATCHPOINT_ERROR(rc);
585 return MEMCACHED_UNKNOWN_READ_FAILURE;
586 }
587 }
588 }
589 break;
590
591 case PROTOCOL_BINARY_CMD_SASL_AUTH:
592 case PROTOCOL_BINARY_CMD_SASL_STEP:
593 {
594 memcached_result_reset(result);
595 result->item_cas= header.response.cas;
596
597 if (memcached_string_check(&result->value,
598 bodylen) != MEMCACHED_SUCCESS)
599 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
600
601 char *vptr= memcached_string_value_mutable(&result->value);
602 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
603 {
604 WATCHPOINT_ERROR(rc);
605 return MEMCACHED_UNKNOWN_READ_FAILURE;
606 }
607
608 memcached_string_set_length(&result->value, bodylen);
609 }
610 break;
611 default:
612 {
613 /* Command not implemented yet! */
614 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
615 }
616 }
617 }
618 else if (header.response.bodylen)
619 {
620 /* What should I do with the error message??? just discard it for now */
621 char hole[SMALL_STRING_LEN];
622 while (bodylen > 0)
623 {
624 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
625 if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
626 {
627 WATCHPOINT_ERROR(rc);
628 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
629 }
630 bodylen-= (uint32_t) nr;
631 }
632
633 /* This might be an error from one of the quiet commands.. if
634 * so, just throw it away and get the next one. What about creating
635 * a callback to the user with the error information?
636 */
637 switch (header.response.opcode)
638 {
639 case PROTOCOL_BINARY_CMD_SETQ:
640 case PROTOCOL_BINARY_CMD_ADDQ:
641 case PROTOCOL_BINARY_CMD_REPLACEQ:
642 case PROTOCOL_BINARY_CMD_APPENDQ:
643 case PROTOCOL_BINARY_CMD_PREPENDQ:
644 return binary_read_one_response(ptr, buffer, buffer_length, result);
645
646 default:
647 break;
648 }
649 }
650
651 rc= MEMCACHED_SUCCESS;
652 if (header.response.status != 0)
653 {
654 switch (header.response.status)
655 {
656 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
657 rc= MEMCACHED_NOTFOUND;
658 break;
659
660 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
661 rc= MEMCACHED_DATA_EXISTS;
662 break;
663
664 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
665 rc= MEMCACHED_NOTSTORED;
666 break;
667
668 case PROTOCOL_BINARY_RESPONSE_E2BIG:
669 rc= MEMCACHED_E2BIG;
670 break;
671
672 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
673 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
674 break;
675
676 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
677 rc= MEMCACHED_AUTH_CONTINUE;
678 break;
679
680 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
681 rc= MEMCACHED_AUTH_FAILURE;
682 break;
683
684 case PROTOCOL_BINARY_RESPONSE_EINVAL:
685 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
686 default:
687 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
688 break;
689 }
690 }
691
692 return rc;
693 }
694
695 static memcached_return_t _read_one_response(memcached_server_write_instance_st ptr,
696 char *buffer, const size_t buffer_length,
697 memcached_result_st *result,
698 uint64_t& numeric_value)
699 {
700 memcached_server_response_decrement(ptr);
701
702 if (result == NULL)
703 {
704 memcached_st *root= (memcached_st *)ptr->root;
705 result = &root->result;
706 }
707
708 memcached_return_t rc;
709 if (memcached_is_binary(ptr->root))
710 {
711 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
712 }
713 else
714 {
715 rc= textual_read_one_response(ptr, buffer, buffer_length, result, numeric_value);
716 assert(rc != MEMCACHED_PROTOCOL_ERROR);
717 }
718
719 if (memcached_fatal(rc))
720 {
721 memcached_io_reset(ptr);
722 }
723
724 return rc;
725 }
726
727 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
728 memcached_result_st *result)
729 {
730 uint64_t numeric_value;
731 char buffer[SMALL_STRING_LEN];
732
733 if (memcached_is_udp(ptr->root))
734 {
735 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
736 }
737
738
739 return _read_one_response(ptr, buffer, sizeof(buffer), result, numeric_value);
740 }
741
742 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
743 char *buffer, size_t buffer_length,
744 memcached_result_st *result)
745 {
746 uint64_t numeric_value;
747
748 return memcached_response(ptr, buffer, buffer_length, result, numeric_value);
749 }
750
751 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
752 char *buffer, size_t buffer_length,
753 memcached_result_st *result,
754 uint64_t& numeric_value)
755 {
756 if (memcached_is_udp(ptr->root))
757 {
758 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
759 }
760
761 /* We may have old commands in the buffer not set, first purge */
762 if ((ptr->root->flags.no_block) and (memcached_is_processing_input(ptr->root) == false))
763 {
764 (void)memcached_io_write(ptr);
765 }
766
767 /*
768 * The previous implementation purged all pending requests and just
769 * returned the last one. Purge all pending messages to ensure backwards
770 * compatibility.
771 */
772 if (memcached_is_binary(ptr->root) == false and memcached_server_response_count(ptr) > 1)
773 {
774 memcached_result_st junked_result;
775 memcached_result_st *junked_result_ptr= memcached_result_create(ptr->root, &junked_result);
776
777 assert(junked_result_ptr);
778
779 while (memcached_server_response_count(ptr) > 1)
780 {
781 memcached_return_t rc= _read_one_response(ptr, buffer, buffer_length, junked_result_ptr, numeric_value);
782
783 // @TODO should we return an error on another but a bad read case?
784 if (
785 rc != MEMCACHED_DATA_EXISTS and
786 rc != MEMCACHED_DELETED and
787 rc != MEMCACHED_E2BIG and
788 rc != MEMCACHED_END and
789 rc != MEMCACHED_ERROR and
790 rc != MEMCACHED_ITEM and
791 rc != MEMCACHED_NOTFOUND and
792 rc != MEMCACHED_NOTSTORED and
793 rc != MEMCACHED_SERVER_ERROR and
794 rc != MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE and
795 rc != MEMCACHED_STAT and
796 rc != MEMCACHED_STORED and
797 rc != MEMCACHED_SUCCESS and
798 rc != MEMCACHED_VALUE
799 )
800 {
801 memcached_result_free(junked_result_ptr);
802 return rc;
803 }
804 }
805 memcached_result_free(junked_result_ptr);
806 }
807
808 return _read_one_response(ptr, buffer, buffer_length, result, numeric_value);
809 }