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