1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include <libmemcached/protocol/common.h>
38 #include <libmemcached/byteorder.h>
41 #include <sys/types.h>
48 ** **********************************************************************
50 ** **********************************************************************
54 * Send a preformatted packet back to the client. If the connection is in
55 * pedantic mode, it will validate the packet and refuse to send it if it
56 * breaks the specification.
58 * @param cookie client identification
59 * @param request the original request packet
60 * @param response the packet to send
61 * @return The status of the operation
63 static protocol_binary_response_status
64 raw_response_handler(const void *cookie
,
65 protocol_binary_request_header
*request
,
66 protocol_binary_response_header
*response
)
68 memcached_protocol_client_st
*client
= (void*)cookie
;
70 if (client
->root
->pedantic
&&
71 !memcached_binary_protocol_pedantic_check_response(request
, response
))
73 return PROTOCOL_BINARY_RESPONSE_EINVAL
;
76 if (!client
->root
->drain(client
))
78 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
81 size_t len
= sizeof(*response
) + htonl(response
->response
.bodylen
);
83 char *ptr
= (void*)response
;
85 if (client
->output
== NULL
)
87 /* I can write directly to the socket.... */
90 size_t num_bytes
= len
- offset
;
91 ssize_t nw
= client
->root
->send(client
,
97 if (get_socket_errno() == EWOULDBLOCK
)
101 else if (get_socket_errno() != EINTR
)
103 client
->error
= errno
;
104 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
109 offset
+= (size_t)nw
;
111 } while (offset
< len
);
114 return client
->root
->spool(client
, ptr
, len
- offset
);
118 * Version 0 of the interface is really low level and protocol specific,
119 * while the version 1 of the interface is more API focused. We need a
120 * way to translate between the command codes on the wire and the
121 * application level interface in V1, so let's just use the V0 of the
122 * interface as a map instead of creating a huuuge switch :-)
126 * Callback for the GET/GETQ/GETK and GETKQ responses
127 * @param cookie client identifier
128 * @param key the key for the item
129 * @param keylen the length of the key
130 * @param body the length of the body
131 * @param bodylen the length of the body
132 * @param flags the flags for the item
133 * @param cas the CAS id for the item
135 static protocol_binary_response_status
136 get_response_handler(const void *cookie
,
144 memcached_protocol_client_st
*client
= (void*)cookie
;
145 uint8_t opcode
= client
->current_command
->request
.opcode
;
147 if (opcode
== PROTOCOL_BINARY_CMD_GET
|| opcode
== PROTOCOL_BINARY_CMD_GETQ
)
152 protocol_binary_response_get response
= {
153 .message
.header
.response
= {
154 .magic
= PROTOCOL_BINARY_RES
,
156 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
157 .opaque
= client
->current_command
->request
.opaque
,
159 .keylen
= htons(keylen
),
161 .bodylen
= htonl(bodylen
+ keylen
+ 4),
165 response
.message
.body
.flags
= htonl(flags
);
167 protocol_binary_response_status rval
;
168 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
169 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
170 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
171 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
176 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
180 * Callback for the STAT responses
181 * @param cookie client identifier
182 * @param key the key for the item
183 * @param keylen the length of the key
184 * @param body the length of the body
185 * @param bodylen the length of the body
187 static protocol_binary_response_status
188 stat_response_handler(const void *cookie
,
194 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 .keylen
= htons(keylen
),
203 .bodylen
= htonl(bodylen
+ keylen
),
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
, key
, keylen
)) != success
||
211 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
216 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
220 * Callback for the VERSION responses
221 * @param cookie client identifier
222 * @param text the length of the body
223 * @param textlen the length of the body
225 static protocol_binary_response_status
226 version_response_handler(const void *cookie
,
230 memcached_protocol_client_st
*client
= (void*)cookie
;
232 protocol_binary_response_no_extras response
= {
233 .message
.header
.response
= {
234 .magic
= PROTOCOL_BINARY_RES
,
235 .opcode
= client
->current_command
->request
.opcode
,
236 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
237 .opaque
= client
->current_command
->request
.opaque
,
238 .bodylen
= htonl(textlen
),
242 protocol_binary_response_status rval
;
243 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
244 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
245 (rval
= client
->root
->spool(client
, text
, textlen
)) != success
)
250 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
254 * Callback for ADD and ADDQ
255 * @param cookie the calling client
256 * @param header the add/addq command
257 * @param response_handler not used
258 * @return the result of the operation
260 static protocol_binary_response_status
261 add_command_handler(const void *cookie
,
262 protocol_binary_request_header
*header
,
263 memcached_binary_protocol_raw_response_handler response_handler
)
265 protocol_binary_response_status rval
;
267 memcached_protocol_client_st
*client
= (void*)cookie
;
268 if (client
->root
->callback
->interface
.v1
.add
!= NULL
)
270 uint16_t keylen
= ntohs(header
->request
.keylen
);
271 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
272 protocol_binary_request_add
*request
= (void*)header
;
273 uint32_t flags
= ntohl(request
->message
.body
.flags
);
274 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
275 char *key
= ((char*)header
) + sizeof(*header
) + 8;
276 char *data
= key
+ keylen
;
279 rval
= client
->root
->callback
->interface
.v1
.add(cookie
, key
, keylen
,
280 data
, datalen
, flags
,
283 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
284 header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
286 /* Send a positive request */
287 protocol_binary_response_no_extras response
= {
290 .magic
= PROTOCOL_BINARY_RES
,
291 .opcode
= PROTOCOL_BINARY_CMD_ADD
,
292 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
293 .opaque
= header
->request
.opaque
,
298 rval
= response_handler(cookie
, header
, (void*)&response
);
303 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
310 * Callback for DECREMENT and DECREMENTQ
311 * @param cookie the calling client
312 * @param header the command
313 * @param response_handler not used
314 * @return the result of the operation
316 static protocol_binary_response_status
317 decrement_command_handler(const void *cookie
,
318 protocol_binary_request_header
*header
,
319 memcached_binary_protocol_raw_response_handler response_handler
)
321 (void)response_handler
;
322 protocol_binary_response_status rval
;
324 memcached_protocol_client_st
*client
= (void*)cookie
;
325 if (client
->root
->callback
->interface
.v1
.decrement
!= NULL
)
327 uint16_t keylen
= ntohs(header
->request
.keylen
);
328 protocol_binary_request_decr
*request
= (void*)header
;
329 uint64_t init
= ntohll(request
->message
.body
.initial
);
330 uint64_t delta
= ntohll(request
->message
.body
.delta
);
331 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
332 void *key
= request
->bytes
+ sizeof(request
->bytes
);
336 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
337 delta
, init
, timeout
,
339 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
340 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
342 /* Send a positive request */
343 protocol_binary_response_decr response
= {
346 .magic
= PROTOCOL_BINARY_RES
,
347 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
348 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
349 .opaque
= header
->request
.opaque
,
353 .body
.value
= htonll(result
)
356 rval
= response_handler(cookie
, header
, (void*)&response
);
361 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
368 * Callback for DELETE and DELETEQ
369 * @param cookie the calling client
370 * @param header the command
371 * @param response_handler not used
372 * @return the result of the operation
374 static protocol_binary_response_status
375 delete_command_handler(const void *cookie
,
376 protocol_binary_request_header
*header
,
377 memcached_binary_protocol_raw_response_handler response_handler
)
379 (void)response_handler
;
380 protocol_binary_response_status rval
;
382 memcached_protocol_client_st
*client
= (void*)cookie
;
383 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
385 uint16_t keylen
= ntohs(header
->request
.keylen
);
386 void *key
= (header
+ 1);
387 uint64_t cas
= ntohll(header
->request
.cas
);
388 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
389 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
390 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
392 /* Send a positive request */
393 protocol_binary_response_no_extras response
= {
396 .magic
= PROTOCOL_BINARY_RES
,
397 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
398 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
399 .opaque
= header
->request
.opaque
,
403 rval
= response_handler(cookie
, header
, (void*)&response
);
408 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
415 * Callback for FLUSH and FLUSHQ
416 * @param cookie the calling client
417 * @param header the command
418 * @param response_handler not used
419 * @return the result of the operation
421 static protocol_binary_response_status
422 flush_command_handler(const void *cookie
,
423 protocol_binary_request_header
*header
,
424 memcached_binary_protocol_raw_response_handler response_handler
)
426 (void)response_handler
;
427 protocol_binary_response_status rval
;
429 memcached_protocol_client_st
*client
= (void*)cookie
;
430 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
432 protocol_binary_request_flush
*flush
= (void*)header
;
434 if (htonl(header
->request
.bodylen
) == 4)
436 timeout
= ntohl(flush
->message
.body
.expiration
);
439 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
440 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
441 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
443 /* Send a positive request */
444 protocol_binary_response_no_extras response
= {
447 .magic
= PROTOCOL_BINARY_RES
,
448 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
449 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
450 .opaque
= header
->request
.opaque
,
454 rval
= response_handler(cookie
, header
, (void*)&response
);
459 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
466 * Callback for GET, GETK, GETQ, GETKQ
467 * @param cookie the calling client
468 * @param header the command
469 * @param response_handler not used
470 * @return the result of the operation
472 static protocol_binary_response_status
473 get_command_handler(const void *cookie
,
474 protocol_binary_request_header
*header
,
475 memcached_binary_protocol_raw_response_handler response_handler
)
477 (void)response_handler
;
478 protocol_binary_response_status rval
;
480 memcached_protocol_client_st
*client
= (void*)cookie
;
481 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
483 uint16_t keylen
= ntohs(header
->request
.keylen
);
484 void *key
= (header
+ 1);
485 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
486 get_response_handler
);
488 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
489 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
490 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
492 /* Quiet commands shouldn't respond on cache misses */
493 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
498 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
505 * Callback for INCREMENT and INCREMENTQ
506 * @param cookie the calling client
507 * @param header the command
508 * @param response_handler not used
509 * @return the result of the operation
511 static protocol_binary_response_status
512 increment_command_handler(const void *cookie
,
513 protocol_binary_request_header
*header
,
514 memcached_binary_protocol_raw_response_handler response_handler
)
516 (void)response_handler
;
517 protocol_binary_response_status rval
;
519 memcached_protocol_client_st
*client
= (void*)cookie
;
520 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
522 uint16_t keylen
= ntohs(header
->request
.keylen
);
523 protocol_binary_request_incr
*request
= (void*)header
;
524 uint64_t init
= ntohll(request
->message
.body
.initial
);
525 uint64_t delta
= ntohll(request
->message
.body
.delta
);
526 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
527 void *key
= request
->bytes
+ sizeof(request
->bytes
);
531 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
532 delta
, init
, timeout
,
534 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
535 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
537 /* Send a positive request */
538 protocol_binary_response_incr response
= {
541 .magic
= PROTOCOL_BINARY_RES
,
542 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
543 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
544 .opaque
= header
->request
.opaque
,
548 .body
.value
= htonll(result
)
552 rval
= response_handler(cookie
, header
, (void*)&response
);
557 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
564 * Callback for noop. Inform the v1 interface about the noop packet, and
565 * create and send a packet back to the client
567 * @param cookie the calling client
568 * @param header the command
569 * @param response_handler the response handler
570 * @return the result of the operation
572 static protocol_binary_response_status
573 noop_command_handler(const void *cookie
,
574 protocol_binary_request_header
*header
,
575 memcached_binary_protocol_raw_response_handler response_handler
)
577 memcached_protocol_client_st
*client
= (void*)cookie
;
578 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
580 client
->root
->callback
->interface
.v1
.noop(cookie
);
583 protocol_binary_response_no_extras response
= {
586 .magic
= PROTOCOL_BINARY_RES
,
587 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
588 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
589 .opaque
= header
->request
.opaque
,
594 return response_handler(cookie
, header
, (void*)&response
);
598 * Callback for APPEND and APPENDQ
599 * @param cookie the calling client
600 * @param header the command
601 * @param response_handler not used
602 * @return the result of the operation
604 static protocol_binary_response_status
605 append_command_handler(const void *cookie
,
606 protocol_binary_request_header
*header
,
607 memcached_binary_protocol_raw_response_handler response_handler
)
609 (void)response_handler
;
610 protocol_binary_response_status rval
;
612 memcached_protocol_client_st
*client
= (void*)cookie
;
613 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
615 uint16_t keylen
= ntohs(header
->request
.keylen
);
616 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
617 char *key
= (void*)(header
+ 1);
618 char *data
= key
+ keylen
;
619 uint64_t cas
= ntohll(header
->request
.cas
);
622 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
625 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
626 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
628 /* Send a positive request */
629 protocol_binary_response_no_extras response
= {
632 .magic
= PROTOCOL_BINARY_RES
,
633 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
634 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
635 .opaque
= header
->request
.opaque
,
636 .cas
= ntohll(result_cas
),
640 rval
= response_handler(cookie
, header
, (void*)&response
);
645 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
652 * Callback for PREPEND and PREPENDQ
653 * @param cookie the calling client
654 * @param header the command
655 * @param response_handler not used
656 * @return the result of the operation
658 static protocol_binary_response_status
659 prepend_command_handler(const void *cookie
,
660 protocol_binary_request_header
*header
,
661 memcached_binary_protocol_raw_response_handler response_handler
)
663 (void)response_handler
;
664 protocol_binary_response_status rval
;
666 memcached_protocol_client_st
*client
= (void*)cookie
;
667 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
669 uint16_t keylen
= ntohs(header
->request
.keylen
);
670 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
671 char *key
= (char*)(header
+ 1);
672 char *data
= key
+ keylen
;
673 uint64_t cas
= ntohll(header
->request
.cas
);
675 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
678 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
679 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
681 /* Send a positive request */
682 protocol_binary_response_no_extras response
= {
685 .magic
= PROTOCOL_BINARY_RES
,
686 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
687 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
688 .opaque
= header
->request
.opaque
,
689 .cas
= ntohll(result_cas
),
693 rval
= response_handler(cookie
, header
, (void*)&response
);
698 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
705 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
706 * @param cookie the calling client
707 * @param header the command
708 * @param response_handler not used
709 * @return the result of the operation
711 static protocol_binary_response_status
712 quit_command_handler(const void *cookie
,
713 protocol_binary_request_header
*header
,
714 memcached_binary_protocol_raw_response_handler response_handler
)
716 memcached_protocol_client_st
*client
= (void*)cookie
;
717 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
719 client
->root
->callback
->interface
.v1
.quit(cookie
);
722 protocol_binary_response_no_extras response
= {
725 .magic
= PROTOCOL_BINARY_RES
,
726 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
727 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
728 .opaque
= header
->request
.opaque
733 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
735 response_handler(cookie
, header
, (void*)&response
);
738 /* I need a better way to signal to close the connection */
739 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
743 * Callback for REPLACE and REPLACEQ
744 * @param cookie the calling client
745 * @param header the command
746 * @param response_handler not used
747 * @return the result of the operation
749 static protocol_binary_response_status
750 replace_command_handler(const void *cookie
,
751 protocol_binary_request_header
*header
,
752 memcached_binary_protocol_raw_response_handler response_handler
)
754 (void)response_handler
;
755 protocol_binary_response_status rval
;
757 memcached_protocol_client_st
*client
= (void*)cookie
;
758 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
760 uint16_t keylen
= ntohs(header
->request
.keylen
);
761 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
762 protocol_binary_request_replace
*request
= (void*)header
;
763 uint32_t flags
= ntohl(request
->message
.body
.flags
);
764 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
765 char *key
= ((char*)header
) + sizeof(*header
) + 8;
766 char *data
= key
+ keylen
;
767 uint64_t cas
= ntohll(header
->request
.cas
);
770 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
771 data
, datalen
, flags
,
774 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
775 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
777 /* Send a positive request */
778 protocol_binary_response_no_extras response
= {
781 .magic
= PROTOCOL_BINARY_RES
,
782 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
783 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
784 .opaque
= header
->request
.opaque
,
785 .cas
= ntohll(result_cas
),
789 rval
= response_handler(cookie
, header
, (void*)&response
);
794 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
801 * Callback for SET and SETQ
802 * @param cookie the calling client
803 * @param header the command
804 * @param response_handler not used
805 * @return the result of the operation
807 static protocol_binary_response_status
808 set_command_handler(const void *cookie
,
809 protocol_binary_request_header
*header
,
810 memcached_binary_protocol_raw_response_handler response_handler
)
812 (void)response_handler
;
813 protocol_binary_response_status rval
;
815 memcached_protocol_client_st
*client
= (void*)cookie
;
816 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
818 uint16_t keylen
= ntohs(header
->request
.keylen
);
819 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
820 protocol_binary_request_replace
*request
= (void*)header
;
821 uint32_t flags
= ntohl(request
->message
.body
.flags
);
822 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
823 char *key
= ((char*)header
) + sizeof(*header
) + 8;
824 char *data
= key
+ keylen
;
825 uint64_t cas
= ntohll(header
->request
.cas
);
829 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
830 data
, datalen
, flags
,
831 timeout
, cas
, &result_cas
);
832 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
833 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
835 /* Send a positive request */
836 protocol_binary_response_no_extras response
= {
839 .magic
= PROTOCOL_BINARY_RES
,
840 .opcode
= PROTOCOL_BINARY_CMD_SET
,
841 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
842 .opaque
= header
->request
.opaque
,
843 .cas
= ntohll(result_cas
),
847 rval
= response_handler(cookie
, header
, (void*)&response
);
852 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
860 * @param cookie the calling client
861 * @param header the command
862 * @param response_handler not used
863 * @return the result of the operation
865 static protocol_binary_response_status
866 stat_command_handler(const void *cookie
,
867 protocol_binary_request_header
*header
,
868 memcached_binary_protocol_raw_response_handler response_handler
)
870 (void)response_handler
;
871 protocol_binary_response_status rval
;
873 memcached_protocol_client_st
*client
= (void*)cookie
;
874 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
876 uint16_t keylen
= ntohs(header
->request
.keylen
);
878 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
881 stat_response_handler
);
885 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
892 * Callback for VERSION
893 * @param cookie the calling client
894 * @param header the command
895 * @param response_handler not used
896 * @return the result of the operation
898 static protocol_binary_response_status
899 version_command_handler(const void *cookie
,
900 protocol_binary_request_header
*header
,
901 memcached_binary_protocol_raw_response_handler response_handler
)
903 (void)response_handler
;
905 protocol_binary_response_status rval
;
907 memcached_protocol_client_st
*client
= (void*)cookie
;
908 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
910 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
911 version_response_handler
);
915 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
922 * The map to remap between the com codes and the v1 logical setting
924 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
925 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
926 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
927 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
928 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
929 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
930 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
931 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
932 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
933 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
934 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
935 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
936 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
937 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
938 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
939 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
940 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
941 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
942 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
943 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
944 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
945 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
946 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
947 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
948 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
949 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
950 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
951 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
955 * Try to execute a command. Fire the pre/post functions and the specialized
956 * handler function if it's set. If not, the unknown probe should be fired
958 * @param client the client connection to operate on
959 * @param header the command to execute
960 * @return true if success or false if a fatal error occured so that the
961 * connection should be shut down.
963 static protocol_binary_response_status
execute_command(memcached_protocol_client_st
*client
, protocol_binary_request_header
*header
)
965 if (client
->root
->pedantic
&&
966 memcached_binary_protocol_pedantic_check_request(header
))
968 /* @todo return invalid command packet */
971 /* we got all data available, execute the callback! */
972 if (client
->root
->callback
->pre_execute
!= NULL
)
974 client
->root
->callback
->pre_execute(client
, header
);
977 protocol_binary_response_status rval
;
978 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
979 uint8_t cc
= header
->request
.opcode
;
981 switch (client
->root
->callback
->interface_version
)
984 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
985 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
989 if (comcode_v0_v1_remap
[cc
] != NULL
) {
990 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
994 /* Unknown interface.
995 * It should be impossible to get here so I'll just call abort
996 * to avoid getting a compiler warning :-)
1002 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
1003 client
->root
->callback
->unknown
!= NULL
)
1005 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
1008 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
1009 rval
!= PROTOCOL_BINARY_RESPONSE_EINTERNAL
&&
1010 rval
!= PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED
)
1012 protocol_binary_response_no_extras response
= {
1015 .magic
= PROTOCOL_BINARY_RES
,
1017 .status
= htons(rval
),
1018 .opaque
= header
->request
.opaque
,
1022 rval
= raw_response_handler(client
, header
, (void*)&response
);
1025 if (client
->root
->callback
->post_execute
!= NULL
)
1027 client
->root
->callback
->post_execute(client
, header
);
1034 ** **********************************************************************
1035 ** "PROTOECTED" INTERFACE
1036 ** **********************************************************************
1038 memcached_protocol_event_t
memcached_binary_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
, void **endptr
)
1040 /* try to parse all of the received packets */
1041 protocol_binary_request_header
*header
;
1042 header
= (void*)client
->root
->input_buffer
;
1043 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1045 client
->error
= EINVAL
;
1046 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1048 ssize_t len
= *length
;
1050 while (len
>= (ssize_t
)sizeof(*header
) &&
1051 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1053 /* I have the complete package */
1054 client
->current_command
= header
;
1055 protocol_binary_response_status rv
= execute_command(client
, header
);
1057 if (rv
== PROTOCOL_BINARY_RESPONSE_EINTERNAL
)
1060 *endptr
= (void*)header
;
1061 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1062 } else if (rv
== PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED
)
1063 return MEMCACHED_PROTOCOL_PAUSE_EVENT
;
1065 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1069 intptr_t ptr
= (intptr_t)header
;
1078 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1079 header
= (void*)client
->root
->input_buffer
;
1083 *endptr
= (void*)header
;
1086 return MEMCACHED_PROTOCOL_READ_EVENT
;
1090 ** **********************************************************************
1092 ** **********************************************************************
1094 memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(memcached_protocol_st
*instance
)
1096 return instance
->callback
;
1099 void memcached_binary_protocol_set_callbacks(memcached_protocol_st
*instance
, memcached_binary_protocol_callback_st
*callback
)
1101 instance
->callback
= callback
;
1104 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1107 return raw_response_handler
;
1110 void memcached_binary_protocol_set_pedantic(memcached_protocol_st
*instance
, bool enable
)
1112 instance
->pedantic
= enable
;
1115 bool memcached_binary_protocol_get_pedantic(memcached_protocol_st
*instance
)
1117 return instance
->pedantic
;