Update tests, improved UDP performance.
[m6w6/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 // Move past the basic error message and whitespace
252 char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
253 if (startptr[0] == ' ')
254 {
255 startptr++;
256 }
257
258 char *endptr= startptr;
259 while (*endptr != '\r' && *endptr != '\n') endptr++;
260
261 return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
262 }
263 // STORED
264 else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
265 {
266 return MEMCACHED_STORED;
267 }
268 }
269 break;
270
271 case 'D':
272 {
273 // DELETED
274 if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T' and buffer[5] == 'E' and buffer[6] == 'D')
275 {
276 return MEMCACHED_DELETED;
277 }
278 }
279 break;
280
281 case 'N':
282 {
283 // NOT_FOUND
284 if (buffer[1] == 'O' and buffer[2] == 'T'
285 and buffer[3] == '_'
286 and buffer[4] == 'F' and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
287 {
288 return MEMCACHED_NOTFOUND;
289 }
290 // NOT_STORED
291 else if (buffer[1] == 'O' and buffer[2] == 'T'
292 and buffer[3] == '_'
293 and buffer[4] == 'S' and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E' and buffer[9] == 'D')
294 {
295 return MEMCACHED_NOTSTORED;
296 }
297 }
298 break;
299
300 case 'E': /* PROTOCOL ERROR or END */
301 {
302 // END
303 if (buffer[1] == 'N' and buffer[2] == 'D')
304 {
305 return MEMCACHED_END;
306 }
307 #if 0
308 // PROTOCOL_ERROR
309 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'
310 and buffer[8] == '_'
311 and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
312 {
313 return MEMCACHED_PROTOCOL_ERROR;
314 }
315 #endif
316 // EXISTS
317 else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T' and buffer[5] == 'S')
318 {
319 return MEMCACHED_DATA_EXISTS;
320 }
321 }
322 break;
323
324 case 'T': /* TOUCHED */
325 {
326 // TOUCHED
327 if (buffer[1] == 'O' and buffer[2] == 'U' and buffer[3] == 'C' and buffer[4] == 'H' and buffer[5] == 'E' and buffer[6] == 'D')
328 {
329 return MEMCACHED_SUCCESS;
330 }
331 }
332 break;
333
334 case 'I': /* ITEM */
335 {
336 // ITEM
337 if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M')
338 {
339 /* We add back in one because we will need to search for END */
340 memcached_server_response_increment(ptr);
341 return MEMCACHED_ITEM;
342 }
343 }
344 break;
345
346 case 'C': /* CLIENT ERROR */
347 {
348 // CLIENT_ERROR
349 if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N' and buffer[5] == 'T'
350 and buffer[6] == '_'
351 and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
352 {
353 return MEMCACHED_CLIENT_ERROR;
354 }
355 }
356 break;
357
358 case '0': /* INCR/DECR response */
359 case '1': /* INCR/DECR response */
360 case '2': /* INCR/DECR response */
361 case '3': /* INCR/DECR response */
362 case '4': /* INCR/DECR response */
363 case '5': /* INCR/DECR response */
364 case '6': /* INCR/DECR response */
365 case '7': /* INCR/DECR response */
366 case '8': /* INCR/DECR response */
367 case '9': /* INCR/DECR response */
368 {
369 unsigned long long int auto_return_value= strtoull(buffer, (char **)NULL, 10);
370
371 if (auto_return_value == ULLONG_MAX and errno == ERANGE)
372 {
373 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
374 memcached_literal_param("Numeric response was out of range"));
375 }
376 else if (errno == EINVAL)
377 {
378 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
379 memcached_literal_param("Numeric response was out of range"));
380 }
381
382 numeric_value= uint64_t(auto_return_value);
383
384 WATCHPOINT_STRING(buffer);
385 return MEMCACHED_SUCCESS;
386 }
387
388 default:
389 break;
390 }
391
392 buffer[total_read]= 0;
393 #if 0
394 if (total_read >= sizeof("STORSTORED") -1)
395 {
396 fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
397 buffer, MEMCACHED_MAX_BUFFER, ptr->read_buffer);
398 assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
399 }
400 #endif
401 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
402 buffer, total_read);
403 }
404
405 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
406 char *buffer, const size_t buffer_length,
407 memcached_result_st *result)
408 {
409 memcached_return_t rc;
410 protocol_binary_response_header header;
411
412 if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
413 {
414 WATCHPOINT_ERROR(rc);
415 return rc;
416 }
417
418 if (header.response.magic != PROTOCOL_BINARY_RES)
419 {
420 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
421 }
422
423 /*
424 ** Convert the header to host local endian!
425 */
426 header.response.keylen= ntohs(header.response.keylen);
427 header.response.status= ntohs(header.response.status);
428 header.response.bodylen= ntohl(header.response.bodylen);
429 header.response.cas= memcached_ntohll(header.response.cas);
430 uint32_t bodylen= header.response.bodylen;
431
432 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS or
433 header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
434 {
435 switch (header.response.opcode)
436 {
437 case PROTOCOL_BINARY_CMD_GETKQ:
438 /*
439 * We didn't increment the response counter for the GETKQ packet
440 * (only the final NOOP), so we need to increment the counter again.
441 */
442 memcached_server_response_increment(ptr);
443 /* FALLTHROUGH */
444 case PROTOCOL_BINARY_CMD_GETK:
445 {
446 uint16_t keylen= header.response.keylen;
447 memcached_result_reset(result);
448 result->item_cas= header.response.cas;
449
450 if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
451 {
452 WATCHPOINT_ERROR(rc);
453 return MEMCACHED_UNKNOWN_READ_FAILURE;
454 }
455
456 result->item_flags= ntohl(result->item_flags);
457 bodylen -= header.response.extlen;
458
459 result->key_length= keylen;
460 if (memcached_failed(rc= memcached_safe_read(ptr, result->item_key, keylen)))
461 {
462 WATCHPOINT_ERROR(rc);
463 return MEMCACHED_UNKNOWN_READ_FAILURE;
464 }
465
466 // Only bother with doing this if key_length > 0
467 if (result->key_length)
468 {
469 if (memcached_array_size(ptr->root->_namespace) and memcached_array_size(ptr->root->_namespace) >= result->key_length)
470 {
471 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
472 }
473
474 if (memcached_array_size(ptr->root->_namespace))
475 {
476 result->key_length-= memcached_array_size(ptr->root->_namespace);
477 memmove(result->item_key, result->item_key +memcached_array_size(ptr->root->_namespace), result->key_length);
478 }
479 }
480
481 bodylen -= keylen;
482 if (memcached_failed(memcached_string_check(&result->value, bodylen)))
483 {
484 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
485 }
486
487 char *vptr= memcached_string_value_mutable(&result->value);
488 if (memcached_failed(rc= memcached_safe_read(ptr, vptr, bodylen)))
489 {
490 WATCHPOINT_ERROR(rc);
491 return MEMCACHED_UNKNOWN_READ_FAILURE;
492 }
493
494 memcached_string_set_length(&result->value, bodylen);
495 }
496 break;
497
498 case PROTOCOL_BINARY_CMD_INCREMENT:
499 case PROTOCOL_BINARY_CMD_DECREMENT:
500 {
501 if (bodylen != sizeof(uint64_t) or buffer_length != sizeof(uint64_t))
502 {
503 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
504 }
505
506 WATCHPOINT_ASSERT(bodylen == buffer_length);
507 uint64_t val;
508 if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
509 {
510 WATCHPOINT_ERROR(rc);
511 return MEMCACHED_UNKNOWN_READ_FAILURE;
512 }
513
514 val= memcached_ntohll(val);
515 memcpy(buffer, &val, sizeof(val));
516 }
517 break;
518
519 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
520 case PROTOCOL_BINARY_CMD_VERSION:
521 {
522 memset(buffer, 0, buffer_length);
523 if (bodylen >= buffer_length)
524 {
525 /* not enough space in buffer.. should not happen... */
526 return MEMCACHED_UNKNOWN_READ_FAILURE;
527 }
528 else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
529 {
530 WATCHPOINT_ERROR(rc);
531 return MEMCACHED_UNKNOWN_READ_FAILURE;
532 }
533 }
534 break;
535 case PROTOCOL_BINARY_CMD_FLUSH:
536 case PROTOCOL_BINARY_CMD_QUIT:
537 case PROTOCOL_BINARY_CMD_SET:
538 case PROTOCOL_BINARY_CMD_ADD:
539 case PROTOCOL_BINARY_CMD_REPLACE:
540 case PROTOCOL_BINARY_CMD_APPEND:
541 case PROTOCOL_BINARY_CMD_PREPEND:
542 case PROTOCOL_BINARY_CMD_DELETE:
543 case PROTOCOL_BINARY_CMD_TOUCH:
544 {
545 WATCHPOINT_ASSERT(bodylen == 0);
546 return MEMCACHED_SUCCESS;
547 }
548
549 case PROTOCOL_BINARY_CMD_NOOP:
550 {
551 WATCHPOINT_ASSERT(bodylen == 0);
552 return MEMCACHED_END;
553 }
554
555 case PROTOCOL_BINARY_CMD_STAT:
556 {
557 if (bodylen == 0)
558 {
559 return MEMCACHED_END;
560 }
561 else if (bodylen + 1 > buffer_length)
562 {
563 /* not enough space in buffer.. should not happen... */
564 return MEMCACHED_UNKNOWN_READ_FAILURE;
565 }
566 else
567 {
568 size_t keylen= header.response.keylen;
569 memset(buffer, 0, buffer_length);
570 if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
571 (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
572 {
573 WATCHPOINT_ERROR(rc);
574 return MEMCACHED_UNKNOWN_READ_FAILURE;
575 }
576 }
577 }
578 break;
579
580 case PROTOCOL_BINARY_CMD_SASL_AUTH:
581 case PROTOCOL_BINARY_CMD_SASL_STEP:
582 {
583 memcached_result_reset(result);
584 result->item_cas= header.response.cas;
585
586 if (memcached_string_check(&result->value,
587 bodylen) != MEMCACHED_SUCCESS)
588 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
589
590 char *vptr= memcached_string_value_mutable(&result->value);
591 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
592 {
593 WATCHPOINT_ERROR(rc);
594 return MEMCACHED_UNKNOWN_READ_FAILURE;
595 }
596
597 memcached_string_set_length(&result->value, bodylen);
598 }
599 break;
600 default:
601 {
602 /* Command not implemented yet! */
603 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
604 }
605 }
606 }
607 else if (header.response.bodylen)
608 {
609 /* What should I do with the error message??? just discard it for now */
610 char hole[SMALL_STRING_LEN];
611 while (bodylen > 0)
612 {
613 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
614 if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
615 {
616 WATCHPOINT_ERROR(rc);
617 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
618 }
619 bodylen-= (uint32_t) nr;
620 }
621
622 /* This might be an error from one of the quiet commands.. if
623 * so, just throw it away and get the next one. What about creating
624 * a callback to the user with the error information?
625 */
626 switch (header.response.opcode)
627 {
628 case PROTOCOL_BINARY_CMD_SETQ:
629 case PROTOCOL_BINARY_CMD_ADDQ:
630 case PROTOCOL_BINARY_CMD_REPLACEQ:
631 case PROTOCOL_BINARY_CMD_APPENDQ:
632 case PROTOCOL_BINARY_CMD_PREPENDQ:
633 return binary_read_one_response(ptr, buffer, buffer_length, result);
634
635 default:
636 break;
637 }
638 }
639
640 rc= MEMCACHED_SUCCESS;
641 if (header.response.status != 0)
642 {
643 switch (header.response.status)
644 {
645 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
646 rc= MEMCACHED_NOTFOUND;
647 break;
648
649 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
650 rc= MEMCACHED_DATA_EXISTS;
651 break;
652
653 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
654 rc= MEMCACHED_NOTSTORED;
655 break;
656
657 case PROTOCOL_BINARY_RESPONSE_E2BIG:
658 rc= MEMCACHED_E2BIG;
659 break;
660
661 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
662 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
663 break;
664
665 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
666 rc= MEMCACHED_AUTH_CONTINUE;
667 break;
668
669 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
670 rc= MEMCACHED_AUTH_FAILURE;
671 break;
672
673 case PROTOCOL_BINARY_RESPONSE_EINVAL:
674 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
675 default:
676 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
677 break;
678 }
679 }
680
681 return rc;
682 }
683
684 static memcached_return_t _read_one_response(memcached_server_write_instance_st ptr,
685 char *buffer, const size_t buffer_length,
686 memcached_result_st *result,
687 uint64_t& numeric_value)
688 {
689 memcached_server_response_decrement(ptr);
690
691 if (result == NULL)
692 {
693 memcached_st *root= (memcached_st *)ptr->root;
694 result = &root->result;
695 }
696
697 memcached_return_t rc;
698 if (memcached_is_binary(ptr->root))
699 {
700 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
701 }
702 else
703 {
704 rc= textual_read_one_response(ptr, buffer, buffer_length, result, numeric_value);
705 assert(rc != MEMCACHED_PROTOCOL_ERROR);
706 }
707
708 if (rc == MEMCACHED_UNKNOWN_READ_FAILURE or
709 rc == MEMCACHED_READ_FAILURE or
710 rc == MEMCACHED_PROTOCOL_ERROR or
711 rc == MEMCACHED_CLIENT_ERROR or
712 rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
713 {
714 memcached_io_reset(ptr);
715 }
716
717 return rc;
718 }
719
720 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
721 memcached_result_st *result)
722 {
723 uint64_t numeric_value;
724 char buffer[SMALL_STRING_LEN];
725
726 if (memcached_is_udp(ptr->root))
727 {
728 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
729 }
730
731
732 return _read_one_response(ptr, buffer, sizeof(buffer), result, numeric_value);
733 }
734
735 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
736 char *buffer, size_t buffer_length,
737 memcached_result_st *result)
738 {
739 uint64_t numeric_value;
740
741 return memcached_response(ptr, buffer, buffer_length, result, numeric_value);
742 }
743
744 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
745 char *buffer, size_t buffer_length,
746 memcached_result_st *result,
747 uint64_t& numeric_value)
748 {
749 if (memcached_is_udp(ptr->root))
750 {
751 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
752 }
753
754 /* We may have old commands in the buffer not set, first purge */
755 if ((ptr->root->flags.no_block) and (memcached_is_processing_input(ptr->root) == false))
756 {
757 (void)memcached_io_write(ptr);
758 }
759
760 /*
761 * The previous implementation purged all pending requests and just
762 * returned the last one. Purge all pending messages to ensure backwards
763 * compatibility.
764 */
765 if (memcached_is_binary(ptr->root) == false and memcached_server_response_count(ptr) > 1)
766 {
767 memcached_result_st junked_result;
768 memcached_result_st *junked_result_ptr= memcached_result_create(ptr->root, &junked_result);
769
770 assert(junked_result_ptr);
771
772 while (memcached_server_response_count(ptr) > 1)
773 {
774 memcached_return_t rc= _read_one_response(ptr, buffer, buffer_length, junked_result_ptr, numeric_value);
775
776 // @TODO should we return an error on another but a bad read case?
777 if (rc != MEMCACHED_END and
778 rc != MEMCACHED_STORED and
779 rc != MEMCACHED_SUCCESS and
780 rc != MEMCACHED_STAT and
781 rc != MEMCACHED_DELETED and
782 rc != MEMCACHED_NOTFOUND and
783 rc != MEMCACHED_NOTSTORED and
784 rc != MEMCACHED_DATA_EXISTS)
785 {
786 memcached_result_free(junked_result_ptr);
787 return rc;
788 }
789 }
790 memcached_result_free(junked_result_ptr);
791 }
792
793 return _read_one_response(ptr, buffer, buffer_length, result, numeric_value);
794 }