2 * Copyright (C) 2006-2009 Brian Aker
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
8 * Summary: memcached_response() is used to determine the return result from an issued command.
14 static memcached_return_t
textual_read_one_response(memcached_server_instance_st
*ptr
,
15 char *buffer
, size_t buffer_length
,
16 memcached_result_st
*result
);
17 static memcached_return_t
binary_read_one_response(memcached_server_instance_st
*ptr
,
18 char *buffer
, size_t buffer_length
,
19 memcached_result_st
*result
);
21 memcached_return_t
memcached_read_one_response(memcached_server_instance_st
*ptr
,
22 char *buffer
, size_t buffer_length
,
23 memcached_result_st
*result
)
25 memcached_server_response_decrement(ptr
);
29 memcached_st
*root
= (memcached_st
*)ptr
->root
;
30 result
= &root
->result
;
33 memcached_return_t rc
;
34 if (ptr
->root
->flags
.binary_protocol
)
35 rc
= binary_read_one_response(ptr
, buffer
, buffer_length
, result
);
37 rc
= textual_read_one_response(ptr
, buffer
, buffer_length
, result
);
39 unlikely(rc
== MEMCACHED_UNKNOWN_READ_FAILURE
||
40 rc
== MEMCACHED_PROTOCOL_ERROR
||
41 rc
== MEMCACHED_CLIENT_ERROR
||
42 rc
== MEMCACHED_MEMORY_ALLOCATION_FAILURE
)
43 memcached_io_reset(ptr
);
48 memcached_return_t
memcached_response(memcached_server_instance_st
*ptr
,
49 char *buffer
, size_t buffer_length
,
50 memcached_result_st
*result
)
52 /* We may have old commands in the buffer not set, first purge */
53 if ((ptr
->root
->flags
.no_block
) && (memcached_is_processing_input(ptr
->root
) == false))
55 (void)memcached_io_write(ptr
, NULL
, 0, true);
59 * The previous implementation purged all pending requests and just
60 * returned the last one. Purge all pending messages to ensure backwards
63 if (ptr
->root
->flags
.binary_protocol
== false)
64 while (memcached_server_response_count(ptr
) > 1)
66 memcached_return_t rc
= memcached_read_one_response(ptr
, buffer
, buffer_length
, result
);
68 unlikely (rc
!= MEMCACHED_END
&&
69 rc
!= MEMCACHED_STORED
&&
70 rc
!= MEMCACHED_SUCCESS
&&
71 rc
!= MEMCACHED_STAT
&&
72 rc
!= MEMCACHED_DELETED
&&
73 rc
!= MEMCACHED_NOTFOUND
&&
74 rc
!= MEMCACHED_NOTSTORED
&&
75 rc
!= MEMCACHED_DATA_EXISTS
)
79 return memcached_read_one_response(ptr
, buffer
, buffer_length
, result
);
82 static memcached_return_t
textual_value_fetch(memcached_server_instance_st
*ptr
,
84 memcached_result_st
*result
)
86 memcached_return_t rc
= MEMCACHED_SUCCESS
;
93 ssize_t read_length
= 0;
94 memcached_return_t rrc
;
96 if (ptr
->root
->flags
.use_udp
)
97 return MEMCACHED_NOT_SUPPORTED
;
99 WATCHPOINT_ASSERT(ptr
->root
);
100 end_ptr
= buffer
+ MEMCACHED_DEFAULT_COMMAND_SIZE
;
102 memcached_result_reset(result
);
105 string_ptr
+= 6; /* "VALUE " */
108 /* We load the key */
111 size_t prefix_length
;
113 key
= result
->item_key
;
114 result
->key_length
= 0;
116 for (prefix_length
= ptr
->root
->prefix_key_length
; !(iscntrl(*string_ptr
) || isspace(*string_ptr
)) ; string_ptr
++)
118 if (prefix_length
== 0)
122 result
->key_length
++;
127 result
->item_key
[result
->key_length
]= 0;
130 if (end_ptr
== string_ptr
)
133 /* Flags fetch move past space */
135 if (end_ptr
== string_ptr
)
137 for (next_ptr
= string_ptr
; isdigit(*string_ptr
); string_ptr
++);
138 result
->item_flags
= (uint32_t) strtoul(next_ptr
, &string_ptr
, 10);
140 if (end_ptr
== string_ptr
)
143 /* Length fetch move past space*/
145 if (end_ptr
== string_ptr
)
148 for (next_ptr
= string_ptr
; isdigit(*string_ptr
); string_ptr
++);
149 value_length
= (size_t)strtoull(next_ptr
, &string_ptr
, 10);
151 if (end_ptr
== string_ptr
)
155 if (*string_ptr
== '\r')
157 /* Skip past the \r\n */
163 for (next_ptr
= string_ptr
; isdigit(*string_ptr
); string_ptr
++);
164 result
->item_cas
= strtoull(next_ptr
, &string_ptr
, 10);
167 if (end_ptr
< string_ptr
)
170 /* We add two bytes so that we can walk the \r\n */
171 rc
= memcached_string_check(&result
->value
, value_length
+2);
172 if (rc
!= MEMCACHED_SUCCESS
)
175 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
178 value_ptr
= memcached_string_value_mutable(&result
->value
);
180 We read the \r\n into the string since not doing so is more
181 cycles then the waster of memory to do so.
183 We are null terminating through, which will most likely make
184 some people lazy about using the return length.
186 to_read
= (value_length
) + 2;
187 rrc
= memcached_io_read(ptr
, value_ptr
, to_read
, &read_length
);
188 if (rrc
!= MEMCACHED_SUCCESS
)
191 if (read_length
!= (ssize_t
)(value_length
+ 2))
196 /* This next bit blows the API, but this is internal....*/
199 char_ptr
= memcached_string_value_mutable(&result
->value
);;
200 char_ptr
[value_length
]= 0;
201 char_ptr
[value_length
+ 1]= 0;
202 memcached_string_set_length(&result
->value
, value_length
);
205 return MEMCACHED_SUCCESS
;
208 memcached_io_reset(ptr
);
210 return MEMCACHED_PARTIAL_READ
;
213 static memcached_return_t
textual_read_one_response(memcached_server_instance_st
*ptr
,
214 char *buffer
, size_t buffer_length
,
215 memcached_result_st
*result
)
217 memcached_return_t rc
= memcached_io_readline(ptr
, buffer
, buffer_length
);
218 if (rc
!= MEMCACHED_SUCCESS
)
223 case 'V': /* VALUE || VERSION */
224 if (buffer
[1] == 'A') /* VALUE */
226 /* We add back in one because we will need to search for END */
227 memcached_server_response_increment(ptr
);
228 return textual_value_fetch(ptr
, buffer
, result
);
230 else if (buffer
[1] == 'E') /* VERSION */
232 return MEMCACHED_SUCCESS
;
236 WATCHPOINT_STRING(buffer
);
237 WATCHPOINT_ASSERT(0);
238 return MEMCACHED_UNKNOWN_READ_FAILURE
;
241 return MEMCACHED_SUCCESS
;
242 case 'S': /* STORED STATS SERVER_ERROR */
244 if (buffer
[2] == 'A') /* STORED STATS */
246 memcached_server_response_increment(ptr
);
247 return MEMCACHED_STAT
;
249 else if (buffer
[1] == 'E') /* SERVER_ERROR */
252 char *startptr
= buffer
+ 13, *endptr
= startptr
;
254 while (*endptr
!= '\r' && *endptr
!= '\n') endptr
++;
257 Yes, we could make this "efficent" but to do that we would need
258 to maintain more state for the size of the buffer. Why waste
259 memory in the struct, which is important, for something that
260 rarely should happen?
262 rel_ptr
= (char *)libmemcached_realloc(ptr
->root
,
263 ptr
->cached_server_error
,
264 (size_t) (endptr
- startptr
+ 1));
268 /* If we happened to have some memory, we just null it since we don't know the size */
269 if (ptr
->cached_server_error
)
270 ptr
->cached_server_error
[0]= 0;
271 return MEMCACHED_SERVER_ERROR
;
273 ptr
->cached_server_error
= rel_ptr
;
275 memcpy(ptr
->cached_server_error
, startptr
, (size_t) (endptr
- startptr
));
276 ptr
->cached_server_error
[endptr
- startptr
]= 0;
277 return MEMCACHED_SERVER_ERROR
;
279 else if (buffer
[1] == 'T')
280 return MEMCACHED_STORED
;
283 WATCHPOINT_STRING(buffer
);
284 WATCHPOINT_ASSERT(0);
285 return MEMCACHED_UNKNOWN_READ_FAILURE
;
288 case 'D': /* DELETED */
289 return MEMCACHED_DELETED
;
290 case 'N': /* NOT_FOUND */
292 if (buffer
[4] == 'F')
293 return MEMCACHED_NOTFOUND
;
294 else if (buffer
[4] == 'S')
295 return MEMCACHED_NOTSTORED
;
297 return MEMCACHED_UNKNOWN_READ_FAILURE
;
299 case 'E': /* PROTOCOL ERROR or END */
301 if (buffer
[1] == 'N')
302 return MEMCACHED_END
;
303 else if (buffer
[1] == 'R')
304 return MEMCACHED_PROTOCOL_ERROR
;
305 else if (buffer
[1] == 'X')
306 return MEMCACHED_DATA_EXISTS
;
308 return MEMCACHED_UNKNOWN_READ_FAILURE
;
310 case 'I': /* CLIENT ERROR */
311 /* We add back in one because we will need to search for END */
312 memcached_server_response_increment(ptr
);
313 return MEMCACHED_ITEM
;
314 case 'C': /* CLIENT ERROR */
315 return MEMCACHED_CLIENT_ERROR
;
318 unsigned long long auto_return_value
;
320 if (sscanf(buffer
, "%llu", &auto_return_value
) == 1)
321 return MEMCACHED_SUCCESS
;
323 return MEMCACHED_UNKNOWN_READ_FAILURE
;
330 static memcached_return_t
binary_read_one_response(memcached_server_instance_st
*ptr
,
331 char *buffer
, size_t buffer_length
,
332 memcached_result_st
*result
)
334 protocol_binary_response_header header
;
336 unlikely (memcached_safe_read(ptr
, &header
.bytes
,
337 sizeof(header
.bytes
)) != MEMCACHED_SUCCESS
)
338 return MEMCACHED_UNKNOWN_READ_FAILURE
;
340 unlikely (header
.response
.magic
!= PROTOCOL_BINARY_RES
)
341 return MEMCACHED_PROTOCOL_ERROR
;
344 ** Convert the header to host local endian!
346 header
.response
.keylen
= ntohs(header
.response
.keylen
);
347 header
.response
.status
= ntohs(header
.response
.status
);
348 header
.response
.bodylen
= ntohl(header
.response
.bodylen
);
349 header
.response
.cas
= ntohll(header
.response
.cas
);
350 uint32_t bodylen
= header
.response
.bodylen
;
352 if (header
.response
.status
== 0)
354 switch (header
.response
.opcode
)
356 case PROTOCOL_BINARY_CMD_GETKQ
:
358 * We didn't increment the response counter for the GETKQ packet
359 * (only the final NOOP), so we need to increment the counter again.
361 memcached_server_response_increment(ptr
);
363 case PROTOCOL_BINARY_CMD_GETK
:
365 uint16_t keylen
= header
.response
.keylen
;
366 memcached_result_reset(result
);
367 result
->item_cas
= header
.response
.cas
;
369 if (memcached_safe_read(ptr
, &result
->item_flags
,
370 sizeof (result
->item_flags
)) != MEMCACHED_SUCCESS
)
371 return MEMCACHED_UNKNOWN_READ_FAILURE
;
373 result
->item_flags
= ntohl(result
->item_flags
);
374 bodylen
-= header
.response
.extlen
;
376 result
->key_length
= keylen
;
377 if (memcached_safe_read(ptr
, result
->item_key
, keylen
) != MEMCACHED_SUCCESS
)
378 return MEMCACHED_UNKNOWN_READ_FAILURE
;
381 if (memcached_string_check(&result
->value
,
382 bodylen
) != MEMCACHED_SUCCESS
)
383 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
385 char *vptr
= memcached_string_value_mutable(&result
->value
);
386 if (memcached_safe_read(ptr
, vptr
, bodylen
) != MEMCACHED_SUCCESS
)
387 return MEMCACHED_UNKNOWN_READ_FAILURE
;
389 memcached_string_set_length(&result
->value
, bodylen
);
392 case PROTOCOL_BINARY_CMD_INCREMENT
:
393 case PROTOCOL_BINARY_CMD_DECREMENT
:
395 if (bodylen
!= sizeof(uint64_t) || buffer_length
!= sizeof(uint64_t))
396 return MEMCACHED_PROTOCOL_ERROR
;
398 WATCHPOINT_ASSERT(bodylen
== buffer_length
);
400 if (memcached_safe_read(ptr
, &val
, sizeof(val
)) != MEMCACHED_SUCCESS
)
401 return MEMCACHED_UNKNOWN_READ_FAILURE
;
404 memcpy(buffer
, &val
, sizeof(val
));
407 case PROTOCOL_BINARY_CMD_VERSION
:
409 memset(buffer
, 0, buffer_length
);
410 if (bodylen
>= buffer_length
)
411 /* not enough space in buffer.. should not happen... */
412 return MEMCACHED_UNKNOWN_READ_FAILURE
;
413 else if (memcached_safe_read(ptr
, buffer
, bodylen
) != MEMCACHED_SUCCESS
)
414 return MEMCACHED_UNKNOWN_READ_FAILURE
;
417 case PROTOCOL_BINARY_CMD_FLUSH
:
418 case PROTOCOL_BINARY_CMD_QUIT
:
419 case PROTOCOL_BINARY_CMD_SET
:
420 case PROTOCOL_BINARY_CMD_ADD
:
421 case PROTOCOL_BINARY_CMD_REPLACE
:
422 case PROTOCOL_BINARY_CMD_APPEND
:
423 case PROTOCOL_BINARY_CMD_PREPEND
:
424 case PROTOCOL_BINARY_CMD_DELETE
:
426 WATCHPOINT_ASSERT(bodylen
== 0);
427 return MEMCACHED_SUCCESS
;
429 case PROTOCOL_BINARY_CMD_NOOP
:
431 WATCHPOINT_ASSERT(bodylen
== 0);
432 return MEMCACHED_END
;
434 case PROTOCOL_BINARY_CMD_STAT
:
437 return MEMCACHED_END
;
438 else if (bodylen
+ 1 > buffer_length
)
439 /* not enough space in buffer.. should not happen... */
440 return MEMCACHED_UNKNOWN_READ_FAILURE
;
443 size_t keylen
= header
.response
.keylen
;
444 memset(buffer
, 0, buffer_length
);
445 if (memcached_safe_read(ptr
, buffer
, keylen
) != MEMCACHED_SUCCESS
||
446 memcached_safe_read(ptr
, buffer
+ keylen
+ 1,
447 bodylen
- keylen
) != MEMCACHED_SUCCESS
)
448 return MEMCACHED_UNKNOWN_READ_FAILURE
;
454 /* Command not implemented yet! */
455 WATCHPOINT_ASSERT(0);
456 return MEMCACHED_PROTOCOL_ERROR
;
460 else if (header
.response
.bodylen
)
462 /* What should I do with the error message??? just discard it for now */
463 char hole
[SMALL_STRING_LEN
];
466 size_t nr
= (bodylen
> SMALL_STRING_LEN
) ? SMALL_STRING_LEN
: bodylen
;
467 if (memcached_safe_read(ptr
, hole
, nr
) != MEMCACHED_SUCCESS
)
468 return MEMCACHED_UNKNOWN_READ_FAILURE
;
469 bodylen
-= (uint32_t) nr
;
472 /* This might be an error from one of the quiet commands.. if
473 * so, just throw it away and get the next one. What about creating
474 * a callback to the user with the error information?
476 switch (header
.response
.opcode
)
478 case PROTOCOL_BINARY_CMD_SETQ
:
479 case PROTOCOL_BINARY_CMD_ADDQ
:
480 case PROTOCOL_BINARY_CMD_REPLACEQ
:
481 case PROTOCOL_BINARY_CMD_APPENDQ
:
482 case PROTOCOL_BINARY_CMD_PREPENDQ
:
483 return binary_read_one_response(ptr
, buffer
, buffer_length
, result
);
489 memcached_return_t rc
= MEMCACHED_SUCCESS
;
490 unlikely(header
.response
.status
!= 0)
491 switch (header
.response
.status
)
493 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
:
494 rc
= MEMCACHED_NOTFOUND
;
496 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
:
497 rc
= MEMCACHED_DATA_EXISTS
;
499 case PROTOCOL_BINARY_RESPONSE_NOT_STORED
:
500 rc
= MEMCACHED_NOTSTORED
;
502 case PROTOCOL_BINARY_RESPONSE_E2BIG
:
505 case PROTOCOL_BINARY_RESPONSE_ENOMEM
:
506 rc
= MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
508 case PROTOCOL_BINARY_RESPONSE_EINVAL
:
509 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
:
511 /* @todo fix the error mappings */
512 rc
= MEMCACHED_PROTOCOL_ERROR
;