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