Merge in trunk
[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 {
187 size_t total_read;
188 memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
189
190 if (memcached_failed(rc))
191 {
192 return rc;
193 }
194 assert(total_read);
195
196 switch(buffer[0])
197 {
198 case 'V':
199 {
200 // VALUE
201 if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
202 {
203 /* We add back in one because we will need to search for END */
204 memcached_server_response_increment(ptr);
205 return textual_value_fetch(ptr, buffer, result);
206 }
207 // VERSION
208 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 */
209 {
210 return MEMCACHED_SUCCESS;
211 }
212 }
213 break;
214
215 case 'O':
216 {
217 // OK
218 if (buffer[1] == 'K')
219 {
220 return MEMCACHED_SUCCESS;
221 }
222 }
223 break;
224
225 case 'S':
226 {
227 // STAT
228 if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */
229 {
230 memcached_server_response_increment(ptr);
231 return MEMCACHED_STAT;
232 }
233 // SERVER_ERROR
234 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E' and buffer[5] == 'R'
235 and buffer[6] == '_'
236 and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R' )
237 {
238 if (total_read == memcached_literal_param_size("SERVER_ERROR"))
239 {
240 return MEMCACHED_SERVER_ERROR;
241 }
242
243 if (total_read >= memcached_literal_param_size("SERVER_ERROR object too large for cache") and
244 (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
245 {
246 return MEMCACHED_E2BIG;
247 }
248
249 if (total_read >= memcached_literal_param_size("SERVER_ERROR out of memory storing object") and
250 (memcmp(buffer, memcached_literal_param("SERVER_ERROR out of memory storing object")) == 0))
251 {
252 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
253 }
254
255 // Move past the basic error message and whitespace
256 char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
257 if (startptr[0] == ' ')
258 {
259 startptr++;
260 }
261
262 char *endptr= startptr;
263 while (*endptr != '\r' && *endptr != '\n') endptr++;
264
265 return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
266 }
267 // STORED
268 else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
269 {
270 return MEMCACHED_STORED;
271 }
272 }
273 break;
274
275 case 'D':
276 {
277 // DELETED
278 if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T' and buffer[5] == 'E' and buffer[6] == 'D')
279 {
280 return MEMCACHED_DELETED;
281 }
282 }
283 break;
284
285 case 'N':
286 {
287 // NOT_FOUND
288 if (buffer[1] == 'O' and buffer[2] == 'T'
289 and buffer[3] == '_'
290 and buffer[4] == 'F' and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
291 {
292 return MEMCACHED_NOTFOUND;
293 }
294 // NOT_STORED
295 else if (buffer[1] == 'O' and buffer[2] == 'T'
296 and buffer[3] == '_'
297 and buffer[4] == 'S' and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E' and buffer[9] == 'D')
298 {
299 return MEMCACHED_NOTSTORED;
300 }
301 }
302 break;
303
304 case 'E': /* PROTOCOL ERROR or END */
305 {
306 // END
307 if (buffer[1] == 'N' and buffer[2] == 'D')
308 {
309 return MEMCACHED_END;
310 }
311 #if 0
312 // PROTOCOL_ERROR
313 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'
314 and buffer[8] == '_'
315 and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
316 {
317 return MEMCACHED_PROTOCOL_ERROR;
318 }
319 #endif
320 // ERROR
321 else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
322 {
323 return MEMCACHED_ERROR;
324 }
325 // EXISTS
326 else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T' and buffer[5] == 'S')
327 {
328 return MEMCACHED_DATA_EXISTS;
329 }
330 }
331 break;
332
333 case 'T': /* TOUCHED */
334 {
335 // TOUCHED
336 if (buffer[1] == 'O' and buffer[2] == 'U' and buffer[3] == 'C' and buffer[4] == 'H' and buffer[5] == 'E' and buffer[6] == 'D')
337 {
338 return MEMCACHED_SUCCESS;
339 }
340 }
341 break;
342
343 case 'I': /* ITEM */
344 {
345 // ITEM
346 if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M')
347 {
348 /* We add back in one because we will need to search for END */
349 memcached_server_response_increment(ptr);
350 return MEMCACHED_ITEM;
351 }
352 }
353 break;
354
355 case 'C': /* CLIENT ERROR */
356 {
357 // CLIENT_ERROR
358 if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N' and buffer[5] == 'T'
359 and buffer[6] == '_'
360 and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
361 {
362 return MEMCACHED_CLIENT_ERROR;
363 }
364 }
365 break;
366
367 case '0': /* INCR/DECR response */
368 case '1': /* INCR/DECR response */
369 case '2': /* INCR/DECR response */
370 case '3': /* INCR/DECR response */
371 case '4': /* INCR/DECR response */
372 case '5': /* INCR/DECR response */
373 case '6': /* INCR/DECR response */
374 case '7': /* INCR/DECR response */
375 case '8': /* INCR/DECR response */
376 case '9': /* INCR/DECR response */
377 {
378 unsigned long long int auto_return_value= strtoull(buffer, (char **)NULL, 10);
379
380 if (auto_return_value == ULLONG_MAX and errno == ERANGE)
381 {
382 result->numeric_value= UINT64_MAX;
383 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
384 memcached_literal_param("Numeric response was out of range"));
385 }
386 else if (errno == EINVAL)
387 {
388 result->numeric_value= UINT64_MAX;
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 result->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))
513 {
514 result->numeric_value= UINT64_MAX;
515 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
516 }
517
518 uint64_t val;
519 if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
520 {
521 result->numeric_value= UINT64_MAX;
522 return MEMCACHED_UNKNOWN_READ_FAILURE;
523 }
524
525 result->numeric_value= memcached_ntohll(val);
526 }
527 break;
528
529 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
530 case PROTOCOL_BINARY_CMD_VERSION:
531 {
532 memset(buffer, 0, buffer_length);
533 if (bodylen >= buffer_length)
534 {
535 /* not enough space in buffer.. should not happen... */
536 return MEMCACHED_UNKNOWN_READ_FAILURE;
537 }
538 else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
539 {
540 WATCHPOINT_ERROR(rc);
541 return MEMCACHED_UNKNOWN_READ_FAILURE;
542 }
543 }
544 break;
545 case PROTOCOL_BINARY_CMD_FLUSH:
546 case PROTOCOL_BINARY_CMD_QUIT:
547 case PROTOCOL_BINARY_CMD_SET:
548 case PROTOCOL_BINARY_CMD_ADD:
549 case PROTOCOL_BINARY_CMD_REPLACE:
550 case PROTOCOL_BINARY_CMD_APPEND:
551 case PROTOCOL_BINARY_CMD_PREPEND:
552 case PROTOCOL_BINARY_CMD_DELETE:
553 case PROTOCOL_BINARY_CMD_TOUCH:
554 {
555 WATCHPOINT_ASSERT(bodylen == 0);
556 return MEMCACHED_SUCCESS;
557 }
558
559 case PROTOCOL_BINARY_CMD_NOOP:
560 {
561 WATCHPOINT_ASSERT(bodylen == 0);
562 return MEMCACHED_END;
563 }
564
565 case PROTOCOL_BINARY_CMD_STAT:
566 {
567 if (bodylen == 0)
568 {
569 return MEMCACHED_END;
570 }
571 else if (bodylen + 1 > buffer_length)
572 {
573 /* not enough space in buffer.. should not happen... */
574 return MEMCACHED_UNKNOWN_READ_FAILURE;
575 }
576 else
577 {
578 size_t keylen= header.response.keylen;
579 memset(buffer, 0, buffer_length);
580 if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
581 (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
582 {
583 WATCHPOINT_ERROR(rc);
584 return MEMCACHED_UNKNOWN_READ_FAILURE;
585 }
586 }
587 }
588 break;
589
590 case PROTOCOL_BINARY_CMD_SASL_AUTH:
591 case PROTOCOL_BINARY_CMD_SASL_STEP:
592 {
593 memcached_result_reset(result);
594 result->item_cas= header.response.cas;
595
596 if (memcached_string_check(&result->value,
597 bodylen) != MEMCACHED_SUCCESS)
598 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
599
600 char *vptr= memcached_string_value_mutable(&result->value);
601 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
602 {
603 WATCHPOINT_ERROR(rc);
604 return MEMCACHED_UNKNOWN_READ_FAILURE;
605 }
606
607 memcached_string_set_length(&result->value, bodylen);
608 }
609 break;
610 default:
611 {
612 /* Command not implemented yet! */
613 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
614 }
615 }
616 }
617 else if (header.response.bodylen)
618 {
619 /* What should I do with the error message??? just discard it for now */
620 char hole[SMALL_STRING_LEN];
621 while (bodylen > 0)
622 {
623 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
624 if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
625 {
626 WATCHPOINT_ERROR(rc);
627 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
628 }
629 bodylen-= (uint32_t) nr;
630 }
631
632 /* This might be an error from one of the quiet commands.. if
633 * so, just throw it away and get the next one. What about creating
634 * a callback to the user with the error information?
635 */
636 switch (header.response.opcode)
637 {
638 case PROTOCOL_BINARY_CMD_SETQ:
639 case PROTOCOL_BINARY_CMD_ADDQ:
640 case PROTOCOL_BINARY_CMD_REPLACEQ:
641 case PROTOCOL_BINARY_CMD_APPENDQ:
642 case PROTOCOL_BINARY_CMD_PREPENDQ:
643 return binary_read_one_response(ptr, buffer, buffer_length, result);
644
645 default:
646 break;
647 }
648 }
649
650 rc= MEMCACHED_SUCCESS;
651 if (header.response.status != 0)
652 {
653 switch (header.response.status)
654 {
655 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
656 rc= MEMCACHED_NOTFOUND;
657 break;
658
659 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
660 rc= MEMCACHED_DATA_EXISTS;
661 break;
662
663 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
664 rc= MEMCACHED_NOTSTORED;
665 break;
666
667 case PROTOCOL_BINARY_RESPONSE_E2BIG:
668 rc= MEMCACHED_E2BIG;
669 break;
670
671 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
672 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
673 break;
674
675 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
676 rc= MEMCACHED_AUTH_CONTINUE;
677 break;
678
679 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
680 rc= MEMCACHED_AUTH_FAILURE;
681 break;
682
683 case PROTOCOL_BINARY_RESPONSE_EINVAL:
684 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
685 default:
686 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
687 break;
688 }
689 }
690
691 return rc;
692 }
693
694 static memcached_return_t _read_one_response(memcached_server_write_instance_st ptr,
695 char *buffer, const size_t buffer_length,
696 memcached_result_st *result)
697 {
698 memcached_server_response_decrement(ptr);
699
700 if (result == NULL)
701 {
702 memcached_st *root= (memcached_st *)ptr->root;
703 result = &root->result;
704 }
705
706 memcached_return_t rc;
707 if (memcached_is_binary(ptr->root))
708 {
709 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
710 }
711 else
712 {
713 rc= textual_read_one_response(ptr, buffer, buffer_length, result);
714 assert(rc != MEMCACHED_PROTOCOL_ERROR);
715 }
716
717 if (memcached_fatal(rc))
718 {
719 memcached_io_reset(ptr);
720 }
721
722 return rc;
723 }
724
725 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
726 memcached_result_st *result)
727 {
728 char buffer[SMALL_STRING_LEN];
729
730 if (memcached_is_udp(ptr->root))
731 {
732 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
733 }
734
735
736 return _read_one_response(ptr, buffer, sizeof(buffer), result);
737 }
738
739 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
740 memcached_result_st *result)
741 {
742 char buffer[1024];
743
744 return memcached_response(ptr, buffer, sizeof(buffer), result);
745 }
746
747 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
748 char *buffer, size_t buffer_length,
749 memcached_result_st *result)
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);
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_ERROR and
785 rc != MEMCACHED_ITEM and
786 rc != MEMCACHED_NOTFOUND and
787 rc != MEMCACHED_NOTSTORED and
788 rc != MEMCACHED_SERVER_ERROR and
789 rc != MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE and
790 rc != MEMCACHED_STAT and
791 rc != MEMCACHED_STORED and
792 rc != MEMCACHED_SUCCESS and
793 rc != MEMCACHED_VALUE
794 )
795 {
796 memcached_result_free(junked_result_ptr);
797 return rc;
798 }
799 }
800 memcached_result_free(junked_result_ptr);
801 }
802
803 return _read_one_response(ptr, buffer, buffer_length, result);
804 }