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