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 <libmemcachedprotocol/common.h>
40 #include <sys/types.h>
47 ** **********************************************************************
49 ** **********************************************************************
53 * Send a preformatted packet back to the client. If the connection is in
54 * pedantic mode, it will validate the packet and refuse to send it if it
55 * breaks the specification.
57 * @param cookie client identification
58 * @param request the original request packet
59 * @param response the packet to send
60 * @return The status of the operation
62 static protocol_binary_response_status
63 raw_response_handler(const void *cookie
,
64 protocol_binary_request_header
*request
,
65 protocol_binary_response_header
*response
)
67 memcached_protocol_client_st
*client
= (void*)cookie
;
69 if (client
->root
->pedantic
&&
70 !memcached_binary_protocol_pedantic_check_response(request
, response
))
72 return PROTOCOL_BINARY_RESPONSE_EINVAL
;
75 if (!client
->root
->drain(client
))
77 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
80 size_t len
= sizeof(*response
) + htonl(response
->response
.bodylen
);
82 char *ptr
= (void*)response
;
84 if (client
->output
== NULL
)
86 /* I can write directly to the socket.... */
89 size_t num_bytes
= len
- offset
;
90 ssize_t nw
= client
->root
->send(client
,
96 if (get_socket_errno() == EWOULDBLOCK
)
100 else if (get_socket_errno() != EINTR
)
102 client
->error
= errno
;
103 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
108 offset
+= (size_t)nw
;
110 } while (offset
< len
);
113 return client
->root
->spool(client
, ptr
, len
- offset
);
117 * Version 0 of the interface is really low level and protocol specific,
118 * while the version 1 of the interface is more API focused. We need a
119 * way to translate between the command codes on the wire and the
120 * application level interface in V1, so let's just use the V0 of the
121 * interface as a map instead of creating a huuuge switch :-)
125 * Callback for the GET/GETQ/GETK and GETKQ responses
126 * @param cookie client identifier
127 * @param key the key for the item
128 * @param keylen the length of the key
129 * @param body the length of the body
130 * @param bodylen the length of the body
131 * @param flags the flags for the item
132 * @param cas the CAS id for the item
134 static protocol_binary_response_status
135 get_response_handler(const void *cookie
,
143 memcached_protocol_client_st
*client
= (void*)cookie
;
144 uint8_t opcode
= client
->current_command
->request
.opcode
;
146 if (opcode
== PROTOCOL_BINARY_CMD_GET
|| opcode
== PROTOCOL_BINARY_CMD_GETQ
)
151 protocol_binary_response_get response
= {
152 .message
.header
.response
= {
153 .magic
= PROTOCOL_BINARY_RES
,
155 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
156 .opaque
= client
->current_command
->request
.opaque
,
157 .cas
= memcached_htonll(cas
),
158 .keylen
= htons(keylen
),
160 .bodylen
= htonl(bodylen
+ keylen
+ 4),
164 response
.message
.body
.flags
= htonl(flags
);
166 protocol_binary_response_status rval
;
167 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
168 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
169 (rval
= client
->root
->spool(client
, key
, keylen
)) != success
||
170 (rval
= client
->root
->spool(client
, body
, bodylen
)) != success
)
175 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
179 * Callback for the STAT responses
180 * @param cookie client identifier
181 * @param key the key for the item
182 * @param keylen the length of the key
183 * @param body the length of the body
184 * @param bodylen the length of the body
186 static protocol_binary_response_status
stat_response_handler(const void *cookie
,
193 memcached_protocol_client_st
*client
= (void*)cookie
;
195 protocol_binary_response_no_extras response
= {
196 .message
.header
.response
= {
197 .magic
= PROTOCOL_BINARY_RES
,
198 .opcode
= client
->current_command
->request
.opcode
,
199 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
200 .opaque
= client
->current_command
->request
.opaque
,
201 .keylen
= htons(keylen
),
202 .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
),
243 protocol_binary_response_status rval
;
244 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
245 if ((rval
= client
->root
->spool(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
246 (rval
= client
->root
->spool(client
, text
, textlen
)) != success
)
251 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
255 * Callback for ADD and ADDQ
256 * @param cookie the calling client
257 * @param header the add/addq command
258 * @param response_handler not used
259 * @return the result of the operation
261 static protocol_binary_response_status
262 add_command_handler(const void *cookie
,
263 protocol_binary_request_header
*header
,
264 memcached_binary_protocol_raw_response_handler response_handler
)
266 protocol_binary_response_status rval
;
268 memcached_protocol_client_st
*client
= (void*)cookie
;
269 if (client
->root
->callback
->interface
.v1
.add
!= NULL
)
271 uint16_t keylen
= ntohs(header
->request
.keylen
);
272 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
273 protocol_binary_request_add
*request
= (void*)header
;
274 uint32_t flags
= ntohl(request
->message
.body
.flags
);
275 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
276 char *key
= ((char*)header
) + sizeof(*header
) + 8;
277 char *data
= key
+ keylen
;
280 rval
= client
->root
->callback
->interface
.v1
.add(cookie
, key
, keylen
,
281 data
, datalen
, flags
,
284 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
285 header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
287 /* Send a positive request */
288 protocol_binary_response_no_extras response
= {
291 .magic
= PROTOCOL_BINARY_RES
,
292 .opcode
= PROTOCOL_BINARY_CMD_ADD
,
293 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
294 .opaque
= header
->request
.opaque
,
295 .cas
= memcached_ntohll(cas
)
299 rval
= response_handler(cookie
, header
, (void*)&response
);
304 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
311 * Callback for DECREMENT and DECREMENTQ
312 * @param cookie the calling client
313 * @param header the command
314 * @param response_handler not used
315 * @return the result of the operation
317 static protocol_binary_response_status
318 decrement_command_handler(const void *cookie
,
319 protocol_binary_request_header
*header
,
320 memcached_binary_protocol_raw_response_handler response_handler
)
322 (void)response_handler
;
323 protocol_binary_response_status rval
;
325 memcached_protocol_client_st
*client
= (void*)cookie
;
326 if (client
->root
->callback
->interface
.v1
.decrement
!= NULL
)
328 uint16_t keylen
= ntohs(header
->request
.keylen
);
329 protocol_binary_request_decr
*request
= (void*)header
;
330 uint64_t init
= memcached_ntohll(request
->message
.body
.initial
);
331 uint64_t delta
= memcached_ntohll(request
->message
.body
.delta
);
332 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
333 void *key
= request
->bytes
+ sizeof(request
->bytes
);
337 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
338 delta
, init
, timeout
,
340 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
341 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
343 /* Send a positive request */
344 protocol_binary_response_decr response
= {
347 .magic
= PROTOCOL_BINARY_RES
,
348 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
349 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
350 .opaque
= header
->request
.opaque
,
351 .cas
= memcached_ntohll(cas
),
354 .body
.value
= memcached_htonll(result
)
357 rval
= response_handler(cookie
, header
, (void*)&response
);
362 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
369 * Callback for DELETE and DELETEQ
370 * @param cookie the calling client
371 * @param header the command
372 * @param response_handler not used
373 * @return the result of the operation
375 static protocol_binary_response_status
376 delete_command_handler(const void *cookie
,
377 protocol_binary_request_header
*header
,
378 memcached_binary_protocol_raw_response_handler response_handler
)
380 (void)response_handler
;
381 protocol_binary_response_status rval
;
383 memcached_protocol_client_st
*client
= (void*)cookie
;
384 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
386 uint16_t keylen
= ntohs(header
->request
.keylen
);
387 void *key
= (header
+1);
388 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
389 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
390 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
391 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
393 /* Send a positive request */
394 protocol_binary_response_no_extras response
= {
397 .magic
= PROTOCOL_BINARY_RES
,
398 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
399 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
400 .opaque
= header
->request
.opaque
,
404 rval
= response_handler(cookie
, header
, (void*)&response
);
409 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
416 * Callback for FLUSH and FLUSHQ
417 * @param cookie the calling client
418 * @param header the command
419 * @param response_handler not used
420 * @return the result of the operation
422 static protocol_binary_response_status
423 flush_command_handler(const void *cookie
,
424 protocol_binary_request_header
*header
,
425 memcached_binary_protocol_raw_response_handler response_handler
)
427 (void)response_handler
;
428 protocol_binary_response_status rval
;
430 memcached_protocol_client_st
*client
= (void*)cookie
;
431 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
433 protocol_binary_request_flush
*flush
= (void*)header
;
435 if (htonl(header
->request
.bodylen
) == 4)
437 timeout
= ntohl(flush
->message
.body
.expiration
);
440 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
441 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
442 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
444 /* Send a positive request */
445 protocol_binary_response_no_extras response
= {
448 .magic
= PROTOCOL_BINARY_RES
,
449 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
450 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
451 .opaque
= header
->request
.opaque
,
455 rval
= response_handler(cookie
, header
, (void*)&response
);
460 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
467 * Callback for GET, GETK, GETQ, GETKQ
468 * @param cookie the calling client
469 * @param header the command
470 * @param response_handler not used
471 * @return the result of the operation
473 static protocol_binary_response_status
474 get_command_handler(const void *cookie
,
475 protocol_binary_request_header
*header
,
476 memcached_binary_protocol_raw_response_handler response_handler
)
478 (void)response_handler
;
479 protocol_binary_response_status rval
;
481 memcached_protocol_client_st
*client
= (void*)cookie
;
482 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
484 uint16_t keylen
= ntohs(header
->request
.keylen
);
485 void *key
= (header
+ 1);
486 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
487 get_response_handler
);
489 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
490 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
491 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
493 /* Quiet commands shouldn't respond on cache misses */
494 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
499 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
506 * Callback for INCREMENT and INCREMENTQ
507 * @param cookie the calling client
508 * @param header the command
509 * @param response_handler not used
510 * @return the result of the operation
512 static protocol_binary_response_status
513 increment_command_handler(const void *cookie
,
514 protocol_binary_request_header
*header
,
515 memcached_binary_protocol_raw_response_handler response_handler
)
517 (void)response_handler
;
518 protocol_binary_response_status rval
;
520 memcached_protocol_client_st
*client
= (void*)cookie
;
521 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
523 uint16_t keylen
= ntohs(header
->request
.keylen
);
524 protocol_binary_request_incr
*request
= (void*)header
;
525 uint64_t init
= memcached_ntohll(request
->message
.body
.initial
);
526 uint64_t delta
= memcached_ntohll(request
->message
.body
.delta
);
527 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
528 void *key
= request
->bytes
+ sizeof(request
->bytes
);
532 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
533 delta
, init
, timeout
,
535 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
536 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
538 /* Send a positive request */
539 protocol_binary_response_incr response
= {
542 .magic
= PROTOCOL_BINARY_RES
,
543 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
544 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
545 .opaque
= header
->request
.opaque
,
546 .cas
= memcached_ntohll(cas
),
549 .body
.value
= memcached_htonll(result
)
553 rval
= response_handler(cookie
, header
, (void*)&response
);
558 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
565 * Callback for noop. Inform the v1 interface about the noop packet, and
566 * create and send a packet back to the client
568 * @param cookie the calling client
569 * @param header the command
570 * @param response_handler the response handler
571 * @return the result of the operation
573 static protocol_binary_response_status
574 noop_command_handler(const void *cookie
,
575 protocol_binary_request_header
*header
,
576 memcached_binary_protocol_raw_response_handler response_handler
)
578 memcached_protocol_client_st
*client
= (void*)cookie
;
579 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
581 client
->root
->callback
->interface
.v1
.noop(cookie
);
584 protocol_binary_response_no_extras response
= {
587 .magic
= PROTOCOL_BINARY_RES
,
588 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
589 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
590 .opaque
= header
->request
.opaque
,
595 return response_handler(cookie
, header
, (void*)&response
);
599 * Callback for APPEND and APPENDQ
600 * @param cookie the calling client
601 * @param header the command
602 * @param response_handler not used
603 * @return the result of the operation
605 static protocol_binary_response_status
606 append_command_handler(const void *cookie
,
607 protocol_binary_request_header
*header
,
608 memcached_binary_protocol_raw_response_handler response_handler
)
610 (void)response_handler
;
611 protocol_binary_response_status rval
;
613 memcached_protocol_client_st
*client
= (void*)cookie
;
614 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
616 uint16_t keylen
= ntohs(header
->request
.keylen
);
617 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
618 char *key
= (void*)(header
+1);
619 char *data
= key
+keylen
;
620 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
623 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
626 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
627 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
629 /* Send a positive request */
630 protocol_binary_response_no_extras response
= {
633 .magic
= PROTOCOL_BINARY_RES
,
634 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
635 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
636 .opaque
= header
->request
.opaque
,
637 .cas
= memcached_ntohll(result_cas
),
641 rval
= response_handler(cookie
, header
, (void*)&response
);
646 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
653 * Callback for PREPEND and PREPENDQ
654 * @param cookie the calling client
655 * @param header the command
656 * @param response_handler not used
657 * @return the result of the operation
659 static protocol_binary_response_status
660 prepend_command_handler(const void *cookie
,
661 protocol_binary_request_header
*header
,
662 memcached_binary_protocol_raw_response_handler response_handler
)
664 (void)response_handler
;
665 protocol_binary_response_status rval
;
667 memcached_protocol_client_st
*client
= (void*)cookie
;
668 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
670 uint16_t keylen
= ntohs(header
->request
.keylen
);
671 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
672 char *key
= (char*)(header
+ 1);
673 char *data
= key
+ keylen
;
674 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
676 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
679 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
680 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
682 /* Send a positive request */
683 protocol_binary_response_no_extras response
= {
686 .magic
= PROTOCOL_BINARY_RES
,
687 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
688 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
689 .opaque
= header
->request
.opaque
,
690 .cas
= memcached_ntohll(result_cas
),
694 rval
= response_handler(cookie
, header
, (void*)&response
);
699 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
706 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
707 * @param cookie the calling client
708 * @param header the command
709 * @param response_handler not used
710 * @return the result of the operation
712 static protocol_binary_response_status
713 quit_command_handler(const void *cookie
,
714 protocol_binary_request_header
*header
,
715 memcached_binary_protocol_raw_response_handler response_handler
)
717 memcached_protocol_client_st
*client
= (void*)cookie
;
718 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
720 client
->root
->callback
->interface
.v1
.quit(cookie
);
723 protocol_binary_response_no_extras response
= {
726 .magic
= PROTOCOL_BINARY_RES
,
727 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
728 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
729 .opaque
= header
->request
.opaque
734 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
736 response_handler(cookie
, header
, (void*)&response
);
739 /* I need a better way to signal to close the connection */
740 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
744 * Callback for REPLACE and REPLACEQ
745 * @param cookie the calling client
746 * @param header the command
747 * @param response_handler not used
748 * @return the result of the operation
750 static protocol_binary_response_status
751 replace_command_handler(const void *cookie
,
752 protocol_binary_request_header
*header
,
753 memcached_binary_protocol_raw_response_handler response_handler
)
755 (void)response_handler
;
756 protocol_binary_response_status rval
;
758 memcached_protocol_client_st
*client
= (void*)cookie
;
759 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
761 uint16_t keylen
= ntohs(header
->request
.keylen
);
762 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
763 protocol_binary_request_replace
*request
= (void*)header
;
764 uint32_t flags
= ntohl(request
->message
.body
.flags
);
765 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
766 char *key
= ((char*)header
) + sizeof(*header
) + 8;
767 char *data
= key
+ keylen
;
768 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
771 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
772 data
, datalen
, flags
,
775 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
776 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
778 /* Send a positive request */
779 protocol_binary_response_no_extras response
= {
782 .magic
= PROTOCOL_BINARY_RES
,
783 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
784 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
785 .opaque
= header
->request
.opaque
,
786 .cas
= memcached_ntohll(result_cas
),
790 rval
= response_handler(cookie
, header
, (void*)&response
);
795 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
802 * Callback for SET and SETQ
803 * @param cookie the calling client
804 * @param header the command
805 * @param response_handler not used
806 * @return the result of the operation
808 static protocol_binary_response_status
809 set_command_handler(const void *cookie
,
810 protocol_binary_request_header
*header
,
811 memcached_binary_protocol_raw_response_handler response_handler
)
813 (void)response_handler
;
814 protocol_binary_response_status rval
;
816 memcached_protocol_client_st
*client
= (void*)cookie
;
817 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
819 uint16_t keylen
= ntohs(header
->request
.keylen
);
820 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
821 protocol_binary_request_replace
*request
= (void*)header
;
822 uint32_t flags
= ntohl(request
->message
.body
.flags
);
823 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
824 char *key
= ((char*)header
) + sizeof(*header
) + 8;
825 char *data
= key
+ keylen
;
826 uint64_t cas
= memcached_ntohll(header
->request
.cas
);
830 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
831 data
, datalen
, flags
,
832 timeout
, cas
, &result_cas
);
833 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
834 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
836 /* Send a positive request */
837 protocol_binary_response_no_extras response
= {
840 .magic
= PROTOCOL_BINARY_RES
,
841 .opcode
= PROTOCOL_BINARY_CMD_SET
,
842 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
843 .opaque
= header
->request
.opaque
,
844 .cas
= memcached_ntohll(result_cas
),
848 rval
= response_handler(cookie
, header
, (void*)&response
);
853 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
861 * @param cookie the calling client
862 * @param header the command
863 * @param response_handler not used
864 * @return the result of the operation
866 static protocol_binary_response_status
867 stat_command_handler(const void *cookie
,
868 protocol_binary_request_header
*header
,
869 memcached_binary_protocol_raw_response_handler response_handler
)
871 (void)response_handler
;
872 protocol_binary_response_status rval
;
874 memcached_protocol_client_st
*client
= (void*)cookie
;
875 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
877 uint16_t keylen
= ntohs(header
->request
.keylen
);
879 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
882 stat_response_handler
);
886 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
893 * Callback for VERSION
894 * @param cookie the calling client
895 * @param header the command
896 * @param response_handler not used
897 * @return the result of the operation
899 static protocol_binary_response_status
900 version_command_handler(const void *cookie
,
901 protocol_binary_request_header
*header
,
902 memcached_binary_protocol_raw_response_handler response_handler
)
904 (void)response_handler
;
906 protocol_binary_response_status rval
;
908 memcached_protocol_client_st
*client
= (void*)cookie
;
909 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
911 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
912 version_response_handler
);
916 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
923 * The map to remap between the com codes and the v1 logical setting
925 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
926 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
927 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
928 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
929 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
930 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
931 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
932 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
933 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
934 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
935 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
936 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
937 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
938 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
939 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
940 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
941 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
942 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
943 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
944 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
945 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
946 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
947 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
948 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
949 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
950 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
951 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
952 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
956 * Try to execute a command. Fire the pre/post functions and the specialized
957 * handler function if it's set. If not, the unknown probe should be fired
959 * @param client the client connection to operate on
960 * @param header the command to execute
961 * @return true if success or false if a fatal error occured so that the
962 * connection should be shut down.
964 static protocol_binary_response_status
execute_command(memcached_protocol_client_st
*client
, protocol_binary_request_header
*header
)
966 if (client
->root
->pedantic
&&
967 memcached_binary_protocol_pedantic_check_request(header
))
969 /* @todo return invalid command packet */
972 /* we got all data available, execute the callback! */
973 if (client
->root
->callback
->pre_execute
!= NULL
)
975 client
->root
->callback
->pre_execute(client
, header
);
978 protocol_binary_response_status rval
;
979 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
980 uint8_t cc
= header
->request
.opcode
;
982 switch (client
->root
->callback
->interface_version
)
985 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
986 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
990 if (comcode_v0_v1_remap
[cc
] != NULL
) {
991 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
995 /* Unknown interface.
996 * It should be impossible to get here so I'll just call abort
997 * to avoid getting a compiler warning :-)
1003 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
1004 client
->root
->callback
->unknown
!= NULL
)
1006 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
1009 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
1010 rval
!= PROTOCOL_BINARY_RESPONSE_EINTERNAL
&&
1011 rval
!= PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED
)
1013 protocol_binary_response_no_extras response
= {
1016 .magic
= PROTOCOL_BINARY_RES
,
1018 .status
= htons(rval
),
1019 .opaque
= header
->request
.opaque
,
1023 rval
= raw_response_handler(client
, header
, (void*)&response
);
1026 if (client
->root
->callback
->post_execute
!= NULL
)
1028 client
->root
->callback
->post_execute(client
, header
);
1035 ** **********************************************************************
1036 ** "PROTOECTED" INTERFACE
1037 ** **********************************************************************
1039 memcached_protocol_event_t
memcached_binary_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
, void **endptr
)
1041 /* try to parse all of the received packets */
1042 protocol_binary_request_header
*header
;
1043 header
= (void*)client
->root
->input_buffer
;
1044 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1046 client
->error
= EINVAL
;
1047 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1049 ssize_t len
= *length
;
1051 while (len
>= (ssize_t
)sizeof(*header
) &&
1052 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1054 /* I have the complete package */
1055 client
->current_command
= header
;
1056 protocol_binary_response_status rv
= execute_command(client
, header
);
1058 if (rv
== PROTOCOL_BINARY_RESPONSE_EINTERNAL
)
1061 *endptr
= (void*)header
;
1062 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1063 } else if (rv
== PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED
)
1064 return MEMCACHED_PROTOCOL_PAUSE_EVENT
;
1066 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1070 intptr_t ptr
= (intptr_t)header
;
1079 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1080 header
= (void*)client
->root
->input_buffer
;
1084 *endptr
= (void*)header
;
1087 return MEMCACHED_PROTOCOL_READ_EVENT
;
1091 ** **********************************************************************
1093 ** **********************************************************************
1095 memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(memcached_protocol_st
*instance
)
1097 return instance
->callback
;
1100 void memcached_binary_protocol_set_callbacks(memcached_protocol_st
*instance
, memcached_binary_protocol_callback_st
*callback
)
1102 instance
->callback
= callback
;
1105 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1108 return raw_response_handler
;
1111 void memcached_binary_protocol_set_pedantic(memcached_protocol_st
*instance
, bool enable
)
1113 instance
->pedantic
= enable
;
1116 bool memcached_binary_protocol_get_pedantic(memcached_protocol_st
*instance
)
1118 return instance
->pedantic
;