c14dd1ff6688c6678922c5d4356c77e02bbe6e17
[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 WATCHPOINT_ASSERT(0);
238 return MEMCACHED_UNKNOWN_READ_FAILURE;
239 }
240 case 'O': /* OK */
241 return MEMCACHED_SUCCESS;
242 case 'S': /* STORED STATS SERVER_ERROR */
243 {
244 if (buffer[2] == 'A') /* STORED STATS */
245 {
246 memcached_server_response_increment(ptr);
247 return MEMCACHED_STAT;
248 }
249 else if (buffer[1] == 'E') /* SERVER_ERROR */
250 {
251 char *rel_ptr;
252 char *startptr= buffer + 13, *endptr= startptr;
253
254 while (*endptr != '\r' && *endptr != '\n') endptr++;
255
256 /*
257 Yes, we could make this "efficent" but to do that we would need
258 to maintain more state for the size of the buffer. Why waste
259 memory in the struct, which is important, for something that
260 rarely should happen?
261 */
262 rel_ptr= (char *)libmemcached_realloc(ptr->root,
263 ptr->cached_server_error,
264 (size_t) (endptr - startptr + 1));
265
266 if (rel_ptr == NULL)
267 {
268 /* If we happened to have some memory, we just null it since we don't know the size */
269 if (ptr->cached_server_error)
270 ptr->cached_server_error[0]= 0;
271 return MEMCACHED_SERVER_ERROR;
272 }
273 ptr->cached_server_error= rel_ptr;
274
275 memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr));
276 ptr->cached_server_error[endptr - startptr]= 0;
277 return MEMCACHED_SERVER_ERROR;
278 }
279 else if (buffer[1] == 'T')
280 return MEMCACHED_STORED;
281 else
282 {
283 WATCHPOINT_STRING(buffer);
284 WATCHPOINT_ASSERT(0);
285 return MEMCACHED_UNKNOWN_READ_FAILURE;
286 }
287 }
288 case 'D': /* DELETED */
289 return MEMCACHED_DELETED;
290 case 'N': /* NOT_FOUND */
291 {
292 if (buffer[4] == 'F')
293 return MEMCACHED_NOTFOUND;
294 else if (buffer[4] == 'S')
295 return MEMCACHED_NOTSTORED;
296 else
297 return MEMCACHED_UNKNOWN_READ_FAILURE;
298 }
299 case 'E': /* PROTOCOL ERROR or END */
300 {
301 if (buffer[1] == 'N')
302 return MEMCACHED_END;
303 else if (buffer[1] == 'R')
304 return MEMCACHED_PROTOCOL_ERROR;
305 else if (buffer[1] == 'X')
306 return MEMCACHED_DATA_EXISTS;
307 else
308 return MEMCACHED_UNKNOWN_READ_FAILURE;
309 }
310 case 'I': /* CLIENT ERROR */
311 /* We add back in one because we will need to search for END */
312 memcached_server_response_increment(ptr);
313 return MEMCACHED_ITEM;
314 case 'C': /* CLIENT ERROR */
315 return MEMCACHED_CLIENT_ERROR;
316 default:
317 {
318 unsigned long long auto_return_value;
319
320 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
321 return MEMCACHED_SUCCESS;
322
323 return MEMCACHED_UNKNOWN_READ_FAILURE;
324 }
325 }
326
327 /* NOTREACHED */
328 }
329
330 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
331 char *buffer, size_t buffer_length,
332 memcached_result_st *result)
333 {
334 protocol_binary_response_header header;
335
336 unlikely (memcached_safe_read(ptr, &header.bytes,
337 sizeof(header.bytes)) != MEMCACHED_SUCCESS)
338 return MEMCACHED_UNKNOWN_READ_FAILURE;
339
340 unlikely (header.response.magic != PROTOCOL_BINARY_RES)
341 return MEMCACHED_PROTOCOL_ERROR;
342
343 /*
344 ** Convert the header to host local endian!
345 */
346 header.response.keylen= ntohs(header.response.keylen);
347 header.response.status= ntohs(header.response.status);
348 header.response.bodylen= ntohl(header.response.bodylen);
349 header.response.cas= ntohll(header.response.cas);
350 uint32_t bodylen= header.response.bodylen;
351
352 if (header.response.status == 0)
353 {
354 switch (header.response.opcode)
355 {
356 case PROTOCOL_BINARY_CMD_GETKQ:
357 /*
358 * We didn't increment the response counter for the GETKQ packet
359 * (only the final NOOP), so we need to increment the counter again.
360 */
361 memcached_server_response_increment(ptr);
362 /* FALLTHROUGH */
363 case PROTOCOL_BINARY_CMD_GETK:
364 {
365 uint16_t keylen= header.response.keylen;
366 memcached_result_reset(result);
367 result->item_cas= header.response.cas;
368
369 if (memcached_safe_read(ptr, &result->item_flags,
370 sizeof (result->item_flags)) != MEMCACHED_SUCCESS)
371 return MEMCACHED_UNKNOWN_READ_FAILURE;
372
373 result->item_flags= ntohl(result->item_flags);
374 bodylen -= header.response.extlen;
375
376 result->key_length= keylen;
377 if (memcached_safe_read(ptr, result->item_key, keylen) != MEMCACHED_SUCCESS)
378 return MEMCACHED_UNKNOWN_READ_FAILURE;
379
380 bodylen -= keylen;
381 if (memcached_string_check(&result->value,
382 bodylen) != MEMCACHED_SUCCESS)
383 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
384
385 char *vptr= memcached_string_value_mutable(&result->value);
386 if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
387 return MEMCACHED_UNKNOWN_READ_FAILURE;
388
389 memcached_string_set_length(&result->value, bodylen);
390 }
391 break;
392 case PROTOCOL_BINARY_CMD_INCREMENT:
393 case PROTOCOL_BINARY_CMD_DECREMENT:
394 {
395 if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
396 return MEMCACHED_PROTOCOL_ERROR;
397
398 WATCHPOINT_ASSERT(bodylen == buffer_length);
399 uint64_t val;
400 if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS)
401 return MEMCACHED_UNKNOWN_READ_FAILURE;
402
403 val= ntohll(val);
404 memcpy(buffer, &val, sizeof(val));
405 }
406 break;
407 case PROTOCOL_BINARY_CMD_VERSION:
408 {
409 memset(buffer, 0, buffer_length);
410 if (bodylen >= buffer_length)
411 /* not enough space in buffer.. should not happen... */
412 return MEMCACHED_UNKNOWN_READ_FAILURE;
413 else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS)
414 return MEMCACHED_UNKNOWN_READ_FAILURE;
415 }
416 break;
417 case PROTOCOL_BINARY_CMD_FLUSH:
418 case PROTOCOL_BINARY_CMD_QUIT:
419 case PROTOCOL_BINARY_CMD_SET:
420 case PROTOCOL_BINARY_CMD_ADD:
421 case PROTOCOL_BINARY_CMD_REPLACE:
422 case PROTOCOL_BINARY_CMD_APPEND:
423 case PROTOCOL_BINARY_CMD_PREPEND:
424 case PROTOCOL_BINARY_CMD_DELETE:
425 {
426 WATCHPOINT_ASSERT(bodylen == 0);
427 return MEMCACHED_SUCCESS;
428 }
429 case PROTOCOL_BINARY_CMD_NOOP:
430 {
431 WATCHPOINT_ASSERT(bodylen == 0);
432 return MEMCACHED_END;
433 }
434 case PROTOCOL_BINARY_CMD_STAT:
435 {
436 if (bodylen == 0)
437 return MEMCACHED_END;
438 else if (bodylen + 1 > buffer_length)
439 /* not enough space in buffer.. should not happen... */
440 return MEMCACHED_UNKNOWN_READ_FAILURE;
441 else
442 {
443 size_t keylen= header.response.keylen;
444 memset(buffer, 0, buffer_length);
445 if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS ||
446 memcached_safe_read(ptr, buffer + keylen + 1,
447 bodylen - keylen) != MEMCACHED_SUCCESS)
448 return MEMCACHED_UNKNOWN_READ_FAILURE;
449 }
450 }
451 break;
452 default:
453 {
454 /* Command not implemented yet! */
455 WATCHPOINT_ASSERT(0);
456 return MEMCACHED_PROTOCOL_ERROR;
457 }
458 }
459 }
460 else if (header.response.bodylen)
461 {
462 /* What should I do with the error message??? just discard it for now */
463 char hole[SMALL_STRING_LEN];
464 while (bodylen > 0)
465 {
466 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
467 if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS)
468 return MEMCACHED_UNKNOWN_READ_FAILURE;
469 bodylen-= (uint32_t) nr;
470 }
471
472 /* This might be an error from one of the quiet commands.. if
473 * so, just throw it away and get the next one. What about creating
474 * a callback to the user with the error information?
475 */
476 switch (header.response.opcode)
477 {
478 case PROTOCOL_BINARY_CMD_SETQ:
479 case PROTOCOL_BINARY_CMD_ADDQ:
480 case PROTOCOL_BINARY_CMD_REPLACEQ:
481 case PROTOCOL_BINARY_CMD_APPENDQ:
482 case PROTOCOL_BINARY_CMD_PREPENDQ:
483 return binary_read_one_response(ptr, buffer, buffer_length, result);
484 default:
485 break;
486 }
487 }
488
489 memcached_return_t rc= MEMCACHED_SUCCESS;
490 unlikely(header.response.status != 0)
491 switch (header.response.status)
492 {
493 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
494 rc= MEMCACHED_NOTFOUND;
495 break;
496 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
497 rc= MEMCACHED_DATA_EXISTS;
498 break;
499 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
500 rc= MEMCACHED_NOTSTORED;
501 break;
502 case PROTOCOL_BINARY_RESPONSE_E2BIG:
503 rc= MEMCACHED_E2BIG;
504 break;
505 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
506 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
507 break;
508 case PROTOCOL_BINARY_RESPONSE_EINVAL:
509 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
510 default:
511 /* @todo fix the error mappings */
512 rc= MEMCACHED_PROTOCOL_ERROR;
513 break;
514 }
515
516 return rc;
517 }