Fix detangle, minor style cleanups.
[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_read_one_response(memcached_server_write_instance_st ptr,
42 char *buffer, size_t buffer_length,
43 memcached_result_st *result);
44 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
45 char *buffer, size_t buffer_length,
46 memcached_result_st *result);
47
48 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
49 char *buffer, size_t buffer_length,
50 memcached_result_st *result)
51 {
52 memcached_server_response_decrement(ptr);
53
54 if (result == NULL)
55 {
56 memcached_st *root= (memcached_st *)ptr->root;
57 result = &root->result;
58 }
59
60 memcached_return_t rc;
61 if (ptr->root->flags.binary_protocol)
62 {
63 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
64 }
65 else
66 {
67 rc= textual_read_one_response(ptr, buffer, buffer_length, result);
68 }
69
70 if (rc == MEMCACHED_UNKNOWN_READ_FAILURE or
71 rc == MEMCACHED_READ_FAILURE or
72 rc == MEMCACHED_PROTOCOL_ERROR or
73 rc == MEMCACHED_CLIENT_ERROR or
74 rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
75 {
76 memcached_io_reset(ptr);
77 }
78
79 return rc;
80 }
81
82 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
83 char *buffer, size_t buffer_length,
84 memcached_result_st *result)
85 {
86 /* We may have old commands in the buffer not set, first purge */
87 if ((ptr->root->flags.no_block) && (memcached_is_processing_input(ptr->root) == false))
88 {
89 (void)memcached_io_write(ptr, NULL, 0, true);
90 }
91
92 /*
93 * The previous implementation purged all pending requests and just
94 * returned the last one. Purge all pending messages to ensure backwards
95 * compatibility.
96 */
97 if (ptr->root->flags.binary_protocol == false)
98 {
99 while (memcached_server_response_count(ptr) > 1)
100 {
101 memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result);
102
103 unlikely (rc != MEMCACHED_END &&
104 rc != MEMCACHED_STORED &&
105 rc != MEMCACHED_SUCCESS &&
106 rc != MEMCACHED_STAT &&
107 rc != MEMCACHED_DELETED &&
108 rc != MEMCACHED_NOTFOUND &&
109 rc != MEMCACHED_NOTSTORED &&
110 rc != MEMCACHED_DATA_EXISTS)
111 return rc;
112 }
113 }
114
115 return memcached_read_one_response(ptr, buffer, buffer_length, result);
116 }
117
118 static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr,
119 char *buffer,
120 memcached_result_st *result)
121 {
122 char *string_ptr;
123 char *end_ptr;
124 char *next_ptr;
125 size_t value_length;
126 size_t to_read;
127 ssize_t read_length= 0;
128
129 if (ptr->root->flags.use_udp)
130 {
131 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
132 }
133
134 WATCHPOINT_ASSERT(ptr->root);
135 end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
136
137 memcached_result_reset(result);
138
139 string_ptr= buffer;
140 string_ptr+= 6; /* "VALUE " */
141
142
143 /* We load the key */
144 {
145 char *key;
146 size_t prefix_length;
147
148 key= result->item_key;
149 result->key_length= 0;
150
151 for (prefix_length= memcached_array_size(ptr->root->_namespace); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
152 {
153 if (prefix_length == 0)
154 {
155 *key= *string_ptr;
156 key++;
157 result->key_length++;
158 }
159 else
160 prefix_length--;
161 }
162 result->item_key[result->key_length]= 0;
163 }
164
165 if (end_ptr == string_ptr)
166 goto read_error;
167
168 /* Flags fetch move past space */
169 string_ptr++;
170 if (end_ptr == string_ptr)
171 goto read_error;
172
173 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
174 result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10);
175
176 if (end_ptr == string_ptr)
177 goto read_error;
178
179 /* Length fetch move past space*/
180 string_ptr++;
181 if (end_ptr == string_ptr)
182 goto read_error;
183
184 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
185 value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);
186
187 if (end_ptr == string_ptr)
188 {
189 goto read_error;
190 }
191
192 /* Skip spaces */
193 if (*string_ptr == '\r')
194 {
195 /* Skip past the \r\n */
196 string_ptr+= 2;
197 }
198 else
199 {
200 string_ptr++;
201 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
202 result->item_cas= strtoull(next_ptr, &string_ptr, 10);
203 }
204
205 if (end_ptr < string_ptr)
206 goto read_error;
207
208 /* We add two bytes so that we can walk the \r\n */
209 if (memcached_failed(memcached_string_check(&result->value, value_length +2)))
210 {
211 return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
212 }
213
214 {
215 char *value_ptr= memcached_string_value_mutable(&result->value);
216 /*
217 We read the \r\n into the string since not doing so is more
218 cycles then the waster of memory to do so.
219
220 We are null terminating through, which will most likely make
221 some people lazy about using the return length.
222 */
223 to_read= (value_length) + 2;
224 memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
225 if (memcached_failed(rrc) and rrc == MEMCACHED_IN_PROGRESS)
226 {
227 memcached_quit_server(ptr, true);
228 return memcached_set_error(*ptr, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
229 }
230 else if (memcached_failed(rrc))
231 {
232 return rrc;
233 }
234 }
235
236 if (read_length != (ssize_t)(value_length + 2))
237 {
238 goto read_error;
239 }
240
241 /* This next bit blows the API, but this is internal....*/
242 {
243 char *char_ptr;
244 char_ptr= memcached_string_value_mutable(&result->value);;
245 char_ptr[value_length]= 0;
246 char_ptr[value_length +1]= 0;
247 memcached_string_set_length(&result->value, value_length);
248 }
249
250 return MEMCACHED_SUCCESS;
251
252 read_error:
253 memcached_io_reset(ptr);
254
255 return MEMCACHED_PARTIAL_READ;
256 }
257
258 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
259 char *buffer, size_t buffer_length,
260 memcached_result_st *result)
261 {
262 size_t total_read;
263 memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
264
265 if (memcached_failed(rc))
266 {
267 return rc;
268 }
269
270 switch(buffer[0])
271 {
272 case 'V': /* VALUE || VERSION */
273 if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
274 {
275 /* We add back in one because we will need to search for END */
276 memcached_server_response_increment(ptr);
277 return textual_value_fetch(ptr, buffer, result);
278 }
279 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 */
280 {
281 return MEMCACHED_SUCCESS;
282 }
283 break;
284
285 case 'O': /* OK */
286 if (buffer[1] == 'K')
287 {
288 return MEMCACHED_SUCCESS;
289 }
290 break;
291
292 case 'S': /* STORED STATS SERVER_ERROR */
293 {
294 if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */
295 {
296 memcached_server_response_increment(ptr);
297 return MEMCACHED_STAT;
298 }
299 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E' and buffer[5] == 'R'
300 and buffer[6] == '_'
301 and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R' ) /* SERVER_ERROR */
302 {
303 if (total_read == memcached_literal_param_size("SERVER_ERROR"))
304 {
305 return MEMCACHED_SERVER_ERROR;
306 }
307
308 if (total_read > memcached_literal_param_size("SERVER_ERROR object too large for cache") and
309 (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
310 {
311 return MEMCACHED_E2BIG;
312 }
313
314 // Move past the basic error message and whitespace
315 char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
316 if (startptr[0] == ' ')
317 {
318 startptr++;
319 }
320
321 char *endptr= startptr;
322 while (*endptr != '\r' && *endptr != '\n') endptr++;
323
324 return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
325 }
326 else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R' and buffer[4] == 'E' and buffer[5] == 'D')
327 {
328 return MEMCACHED_STORED;
329 }
330 }
331 break;
332
333 case 'D': /* DELETED */
334 if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T' and buffer[5] == 'E' and buffer[6] == 'D')
335 {
336 return MEMCACHED_DELETED;
337 }
338 break;
339
340 case 'N': /* NOT_FOUND */
341 {
342 if (buffer[1] == 'O' and buffer[2] == 'T'
343 and buffer[3] == '_'
344 and buffer[4] == 'F' and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
345 {
346 return MEMCACHED_NOTFOUND;
347 }
348 else if (buffer[1] == 'O' and buffer[2] == 'T'
349 and buffer[3] == '_'
350 and buffer[4] == 'S' and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E' and buffer[9] == 'D')
351 {
352 return MEMCACHED_NOTSTORED;
353 }
354 }
355 break;
356
357 case 'E': /* PROTOCOL ERROR or END */
358 {
359 if (buffer[1] == 'N' and buffer[2] == 'D')
360 {
361 return MEMCACHED_END;
362 }
363 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'
364 and buffer[8] == '_'
365 and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
366 {
367 return MEMCACHED_PROTOCOL_ERROR;
368 }
369 else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T' and buffer[5] == 'S')
370 {
371 return MEMCACHED_DATA_EXISTS;
372 }
373 }
374 break;
375
376 case 'T': /* TOUCHED */
377 {
378 if (buffer[1] == 'O' and buffer[2] == 'U' and buffer[3] == 'C' and buffer[4] == 'H' and buffer[5] == 'E' and buffer[6] == 'D')
379 {
380 return MEMCACHED_SUCCESS;
381 }
382 }
383 break;
384
385 case 'I': /* ITEM */
386 if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M')
387 {
388 /* We add back in one because we will need to search for END */
389 memcached_server_response_increment(ptr);
390 return MEMCACHED_ITEM;
391 }
392 break;
393
394 case 'C': /* CLIENT ERROR */
395 if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N' and buffer[5] == 'T')
396 {
397 return MEMCACHED_CLIENT_ERROR;
398 }
399 break;
400
401 case '0': /* INCR/DECR response */
402 case '1': /* INCR/DECR response */
403 case '2': /* INCR/DECR response */
404 case '3': /* INCR/DECR response */
405 case '4': /* INCR/DECR response */
406 case '5': /* INCR/DECR response */
407 case '6': /* INCR/DECR response */
408 case '7': /* INCR/DECR response */
409 case '8': /* INCR/DECR response */
410 case '9': /* INCR/DECR response */
411 {
412 unsigned long long int auto_return_value= strtoull(buffer, (char **)NULL, 10);
413
414 if (auto_return_value == ULLONG_MAX and errno == ERANGE)
415 {
416 return MEMCACHED_UNKNOWN_READ_FAILURE;
417 }
418 else if (errno == EINVAL)
419 {
420 return MEMCACHED_UNKNOWN_READ_FAILURE;
421 }
422
423 WATCHPOINT_STRING(buffer);
424 return MEMCACHED_SUCCESS;
425 }
426
427 default:
428 break;
429 }
430
431 return MEMCACHED_UNKNOWN_READ_FAILURE;
432 }
433
434 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
435 char *buffer, size_t buffer_length,
436 memcached_result_st *result)
437 {
438 memcached_return_t rc;
439 protocol_binary_response_header header;
440
441 if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
442 {
443 WATCHPOINT_ERROR(rc);
444 return rc;
445 }
446
447 if (header.response.magic != PROTOCOL_BINARY_RES)
448 {
449 return MEMCACHED_PROTOCOL_ERROR;
450 }
451
452 /*
453 ** Convert the header to host local endian!
454 */
455 header.response.keylen= ntohs(header.response.keylen);
456 header.response.status= ntohs(header.response.status);
457 header.response.bodylen= ntohl(header.response.bodylen);
458 header.response.cas= memcached_ntohll(header.response.cas);
459 uint32_t bodylen= header.response.bodylen;
460
461 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS or
462 header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
463 {
464 switch (header.response.opcode)
465 {
466 case PROTOCOL_BINARY_CMD_GETKQ:
467 /*
468 * We didn't increment the response counter for the GETKQ packet
469 * (only the final NOOP), so we need to increment the counter again.
470 */
471 memcached_server_response_increment(ptr);
472 /* FALLTHROUGH */
473 case PROTOCOL_BINARY_CMD_GETK:
474 {
475 uint16_t keylen= header.response.keylen;
476 memcached_result_reset(result);
477 result->item_cas= header.response.cas;
478
479 if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
480 {
481 WATCHPOINT_ERROR(rc);
482 return MEMCACHED_UNKNOWN_READ_FAILURE;
483 }
484
485 result->item_flags= ntohl(result->item_flags);
486 bodylen -= header.response.extlen;
487
488 result->key_length= keylen;
489 if (memcached_failed(rc= memcached_safe_read(ptr, result->item_key, keylen)))
490 {
491 WATCHPOINT_ERROR(rc);
492 return MEMCACHED_UNKNOWN_READ_FAILURE;
493 }
494
495 // Only bother with doing this if key_length > 0
496 if (result->key_length)
497 {
498 if (memcached_array_size(ptr->root->_namespace) and memcached_array_size(ptr->root->_namespace) >= result->key_length)
499 {
500 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
501 }
502
503 if (memcached_array_size(ptr->root->_namespace))
504 {
505 result->key_length-= memcached_array_size(ptr->root->_namespace);
506 memmove(result->item_key, result->item_key +memcached_array_size(ptr->root->_namespace), result->key_length);
507 }
508 }
509
510 bodylen -= keylen;
511 if (memcached_failed(memcached_string_check(&result->value, bodylen)))
512 {
513 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
514 }
515
516 char *vptr= memcached_string_value_mutable(&result->value);
517 if (memcached_failed(rc= memcached_safe_read(ptr, vptr, bodylen)))
518 {
519 WATCHPOINT_ERROR(rc);
520 return MEMCACHED_UNKNOWN_READ_FAILURE;
521 }
522
523 memcached_string_set_length(&result->value, bodylen);
524 }
525 break;
526
527 case PROTOCOL_BINARY_CMD_INCREMENT:
528 case PROTOCOL_BINARY_CMD_DECREMENT:
529 {
530 if (bodylen != sizeof(uint64_t) or buffer_length != sizeof(uint64_t))
531 {
532 return MEMCACHED_PROTOCOL_ERROR;
533 }
534
535 WATCHPOINT_ASSERT(bodylen == buffer_length);
536 uint64_t val;
537 if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
538 {
539 WATCHPOINT_ERROR(rc);
540 return MEMCACHED_UNKNOWN_READ_FAILURE;
541 }
542
543 val= memcached_ntohll(val);
544 memcpy(buffer, &val, sizeof(val));
545 }
546 break;
547
548 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
549 case PROTOCOL_BINARY_CMD_VERSION:
550 {
551 memset(buffer, 0, buffer_length);
552 if (bodylen >= buffer_length)
553 {
554 /* not enough space in buffer.. should not happen... */
555 return MEMCACHED_UNKNOWN_READ_FAILURE;
556 }
557 else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
558 {
559 WATCHPOINT_ERROR(rc);
560 return MEMCACHED_UNKNOWN_READ_FAILURE;
561 }
562 }
563 break;
564 case PROTOCOL_BINARY_CMD_FLUSH:
565 case PROTOCOL_BINARY_CMD_QUIT:
566 case PROTOCOL_BINARY_CMD_SET:
567 case PROTOCOL_BINARY_CMD_ADD:
568 case PROTOCOL_BINARY_CMD_REPLACE:
569 case PROTOCOL_BINARY_CMD_APPEND:
570 case PROTOCOL_BINARY_CMD_PREPEND:
571 case PROTOCOL_BINARY_CMD_DELETE:
572 case PROTOCOL_BINARY_CMD_TOUCH:
573 {
574 WATCHPOINT_ASSERT(bodylen == 0);
575 return MEMCACHED_SUCCESS;
576 }
577
578 case PROTOCOL_BINARY_CMD_NOOP:
579 {
580 WATCHPOINT_ASSERT(bodylen == 0);
581 return MEMCACHED_END;
582 }
583
584 case PROTOCOL_BINARY_CMD_STAT:
585 {
586 if (bodylen == 0)
587 {
588 return MEMCACHED_END;
589 }
590 else if (bodylen + 1 > buffer_length)
591 {
592 /* not enough space in buffer.. should not happen... */
593 return MEMCACHED_UNKNOWN_READ_FAILURE;
594 }
595 else
596 {
597 size_t keylen= header.response.keylen;
598 memset(buffer, 0, buffer_length);
599 if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
600 (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
601 {
602 WATCHPOINT_ERROR(rc);
603 return MEMCACHED_UNKNOWN_READ_FAILURE;
604 }
605 }
606 }
607 break;
608
609 case PROTOCOL_BINARY_CMD_SASL_AUTH:
610 case PROTOCOL_BINARY_CMD_SASL_STEP:
611 {
612 memcached_result_reset(result);
613 result->item_cas= header.response.cas;
614
615 if (memcached_string_check(&result->value,
616 bodylen) != MEMCACHED_SUCCESS)
617 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
618
619 char *vptr= memcached_string_value_mutable(&result->value);
620 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
621 {
622 WATCHPOINT_ERROR(rc);
623 return MEMCACHED_UNKNOWN_READ_FAILURE;
624 }
625
626 memcached_string_set_length(&result->value, bodylen);
627 }
628 break;
629 default:
630 {
631 /* Command not implemented yet! */
632 WATCHPOINT_ASSERT(0);
633 return MEMCACHED_PROTOCOL_ERROR;
634 }
635 }
636 }
637 else if (header.response.bodylen)
638 {
639 /* What should I do with the error message??? just discard it for now */
640 char hole[SMALL_STRING_LEN];
641 while (bodylen > 0)
642 {
643 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
644 if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
645 {
646 WATCHPOINT_ERROR(rc);
647 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
648 }
649 bodylen-= (uint32_t) nr;
650 }
651
652 /* This might be an error from one of the quiet commands.. if
653 * so, just throw it away and get the next one. What about creating
654 * a callback to the user with the error information?
655 */
656 switch (header.response.opcode)
657 {
658 case PROTOCOL_BINARY_CMD_SETQ:
659 case PROTOCOL_BINARY_CMD_ADDQ:
660 case PROTOCOL_BINARY_CMD_REPLACEQ:
661 case PROTOCOL_BINARY_CMD_APPENDQ:
662 case PROTOCOL_BINARY_CMD_PREPENDQ:
663 return binary_read_one_response(ptr, buffer, buffer_length, result);
664
665 default:
666 break;
667 }
668 }
669
670 rc= MEMCACHED_SUCCESS;
671 if (header.response.status != 0)
672 {
673 switch (header.response.status)
674 {
675 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
676 rc= MEMCACHED_NOTFOUND;
677 break;
678
679 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
680 rc= MEMCACHED_DATA_EXISTS;
681 break;
682
683 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
684 rc= MEMCACHED_NOTSTORED;
685 break;
686
687 case PROTOCOL_BINARY_RESPONSE_E2BIG:
688 rc= MEMCACHED_E2BIG;
689 break;
690
691 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
692 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
693 break;
694
695 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
696 rc= MEMCACHED_AUTH_CONTINUE;
697 break;
698
699 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
700 rc= MEMCACHED_AUTH_FAILURE;
701 break;
702
703 case PROTOCOL_BINARY_RESPONSE_EINVAL:
704 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
705 default:
706 /* @todo fix the error mappings */
707 rc= MEMCACHED_PROTOCOL_ERROR;
708 break;
709 }
710 }
711
712 return rc;
713 }