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