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