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