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