Updated with Twitter's memcached_server_error() function.
[awesomized/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 textual_read_one_response(memcached_server_st *ptr,
12 char *buffer, size_t buffer_length,
13 memcached_result_st *result);
14 static memcached_return binary_read_one_response(memcached_server_st *ptr,
15 char *buffer, size_t buffer_length,
16 memcached_result_st *result);
17
18 memcached_return 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 rc;
28 if (ptr->root->flags & MEM_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 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 & MEM_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 & MEM_BINARY_PROTOCOL) == 0)
56 while (memcached_server_response_count(ptr) > 1)
57 {
58 memcached_return 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 textual_value_fetch(memcached_server_st *ptr,
75 char *buffer,
76 memcached_result_st *result)
77 {
78 memcached_return 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 & MEM_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= 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 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 textual_read_one_response(memcached_server_st *ptr,
205 char *buffer, size_t buffer_length,
206 memcached_result_st *result)
207 {
208 memcached_return 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, ptr->cached_server_error, endptr - startptr + 1);
254
255 if (rel_ptr == NULL)
256 {
257 /* If we happened to have some memory, we just null it since we don't know the size */
258 if (ptr->cached_server_error)
259 ptr->cached_server_error[0]= 0;
260 return MEMCACHED_SERVER_ERROR;
261 }
262 ptr->cached_server_error= rel_ptr;
263
264 memcpy(ptr->cached_server_error, startptr, endptr - startptr);
265 ptr->cached_server_error[endptr - startptr]= 0;
266 return MEMCACHED_SERVER_ERROR;
267 }
268 else if (buffer[1] == 'T')
269 return MEMCACHED_STORED;
270 else
271 {
272 WATCHPOINT_STRING(buffer);
273 WATCHPOINT_ASSERT(0);
274 return MEMCACHED_UNKNOWN_READ_FAILURE;
275 }
276 }
277 case 'D': /* DELETED */
278 return MEMCACHED_DELETED;
279 case 'N': /* NOT_FOUND */
280 {
281 if (buffer[4] == 'F')
282 return MEMCACHED_NOTFOUND;
283 else if (buffer[4] == 'S')
284 return MEMCACHED_NOTSTORED;
285 else
286 return MEMCACHED_UNKNOWN_READ_FAILURE;
287 }
288 case 'E': /* PROTOCOL ERROR or END */
289 {
290 if (buffer[1] == 'N')
291 return MEMCACHED_END;
292 else if (buffer[1] == 'R')
293 return MEMCACHED_PROTOCOL_ERROR;
294 else if (buffer[1] == 'X')
295 return MEMCACHED_DATA_EXISTS;
296 else
297 return MEMCACHED_UNKNOWN_READ_FAILURE;
298 }
299 case 'I': /* CLIENT ERROR */
300 /* We add back in one because we will need to search for END */
301 memcached_server_response_increment(ptr);
302 return MEMCACHED_ITEM;
303 case 'C': /* CLIENT ERROR */
304 return MEMCACHED_CLIENT_ERROR;
305 default:
306 {
307 unsigned long long auto_return_value;
308
309 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
310 return MEMCACHED_SUCCESS;
311
312 return MEMCACHED_UNKNOWN_READ_FAILURE;
313 }
314 }
315
316 /* NOTREACHED */
317 }
318
319 char *memcached_result_value(memcached_result_st *ptr)
320 {
321 memcached_string_st *sptr= &ptr->value;
322 return memcached_string_value(sptr);
323 }
324
325 size_t memcached_result_length(memcached_result_st *ptr)
326 {
327 memcached_string_st *sptr= &ptr->value;
328 return memcached_string_length(sptr);
329 }
330
331 static memcached_return binary_read_one_response(memcached_server_st *ptr,
332 char *buffer, size_t buffer_length,
333 memcached_result_st *result)
334 {
335 protocol_binary_response_header header;
336
337 unlikely (memcached_safe_read(ptr, &header.bytes,
338 sizeof(header.bytes)) != MEMCACHED_SUCCESS)
339 return MEMCACHED_UNKNOWN_READ_FAILURE;
340
341 unlikely (header.response.magic != PROTOCOL_BINARY_RES)
342 return MEMCACHED_PROTOCOL_ERROR;
343
344 /*
345 ** Convert the header to host local endian!
346 */
347 header.response.keylen= ntohs(header.response.keylen);
348 header.response.status= ntohs(header.response.status);
349 header.response.bodylen= ntohl(header.response.bodylen);
350 header.response.cas= ntohll(header.response.cas);
351 uint32_t bodylen= header.response.bodylen;
352
353 if (header.response.status == 0)
354 {
355 switch (header.response.opcode)
356 {
357 case PROTOCOL_BINARY_CMD_GETK:
358 case PROTOCOL_BINARY_CMD_GETKQ:
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 -= 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 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_E2BIG:
495 case PROTOCOL_BINARY_RESPONSE_EINVAL:
496 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
497 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
498 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
499 default:
500 /* @todo fix the error mappings */
501 rc= MEMCACHED_PROTOCOL_ERROR;
502 break;
503 }
504
505 return rc;
506 }