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
,
158 .cas
= memcached_htonll(cas
),
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
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
),
208 protocol_binary_response_status rval
;
209 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
210 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
211 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
212 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
217 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
221 * Callback for the VERSION responses
222 * @param cookie client identifier
223 * @param text the length of the body
224 * @param textlen the length of the body
226 static protocol_binary_response_status
227 version_response_handler(const void *cookie
,
231 memcached_protocol_client_st
*client
= (void*)cookie
;
233 protocol_binary_response_no_extras response
= {
234 .message
.header
.response
= {
235 .magic
= PROTOCOL_BINARY_RES
,
236 .opcode
= client
->current_command
->request
.opcode
,
237 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
238 .opaque
= client
->current_command
->request
.opaque
,
239 .bodylen
= htonl(textlen
),
244 protocol_binary_response_status rval
;
245 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
246 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
247 (rval
= client
->root
->spool(client
, text
, textlen
)) != success
)
252 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
256 * Callback for ADD and ADDQ
257 * @param cookie the calling client
258 * @param header the add/addq command
259 * @param response_handler not used
260 * @return the result of the operation
262 static protocol_binary_response_status
263 add_command_handler(const void *cookie
,
264 protocol_binary_request_header
*header
,
265 memcached_binary_protocol_raw_response_handler response_handler
)
267 protocol_binary_response_status rval
;
269 memcached_protocol_client_st
*client
= (void*)cookie
;
270 if (client
->root
->callback
->interface
.v1
.add
!= NULL
)
272 uint16_t keylen
= ntohs(header
->request
.keylen
);
273 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
274 protocol_binary_request_add
*request
= (void*)header
;
275 uint32_t flags
= ntohl(request
->message
.body
.flags
);
276 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
277 char *key
= ((char*)header
) + sizeof(*header
) + 8;
278 char *data
= key
+ keylen
;
281 rval
= client
->root
->callback
->interface
.v1
.add(cookie
, key
, keylen
,
282 data
, datalen
, flags
,
285 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
286 header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
288 /* Send a positive request */
289 protocol_binary_response_no_extras response
= {
292 .magic
= PROTOCOL_BINARY_RES
,
293 .opcode
= PROTOCOL_BINARY_CMD_ADD
,
294 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
295 .opaque
= header
->request
.opaque
,
296 .cas
= memcached_ntohll(cas
)
300 rval
= response_handler(cookie
, header
, (void*)&response
);
305 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
312 * Callback for DECREMENT and DECREMENTQ
313 * @param cookie the calling client
314 * @param header the command
315 * @param response_handler not used
316 * @return the result of the operation
318 static protocol_binary_response_status
319 decrement_command_handler(const void *cookie
,
320 protocol_binary_request_header
*header
,
321 memcached_binary_protocol_raw_response_handler response_handler
)
323 (void)response_handler
;
324 protocol_binary_response_status rval
;
326 memcached_protocol_client_st
*client
= (void*)cookie
;
327 if (client
->root
->callback
->interface
.v1
.decrement
!= NULL
)
329 uint16_t keylen
= ntohs(header
->request
.keylen
);
330 protocol_binary_request_decr
*request
= (void*)header
;
331 uint64_t init
= memcached_ntohll(request
->message
.body
.initial
);
332 uint64_t delta
= memcached_ntohll(request
->message
.body
.delta
);
333 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
334 void *key
= request
->bytes
+ sizeof(request
->bytes
);
338 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
339 delta
, init
, timeout
,
341 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
342 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
344 /* Send a positive request */
345 protocol_binary_response_decr response
= {
348 .magic
= PROTOCOL_BINARY_RES
,
349 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
350 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
351 .opaque
= header
->request
.opaque
,
352 .cas
= memcached_ntohll(cas
),
355 .body
.value
= memcached_htonll(result
)
358 rval
= response_handler(cookie
, header
, (void*)&response
);
363 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
370 * Callback for DELETE and DELETEQ
371 * @param cookie the calling client
372 * @param header the command
373 * @param response_handler not used
374 * @return the result of the operation
376 static protocol_binary_response_status
377 delete_command_handler(const void *cookie
,
378 protocol_binary_request_header
*header
,
379 memcached_binary_protocol_raw_response_handler response_handler
)
381 (void)response_handler
;
382 protocol_binary_response_status rval
;
384 memcached_protocol_client_st
*client
= (void*)cookie
;
385 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
387 uint16_t keylen
= ntohs(header
->request
.keylen
);
388 void *key
= (header
+1);
389 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
390 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
391 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
392 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
394 /* Send a positive request */
395 protocol_binary_response_no_extras response
= {
398 .magic
= PROTOCOL_BINARY_RES
,
399 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
400 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
401 .opaque
= header
->request
.opaque
,
405 rval
= response_handler(cookie
, header
, (void*)&response
);
410 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
417 * Callback for FLUSH and FLUSHQ
418 * @param cookie the calling client
419 * @param header the command
420 * @param response_handler not used
421 * @return the result of the operation
423 static protocol_binary_response_status
424 flush_command_handler(const void *cookie
,
425 protocol_binary_request_header
*header
,
426 memcached_binary_protocol_raw_response_handler response_handler
)
428 (void)response_handler
;
429 protocol_binary_response_status rval
;
431 memcached_protocol_client_st
*client
= (void*)cookie
;
432 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
434 protocol_binary_request_flush
*flush
= (void*)header
;
436 if (htonl(header
->request
.bodylen
) == 4)
438 timeout
= ntohl(flush
->message
.body
.expiration
);
441 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
442 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
443 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
445 /* Send a positive request */
446 protocol_binary_response_no_extras response
= {
449 .magic
= PROTOCOL_BINARY_RES
,
450 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
451 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
452 .opaque
= header
->request
.opaque
,
456 rval
= response_handler(cookie
, header
, (void*)&response
);
461 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
468 * Callback for GET, GETK, GETQ, GETKQ
469 * @param cookie the calling client
470 * @param header the command
471 * @param response_handler not used
472 * @return the result of the operation
474 static protocol_binary_response_status
475 get_command_handler(const void *cookie
,
476 protocol_binary_request_header
*header
,
477 memcached_binary_protocol_raw_response_handler response_handler
)
479 (void)response_handler
;
480 protocol_binary_response_status rval
;
482 memcached_protocol_client_st
*client
= (void*)cookie
;
483 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
485 uint16_t keylen
= ntohs(header
->request
.keylen
);
486 void *key
= (header
+ 1);
487 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
488 get_response_handler
);
490 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
491 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
492 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
494 /* Quiet commands shouldn't respond on cache misses */
495 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
500 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
507 * Callback for INCREMENT and INCREMENTQ
508 * @param cookie the calling client
509 * @param header the command
510 * @param response_handler not used
511 * @return the result of the operation
513 static protocol_binary_response_status
514 increment_command_handler(const void *cookie
,
515 protocol_binary_request_header
*header
,
516 memcached_binary_protocol_raw_response_handler response_handler
)
518 (void)response_handler
;
519 protocol_binary_response_status rval
;
521 memcached_protocol_client_st
*client
= (void*)cookie
;
522 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
524 uint16_t keylen
= ntohs(header
->request
.keylen
);
525 protocol_binary_request_incr
*request
= (void*)header
;
526 uint64_t init
= memcached_ntohll(request
->message
.body
.initial
);
527 uint64_t delta
= memcached_ntohll(request
->message
.body
.delta
);
528 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
529 void *key
= request
->bytes
+ sizeof(request
->bytes
);
533 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
534 delta
, init
, timeout
,
536 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
537 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
539 /* Send a positive request */
540 protocol_binary_response_incr response
= {
543 .magic
= PROTOCOL_BINARY_RES
,
544 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
545 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
546 .opaque
= header
->request
.opaque
,
547 .cas
= memcached_ntohll(cas
),
550 .body
.value
= memcached_htonll(result
)
554 rval
= response_handler(cookie
, header
, (void*)&response
);
559 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
566 * Callback for noop. Inform the v1 interface about the noop packet, and
567 * create and send a packet back to the client
569 * @param cookie the calling client
570 * @param header the command
571 * @param response_handler the response handler
572 * @return the result of the operation
574 static protocol_binary_response_status
575 noop_command_handler(const void *cookie
,
576 protocol_binary_request_header
*header
,
577 memcached_binary_protocol_raw_response_handler response_handler
)
579 memcached_protocol_client_st
*client
= (void*)cookie
;
580 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
582 client
->root
->callback
->interface
.v1
.noop(cookie
);
585 protocol_binary_response_no_extras response
= {
588 .magic
= PROTOCOL_BINARY_RES
,
589 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
590 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
591 .opaque
= header
->request
.opaque
,
596 return response_handler(cookie
, header
, (void*)&response
);
600 * Callback for APPEND and APPENDQ
601 * @param cookie the calling client
602 * @param header the command
603 * @param response_handler not used
604 * @return the result of the operation
606 static protocol_binary_response_status
607 append_command_handler(const void *cookie
,
608 protocol_binary_request_header
*header
,
609 memcached_binary_protocol_raw_response_handler response_handler
)
611 (void)response_handler
;
612 protocol_binary_response_status rval
;
614 memcached_protocol_client_st
*client
= (void*)cookie
;
615 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
617 uint16_t keylen
= ntohs(header
->request
.keylen
);
618 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
619 char *key
= (void*)(header
+1);
620 char *data
= key
+keylen
;
621 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
624 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
627 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
628 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
630 /* Send a positive request */
631 protocol_binary_response_no_extras response
= {
634 .magic
= PROTOCOL_BINARY_RES
,
635 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
636 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
637 .opaque
= header
->request
.opaque
,
638 .cas
= memcached_ntohll(result_cas
),
642 rval
= response_handler(cookie
, header
, (void*)&response
);
647 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
654 * Callback for PREPEND and PREPENDQ
655 * @param cookie the calling client
656 * @param header the command
657 * @param response_handler not used
658 * @return the result of the operation
660 static protocol_binary_response_status
661 prepend_command_handler(const void *cookie
,
662 protocol_binary_request_header
*header
,
663 memcached_binary_protocol_raw_response_handler response_handler
)
665 (void)response_handler
;
666 protocol_binary_response_status rval
;
668 memcached_protocol_client_st
*client
= (void*)cookie
;
669 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
671 uint16_t keylen
= ntohs(header
->request
.keylen
);
672 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
673 char *key
= (char*)(header
+ 1);
674 char *data
= key
+ keylen
;
675 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
677 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
680 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
681 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
683 /* Send a positive request */
684 protocol_binary_response_no_extras response
= {
687 .magic
= PROTOCOL_BINARY_RES
,
688 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
689 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
690 .opaque
= header
->request
.opaque
,
691 .cas
= memcached_ntohll(result_cas
),
695 rval
= response_handler(cookie
, header
, (void*)&response
);
700 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
707 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
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 quit_command_handler(const void *cookie
,
715 protocol_binary_request_header
*header
,
716 memcached_binary_protocol_raw_response_handler response_handler
)
718 memcached_protocol_client_st
*client
= (void*)cookie
;
719 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
721 client
->root
->callback
->interface
.v1
.quit(cookie
);
724 protocol_binary_response_no_extras response
= {
727 .magic
= PROTOCOL_BINARY_RES
,
728 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
729 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
730 .opaque
= header
->request
.opaque
735 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
737 response_handler(cookie
, header
, (void*)&response
);
740 /* I need a better way to signal to close the connection */
741 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
745 * Callback for REPLACE and REPLACEQ
746 * @param cookie the calling client
747 * @param header the command
748 * @param response_handler not used
749 * @return the result of the operation
751 static protocol_binary_response_status
752 replace_command_handler(const void *cookie
,
753 protocol_binary_request_header
*header
,
754 memcached_binary_protocol_raw_response_handler response_handler
)
756 (void)response_handler
;
757 protocol_binary_response_status rval
;
759 memcached_protocol_client_st
*client
= (void*)cookie
;
760 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
762 uint16_t keylen
= ntohs(header
->request
.keylen
);
763 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
764 protocol_binary_request_replace
*request
= (void*)header
;
765 uint32_t flags
= ntohl(request
->message
.body
.flags
);
766 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
767 char *key
= ((char*)header
) + sizeof(*header
) + 8;
768 char *data
= key
+ keylen
;
769 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
772 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
773 data
, datalen
, flags
,
776 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
777 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
779 /* Send a positive request */
780 protocol_binary_response_no_extras response
= {
783 .magic
= PROTOCOL_BINARY_RES
,
784 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
785 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
786 .opaque
= header
->request
.opaque
,
787 .cas
= memcached_ntohll(result_cas
),
791 rval
= response_handler(cookie
, header
, (void*)&response
);
796 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
803 * Callback for SET and SETQ
804 * @param cookie the calling client
805 * @param header the command
806 * @param response_handler not used
807 * @return the result of the operation
809 static protocol_binary_response_status
810 set_command_handler(const void *cookie
,
811 protocol_binary_request_header
*header
,
812 memcached_binary_protocol_raw_response_handler response_handler
)
814 (void)response_handler
;
815 protocol_binary_response_status rval
;
817 memcached_protocol_client_st
*client
= (void*)cookie
;
818 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
820 uint16_t keylen
= ntohs(header
->request
.keylen
);
821 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
822 protocol_binary_request_replace
*request
= (void*)header
;
823 uint32_t flags
= ntohl(request
->message
.body
.flags
);
824 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
825 char *key
= ((char*)header
) + sizeof(*header
) + 8;
826 char *data
= key
+ keylen
;
827 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
831 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
832 data
, datalen
, flags
,
833 timeout
, cas
, &result_cas
);
834 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
835 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
837 /* Send a positive request */
838 protocol_binary_response_no_extras response
= {
841 .magic
= PROTOCOL_BINARY_RES
,
842 .opcode
= PROTOCOL_BINARY_CMD_SET
,
843 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
844 .opaque
= header
->request
.opaque
,
845 .cas
= memcached_ntohll(result_cas
),
849 rval
= response_handler(cookie
, header
, (void*)&response
);
854 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
862 * @param cookie the calling client
863 * @param header the command
864 * @param response_handler not used
865 * @return the result of the operation
867 static protocol_binary_response_status
868 stat_command_handler(const void *cookie
,
869 protocol_binary_request_header
*header
,
870 memcached_binary_protocol_raw_response_handler response_handler
)
872 (void)response_handler
;
873 protocol_binary_response_status rval
;
875 memcached_protocol_client_st
*client
= (void*)cookie
;
876 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
878 uint16_t keylen
= ntohs(header
->request
.keylen
);
880 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
883 stat_response_handler
);
887 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
894 * Callback for VERSION
895 * @param cookie the calling client
896 * @param header the command
897 * @param response_handler not used
898 * @return the result of the operation
900 static protocol_binary_response_status
901 version_command_handler(const void *cookie
,
902 protocol_binary_request_header
*header
,
903 memcached_binary_protocol_raw_response_handler response_handler
)
905 (void)response_handler
;
907 protocol_binary_response_status rval
;
909 memcached_protocol_client_st
*client
= (void*)cookie
;
910 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
912 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
913 version_response_handler
);
917 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
924 * The map to remap between the com codes and the v1 logical setting
926 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
927 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
928 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
929 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
930 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
931 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
932 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
933 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
934 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
935 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
936 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
937 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
938 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
939 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
940 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
941 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
942 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
943 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
944 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
945 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
946 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
947 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
948 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
949 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
950 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
951 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
952 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
953 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
957 * Try to execute a command. Fire the pre/post functions and the specialized
958 * handler function if it's set. If not, the unknown probe should be fired
960 * @param client the client connection to operate on
961 * @param header the command to execute
962 * @return true if success or false if a fatal error occured so that the
963 * connection should be shut down.
965 static protocol_binary_response_status
execute_command(memcached_protocol_client_st
*client
, protocol_binary_request_header
*header
)
967 if (client
->root
->pedantic
&&
968 memcached_binary_protocol_pedantic_check_request(header
))
970 /* @todo return invalid command packet */
973 /* we got all data available, execute the callback! */
974 if (client
->root
->callback
->pre_execute
!= NULL
)
976 client
->root
->callback
->pre_execute(client
, header
);
979 protocol_binary_response_status rval
;
980 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
981 uint8_t cc
= header
->request
.opcode
;
983 switch (client
->root
->callback
->interface_version
)
986 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
987 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
991 if (comcode_v0_v1_remap
[cc
] != NULL
) {
992 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
996 /* Unknown interface.
997 * It should be impossible to get here so I'll just call abort
998 * to avoid getting a compiler warning :-)
1004 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
1005 client
->root
->callback
->unknown
!= NULL
)
1007 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
1010 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
1011 rval
!= PROTOCOL_BINARY_RESPONSE_EINTERNAL
&&
1012 rval
!= PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED
)
1014 protocol_binary_response_no_extras response
= {
1017 .magic
= PROTOCOL_BINARY_RES
,
1019 .status
= htons(rval
),
1020 .opaque
= header
->request
.opaque
,
1024 rval
= raw_response_handler(client
, header
, (void*)&response
);
1027 if (client
->root
->callback
->post_execute
!= NULL
)
1029 client
->root
->callback
->post_execute(client
, header
);
1036 ** **********************************************************************
1037 ** "PROTOECTED" INTERFACE
1038 ** **********************************************************************
1040 memcached_protocol_event_t
memcached_binary_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
, void **endptr
)
1042 /* try to parse all of the received packets */
1043 protocol_binary_request_header
*header
;
1044 header
= (void*)client
->root
->input_buffer
;
1045 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1047 client
->error
= EINVAL
;
1048 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1050 ssize_t len
= *length
;
1052 while (len
>= (ssize_t
)sizeof(*header
) &&
1053 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1055 /* I have the complete package */
1056 client
->current_command
= header
;
1057 protocol_binary_response_status rv
= execute_command(client
, header
);
1059 if (rv
== PROTOCOL_BINARY_RESPONSE_EINTERNAL
)
1062 *endptr
= (void*)header
;
1063 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1064 } else if (rv
== PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED
)
1065 return MEMCACHED_PROTOCOL_PAUSE_EVENT
;
1067 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1071 intptr_t ptr
= (intptr_t)header
;
1080 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1081 header
= (void*)client
->root
->input_buffer
;
1085 *endptr
= (void*)header
;
1088 return MEMCACHED_PROTOCOL_READ_EVENT
;
1092 ** **********************************************************************
1094 ** **********************************************************************
1096 memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(memcached_protocol_st
*instance
)
1098 return instance
->callback
;
1101 void memcached_binary_protocol_set_callbacks(memcached_protocol_st
*instance
, memcached_binary_protocol_callback_st
*callback
)
1103 instance
->callback
= callback
;
1106 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1109 return raw_response_handler
;
1112 void memcached_binary_protocol_set_pedantic(memcached_protocol_st
*instance
, bool enable
)
1114 instance
->pedantic
= enable
;
1117 bool memcached_binary_protocol_get_pedantic(memcached_protocol_st
*instance
)
1119 return instance
->pedantic
;