1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 #include "libmemcached/protocol/common.h"
6 #include <sys/socket.h>
13 ** **********************************************************************
15 ** **********************************************************************
19 * Send a preformatted packet back to the client. If the connection is in
20 * pedantic mode, it will validate the packet and refuse to send it if it
21 * breaks the specification.
23 * @param cookie client identification
24 * @param request the original request packet
25 * @param response the packet to send
26 * @return The status of the operation
28 static protocol_binary_response_status
29 raw_response_handler(const void *cookie
,
30 protocol_binary_request_header
*request
,
31 protocol_binary_response_header
*response
)
33 memcached_protocol_client_st
*client
= (void*)cookie
;
35 if (client
->root
->pedantic
&&
36 !memcached_binary_protocol_pedantic_check_response(request
, response
))
38 return PROTOCOL_BINARY_RESPONSE_EINVAL
;
41 if (!client
->root
->drain(client
))
43 return PROTOCOL_BINARY_RESPONSE_EIO
;
46 size_t len
= sizeof(*response
) + htonl(response
->response
.bodylen
);
48 char *ptr
= (void*)response
;
50 if (client
->output
== NULL
)
52 /* I can write directly to the socket.... */
55 size_t num_bytes
= len
- offset
;
56 ssize_t nw
= client
->root
->send(client
,
62 if (errno
== EWOULDBLOCK
)
66 else if (errno
!= EINTR
)
69 return PROTOCOL_BINARY_RESPONSE_EIO
;
76 } while (offset
< len
);
79 return client
->root
->spool(client
, ptr
, len
- offset
);
83 * Version 0 of the interface is really low level and protocol specific,
84 * while the version 1 of the interface is more API focused. We need a
85 * way to translate between the command codes on the wire and the
86 * application level interface in V1, so let's just use the V0 of the
87 * interface as a map instead of creating a huuuge switch :-)
91 * Callback for the GET/GETQ/GETK and GETKQ responses
92 * @param cookie client identifier
93 * @param key the key for the item
94 * @param keylen the length of the key
95 * @param body the length of the body
96 * @param bodylen the length of the body
97 * @param flags the flags for the item
98 * @param cas the CAS id for the item
100 static protocol_binary_response_status
101 get_response_handler(const void *cookie
,
109 memcached_protocol_client_st
*client
= (void*)cookie
;
110 uint8_t opcode
= client
->current_command
->request
.opcode
;
112 if (opcode
== PROTOCOL_BINARY_CMD_GET
|| opcode
== PROTOCOL_BINARY_CMD_GETQ
)
117 protocol_binary_response_get response
= {
118 .message
.header
.response
= {
119 .magic
= PROTOCOL_BINARY_RES
,
121 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
122 .opaque
= client
->current_command
->request
.opaque
,
124 .keylen
= htons(keylen
),
126 .bodylen
= htonl(bodylen
+ keylen
+ 4),
130 response
.message
.body
.flags
= htonl(flags
);
132 protocol_binary_response_status rval
;
133 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
134 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
135 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
136 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
141 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
145 * Callback for the STAT responses
146 * @param cookie client identifier
147 * @param key the key for the item
148 * @param keylen the length of the key
149 * @param body the length of the body
150 * @param bodylen the length of the body
152 static protocol_binary_response_status
153 stat_response_handler(const void *cookie
,
159 memcached_protocol_client_st
*client
= (void*)cookie
;
161 protocol_binary_response_no_extras response
= {
162 .message
.header
.response
= {
163 .magic
= PROTOCOL_BINARY_RES
,
164 .opcode
= client
->current_command
->request
.opcode
,
165 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
166 .opaque
= client
->current_command
->request
.opaque
,
167 .keylen
= htons(keylen
),
168 .bodylen
= htonl(bodylen
+ keylen
),
172 protocol_binary_response_status rval
;
173 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
174 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
175 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
176 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
181 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
185 * Callback for the VERSION responses
186 * @param cookie client identifier
187 * @param text the length of the body
188 * @param textlen the length of the body
190 static protocol_binary_response_status
191 version_response_handler(const void *cookie
,
195 memcached_protocol_client_st
*client
= (void*)cookie
;
197 protocol_binary_response_no_extras response
= {
198 .message
.header
.response
= {
199 .magic
= PROTOCOL_BINARY_RES
,
200 .opcode
= client
->current_command
->request
.opcode
,
201 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
202 .opaque
= client
->current_command
->request
.opaque
,
203 .bodylen
= htonl(textlen
),
207 protocol_binary_response_status rval
;
208 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
209 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
210 (rval
= client
->root
->spool(client
, text
, textlen
)) != success
)
215 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
219 * Callback for ADD and ADDQ
220 * @param cookie the calling client
221 * @param header the add/addq command
222 * @param response_handler not used
223 * @return the result of the operation
225 static protocol_binary_response_status
226 add_command_handler(const void *cookie
,
227 protocol_binary_request_header
*header
,
228 memcached_binary_protocol_raw_response_handler response_handler
)
230 protocol_binary_response_status rval
;
232 memcached_protocol_client_st
*client
= (void*)cookie
;
233 if (client
->root
->callback
->interface
.v1
.add
!= NULL
)
235 uint16_t keylen
= ntohs(header
->request
.keylen
);
236 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
237 protocol_binary_request_add
*request
= (void*)header
;
238 uint32_t flags
= ntohl(request
->message
.body
.flags
);
239 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
240 char *key
= ((char*)header
) + sizeof(*header
) + 8;
241 char *data
= key
+ keylen
;
244 rval
= client
->root
->callback
->interface
.v1
.add(cookie
, key
, keylen
,
245 data
, datalen
, flags
,
248 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
249 header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
251 /* Send a positive request */
252 protocol_binary_response_no_extras response
= {
255 .magic
= PROTOCOL_BINARY_RES
,
256 .opcode
= PROTOCOL_BINARY_CMD_ADD
,
257 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
258 .opaque
= header
->request
.opaque
,
263 rval
= response_handler(cookie
, header
, (void*)&response
);
268 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
275 * Callback for DECREMENT and DECREMENTQ
276 * @param cookie the calling client
277 * @param header the command
278 * @param response_handler not used
279 * @return the result of the operation
281 static protocol_binary_response_status
282 decrement_command_handler(const void *cookie
,
283 protocol_binary_request_header
*header
,
284 memcached_binary_protocol_raw_response_handler response_handler
)
286 (void)response_handler
;
287 protocol_binary_response_status rval
;
289 memcached_protocol_client_st
*client
= (void*)cookie
;
290 if (client
->root
->callback
->interface
.v1
.decrement
!= NULL
)
292 uint16_t keylen
= ntohs(header
->request
.keylen
);
293 protocol_binary_request_decr
*request
= (void*)header
;
294 uint64_t init
= ntohll(request
->message
.body
.initial
);
295 uint64_t delta
= ntohll(request
->message
.body
.delta
);
296 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
297 void *key
= request
->bytes
+ sizeof(request
->bytes
);
301 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
302 delta
, init
, timeout
,
304 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
305 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
307 /* Send a positive request */
308 protocol_binary_response_decr response
= {
311 .magic
= PROTOCOL_BINARY_RES
,
312 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
313 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
314 .opaque
= header
->request
.opaque
,
318 .body
.value
= htonll(result
)
321 rval
= response_handler(cookie
, header
, (void*)&response
);
326 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
333 * Callback for DELETE and DELETEQ
334 * @param cookie the calling client
335 * @param header the command
336 * @param response_handler not used
337 * @return the result of the operation
339 static protocol_binary_response_status
340 delete_command_handler(const void *cookie
,
341 protocol_binary_request_header
*header
,
342 memcached_binary_protocol_raw_response_handler response_handler
)
344 (void)response_handler
;
345 protocol_binary_response_status rval
;
347 memcached_protocol_client_st
*client
= (void*)cookie
;
348 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
350 uint16_t keylen
= ntohs(header
->request
.keylen
);
351 void *key
= (header
+ 1);
352 uint64_t cas
= ntohll(header
->request
.cas
);
353 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
354 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
355 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
357 /* Send a positive request */
358 protocol_binary_response_no_extras response
= {
361 .magic
= PROTOCOL_BINARY_RES
,
362 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
363 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
364 .opaque
= header
->request
.opaque
,
368 rval
= response_handler(cookie
, header
, (void*)&response
);
373 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
380 * Callback for FLUSH and FLUSHQ
381 * @param cookie the calling client
382 * @param header the command
383 * @param response_handler not used
384 * @return the result of the operation
386 static protocol_binary_response_status
387 flush_command_handler(const void *cookie
,
388 protocol_binary_request_header
*header
,
389 memcached_binary_protocol_raw_response_handler response_handler
)
391 (void)response_handler
;
392 protocol_binary_response_status rval
;
394 memcached_protocol_client_st
*client
= (void*)cookie
;
395 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
397 protocol_binary_request_flush
*flush
= (void*)header
;
399 if (htonl(header
->request
.bodylen
) == 4)
401 timeout
= ntohl(flush
->message
.body
.expiration
);
404 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
405 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
406 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
408 /* Send a positive request */
409 protocol_binary_response_no_extras response
= {
412 .magic
= PROTOCOL_BINARY_RES
,
413 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
414 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
415 .opaque
= header
->request
.opaque
,
419 rval
= response_handler(cookie
, header
, (void*)&response
);
424 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
431 * Callback for GET, GETK, GETQ, GETKQ
432 * @param cookie the calling client
433 * @param header the command
434 * @param response_handler not used
435 * @return the result of the operation
437 static protocol_binary_response_status
438 get_command_handler(const void *cookie
,
439 protocol_binary_request_header
*header
,
440 memcached_binary_protocol_raw_response_handler response_handler
)
442 (void)response_handler
;
443 protocol_binary_response_status rval
;
445 memcached_protocol_client_st
*client
= (void*)cookie
;
446 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
448 uint16_t keylen
= ntohs(header
->request
.keylen
);
449 void *key
= (header
+ 1);
450 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
451 get_response_handler
);
453 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
454 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
455 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
457 /* Quiet commands shouldn't respond on cache misses */
458 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
463 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
470 * Callback for INCREMENT and INCREMENTQ
471 * @param cookie the calling client
472 * @param header the command
473 * @param response_handler not used
474 * @return the result of the operation
476 static protocol_binary_response_status
477 increment_command_handler(const void *cookie
,
478 protocol_binary_request_header
*header
,
479 memcached_binary_protocol_raw_response_handler response_handler
)
481 (void)response_handler
;
482 protocol_binary_response_status rval
;
484 memcached_protocol_client_st
*client
= (void*)cookie
;
485 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
487 uint16_t keylen
= ntohs(header
->request
.keylen
);
488 protocol_binary_request_incr
*request
= (void*)header
;
489 uint64_t init
= ntohll(request
->message
.body
.initial
);
490 uint64_t delta
= ntohll(request
->message
.body
.delta
);
491 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
492 void *key
= request
->bytes
+ sizeof(request
->bytes
);
496 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
497 delta
, init
, timeout
,
499 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
500 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
502 /* Send a positive request */
503 protocol_binary_response_incr response
= {
506 .magic
= PROTOCOL_BINARY_RES
,
507 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
508 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
509 .opaque
= header
->request
.opaque
,
513 .body
.value
= htonll(result
)
517 rval
= response_handler(cookie
, header
, (void*)&response
);
522 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
529 * Callback for noop. Inform the v1 interface about the noop packet, and
530 * create and send a packet back to the client
532 * @param cookie the calling client
533 * @param header the command
534 * @param response_handler the response handler
535 * @return the result of the operation
537 static protocol_binary_response_status
538 noop_command_handler(const void *cookie
,
539 protocol_binary_request_header
*header
,
540 memcached_binary_protocol_raw_response_handler response_handler
)
542 memcached_protocol_client_st
*client
= (void*)cookie
;
543 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
545 client
->root
->callback
->interface
.v1
.noop(cookie
);
548 protocol_binary_response_no_extras response
= {
551 .magic
= PROTOCOL_BINARY_RES
,
552 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
553 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
554 .opaque
= header
->request
.opaque
,
559 return response_handler(cookie
, header
, (void*)&response
);
563 * Callback for APPEND and APPENDQ
564 * @param cookie the calling client
565 * @param header the command
566 * @param response_handler not used
567 * @return the result of the operation
569 static protocol_binary_response_status
570 append_command_handler(const void *cookie
,
571 protocol_binary_request_header
*header
,
572 memcached_binary_protocol_raw_response_handler response_handler
)
574 (void)response_handler
;
575 protocol_binary_response_status rval
;
577 memcached_protocol_client_st
*client
= (void*)cookie
;
578 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
580 uint16_t keylen
= ntohs(header
->request
.keylen
);
581 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
582 char *key
= (void*)(header
+ 1);
583 char *data
= key
+ keylen
;
584 uint64_t cas
= ntohll(header
->request
.cas
);
587 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
590 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
591 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
593 /* Send a positive request */
594 protocol_binary_response_no_extras response
= {
597 .magic
= PROTOCOL_BINARY_RES
,
598 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
599 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
600 .opaque
= header
->request
.opaque
,
601 .cas
= ntohll(result_cas
),
605 rval
= response_handler(cookie
, header
, (void*)&response
);
610 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
617 * Callback for PREPEND and PREPENDQ
618 * @param cookie the calling client
619 * @param header the command
620 * @param response_handler not used
621 * @return the result of the operation
623 static protocol_binary_response_status
624 prepend_command_handler(const void *cookie
,
625 protocol_binary_request_header
*header
,
626 memcached_binary_protocol_raw_response_handler response_handler
)
628 (void)response_handler
;
629 protocol_binary_response_status rval
;
631 memcached_protocol_client_st
*client
= (void*)cookie
;
632 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
634 uint16_t keylen
= ntohs(header
->request
.keylen
);
635 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
636 char *key
= (char*)(header
+ 1);
637 char *data
= key
+ keylen
;
638 uint64_t cas
= ntohll(header
->request
.cas
);
640 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
643 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
644 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
646 /* Send a positive request */
647 protocol_binary_response_no_extras response
= {
650 .magic
= PROTOCOL_BINARY_RES
,
651 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
652 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
653 .opaque
= header
->request
.opaque
,
654 .cas
= ntohll(result_cas
),
658 rval
= response_handler(cookie
, header
, (void*)&response
);
663 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
670 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
671 * @param cookie the calling client
672 * @param header the command
673 * @param response_handler not used
674 * @return the result of the operation
676 static protocol_binary_response_status
677 quit_command_handler(const void *cookie
,
678 protocol_binary_request_header
*header
,
679 memcached_binary_protocol_raw_response_handler response_handler
)
681 memcached_protocol_client_st
*client
= (void*)cookie
;
682 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
684 client
->root
->callback
->interface
.v1
.quit(cookie
);
687 protocol_binary_response_no_extras response
= {
690 .magic
= PROTOCOL_BINARY_RES
,
691 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
692 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
693 .opaque
= header
->request
.opaque
698 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
700 response_handler(cookie
, header
, (void*)&response
);
703 /* I need a better way to signal to close the connection */
704 return PROTOCOL_BINARY_RESPONSE_EIO
;
708 * Callback for REPLACE and REPLACEQ
709 * @param cookie the calling client
710 * @param header the command
711 * @param response_handler not used
712 * @return the result of the operation
714 static protocol_binary_response_status
715 replace_command_handler(const void *cookie
,
716 protocol_binary_request_header
*header
,
717 memcached_binary_protocol_raw_response_handler response_handler
)
719 (void)response_handler
;
720 protocol_binary_response_status rval
;
722 memcached_protocol_client_st
*client
= (void*)cookie
;
723 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
725 uint16_t keylen
= ntohs(header
->request
.keylen
);
726 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
727 protocol_binary_request_replace
*request
= (void*)header
;
728 uint32_t flags
= ntohl(request
->message
.body
.flags
);
729 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
730 char *key
= ((char*)header
) + sizeof(*header
) + 8;
731 char *data
= key
+ keylen
;
732 uint64_t cas
= ntohll(header
->request
.cas
);
735 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
736 data
, datalen
, flags
,
739 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
740 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
742 /* Send a positive request */
743 protocol_binary_response_no_extras response
= {
746 .magic
= PROTOCOL_BINARY_RES
,
747 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
748 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
749 .opaque
= header
->request
.opaque
,
750 .cas
= ntohll(result_cas
),
754 rval
= response_handler(cookie
, header
, (void*)&response
);
759 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
766 * Callback for SET and SETQ
767 * @param cookie the calling client
768 * @param header the command
769 * @param response_handler not used
770 * @return the result of the operation
772 static protocol_binary_response_status
773 set_command_handler(const void *cookie
,
774 protocol_binary_request_header
*header
,
775 memcached_binary_protocol_raw_response_handler response_handler
)
777 (void)response_handler
;
778 protocol_binary_response_status rval
;
780 memcached_protocol_client_st
*client
= (void*)cookie
;
781 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
783 uint16_t keylen
= ntohs(header
->request
.keylen
);
784 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
785 protocol_binary_request_replace
*request
= (void*)header
;
786 uint32_t flags
= ntohl(request
->message
.body
.flags
);
787 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
788 char *key
= ((char*)header
) + sizeof(*header
) + 8;
789 char *data
= key
+ keylen
;
790 uint64_t cas
= ntohll(header
->request
.cas
);
794 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
795 data
, datalen
, flags
,
796 timeout
, cas
, &result_cas
);
797 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
798 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
800 /* Send a positive request */
801 protocol_binary_response_no_extras response
= {
804 .magic
= PROTOCOL_BINARY_RES
,
805 .opcode
= PROTOCOL_BINARY_CMD_SET
,
806 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
807 .opaque
= header
->request
.opaque
,
808 .cas
= ntohll(result_cas
),
812 rval
= response_handler(cookie
, header
, (void*)&response
);
817 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
825 * @param cookie the calling client
826 * @param header the command
827 * @param response_handler not used
828 * @return the result of the operation
830 static protocol_binary_response_status
831 stat_command_handler(const void *cookie
,
832 protocol_binary_request_header
*header
,
833 memcached_binary_protocol_raw_response_handler response_handler
)
835 (void)response_handler
;
836 protocol_binary_response_status rval
;
838 memcached_protocol_client_st
*client
= (void*)cookie
;
839 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
841 uint16_t keylen
= ntohs(header
->request
.keylen
);
843 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
846 stat_response_handler
);
850 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
857 * Callback for VERSION
858 * @param cookie the calling client
859 * @param header the command
860 * @param response_handler not used
861 * @return the result of the operation
863 static protocol_binary_response_status
864 version_command_handler(const void *cookie
,
865 protocol_binary_request_header
*header
,
866 memcached_binary_protocol_raw_response_handler response_handler
)
868 (void)response_handler
;
870 protocol_binary_response_status rval
;
872 memcached_protocol_client_st
*client
= (void*)cookie
;
873 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
875 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
876 version_response_handler
);
880 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
887 * The map to remap between the com codes and the v1 logical setting
889 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
890 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
891 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
892 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
893 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
894 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
895 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
896 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
897 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
898 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
899 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
900 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
901 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
902 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
903 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
904 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
905 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
906 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
907 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
908 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
909 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
910 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
911 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
912 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
913 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
914 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
915 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
916 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
920 * Try to execute a command. Fire the pre/post functions and the specialized
921 * handler function if it's set. If not, the unknown probe should be fired
923 * @param client the client connection to operate on
924 * @param header the command to execute
925 * @return true if success or false if a fatal error occured so that the
926 * connection should be shut down.
928 static protocol_binary_response_status
execute_command(memcached_protocol_client_st
*client
, protocol_binary_request_header
*header
)
930 if (client
->root
->pedantic
&&
931 memcached_binary_protocol_pedantic_check_request(header
))
933 /* @todo return invalid command packet */
936 /* we got all data available, execute the callback! */
937 if (client
->root
->callback
->pre_execute
!= NULL
)
939 client
->root
->callback
->pre_execute(client
, header
);
942 protocol_binary_response_status rval
;
943 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
944 uint8_t cc
= header
->request
.opcode
;
946 switch (client
->root
->callback
->interface_version
)
949 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
950 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
954 if (comcode_v0_v1_remap
[cc
] != NULL
) {
955 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
959 /* Unknown interface.
960 * It should be impossible to get here so I'll just call abort
961 * to avoid getting a compiler warning :-)
967 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
968 client
->root
->callback
->unknown
!= NULL
)
970 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
973 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
974 rval
!= PROTOCOL_BINARY_RESPONSE_EIO
&&
975 rval
!= PROTOCOL_BINARY_RESPONSE_PAUSE
)
977 protocol_binary_response_no_extras response
= {
980 .magic
= PROTOCOL_BINARY_RES
,
982 .status
= htons(rval
),
983 .opaque
= header
->request
.opaque
,
987 rval
= raw_response_handler(client
, header
, (void*)&response
);
990 if (client
->root
->callback
->post_execute
!= NULL
)
992 client
->root
->callback
->post_execute(client
, header
);
999 ** **********************************************************************
1000 ** "PROTOECTED" INTERFACE
1001 ** **********************************************************************
1003 memcached_protocol_event_t
memcached_binary_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
, void **endptr
)
1005 /* try to parse all of the received packets */
1006 protocol_binary_request_header
*header
;
1007 header
= (void*)client
->root
->input_buffer
;
1008 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1010 client
->error
= EINVAL
;
1011 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1013 ssize_t len
= *length
;
1015 while (len
>= (ssize_t
)sizeof(*header
) &&
1016 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1018 /* I have the complete package */
1019 client
->current_command
= header
;
1020 protocol_binary_response_status rv
= execute_command(client
, header
);
1022 if (rv
== PROTOCOL_BINARY_RESPONSE_EIO
)
1025 *endptr
= (void*)header
;
1026 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1027 } else if (rv
== PROTOCOL_BINARY_RESPONSE_PAUSE
)
1028 return MEMCACHED_PROTOCOL_PAUSE_EVENT
;
1030 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1034 intptr_t ptr
= (intptr_t)header
;
1043 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1044 header
= (void*)client
->root
->input_buffer
;
1048 *endptr
= (void*)header
;
1051 return MEMCACHED_PROTOCOL_READ_EVENT
;
1055 ** **********************************************************************
1057 ** **********************************************************************
1059 memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(memcached_protocol_st
*instance
)
1061 return instance
->callback
;
1064 void memcached_binary_protocol_set_callbacks(memcached_protocol_st
*instance
, memcached_binary_protocol_callback_st
*callback
)
1066 instance
->callback
= callback
;
1069 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1072 return raw_response_handler
;
1075 void memcached_binary_protocol_set_pedantic(memcached_protocol_st
*instance
, bool enable
)
1077 instance
->pedantic
= enable
;
1080 bool memcached_binary_protocol_get_pedantic(memcached_protocol_st
*instance
)
1082 return instance
->pedantic
;