1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
6 #include <sys/socket.h>
13 ** **********************************************************************
15 ** **********************************************************************
19 * The default function to receive data from the client. This function
20 * just wraps the recv function to receive from a socket.
21 * See man -s3socket recv for more information.
23 * @param cookie cookie indentifying a client, not used
24 * @param sock socket to read from
25 * @param buf the destination buffer
26 * @param nbytes the number of bytes to read
27 * @return the number of bytes transferred of -1 upon error
29 static ssize_t
default_recv(const void *cookie
,
35 return recv(sock
, buf
, nbytes
, 0);
39 * The default function to send data to the server. This function
40 * just wraps the send function to send through a socket.
41 * See man -s3socket send for more information.
43 * @param cookie cookie indentifying a client, not used
44 * @param sock socket to send to
45 * @param buf the source buffer
46 * @param nbytes the number of bytes to send
47 * @return the number of bytes transferred of -1 upon error
49 static ssize_t
default_send(const void *cookie
,
55 return send(fd
, buf
, nbytes
, 0);
59 * Try to drain the output buffers without blocking
61 * @param client the client to drain
62 * @return false if an error occured (connection should be shut down)
63 * true otherwise (please note that there may be more data to
64 * left in the buffer to send)
66 static bool drain_output(struct memcached_binary_protocol_client_st
*client
)
70 // Do we have pending data to send?
71 while (client
->output
!= NULL
)
73 len
= client
->root
->send(client
,
75 client
->output
->data
+ client
->output
->offset
,
76 client
->output
->nbytes
- client
->output
->offset
);
80 if (errno
== EWOULDBLOCK
)
84 else if (errno
!= EINTR
)
92 client
->output
->offset
+= (size_t)len
;
93 if (client
->output
->offset
== client
->output
->nbytes
)
95 /* This was the complete buffer */
96 struct chunk_st
*old
= client
->output
;
97 client
->output
= client
->output
->next
;
98 if (client
->output
== NULL
)
100 client
->output_tail
= NULL
;
102 cache_free(client
->root
->buffer_cache
, old
);
111 * Allocate an output buffer and chain it into the output list
113 * @param client the client that needs the buffer
114 * @return pointer to the new chunk if the allocation succeeds, NULL otherwise
116 static struct chunk_st
*
117 allocate_output_chunk(struct memcached_binary_protocol_client_st
*client
)
119 struct chunk_st
*ret
= cache_alloc(client
->root
->buffer_cache
);
124 ret
->offset
= ret
->nbytes
= 0;
126 ret
->size
= CHUNK_BUFFERSIZE
;
127 ret
->data
= (void*)(ret
+ 1);
128 if (client
->output
== NULL
)
130 client
->output
= client
->output_tail
= ret
;
134 client
->output_tail
->next
= ret
;
135 client
->output_tail
= ret
;
142 * Spool data into the send-buffer for a client.
144 * @param client the client to spool the data for
145 * @param data the data to spool
146 * @param length the number of bytes of data to spool
147 * @return PROTOCOL_BINARY_RESPONSE_SUCCESS if success,
148 * PROTOCOL_BINARY_RESPONSE_ENOMEM if we failed to allocate memory
150 static protocol_binary_response_status
151 spool_output(struct memcached_binary_protocol_client_st
*client
,
157 struct chunk_st
*chunk
= client
->output
;
158 while (offset
< length
)
160 if (chunk
== NULL
|| (chunk
->size
- chunk
->nbytes
) == 0)
162 if ((chunk
= allocate_output_chunk(client
)) == NULL
)
164 return PROTOCOL_BINARY_RESPONSE_ENOMEM
;
168 size_t bulk
= length
- offset
;
169 if (bulk
> chunk
->size
- chunk
->nbytes
)
171 bulk
= chunk
->size
- chunk
->nbytes
;
174 memcpy(chunk
->data
+ chunk
->nbytes
, data
, bulk
);
175 chunk
->nbytes
+= bulk
;
179 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
183 * Send a preformatted packet back to the client. If the connection is in
184 * pedantic mode, it will validate the packet and refuse to send it if it
185 * breaks the specification.
187 * @param cookie client identification
188 * @param request the original request packet
189 * @param response the packet to send
190 * @return The status of the operation
192 static protocol_binary_response_status
193 raw_response_handler(const void *cookie
,
194 protocol_binary_request_header
*request
,
195 protocol_binary_response_header
*response
)
197 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
199 if (client
->root
->pedantic
&&
200 !memcached_binary_protocol_pedantic_check_response(request
, response
))
202 return PROTOCOL_BINARY_RESPONSE_EINVAL
;
205 if (!drain_output(client
))
207 return PROTOCOL_BINARY_RESPONSE_EIO
;
210 size_t len
= sizeof(*response
) + htonl(response
->response
.bodylen
);
212 char *ptr
= (void*)response
;
214 if (client
->output
== NULL
)
216 /* I can write directly to the socket.... */
219 size_t num_bytes
= len
- offset
;
220 ssize_t nw
= client
->root
->send(client
,
226 if (errno
== EWOULDBLOCK
)
230 else if (errno
!= EINTR
)
232 client
->error
= errno
;
233 return PROTOCOL_BINARY_RESPONSE_EIO
;
238 offset
+= (size_t)nw
;
240 } while (offset
< len
);
243 return spool_output(client
, ptr
, len
- offset
);
247 * Version 0 of the interface is really low level and protocol specific,
248 * while the version 1 of the interface is more API focused. We need a
249 * way to translate between the command codes on the wire and the
250 * application level interface in V1, so let's just use the V0 of the
251 * interface as a map instead of creating a huuuge switch :-)
255 * Callback for the GET/GETQ/GETK and GETKQ responses
256 * @param cookie client identifier
257 * @param key the key for the item
258 * @param keylen the length of the key
259 * @param body the length of the body
260 * @param bodylen the length of the body
261 * @param flags the flags for the item
262 * @param cas the CAS id for the item
264 static protocol_binary_response_status
265 get_response_handler(const void *cookie
,
273 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
274 uint8_t opcode
= client
->current_command
->request
.opcode
;
276 if (opcode
== PROTOCOL_BINARY_CMD_GET
|| opcode
== PROTOCOL_BINARY_CMD_GETQ
)
281 protocol_binary_response_get response
= {
282 .message
.header
.response
= {
283 .magic
= PROTOCOL_BINARY_RES
,
285 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
286 .opaque
= client
->current_command
->request
.opaque
,
288 .keylen
= htons(keylen
),
290 .bodylen
= htonl(bodylen
+ keylen
+ 4),
292 .message
.body
.flags
= htonl(flags
),
295 protocol_binary_response_status rval
;
296 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
297 if ((rval
= spool_output(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
298 (rval
= spool_output(client
, key
, keylen
)) != success
||
299 (rval
= spool_output(client
, body
, bodylen
)) != success
)
304 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
308 * Callback for the STAT responses
309 * @param cookie client identifier
310 * @param key the key for the item
311 * @param keylen the length of the key
312 * @param body the length of the body
313 * @param bodylen the length of the body
315 static protocol_binary_response_status
316 stat_response_handler(const void *cookie
,
322 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
324 protocol_binary_response_no_extras response
= {
325 .message
.header
.response
= {
326 .magic
= PROTOCOL_BINARY_RES
,
327 .opcode
= client
->current_command
->request
.opcode
,
328 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
329 .opaque
= client
->current_command
->request
.opaque
,
330 .keylen
= htons(keylen
),
331 .bodylen
= htonl(bodylen
+ keylen
),
335 protocol_binary_response_status rval
;
336 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
337 if ((rval
= spool_output(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
338 (rval
= spool_output(client
, key
, keylen
)) != success
||
339 (rval
= spool_output(client
, body
, bodylen
)) != success
)
344 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
348 * Callback for the VERSION responses
349 * @param cookie client identifier
350 * @param text the length of the body
351 * @param textlen the length of the body
353 static protocol_binary_response_status
354 version_response_handler(const void *cookie
,
358 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
360 protocol_binary_response_no_extras response
= {
361 .message
.header
.response
= {
362 .magic
= PROTOCOL_BINARY_RES
,
363 .opcode
= client
->current_command
->request
.opcode
,
364 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
365 .opaque
= client
->current_command
->request
.opaque
,
366 .bodylen
= htonl(textlen
),
370 protocol_binary_response_status rval
;
371 const protocol_binary_response_status success
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
372 if ((rval
= spool_output(client
, response
.bytes
, sizeof(response
.bytes
))) != success
||
373 (rval
= spool_output(client
, text
, textlen
)) != success
)
378 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
382 * Callback for ADD and ADDQ
383 * @param cookie the calling client
384 * @param header the add/addq command
385 * @param response_handler not used
386 * @return the result of the operation
388 static protocol_binary_response_status
389 add_command_handler(const void *cookie
,
390 protocol_binary_request_header
*header
,
391 memcached_binary_protocol_raw_response_handler response_handler
)
393 protocol_binary_response_status rval
;
395 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
396 if (client
->root
->callback
->interface
.v1
.add
!= NULL
)
398 uint16_t keylen
= ntohs(header
->request
.keylen
);
399 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
400 protocol_binary_request_add
*request
= (void*)header
;
401 uint32_t flags
= ntohl(request
->message
.body
.flags
);
402 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
403 char *key
= ((char*)header
) + sizeof(*header
) + 8;
404 char *data
= key
+ keylen
;
407 rval
= client
->root
->callback
->interface
.v1
.add(cookie
, key
, keylen
,
408 data
, datalen
, flags
,
411 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
412 header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
414 /* Send a positive request */
415 protocol_binary_response_no_extras response
= {
418 .magic
= PROTOCOL_BINARY_RES
,
419 .opcode
= PROTOCOL_BINARY_CMD_ADD
,
420 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
421 .opaque
= header
->request
.opaque
,
426 rval
= response_handler(cookie
, header
, (void*)&response
);
431 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
438 * Callback for DECREMENT and DECREMENTQ
439 * @param cookie the calling client
440 * @param header the command
441 * @param response_handler not used
442 * @return the result of the operation
444 static protocol_binary_response_status
445 decrement_command_handler(const void *cookie
,
446 protocol_binary_request_header
*header
,
447 memcached_binary_protocol_raw_response_handler response_handler
)
449 (void)response_handler
;
450 protocol_binary_response_status rval
;
452 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
453 if (client
->root
->callback
->interface
.v1
.decrement
!= NULL
)
455 uint16_t keylen
= ntohs(header
->request
.keylen
);
456 protocol_binary_request_decr
*request
= (void*)header
;
457 uint64_t init
= ntohll(request
->message
.body
.initial
);
458 uint64_t delta
= ntohll(request
->message
.body
.delta
);
459 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
460 void *key
= request
->bytes
+ sizeof(request
->bytes
);
464 char buffer
[1024] = {0};
465 memcpy(buffer
, key
, keylen
);
466 fprintf(stderr
, "%s\n", buffer
);
469 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
470 delta
, init
, timeout
,
472 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
473 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
475 /* Send a positive request */
476 protocol_binary_response_decr response
= {
479 .magic
= PROTOCOL_BINARY_RES
,
480 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
481 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
482 .opaque
= header
->request
.opaque
,
486 .body
.value
= htonll(result
)
489 rval
= response_handler(cookie
, header
, (void*)&response
);
494 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
501 * Callback for DELETE and DELETEQ
502 * @param cookie the calling client
503 * @param header the command
504 * @param response_handler not used
505 * @return the result of the operation
507 static protocol_binary_response_status
508 delete_command_handler(const void *cookie
,
509 protocol_binary_request_header
*header
,
510 memcached_binary_protocol_raw_response_handler response_handler
)
512 (void)response_handler
;
513 protocol_binary_response_status rval
;
515 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
516 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
518 uint16_t keylen
= ntohs(header
->request
.keylen
);
519 void *key
= (header
+ 1);
520 uint64_t cas
= ntohll(header
->request
.cas
);
521 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
522 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
523 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
525 /* Send a positive request */
526 protocol_binary_response_no_extras response
= {
529 .magic
= PROTOCOL_BINARY_RES
,
530 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
531 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
532 .opaque
= header
->request
.opaque
,
536 rval
= response_handler(cookie
, header
, (void*)&response
);
541 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
548 * Callback for FLUSH and FLUSHQ
549 * @param cookie the calling client
550 * @param header the command
551 * @param response_handler not used
552 * @return the result of the operation
554 static protocol_binary_response_status
555 flush_command_handler(const void *cookie
,
556 protocol_binary_request_header
*header
,
557 memcached_binary_protocol_raw_response_handler response_handler
)
559 (void)response_handler
;
560 protocol_binary_response_status rval
;
562 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
563 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
565 protocol_binary_request_flush
*flush
= (void*)header
;
567 if (htonl(header
->request
.bodylen
) == 4)
569 timeout
= ntohl(flush
->message
.body
.expiration
);
572 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
573 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
574 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
576 /* Send a positive request */
577 protocol_binary_response_no_extras response
= {
580 .magic
= PROTOCOL_BINARY_RES
,
581 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
582 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
583 .opaque
= header
->request
.opaque
,
587 rval
= response_handler(cookie
, header
, (void*)&response
);
592 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
599 * Callback for GET, GETK, GETQ, GETKQ
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 get_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 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
614 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
616 uint16_t keylen
= ntohs(header
->request
.keylen
);
617 void *key
= (header
+ 1);
618 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
619 get_response_handler
);
621 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
622 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
623 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
625 /* Quiet commands shouldn't respond on cache misses */
626 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
631 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
638 * Callback for INCREMENT and INCREMENTQ
639 * @param cookie the calling client
640 * @param header the command
641 * @param response_handler not used
642 * @return the result of the operation
644 static protocol_binary_response_status
645 increment_command_handler(const void *cookie
,
646 protocol_binary_request_header
*header
,
647 memcached_binary_protocol_raw_response_handler response_handler
)
649 (void)response_handler
;
650 protocol_binary_response_status rval
;
652 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
653 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
655 uint16_t keylen
= ntohs(header
->request
.keylen
);
656 protocol_binary_request_incr
*request
= (void*)header
;
657 uint64_t init
= ntohll(request
->message
.body
.initial
);
658 uint64_t delta
= ntohll(request
->message
.body
.delta
);
659 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
660 void *key
= request
->bytes
+ sizeof(request
->bytes
);
664 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
665 delta
, init
, timeout
,
667 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
668 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
670 /* Send a positive request */
671 protocol_binary_response_incr response
= {
674 .magic
= PROTOCOL_BINARY_RES
,
675 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
676 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
677 .opaque
= header
->request
.opaque
,
681 .body
.value
= htonll(result
)
684 rval
= response_handler(cookie
, header
, (void*)&response
);
689 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
696 * Callback for noop. Inform the v1 interface about the noop packet, and
697 * create and send a packet back to the client
699 * @param cookie the calling client
700 * @param header the command
701 * @param response_handler the response handler
702 * @return the result of the operation
704 static protocol_binary_response_status
705 noop_command_handler(const void *cookie
,
706 protocol_binary_request_header
*header
,
707 memcached_binary_protocol_raw_response_handler response_handler
)
709 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
710 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
712 client
->root
->callback
->interface
.v1
.noop(cookie
);
715 protocol_binary_response_no_extras response
= {
718 .magic
= PROTOCOL_BINARY_RES
,
719 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
720 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
721 .opaque
= header
->request
.opaque
,
726 return response_handler(cookie
, header
, (void*)&response
);
730 * Callback for APPEND and APPENDQ
731 * @param cookie the calling client
732 * @param header the command
733 * @param response_handler not used
734 * @return the result of the operation
736 static protocol_binary_response_status
737 append_command_handler(const void *cookie
,
738 protocol_binary_request_header
*header
,
739 memcached_binary_protocol_raw_response_handler response_handler
)
741 (void)response_handler
;
742 protocol_binary_response_status rval
;
744 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
745 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
747 uint16_t keylen
= ntohs(header
->request
.keylen
);
748 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
749 char *key
= (void*)(header
+ 1);
750 char *data
= key
+ keylen
;
751 uint64_t cas
= ntohll(header
->request
.cas
);
754 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
757 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
758 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
760 /* Send a positive request */
761 protocol_binary_response_no_extras response
= {
764 .magic
= PROTOCOL_BINARY_RES
,
765 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
766 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
767 .opaque
= header
->request
.opaque
,
768 .cas
= ntohll(result_cas
),
772 rval
= response_handler(cookie
, header
, (void*)&response
);
777 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
784 * Callback for PREPEND and PREPENDQ
785 * @param cookie the calling client
786 * @param header the command
787 * @param response_handler not used
788 * @return the result of the operation
790 static protocol_binary_response_status
791 prepend_command_handler(const void *cookie
,
792 protocol_binary_request_header
*header
,
793 memcached_binary_protocol_raw_response_handler response_handler
)
795 (void)response_handler
;
796 protocol_binary_response_status rval
;
798 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
799 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
801 uint16_t keylen
= ntohs(header
->request
.keylen
);
802 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
803 char *key
= (char*)(header
+ 1);
804 char *data
= key
+ keylen
;
805 uint64_t cas
= ntohll(header
->request
.cas
);
807 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
810 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
811 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
813 /* Send a positive request */
814 protocol_binary_response_no_extras response
= {
817 .magic
= PROTOCOL_BINARY_RES
,
818 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
819 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
820 .opaque
= header
->request
.opaque
,
821 .cas
= ntohll(result_cas
),
825 rval
= response_handler(cookie
, header
, (void*)&response
);
830 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
837 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
838 * @param cookie the calling client
839 * @param header the command
840 * @param response_handler not used
841 * @return the result of the operation
843 static protocol_binary_response_status
844 quit_command_handler(const void *cookie
,
845 protocol_binary_request_header
*header
,
846 memcached_binary_protocol_raw_response_handler response_handler
)
848 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
849 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
851 client
->root
->callback
->interface
.v1
.quit(cookie
);
854 protocol_binary_response_no_extras response
= {
857 .magic
= PROTOCOL_BINARY_RES
,
858 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
859 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
860 .opaque
= header
->request
.opaque
865 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
867 response_handler(cookie
, header
, (void*)&response
);
870 /* I need a better way to signal to close the connection */
871 return PROTOCOL_BINARY_RESPONSE_EIO
;
875 * Callback for REPLACE and REPLACEQ
876 * @param cookie the calling client
877 * @param header the command
878 * @param response_handler not used
879 * @return the result of the operation
881 static protocol_binary_response_status
882 replace_command_handler(const void *cookie
,
883 protocol_binary_request_header
*header
,
884 memcached_binary_protocol_raw_response_handler response_handler
)
886 (void)response_handler
;
887 protocol_binary_response_status rval
;
889 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
890 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
892 uint16_t keylen
= ntohs(header
->request
.keylen
);
893 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
894 protocol_binary_request_replace
*request
= (void*)header
;
895 uint32_t flags
= ntohl(request
->message
.body
.flags
);
896 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
897 char *key
= ((char*)header
) + sizeof(*header
) + 8;
898 char *data
= key
+ keylen
;
899 uint64_t cas
= ntohll(header
->request
.cas
);
902 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
903 data
, datalen
, flags
,
906 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
907 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
909 /* Send a positive request */
910 protocol_binary_response_no_extras response
= {
913 .magic
= PROTOCOL_BINARY_RES
,
914 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
915 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
916 .opaque
= header
->request
.opaque
,
917 .cas
= ntohll(result_cas
),
921 rval
= response_handler(cookie
, header
, (void*)&response
);
926 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
933 * Callback for SET and SETQ
934 * @param cookie the calling client
935 * @param header the command
936 * @param response_handler not used
937 * @return the result of the operation
939 static protocol_binary_response_status
940 set_command_handler(const void *cookie
,
941 protocol_binary_request_header
*header
,
942 memcached_binary_protocol_raw_response_handler response_handler
)
944 (void)response_handler
;
945 protocol_binary_response_status rval
;
947 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
948 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
950 uint16_t keylen
= ntohs(header
->request
.keylen
);
951 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
952 protocol_binary_request_replace
*request
= (void*)header
;
953 uint32_t flags
= ntohl(request
->message
.body
.flags
);
954 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
955 char *key
= ((char*)header
) + sizeof(*header
) + 8;
956 char *data
= key
+ keylen
;
957 uint64_t cas
= ntohll(header
->request
.cas
);
961 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
962 data
, datalen
, flags
,
963 timeout
, cas
, &result_cas
);
964 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
965 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
967 /* Send a positive request */
968 protocol_binary_response_no_extras response
= {
971 .magic
= PROTOCOL_BINARY_RES
,
972 .opcode
= PROTOCOL_BINARY_CMD_SET
,
973 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
974 .opaque
= header
->request
.opaque
,
975 .cas
= ntohll(result_cas
),
979 rval
= response_handler(cookie
, header
, (void*)&response
);
984 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
992 * @param cookie the calling client
993 * @param header the command
994 * @param response_handler not used
995 * @return the result of the operation
997 static protocol_binary_response_status
998 stat_command_handler(const void *cookie
,
999 protocol_binary_request_header
*header
,
1000 memcached_binary_protocol_raw_response_handler response_handler
)
1002 (void)response_handler
;
1003 protocol_binary_response_status rval
;
1005 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
1006 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
1008 uint16_t keylen
= ntohs(header
->request
.keylen
);
1010 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
1011 (void*)(header
+ 1),
1013 stat_response_handler
);
1017 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
1024 * Callback for VERSION
1025 * @param cookie the calling client
1026 * @param header the command
1027 * @param response_handler not used
1028 * @return the result of the operation
1030 static protocol_binary_response_status
1031 version_command_handler(const void *cookie
,
1032 protocol_binary_request_header
*header
,
1033 memcached_binary_protocol_raw_response_handler response_handler
)
1035 (void)response_handler
;
1037 protocol_binary_response_status rval
;
1039 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
1040 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
1042 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
1043 version_response_handler
);
1047 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
1054 * The map to remap between the com codes and the v1 logical setting
1056 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
1057 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
1058 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
1059 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
1060 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
1061 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
1062 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
1063 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
1064 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
1065 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
1066 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
1067 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
1068 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
1069 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
1070 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
1071 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
1072 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
1073 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
1074 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
1075 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
1076 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
1077 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
1078 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
1079 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
1080 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
1081 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
1082 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
1083 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
1087 * Try to execute a command. Fire the pre/post functions and the specialized
1088 * handler function if it's set. If not, the unknown probe should be fired
1090 * @param client the client connection to operate on
1091 * @param header the command to execute
1092 * @return true if success or false if a fatal error occured so that the
1093 * connection should be shut down.
1095 static bool execute_command(struct memcached_binary_protocol_client_st
*client
, protocol_binary_request_header
*header
)
1097 if (client
->root
->pedantic
&&
1098 memcached_binary_protocol_pedantic_check_request(header
))
1100 /* @todo return invalid command packet */
1103 /* we got all data available, execute the callback! */
1104 if (client
->root
->callback
->pre_execute
!= NULL
)
1106 client
->root
->callback
->pre_execute(client
, header
);
1109 protocol_binary_response_status rval
;
1110 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
1111 uint8_t cc
= header
->request
.opcode
;
1113 switch (client
->root
->callback
->interface_version
)
1116 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
1117 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
1121 if (comcode_v0_v1_remap
[cc
] != NULL
) {
1122 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
1126 /* Unknown interface.
1127 * It should be impossible to get here so I'll just call abort
1128 * to avoid getting a compiler warning :-)
1134 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
1135 client
->root
->callback
->unknown
!= NULL
)
1137 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
1140 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
1141 rval
!= PROTOCOL_BINARY_RESPONSE_EIO
)
1143 protocol_binary_response_no_extras response
= {
1146 .magic
= PROTOCOL_BINARY_RES
,
1148 .status
= htons(rval
),
1149 .opaque
= header
->request
.opaque
,
1153 rval
= raw_response_handler(client
, header
, (void*)&response
);
1156 if (client
->root
->callback
->post_execute
!= NULL
)
1158 client
->root
->callback
->post_execute(client
, header
);
1161 return rval
!= PROTOCOL_BINARY_RESPONSE_EIO
;
1165 ** **********************************************************************
1166 ** * PUBLIC INTERFACE
1167 ** * See protocol_handler.h for function description
1168 ** **********************************************************************
1170 struct memcached_binary_protocol_st
*memcached_binary_protocol_create_instance(void)
1172 struct memcached_binary_protocol_st
*ret
= calloc(1, sizeof(*ret
));
1175 ret
->recv
= default_recv
;
1176 ret
->send
= default_send
;
1177 ret
->input_buffer_size
= 1 * 1024 * 1024;
1178 ret
->input_buffer
= malloc(ret
->input_buffer_size
);
1179 if (ret
->input_buffer
== NULL
)
1186 ret
->buffer_cache
= cache_create("protocol_handler",
1187 CHUNK_BUFFERSIZE
+ sizeof(struct chunk_st
),
1189 if (ret
->buffer_cache
== NULL
) {
1190 free(ret
->input_buffer
);
1198 void memcached_binary_protocol_destroy_instance(struct memcached_binary_protocol_st
*instance
)
1200 cache_destroy(instance
->buffer_cache
);
1201 free(instance
->input_buffer
);
1205 struct memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(struct memcached_binary_protocol_st
*instance
)
1207 return instance
->callback
;
1210 void memcached_binary_protocol_set_callbacks(struct memcached_binary_protocol_st
*instance
, struct memcached_binary_protocol_callback_st
*callback
)
1212 instance
->callback
= callback
;
1215 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1218 return raw_response_handler
;
1221 void memcached_binary_protocol_set_pedantic(struct memcached_binary_protocol_st
*instance
, bool enable
)
1223 instance
->pedantic
= enable
;
1226 bool memcached_binary_protocol_get_pedantic(struct memcached_binary_protocol_st
*instance
)
1228 return instance
->pedantic
;
1231 struct memcached_binary_protocol_client_st
*memcached_binary_protocol_create_client(struct memcached_binary_protocol_st
*instance
, int sock
)
1233 struct memcached_binary_protocol_client_st
*ret
= calloc(1, sizeof(*ret
));
1236 ret
->root
= instance
;
1243 void memcached_binary_protocol_client_destroy(struct memcached_binary_protocol_client_st
*client
)
1248 enum MEMCACHED_BINARY_PROTOCOL_EVENT
memcached_binary_protocol_client_work(struct memcached_binary_protocol_client_st
*client
)
1250 /* Try to send data and read from the socket */
1251 bool more_data
= true;
1254 ssize_t len
= client
->root
->recv(client
,
1256 client
->root
->input_buffer
+ client
->input_buffer_offset
,
1257 client
->root
->input_buffer_size
- client
->input_buffer_offset
);
1261 /* Do we have the complete packet? */
1262 if (client
->input_buffer_offset
> 0)
1264 memcpy(client
->root
->input_buffer
, client
->input_buffer
,
1265 client
->input_buffer_offset
);
1266 len
+= (ssize_t
)client
->input_buffer_offset
;
1268 /* @todo use buffer-cache! */
1269 free(client
->input_buffer
);
1270 client
->input_buffer_offset
= 0;
1273 /* try to parse all of the received packets */
1274 protocol_binary_request_header
*header
;
1275 header
= (void*)client
->root
->input_buffer
;
1277 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1279 client
->error
= EINVAL
;
1283 while (len
>= (ssize_t
)sizeof(*header
) &&
1284 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1287 /* I have the complete package */
1288 client
->current_command
= header
;
1289 if (!execute_command(client
, header
))
1294 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1298 intptr_t ptr
= (intptr_t)header
;
1306 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1307 header
= (void*)client
->root
->input_buffer
;
1314 /* save the data for later on */
1315 /* @todo use buffer-cache */
1316 client
->input_buffer
= malloc((size_t)len
);
1317 if (client
->input_buffer
== NULL
)
1319 client
->error
= ENOMEM
;
1322 memcpy(client
->input_buffer
, header
, (size_t)len
);
1323 client
->input_buffer_offset
= (size_t)len
;
1329 /* Connection closed */
1330 drain_output(client
);
1335 if (errno
!= EWOULDBLOCK
)
1337 client
->error
= errno
;
1338 /* mark this client as terminated! */
1343 } while (more_data
);
1345 if (!drain_output(client
))
1350 return (client
->output
) ? READ_WRITE_EVENT
: READ_EVENT
;