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