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