Merge in docs.
[m6w6/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 or
66 rc == MEMCACHED_PROTOCOL_ERROR or
67 rc == MEMCACHED_CLIENT_ERROR or
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) and rrc == MEMCACHED_IN_PROGRESS)
215 {
216 memcached_quit_server(ptr, true);
217 return memcached_set_error(*ptr, rrc, MEMCACHED_AT);
218 }
219 else if (memcached_failed(rrc))
220 {
221 return rrc;
222 }
223 }
224
225 if (read_length != (ssize_t)(value_length + 2))
226 {
227 goto read_error;
228 }
229
230 /* This next bit blows the API, but this is internal....*/
231 {
232 char *char_ptr;
233 char_ptr= memcached_string_value_mutable(&result->value);;
234 char_ptr[value_length]= 0;
235 char_ptr[value_length +1]= 0;
236 memcached_string_set_length(&result->value, value_length);
237 }
238
239 return MEMCACHED_SUCCESS;
240
241 read_error:
242 memcached_io_reset(ptr);
243
244 return MEMCACHED_PARTIAL_READ;
245 }
246
247 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
248 char *buffer, size_t buffer_length,
249 memcached_result_st *result)
250 {
251 memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length);
252 if (memcached_failed(rc))
253 {
254 return rc;
255 }
256
257 switch(buffer[0])
258 {
259 case 'V': /* VALUE || VERSION */
260 if (buffer[1] == 'A') /* VALUE */
261 {
262 /* We add back in one because we will need to search for END */
263 memcached_server_response_increment(ptr);
264 return textual_value_fetch(ptr, buffer, result);
265 }
266 else if (buffer[1] == 'E') /* VERSION */
267 {
268 return MEMCACHED_SUCCESS;
269 }
270 else
271 {
272 WATCHPOINT_STRING(buffer);
273 return MEMCACHED_UNKNOWN_READ_FAILURE;
274 }
275 case 'O': /* OK */
276 return MEMCACHED_SUCCESS;
277 case 'S': /* STORED STATS SERVER_ERROR */
278 {
279 if (buffer[2] == 'A') /* STORED STATS */
280 {
281 memcached_server_response_increment(ptr);
282 return MEMCACHED_STAT;
283 }
284 else if (buffer[1] == 'E') /* SERVER_ERROR */
285 {
286 char *rel_ptr;
287 char *startptr= buffer + 13, *endptr= startptr;
288
289 while (*endptr != '\r' && *endptr != '\n') endptr++;
290
291 /*
292 Yes, we could make this "efficent" but to do that we would need
293 to maintain more state for the size of the buffer. Why waste
294 memory in the struct, which is important, for something that
295 rarely should happen?
296 */
297 rel_ptr= (char *)libmemcached_realloc(ptr->root,
298 ptr->cached_server_error,
299 (size_t) (endptr - startptr + 1));
300
301 if (rel_ptr == NULL)
302 {
303 /* If we happened to have some memory, we just null it since we don't know the size */
304 if (ptr->cached_server_error)
305 ptr->cached_server_error[0]= 0;
306 return MEMCACHED_SERVER_ERROR;
307 }
308 ptr->cached_server_error= rel_ptr;
309
310 memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr));
311 ptr->cached_server_error[endptr - startptr]= 0;
312 return MEMCACHED_SERVER_ERROR;
313 }
314 else if (buffer[1] == 'T')
315 return MEMCACHED_STORED;
316 else
317 {
318 WATCHPOINT_STRING(buffer);
319 return MEMCACHED_UNKNOWN_READ_FAILURE;
320 }
321 }
322 case 'D': /* DELETED */
323 return MEMCACHED_DELETED;
324 case 'N': /* NOT_FOUND */
325 {
326 if (buffer[4] == 'F')
327 return MEMCACHED_NOTFOUND;
328 else if (buffer[4] == 'S')
329 return MEMCACHED_NOTSTORED;
330 else
331 {
332 WATCHPOINT_STRING(buffer);
333 return MEMCACHED_UNKNOWN_READ_FAILURE;
334 }
335 }
336 case 'E': /* PROTOCOL ERROR or END */
337 {
338 if (buffer[1] == 'N')
339 return MEMCACHED_END;
340 else if (buffer[1] == 'R')
341 return MEMCACHED_PROTOCOL_ERROR;
342 else if (buffer[1] == 'X')
343 return MEMCACHED_DATA_EXISTS;
344 else
345 {
346 WATCHPOINT_STRING(buffer);
347 return MEMCACHED_UNKNOWN_READ_FAILURE;
348 }
349
350 }
351 case 'I': /* CLIENT ERROR */
352 /* We add back in one because we will need to search for END */
353 memcached_server_response_increment(ptr);
354 return MEMCACHED_ITEM;
355 case 'C': /* CLIENT ERROR */
356 return MEMCACHED_CLIENT_ERROR;
357 default:
358 {
359 unsigned long long auto_return_value;
360
361 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
362 return MEMCACHED_SUCCESS;
363
364 WATCHPOINT_STRING(buffer);
365 return MEMCACHED_UNKNOWN_READ_FAILURE;
366 }
367 }
368
369 /* NOTREACHED */
370 }
371
372 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
373 char *buffer, size_t buffer_length,
374 memcached_result_st *result)
375 {
376 memcached_return_t rc;
377 protocol_binary_response_header header;
378
379 if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
380 {
381 WATCHPOINT_ERROR(rc);
382 return rc;
383 }
384
385 if (header.response.magic != PROTOCOL_BINARY_RES)
386 {
387 return MEMCACHED_PROTOCOL_ERROR;
388 }
389
390 /*
391 ** Convert the header to host local endian!
392 */
393 header.response.keylen= ntohs(header.response.keylen);
394 header.response.status= ntohs(header.response.status);
395 header.response.bodylen= ntohl(header.response.bodylen);
396 header.response.cas= memcached_ntohll(header.response.cas);
397 uint32_t bodylen= header.response.bodylen;
398
399 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS ||
400 header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
401 {
402 switch (header.response.opcode)
403 {
404 case PROTOCOL_BINARY_CMD_GETKQ:
405 /*
406 * We didn't increment the response counter for the GETKQ packet
407 * (only the final NOOP), so we need to increment the counter again.
408 */
409 memcached_server_response_increment(ptr);
410 /* FALLTHROUGH */
411 case PROTOCOL_BINARY_CMD_GETK:
412 {
413 uint16_t keylen= header.response.keylen;
414 memcached_result_reset(result);
415 result->item_cas= header.response.cas;
416
417 if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
418 {
419 WATCHPOINT_ERROR(rc);
420 return MEMCACHED_UNKNOWN_READ_FAILURE;
421 }
422
423 result->item_flags= ntohl(result->item_flags);
424 bodylen -= header.response.extlen;
425
426 result->key_length= keylen;
427 if ((rc= memcached_safe_read(ptr, result->item_key, keylen)) != MEMCACHED_SUCCESS)
428 {
429 WATCHPOINT_ERROR(rc);
430 return MEMCACHED_UNKNOWN_READ_FAILURE;
431 }
432
433 bodylen -= keylen;
434 if (memcached_string_check(&result->value,
435 bodylen) != MEMCACHED_SUCCESS)
436 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
437
438 char *vptr= memcached_string_value_mutable(&result->value);
439 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
440 {
441 WATCHPOINT_ERROR(rc);
442 return MEMCACHED_UNKNOWN_READ_FAILURE;
443 }
444
445 memcached_string_set_length(&result->value, bodylen);
446 }
447 break;
448 case PROTOCOL_BINARY_CMD_INCREMENT:
449 case PROTOCOL_BINARY_CMD_DECREMENT:
450 {
451 if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
452 return MEMCACHED_PROTOCOL_ERROR;
453
454 WATCHPOINT_ASSERT(bodylen == buffer_length);
455 uint64_t val;
456 if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
457 {
458 WATCHPOINT_ERROR(rc);
459 return MEMCACHED_UNKNOWN_READ_FAILURE;
460 }
461
462 val= memcached_ntohll(val);
463 memcpy(buffer, &val, sizeof(val));
464 }
465 break;
466 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
467 case PROTOCOL_BINARY_CMD_VERSION:
468 {
469 memset(buffer, 0, buffer_length);
470 if (bodylen >= buffer_length)
471 {
472 /* not enough space in buffer.. should not happen... */
473 return MEMCACHED_UNKNOWN_READ_FAILURE;
474 }
475 else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
476 {
477 WATCHPOINT_ERROR(rc);
478 return MEMCACHED_UNKNOWN_READ_FAILURE;
479 }
480 }
481 break;
482 case PROTOCOL_BINARY_CMD_FLUSH:
483 case PROTOCOL_BINARY_CMD_QUIT:
484 case PROTOCOL_BINARY_CMD_SET:
485 case PROTOCOL_BINARY_CMD_ADD:
486 case PROTOCOL_BINARY_CMD_REPLACE:
487 case PROTOCOL_BINARY_CMD_APPEND:
488 case PROTOCOL_BINARY_CMD_PREPEND:
489 case PROTOCOL_BINARY_CMD_DELETE:
490 {
491 WATCHPOINT_ASSERT(bodylen == 0);
492 return MEMCACHED_SUCCESS;
493 }
494 case PROTOCOL_BINARY_CMD_NOOP:
495 {
496 WATCHPOINT_ASSERT(bodylen == 0);
497 return MEMCACHED_END;
498 }
499 case PROTOCOL_BINARY_CMD_STAT:
500 {
501 if (bodylen == 0)
502 {
503 return MEMCACHED_END;
504 }
505 else if (bodylen + 1 > buffer_length)
506 {
507 /* not enough space in buffer.. should not happen... */
508 return MEMCACHED_UNKNOWN_READ_FAILURE;
509 }
510 else
511 {
512 size_t keylen= header.response.keylen;
513 memset(buffer, 0, buffer_length);
514 if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
515 (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
516 {
517 WATCHPOINT_ERROR(rc);
518 return MEMCACHED_UNKNOWN_READ_FAILURE;
519 }
520 }
521 }
522 break;
523
524 case PROTOCOL_BINARY_CMD_SASL_AUTH:
525 case PROTOCOL_BINARY_CMD_SASL_STEP:
526 {
527 memcached_result_reset(result);
528 result->item_cas= header.response.cas;
529
530 if (memcached_string_check(&result->value,
531 bodylen) != MEMCACHED_SUCCESS)
532 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
533
534 char *vptr= memcached_string_value_mutable(&result->value);
535 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
536 {
537 WATCHPOINT_ERROR(rc);
538 return MEMCACHED_UNKNOWN_READ_FAILURE;
539 }
540
541 memcached_string_set_length(&result->value, bodylen);
542 }
543 break;
544 default:
545 {
546 /* Command not implemented yet! */
547 WATCHPOINT_ASSERT(0);
548 return MEMCACHED_PROTOCOL_ERROR;
549 }
550 }
551 }
552 else if (header.response.bodylen)
553 {
554 /* What should I do with the error message??? just discard it for now */
555 char hole[SMALL_STRING_LEN];
556 while (bodylen > 0)
557 {
558 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
559 if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
560 {
561 WATCHPOINT_ERROR(rc);
562 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
563 }
564 bodylen-= (uint32_t) nr;
565 }
566
567 /* This might be an error from one of the quiet commands.. if
568 * so, just throw it away and get the next one. What about creating
569 * a callback to the user with the error information?
570 */
571 switch (header.response.opcode)
572 {
573 case PROTOCOL_BINARY_CMD_SETQ:
574 case PROTOCOL_BINARY_CMD_ADDQ:
575 case PROTOCOL_BINARY_CMD_REPLACEQ:
576 case PROTOCOL_BINARY_CMD_APPENDQ:
577 case PROTOCOL_BINARY_CMD_PREPENDQ:
578 return binary_read_one_response(ptr, buffer, buffer_length, result);
579 default:
580 break;
581 }
582 }
583
584 rc= MEMCACHED_SUCCESS;
585 unlikely(header.response.status != 0)
586 switch (header.response.status)
587 {
588 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
589 rc= MEMCACHED_NOTFOUND;
590 break;
591 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
592 rc= MEMCACHED_DATA_EXISTS;
593 break;
594 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
595 rc= MEMCACHED_NOTSTORED;
596 break;
597 case PROTOCOL_BINARY_RESPONSE_E2BIG:
598 rc= MEMCACHED_E2BIG;
599 break;
600 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
601 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
602 break;
603 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
604 rc= MEMCACHED_AUTH_CONTINUE;
605 break;
606 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
607 rc= MEMCACHED_AUTH_FAILURE;
608 break;
609 case PROTOCOL_BINARY_RESPONSE_EINVAL:
610 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
611 default:
612 /* @todo fix the error mappings */
613 rc= MEMCACHED_PROTOCOL_ERROR;
614 break;
615 }
616
617 return rc;
618 }