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