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 struct 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 struct 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),
128 .message
.body
.flags
= htonl(flags
),
131 protocol_binary_response_status rval
;
132 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
133 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
134 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
135 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
140 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
144 * Callback for the STAT responses
145 * @param cookie client identifier
146 * @param key the key for the item
147 * @param keylen the length of the key
148 * @param body the length of the body
149 * @param bodylen the length of the body
151 static protocol_binary_response_status
152 stat_response_handler(const void *cookie
,
158 struct memcached_protocol_client_st
*client
= (void*)cookie
;
160 protocol_binary_response_no_extras response
= {
161 .message
.header
.response
= {
162 .magic
= PROTOCOL_BINARY_RES
,
163 .opcode
= client
->current_command
->request
.opcode
,
164 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
165 .opaque
= client
->current_command
->request
.opaque
,
166 .keylen
= htons(keylen
),
167 .bodylen
= htonl(bodylen
+ keylen
),
171 protocol_binary_response_status rval
;
172 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
173 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
174 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
175 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
180 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
184 * Callback for the VERSION responses
185 * @param cookie client identifier
186 * @param text the length of the body
187 * @param textlen the length of the body
189 static protocol_binary_response_status
190 version_response_handler(const void *cookie
,
194 struct memcached_protocol_client_st
*client
= (void*)cookie
;
196 protocol_binary_response_no_extras response
= {
197 .message
.header
.response
= {
198 .magic
= PROTOCOL_BINARY_RES
,
199 .opcode
= client
->current_command
->request
.opcode
,
200 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
201 .opaque
= client
->current_command
->request
.opaque
,
202 .bodylen
= htonl(textlen
),
206 protocol_binary_response_status rval
;
207 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
208 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
209 (rval
= client
->root
->spool(client
, text
, textlen
)) != success
)
214 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
218 * Callback for ADD and ADDQ
219 * @param cookie the calling client
220 * @param header the add/addq command
221 * @param response_handler not used
222 * @return the result of the operation
224 static protocol_binary_response_status
225 add_command_handler(const void *cookie
,
226 protocol_binary_request_header
*header
,
227 memcached_binary_protocol_raw_response_handler response_handler
)
229 protocol_binary_response_status rval
;
231 struct memcached_protocol_client_st
*client
= (void*)cookie
;
232 if (client
->root
->callback
->interface
.v1
.add
!= NULL
)
234 uint16_t keylen
= ntohs(header
->request
.keylen
);
235 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
236 protocol_binary_request_add
*request
= (void*)header
;
237 uint32_t flags
= ntohl(request
->message
.body
.flags
);
238 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
239 char *key
= ((char*)header
) + sizeof(*header
) + 8;
240 char *data
= key
+ keylen
;
243 rval
= client
->root
->callback
->interface
.v1
.add(cookie
, key
, keylen
,
244 data
, datalen
, flags
,
247 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
248 header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
250 /* Send a positive request */
251 protocol_binary_response_no_extras response
= {
254 .magic
= PROTOCOL_BINARY_RES
,
255 .opcode
= PROTOCOL_BINARY_CMD_ADD
,
256 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
257 .opaque
= header
->request
.opaque
,
262 rval
= response_handler(cookie
, header
, (void*)&response
);
267 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
274 * Callback for DECREMENT and DECREMENTQ
275 * @param cookie the calling client
276 * @param header the command
277 * @param response_handler not used
278 * @return the result of the operation
280 static protocol_binary_response_status
281 decrement_command_handler(const void *cookie
,
282 protocol_binary_request_header
*header
,
283 memcached_binary_protocol_raw_response_handler response_handler
)
285 (void)response_handler
;
286 protocol_binary_response_status rval
;
288 struct memcached_protocol_client_st
*client
= (void*)cookie
;
289 if (client
->root
->callback
->interface
.v1
.decrement
!= NULL
)
291 uint16_t keylen
= ntohs(header
->request
.keylen
);
292 protocol_binary_request_decr
*request
= (void*)header
;
293 uint64_t init
= ntohll(request
->message
.body
.initial
);
294 uint64_t delta
= ntohll(request
->message
.body
.delta
);
295 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
296 void *key
= request
->bytes
+ sizeof(request
->bytes
);
300 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
301 delta
, init
, timeout
,
303 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
304 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
306 /* Send a positive request */
307 protocol_binary_response_decr response
= {
310 .magic
= PROTOCOL_BINARY_RES
,
311 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
312 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
313 .opaque
= header
->request
.opaque
,
317 .body
.value
= htonll(result
)
320 rval
= response_handler(cookie
, header
, (void*)&response
);
325 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
332 * Callback for DELETE and DELETEQ
333 * @param cookie the calling client
334 * @param header the command
335 * @param response_handler not used
336 * @return the result of the operation
338 static protocol_binary_response_status
339 delete_command_handler(const void *cookie
,
340 protocol_binary_request_header
*header
,
341 memcached_binary_protocol_raw_response_handler response_handler
)
343 (void)response_handler
;
344 protocol_binary_response_status rval
;
346 struct memcached_protocol_client_st
*client
= (void*)cookie
;
347 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
349 uint16_t keylen
= ntohs(header
->request
.keylen
);
350 void *key
= (header
+ 1);
351 uint64_t cas
= ntohll(header
->request
.cas
);
352 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
353 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
354 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
356 /* Send a positive request */
357 protocol_binary_response_no_extras response
= {
360 .magic
= PROTOCOL_BINARY_RES
,
361 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
362 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
363 .opaque
= header
->request
.opaque
,
367 rval
= response_handler(cookie
, header
, (void*)&response
);
372 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
379 * Callback for FLUSH and FLUSHQ
380 * @param cookie the calling client
381 * @param header the command
382 * @param response_handler not used
383 * @return the result of the operation
385 static protocol_binary_response_status
386 flush_command_handler(const void *cookie
,
387 protocol_binary_request_header
*header
,
388 memcached_binary_protocol_raw_response_handler response_handler
)
390 (void)response_handler
;
391 protocol_binary_response_status rval
;
393 struct memcached_protocol_client_st
*client
= (void*)cookie
;
394 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
396 protocol_binary_request_flush
*flush
= (void*)header
;
398 if (htonl(header
->request
.bodylen
) == 4)
400 timeout
= ntohl(flush
->message
.body
.expiration
);
403 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
404 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
405 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
407 /* Send a positive request */
408 protocol_binary_response_no_extras response
= {
411 .magic
= PROTOCOL_BINARY_RES
,
412 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
413 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
414 .opaque
= header
->request
.opaque
,
418 rval
= response_handler(cookie
, header
, (void*)&response
);
423 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
430 * Callback for GET, GETK, GETQ, GETKQ
431 * @param cookie the calling client
432 * @param header the command
433 * @param response_handler not used
434 * @return the result of the operation
436 static protocol_binary_response_status
437 get_command_handler(const void *cookie
,
438 protocol_binary_request_header
*header
,
439 memcached_binary_protocol_raw_response_handler response_handler
)
441 (void)response_handler
;
442 protocol_binary_response_status rval
;
444 struct memcached_protocol_client_st
*client
= (void*)cookie
;
445 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
447 uint16_t keylen
= ntohs(header
->request
.keylen
);
448 void *key
= (header
+ 1);
449 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
450 get_response_handler
);
452 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
453 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
454 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
456 /* Quiet commands shouldn't respond on cache misses */
457 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
462 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
469 * Callback for INCREMENT and INCREMENTQ
470 * @param cookie the calling client
471 * @param header the command
472 * @param response_handler not used
473 * @return the result of the operation
475 static protocol_binary_response_status
476 increment_command_handler(const void *cookie
,
477 protocol_binary_request_header
*header
,
478 memcached_binary_protocol_raw_response_handler response_handler
)
480 (void)response_handler
;
481 protocol_binary_response_status rval
;
483 struct memcached_protocol_client_st
*client
= (void*)cookie
;
484 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
486 uint16_t keylen
= ntohs(header
->request
.keylen
);
487 protocol_binary_request_incr
*request
= (void*)header
;
488 uint64_t init
= ntohll(request
->message
.body
.initial
);
489 uint64_t delta
= ntohll(request
->message
.body
.delta
);
490 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
491 void *key
= request
->bytes
+ sizeof(request
->bytes
);
495 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
496 delta
, init
, timeout
,
498 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
499 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
501 /* Send a positive request */
502 protocol_binary_response_incr response
= {
505 .magic
= PROTOCOL_BINARY_RES
,
506 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
507 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
508 .opaque
= header
->request
.opaque
,
512 .body
.value
= htonll(result
)
516 rval
= response_handler(cookie
, header
, (void*)&response
);
521 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
528 * Callback for noop. Inform the v1 interface about the noop packet, and
529 * create and send a packet back to the client
531 * @param cookie the calling client
532 * @param header the command
533 * @param response_handler the response handler
534 * @return the result of the operation
536 static protocol_binary_response_status
537 noop_command_handler(const void *cookie
,
538 protocol_binary_request_header
*header
,
539 memcached_binary_protocol_raw_response_handler response_handler
)
541 struct memcached_protocol_client_st
*client
= (void*)cookie
;
542 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
544 client
->root
->callback
->interface
.v1
.noop(cookie
);
547 protocol_binary_response_no_extras response
= {
550 .magic
= PROTOCOL_BINARY_RES
,
551 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
552 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
553 .opaque
= header
->request
.opaque
,
558 return response_handler(cookie
, header
, (void*)&response
);
562 * Callback for APPEND and APPENDQ
563 * @param cookie the calling client
564 * @param header the command
565 * @param response_handler not used
566 * @return the result of the operation
568 static protocol_binary_response_status
569 append_command_handler(const void *cookie
,
570 protocol_binary_request_header
*header
,
571 memcached_binary_protocol_raw_response_handler response_handler
)
573 (void)response_handler
;
574 protocol_binary_response_status rval
;
576 struct memcached_protocol_client_st
*client
= (void*)cookie
;
577 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
579 uint16_t keylen
= ntohs(header
->request
.keylen
);
580 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
581 char *key
= (void*)(header
+ 1);
582 char *data
= key
+ keylen
;
583 uint64_t cas
= ntohll(header
->request
.cas
);
586 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
589 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
590 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
592 /* Send a positive request */
593 protocol_binary_response_no_extras response
= {
596 .magic
= PROTOCOL_BINARY_RES
,
597 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
598 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
599 .opaque
= header
->request
.opaque
,
600 .cas
= ntohll(result_cas
),
604 rval
= response_handler(cookie
, header
, (void*)&response
);
609 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
616 * Callback for PREPEND and PREPENDQ
617 * @param cookie the calling client
618 * @param header the command
619 * @param response_handler not used
620 * @return the result of the operation
622 static protocol_binary_response_status
623 prepend_command_handler(const void *cookie
,
624 protocol_binary_request_header
*header
,
625 memcached_binary_protocol_raw_response_handler response_handler
)
627 (void)response_handler
;
628 protocol_binary_response_status rval
;
630 struct memcached_protocol_client_st
*client
= (void*)cookie
;
631 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
633 uint16_t keylen
= ntohs(header
->request
.keylen
);
634 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
635 char *key
= (char*)(header
+ 1);
636 char *data
= key
+ keylen
;
637 uint64_t cas
= ntohll(header
->request
.cas
);
639 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
642 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
643 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
645 /* Send a positive request */
646 protocol_binary_response_no_extras response
= {
649 .magic
= PROTOCOL_BINARY_RES
,
650 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
651 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
652 .opaque
= header
->request
.opaque
,
653 .cas
= ntohll(result_cas
),
657 rval
= response_handler(cookie
, header
, (void*)&response
);
662 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
669 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
670 * @param cookie the calling client
671 * @param header the command
672 * @param response_handler not used
673 * @return the result of the operation
675 static protocol_binary_response_status
676 quit_command_handler(const void *cookie
,
677 protocol_binary_request_header
*header
,
678 memcached_binary_protocol_raw_response_handler response_handler
)
680 struct memcached_protocol_client_st
*client
= (void*)cookie
;
681 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
683 client
->root
->callback
->interface
.v1
.quit(cookie
);
686 protocol_binary_response_no_extras response
= {
689 .magic
= PROTOCOL_BINARY_RES
,
690 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
691 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
692 .opaque
= header
->request
.opaque
697 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
699 response_handler(cookie
, header
, (void*)&response
);
702 /* I need a better way to signal to close the connection */
703 return PROTOCOL_BINARY_RESPONSE_EIO
;
707 * Callback for REPLACE and REPLACEQ
708 * @param cookie the calling client
709 * @param header the command
710 * @param response_handler not used
711 * @return the result of the operation
713 static protocol_binary_response_status
714 replace_command_handler(const void *cookie
,
715 protocol_binary_request_header
*header
,
716 memcached_binary_protocol_raw_response_handler response_handler
)
718 (void)response_handler
;
719 protocol_binary_response_status rval
;
721 struct memcached_protocol_client_st
*client
= (void*)cookie
;
722 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
724 uint16_t keylen
= ntohs(header
->request
.keylen
);
725 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
726 protocol_binary_request_replace
*request
= (void*)header
;
727 uint32_t flags
= ntohl(request
->message
.body
.flags
);
728 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
729 char *key
= ((char*)header
) + sizeof(*header
) + 8;
730 char *data
= key
+ keylen
;
731 uint64_t cas
= ntohll(header
->request
.cas
);
734 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
735 data
, datalen
, flags
,
738 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
739 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
741 /* Send a positive request */
742 protocol_binary_response_no_extras response
= {
745 .magic
= PROTOCOL_BINARY_RES
,
746 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
747 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
748 .opaque
= header
->request
.opaque
,
749 .cas
= ntohll(result_cas
),
753 rval
= response_handler(cookie
, header
, (void*)&response
);
758 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
765 * Callback for SET and SETQ
766 * @param cookie the calling client
767 * @param header the command
768 * @param response_handler not used
769 * @return the result of the operation
771 static protocol_binary_response_status
772 set_command_handler(const void *cookie
,
773 protocol_binary_request_header
*header
,
774 memcached_binary_protocol_raw_response_handler response_handler
)
776 (void)response_handler
;
777 protocol_binary_response_status rval
;
779 struct memcached_protocol_client_st
*client
= (void*)cookie
;
780 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
782 uint16_t keylen
= ntohs(header
->request
.keylen
);
783 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
784 protocol_binary_request_replace
*request
= (void*)header
;
785 uint32_t flags
= ntohl(request
->message
.body
.flags
);
786 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
787 char *key
= ((char*)header
) + sizeof(*header
) + 8;
788 char *data
= key
+ keylen
;
789 uint64_t cas
= ntohll(header
->request
.cas
);
793 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
794 data
, datalen
, flags
,
795 timeout
, cas
, &result_cas
);
796 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
797 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
799 /* Send a positive request */
800 protocol_binary_response_no_extras response
= {
803 .magic
= PROTOCOL_BINARY_RES
,
804 .opcode
= PROTOCOL_BINARY_CMD_SET
,
805 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
806 .opaque
= header
->request
.opaque
,
807 .cas
= ntohll(result_cas
),
811 rval
= response_handler(cookie
, header
, (void*)&response
);
816 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
824 * @param cookie the calling client
825 * @param header the command
826 * @param response_handler not used
827 * @return the result of the operation
829 static protocol_binary_response_status
830 stat_command_handler(const void *cookie
,
831 protocol_binary_request_header
*header
,
832 memcached_binary_protocol_raw_response_handler response_handler
)
834 (void)response_handler
;
835 protocol_binary_response_status rval
;
837 struct memcached_protocol_client_st
*client
= (void*)cookie
;
838 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
840 uint16_t keylen
= ntohs(header
->request
.keylen
);
842 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
845 stat_response_handler
);
849 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
856 * Callback for VERSION
857 * @param cookie the calling client
858 * @param header the command
859 * @param response_handler not used
860 * @return the result of the operation
862 static protocol_binary_response_status
863 version_command_handler(const void *cookie
,
864 protocol_binary_request_header
*header
,
865 memcached_binary_protocol_raw_response_handler response_handler
)
867 (void)response_handler
;
869 protocol_binary_response_status rval
;
871 struct memcached_protocol_client_st
*client
= (void*)cookie
;
872 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
874 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
875 version_response_handler
);
879 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
886 * The map to remap between the com codes and the v1 logical setting
888 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
889 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
890 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
891 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
892 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
893 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
894 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
895 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
896 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
897 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
898 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
899 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
900 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
901 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
902 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
903 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
904 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
905 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
906 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
907 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
908 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
909 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
910 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
911 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
912 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
913 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
914 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
915 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
919 * Try to execute a command. Fire the pre/post functions and the specialized
920 * handler function if it's set. If not, the unknown probe should be fired
922 * @param client the client connection to operate on
923 * @param header the command to execute
924 * @return true if success or false if a fatal error occured so that the
925 * connection should be shut down.
927 static bool execute_command(struct memcached_protocol_client_st
*client
, protocol_binary_request_header
*header
)
929 if (client
->root
->pedantic
&&
930 memcached_binary_protocol_pedantic_check_request(header
))
932 /* @todo return invalid command packet */
935 /* we got all data available, execute the callback! */
936 if (client
->root
->callback
->pre_execute
!= NULL
)
938 client
->root
->callback
->pre_execute(client
, header
);
941 protocol_binary_response_status rval
;
942 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
943 uint8_t cc
= header
->request
.opcode
;
945 switch (client
->root
->callback
->interface_version
)
948 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
949 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
953 if (comcode_v0_v1_remap
[cc
] != NULL
) {
954 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
958 /* Unknown interface.
959 * It should be impossible to get here so I'll just call abort
960 * to avoid getting a compiler warning :-)
966 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
967 client
->root
->callback
->unknown
!= NULL
)
969 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
972 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
973 rval
!= PROTOCOL_BINARY_RESPONSE_EIO
)
975 protocol_binary_response_no_extras response
= {
978 .magic
= PROTOCOL_BINARY_RES
,
980 .status
= htons(rval
),
981 .opaque
= header
->request
.opaque
,
985 rval
= raw_response_handler(client
, header
, (void*)&response
);
988 if (client
->root
->callback
->post_execute
!= NULL
)
990 client
->root
->callback
->post_execute(client
, header
);
993 return rval
!= PROTOCOL_BINARY_RESPONSE_EIO
;
997 ** **********************************************************************
998 ** "PROTOECTED" INTERFACE
999 ** **********************************************************************
1001 enum MEMCACHED_PROTOCOL_EVENT
memcached_binary_protocol_process_data(struct memcached_protocol_client_st
*client
, ssize_t
*length
, void **endptr
)
1003 /* try to parse all of the received packets */
1004 protocol_binary_request_header
*header
;
1005 header
= (void*)client
->root
->input_buffer
;
1006 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1008 client
->error
= EINVAL
;
1011 ssize_t len
= *length
;
1013 while (len
>= (ssize_t
)sizeof(*header
) &&
1014 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1016 /* I have the complete package */
1017 client
->current_command
= header
;
1018 if (!execute_command(client
, header
))
1021 *endptr
= (void*)header
;
1025 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1029 intptr_t ptr
= (intptr_t)header
;
1038 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1039 header
= (void*)client
->root
->input_buffer
;
1045 *endptr
= (void*)header
;
1051 ** **********************************************************************
1053 ** **********************************************************************
1055 struct memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(struct memcached_protocol_st
*instance
)
1057 return instance
->callback
;
1060 void memcached_binary_protocol_set_callbacks(struct memcached_protocol_st
*instance
, struct memcached_binary_protocol_callback_st
*callback
)
1062 instance
->callback
= callback
;
1065 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1068 return raw_response_handler
;
1071 void memcached_binary_protocol_set_pedantic(struct memcached_protocol_st
*instance
, bool enable
)
1073 instance
->pedantic
= enable
;
1076 bool memcached_binary_protocol_get_pedantic(struct memcached_protocol_st
*instance
)
1078 return instance
->pedantic
;