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