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