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