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