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 rval
= client
->root
->callback
->interface
.v1
.decrement(cookie
, key
, keylen
,
465 delta
, init
, timeout
,
467 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
468 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENT
)
470 /* Send a positive request */
471 protocol_binary_response_decr response
= {
474 .magic
= PROTOCOL_BINARY_RES
,
475 .opcode
= PROTOCOL_BINARY_CMD_DECREMENT
,
476 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
477 .opaque
= header
->request
.opaque
,
481 .body
.value
= htonll(result
)
484 rval
= response_handler(cookie
, header
, (void*)&response
);
489 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
496 * Callback for DELETE and DELETEQ
497 * @param cookie the calling client
498 * @param header the command
499 * @param response_handler not used
500 * @return the result of the operation
502 static protocol_binary_response_status
503 delete_command_handler(const void *cookie
,
504 protocol_binary_request_header
*header
,
505 memcached_binary_protocol_raw_response_handler response_handler
)
507 (void)response_handler
;
508 protocol_binary_response_status rval
;
510 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
511 if (client
->root
->callback
->interface
.v1
.delete != NULL
)
513 uint16_t keylen
= ntohs(header
->request
.keylen
);
514 void *key
= (header
+ 1);
515 uint64_t cas
= ntohll(header
->request
.cas
);
516 rval
= client
->root
->callback
->interface
.v1
.delete(cookie
, key
, keylen
, cas
);
517 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
518 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
520 /* Send a positive request */
521 protocol_binary_response_no_extras response
= {
524 .magic
= PROTOCOL_BINARY_RES
,
525 .opcode
= PROTOCOL_BINARY_CMD_DELETE
,
526 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
527 .opaque
= header
->request
.opaque
,
531 rval
= response_handler(cookie
, header
, (void*)&response
);
536 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
543 * Callback for FLUSH and FLUSHQ
544 * @param cookie the calling client
545 * @param header the command
546 * @param response_handler not used
547 * @return the result of the operation
549 static protocol_binary_response_status
550 flush_command_handler(const void *cookie
,
551 protocol_binary_request_header
*header
,
552 memcached_binary_protocol_raw_response_handler response_handler
)
554 (void)response_handler
;
555 protocol_binary_response_status rval
;
557 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
558 if (client
->root
->callback
->interface
.v1
.flush
!= NULL
)
560 protocol_binary_request_flush
*flush
= (void*)header
;
562 if (htonl(header
->request
.bodylen
) == 4)
564 timeout
= ntohl(flush
->message
.body
.expiration
);
567 rval
= client
->root
->callback
->interface
.v1
.flush(cookie
, timeout
);
568 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
569 header
->request
.opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
571 /* Send a positive request */
572 protocol_binary_response_no_extras response
= {
575 .magic
= PROTOCOL_BINARY_RES
,
576 .opcode
= PROTOCOL_BINARY_CMD_FLUSH
,
577 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
578 .opaque
= header
->request
.opaque
,
582 rval
= response_handler(cookie
, header
, (void*)&response
);
587 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
594 * Callback for GET, GETK, GETQ, GETKQ
595 * @param cookie the calling client
596 * @param header the command
597 * @param response_handler not used
598 * @return the result of the operation
600 static protocol_binary_response_status
601 get_command_handler(const void *cookie
,
602 protocol_binary_request_header
*header
,
603 memcached_binary_protocol_raw_response_handler response_handler
)
605 (void)response_handler
;
606 protocol_binary_response_status rval
;
608 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
609 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
611 uint16_t keylen
= ntohs(header
->request
.keylen
);
612 void *key
= (header
+ 1);
613 rval
= client
->root
->callback
->interface
.v1
.get(cookie
, key
, keylen
,
614 get_response_handler
);
616 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
&&
617 (header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETQ
||
618 header
->request
.opcode
== PROTOCOL_BINARY_CMD_GETKQ
))
620 /* Quiet commands shouldn't respond on cache misses */
621 rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
626 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
633 * Callback for INCREMENT and INCREMENTQ
634 * @param cookie the calling client
635 * @param header the command
636 * @param response_handler not used
637 * @return the result of the operation
639 static protocol_binary_response_status
640 increment_command_handler(const void *cookie
,
641 protocol_binary_request_header
*header
,
642 memcached_binary_protocol_raw_response_handler response_handler
)
644 (void)response_handler
;
645 protocol_binary_response_status rval
;
647 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
648 if (client
->root
->callback
->interface
.v1
.increment
!= NULL
)
650 uint16_t keylen
= ntohs(header
->request
.keylen
);
651 protocol_binary_request_incr
*request
= (void*)header
;
652 uint64_t init
= ntohll(request
->message
.body
.initial
);
653 uint64_t delta
= ntohll(request
->message
.body
.delta
);
654 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
655 void *key
= request
->bytes
+ sizeof(request
->bytes
);
659 rval
= client
->root
->callback
->interface
.v1
.increment(cookie
, key
, keylen
,
660 delta
, init
, timeout
,
662 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
663 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
)
665 /* Send a positive request */
666 protocol_binary_response_incr response
= {
669 .magic
= PROTOCOL_BINARY_RES
,
670 .opcode
= PROTOCOL_BINARY_CMD_INCREMENT
,
671 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
672 .opaque
= header
->request
.opaque
,
676 .body
.value
= htonll(result
)
680 rval
= response_handler(cookie
, header
, (void*)&response
);
685 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
692 * Callback for noop. Inform the v1 interface about the noop packet, and
693 * create and send a packet back to the client
695 * @param cookie the calling client
696 * @param header the command
697 * @param response_handler the response handler
698 * @return the result of the operation
700 static protocol_binary_response_status
701 noop_command_handler(const void *cookie
,
702 protocol_binary_request_header
*header
,
703 memcached_binary_protocol_raw_response_handler response_handler
)
705 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
706 if (client
->root
->callback
->interface
.v1
.noop
!= NULL
)
708 client
->root
->callback
->interface
.v1
.noop(cookie
);
711 protocol_binary_response_no_extras response
= {
714 .magic
= PROTOCOL_BINARY_RES
,
715 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
716 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
717 .opaque
= header
->request
.opaque
,
722 return response_handler(cookie
, header
, (void*)&response
);
726 * Callback for APPEND and APPENDQ
727 * @param cookie the calling client
728 * @param header the command
729 * @param response_handler not used
730 * @return the result of the operation
732 static protocol_binary_response_status
733 append_command_handler(const void *cookie
,
734 protocol_binary_request_header
*header
,
735 memcached_binary_protocol_raw_response_handler response_handler
)
737 (void)response_handler
;
738 protocol_binary_response_status rval
;
740 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
741 if (client
->root
->callback
->interface
.v1
.append
!= NULL
)
743 uint16_t keylen
= ntohs(header
->request
.keylen
);
744 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
745 char *key
= (void*)(header
+ 1);
746 char *data
= key
+ keylen
;
747 uint64_t cas
= ntohll(header
->request
.cas
);
750 rval
= client
->root
->callback
->interface
.v1
.append(cookie
, key
, keylen
,
753 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
754 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
)
756 /* Send a positive request */
757 protocol_binary_response_no_extras response
= {
760 .magic
= PROTOCOL_BINARY_RES
,
761 .opcode
= PROTOCOL_BINARY_CMD_APPEND
,
762 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
763 .opaque
= header
->request
.opaque
,
764 .cas
= ntohll(result_cas
),
768 rval
= response_handler(cookie
, header
, (void*)&response
);
773 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
780 * Callback for PREPEND and PREPENDQ
781 * @param cookie the calling client
782 * @param header the command
783 * @param response_handler not used
784 * @return the result of the operation
786 static protocol_binary_response_status
787 prepend_command_handler(const void *cookie
,
788 protocol_binary_request_header
*header
,
789 memcached_binary_protocol_raw_response_handler response_handler
)
791 (void)response_handler
;
792 protocol_binary_response_status rval
;
794 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
795 if (client
->root
->callback
->interface
.v1
.prepend
!= NULL
)
797 uint16_t keylen
= ntohs(header
->request
.keylen
);
798 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
;
799 char *key
= (char*)(header
+ 1);
800 char *data
= key
+ keylen
;
801 uint64_t cas
= ntohll(header
->request
.cas
);
803 rval
= client
->root
->callback
->interface
.v1
.prepend(cookie
, key
, keylen
,
806 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
807 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
809 /* Send a positive request */
810 protocol_binary_response_no_extras response
= {
813 .magic
= PROTOCOL_BINARY_RES
,
814 .opcode
= PROTOCOL_BINARY_CMD_PREPEND
,
815 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
816 .opaque
= header
->request
.opaque
,
817 .cas
= ntohll(result_cas
),
821 rval
= response_handler(cookie
, header
, (void*)&response
);
826 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
833 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
834 * @param cookie the calling client
835 * @param header the command
836 * @param response_handler not used
837 * @return the result of the operation
839 static protocol_binary_response_status
840 quit_command_handler(const void *cookie
,
841 protocol_binary_request_header
*header
,
842 memcached_binary_protocol_raw_response_handler response_handler
)
844 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
845 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
847 client
->root
->callback
->interface
.v1
.quit(cookie
);
850 protocol_binary_response_no_extras response
= {
853 .magic
= PROTOCOL_BINARY_RES
,
854 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
855 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
856 .opaque
= header
->request
.opaque
861 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
863 response_handler(cookie
, header
, (void*)&response
);
866 /* I need a better way to signal to close the connection */
867 return PROTOCOL_BINARY_RESPONSE_EIO
;
871 * Callback for REPLACE and REPLACEQ
872 * @param cookie the calling client
873 * @param header the command
874 * @param response_handler not used
875 * @return the result of the operation
877 static protocol_binary_response_status
878 replace_command_handler(const void *cookie
,
879 protocol_binary_request_header
*header
,
880 memcached_binary_protocol_raw_response_handler response_handler
)
882 (void)response_handler
;
883 protocol_binary_response_status rval
;
885 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
886 if (client
->root
->callback
->interface
.v1
.replace
!= NULL
)
888 uint16_t keylen
= ntohs(header
->request
.keylen
);
889 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
890 protocol_binary_request_replace
*request
= (void*)header
;
891 uint32_t flags
= ntohl(request
->message
.body
.flags
);
892 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
893 char *key
= ((char*)header
) + sizeof(*header
) + 8;
894 char *data
= key
+ keylen
;
895 uint64_t cas
= ntohll(header
->request
.cas
);
898 rval
= client
->root
->callback
->interface
.v1
.replace(cookie
, key
, keylen
,
899 data
, datalen
, flags
,
902 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
903 header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
905 /* Send a positive request */
906 protocol_binary_response_no_extras response
= {
909 .magic
= PROTOCOL_BINARY_RES
,
910 .opcode
= PROTOCOL_BINARY_CMD_REPLACE
,
911 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
912 .opaque
= header
->request
.opaque
,
913 .cas
= ntohll(result_cas
),
917 rval
= response_handler(cookie
, header
, (void*)&response
);
922 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
929 * Callback for SET and SETQ
930 * @param cookie the calling client
931 * @param header the command
932 * @param response_handler not used
933 * @return the result of the operation
935 static protocol_binary_response_status
936 set_command_handler(const void *cookie
,
937 protocol_binary_request_header
*header
,
938 memcached_binary_protocol_raw_response_handler response_handler
)
940 (void)response_handler
;
941 protocol_binary_response_status rval
;
943 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
944 if (client
->root
->callback
->interface
.v1
.set
!= NULL
)
946 uint16_t keylen
= ntohs(header
->request
.keylen
);
947 uint32_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
948 protocol_binary_request_replace
*request
= (void*)header
;
949 uint32_t flags
= ntohl(request
->message
.body
.flags
);
950 uint32_t timeout
= ntohl(request
->message
.body
.expiration
);
951 char *key
= ((char*)header
) + sizeof(*header
) + 8;
952 char *data
= key
+ keylen
;
953 uint64_t cas
= ntohll(header
->request
.cas
);
957 rval
= client
->root
->callback
->interface
.v1
.set(cookie
, key
, keylen
,
958 data
, datalen
, flags
,
959 timeout
, cas
, &result_cas
);
960 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
961 header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
963 /* Send a positive request */
964 protocol_binary_response_no_extras response
= {
967 .magic
= PROTOCOL_BINARY_RES
,
968 .opcode
= PROTOCOL_BINARY_CMD_SET
,
969 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
970 .opaque
= header
->request
.opaque
,
971 .cas
= ntohll(result_cas
),
975 rval
= response_handler(cookie
, header
, (void*)&response
);
980 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
988 * @param cookie the calling client
989 * @param header the command
990 * @param response_handler not used
991 * @return the result of the operation
993 static protocol_binary_response_status
994 stat_command_handler(const void *cookie
,
995 protocol_binary_request_header
*header
,
996 memcached_binary_protocol_raw_response_handler response_handler
)
998 (void)response_handler
;
999 protocol_binary_response_status rval
;
1001 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
1002 if (client
->root
->callback
->interface
.v1
.stat
!= NULL
)
1004 uint16_t keylen
= ntohs(header
->request
.keylen
);
1006 rval
= client
->root
->callback
->interface
.v1
.stat(cookie
,
1007 (void*)(header
+ 1),
1009 stat_response_handler
);
1013 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
1020 * Callback for VERSION
1021 * @param cookie the calling client
1022 * @param header the command
1023 * @param response_handler not used
1024 * @return the result of the operation
1026 static protocol_binary_response_status
1027 version_command_handler(const void *cookie
,
1028 protocol_binary_request_header
*header
,
1029 memcached_binary_protocol_raw_response_handler response_handler
)
1031 (void)response_handler
;
1033 protocol_binary_response_status rval
;
1035 struct memcached_binary_protocol_client_st
*client
= (void*)cookie
;
1036 if (client
->root
->callback
->interface
.v1
.version
!= NULL
)
1038 rval
= client
->root
->callback
->interface
.v1
.version(cookie
,
1039 version_response_handler
);
1043 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
1050 * The map to remap between the com codes and the v1 logical setting
1052 static memcached_binary_protocol_command_handler comcode_v0_v1_remap
[256]= {
1053 [PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
1054 [PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
1055 [PROTOCOL_BINARY_CMD_APPENDQ
]= append_command_handler
,
1056 [PROTOCOL_BINARY_CMD_APPEND
]= append_command_handler
,
1057 [PROTOCOL_BINARY_CMD_DECREMENTQ
]= decrement_command_handler
,
1058 [PROTOCOL_BINARY_CMD_DECREMENT
]= decrement_command_handler
,
1059 [PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
1060 [PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
1061 [PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
1062 [PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
1063 [PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
1064 [PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
1065 [PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
1066 [PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
1067 [PROTOCOL_BINARY_CMD_INCREMENTQ
]= increment_command_handler
,
1068 [PROTOCOL_BINARY_CMD_INCREMENT
]= increment_command_handler
,
1069 [PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
1070 [PROTOCOL_BINARY_CMD_PREPENDQ
]= prepend_command_handler
,
1071 [PROTOCOL_BINARY_CMD_PREPEND
]= prepend_command_handler
,
1072 [PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
1073 [PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
1074 [PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
1075 [PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
1076 [PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
1077 [PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
1078 [PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
1079 [PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
1083 * Try to execute a command. Fire the pre/post functions and the specialized
1084 * handler function if it's set. If not, the unknown probe should be fired
1086 * @param client the client connection to operate on
1087 * @param header the command to execute
1088 * @return true if success or false if a fatal error occured so that the
1089 * connection should be shut down.
1091 static bool execute_command(struct memcached_binary_protocol_client_st
*client
, protocol_binary_request_header
*header
)
1093 if (client
->root
->pedantic
&&
1094 memcached_binary_protocol_pedantic_check_request(header
))
1096 /* @todo return invalid command packet */
1099 /* we got all data available, execute the callback! */
1100 if (client
->root
->callback
->pre_execute
!= NULL
)
1102 client
->root
->callback
->pre_execute(client
, header
);
1105 protocol_binary_response_status rval
;
1106 rval
= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
;
1107 uint8_t cc
= header
->request
.opcode
;
1109 switch (client
->root
->callback
->interface_version
)
1112 if (client
->root
->callback
->interface
.v0
.comcode
[cc
] != NULL
) {
1113 rval
= client
->root
->callback
->interface
.v0
.comcode
[cc
](client
, header
, raw_response_handler
);
1117 if (comcode_v0_v1_remap
[cc
] != NULL
) {
1118 rval
= comcode_v0_v1_remap
[cc
](client
, header
, raw_response_handler
);
1122 /* Unknown interface.
1123 * It should be impossible to get here so I'll just call abort
1124 * to avoid getting a compiler warning :-)
1130 if (rval
== PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
&&
1131 client
->root
->callback
->unknown
!= NULL
)
1133 rval
= client
->root
->callback
->unknown(client
, header
, raw_response_handler
);
1136 if (rval
!= PROTOCOL_BINARY_RESPONSE_SUCCESS
&&
1137 rval
!= PROTOCOL_BINARY_RESPONSE_EIO
)
1139 protocol_binary_response_no_extras response
= {
1142 .magic
= PROTOCOL_BINARY_RES
,
1144 .status
= htons(rval
),
1145 .opaque
= header
->request
.opaque
,
1149 rval
= raw_response_handler(client
, header
, (void*)&response
);
1152 if (client
->root
->callback
->post_execute
!= NULL
)
1154 client
->root
->callback
->post_execute(client
, header
);
1157 return rval
!= PROTOCOL_BINARY_RESPONSE_EIO
;
1161 ** **********************************************************************
1162 ** * PUBLIC INTERFACE
1163 ** * See protocol_handler.h for function description
1164 ** **********************************************************************
1166 struct memcached_binary_protocol_st
*memcached_binary_protocol_create_instance(void)
1168 struct memcached_binary_protocol_st
*ret
= calloc(1, sizeof(*ret
));
1171 ret
->recv
= default_recv
;
1172 ret
->send
= default_send
;
1173 ret
->input_buffer_size
= 1 * 1024 * 1024;
1174 ret
->input_buffer
= malloc(ret
->input_buffer_size
);
1175 if (ret
->input_buffer
== NULL
)
1182 ret
->buffer_cache
= cache_create("protocol_handler",
1183 CHUNK_BUFFERSIZE
+ sizeof(struct chunk_st
),
1185 if (ret
->buffer_cache
== NULL
) {
1186 free(ret
->input_buffer
);
1194 void memcached_binary_protocol_destroy_instance(struct memcached_binary_protocol_st
*instance
)
1196 cache_destroy(instance
->buffer_cache
);
1197 free(instance
->input_buffer
);
1201 struct memcached_binary_protocol_callback_st
*memcached_binary_protocol_get_callbacks(struct memcached_binary_protocol_st
*instance
)
1203 return instance
->callback
;
1206 void memcached_binary_protocol_set_callbacks(struct memcached_binary_protocol_st
*instance
, struct memcached_binary_protocol_callback_st
*callback
)
1208 instance
->callback
= callback
;
1211 memcached_binary_protocol_raw_response_handler
memcached_binary_protocol_get_raw_response_handler(const void *cookie
)
1214 return raw_response_handler
;
1217 void memcached_binary_protocol_set_pedantic(struct memcached_binary_protocol_st
*instance
, bool enable
)
1219 instance
->pedantic
= enable
;
1222 bool memcached_binary_protocol_get_pedantic(struct memcached_binary_protocol_st
*instance
)
1224 return instance
->pedantic
;
1227 struct memcached_binary_protocol_client_st
*memcached_binary_protocol_create_client(struct memcached_binary_protocol_st
*instance
, int sock
)
1229 struct memcached_binary_protocol_client_st
*ret
= calloc(1, sizeof(*ret
));
1232 ret
->root
= instance
;
1239 void memcached_binary_protocol_client_destroy(struct memcached_binary_protocol_client_st
*client
)
1244 enum MEMCACHED_BINARY_PROTOCOL_EVENT
memcached_binary_protocol_client_work(struct memcached_binary_protocol_client_st
*client
)
1246 /* Try to send data and read from the socket */
1247 bool more_data
= true;
1250 ssize_t len
= client
->root
->recv(client
,
1252 client
->root
->input_buffer
+ client
->input_buffer_offset
,
1253 client
->root
->input_buffer_size
- client
->input_buffer_offset
);
1257 /* Do we have the complete packet? */
1258 if (client
->input_buffer_offset
> 0)
1260 memcpy(client
->root
->input_buffer
, client
->input_buffer
,
1261 client
->input_buffer_offset
);
1262 len
+= (ssize_t
)client
->input_buffer_offset
;
1264 /* @todo use buffer-cache! */
1265 free(client
->input_buffer
);
1266 client
->input_buffer_offset
= 0;
1269 /* try to parse all of the received packets */
1270 protocol_binary_request_header
*header
;
1271 header
= (void*)client
->root
->input_buffer
;
1273 if (header
->request
.magic
!= (uint8_t)PROTOCOL_BINARY_REQ
)
1275 client
->error
= EINVAL
;
1279 while (len
>= (ssize_t
)sizeof(*header
) &&
1280 (len
>= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
))))
1283 /* I have the complete package */
1284 client
->current_command
= header
;
1285 if (!execute_command(client
, header
))
1290 ssize_t total
= (ssize_t
)(sizeof(*header
) + ntohl(header
->request
.bodylen
));
1294 intptr_t ptr
= (intptr_t)header
;
1302 memmove(client
->root
->input_buffer
, (void*)ptr
, (size_t)len
);
1303 header
= (void*)client
->root
->input_buffer
;
1310 /* save the data for later on */
1311 /* @todo use buffer-cache */
1312 client
->input_buffer
= malloc((size_t)len
);
1313 if (client
->input_buffer
== NULL
)
1315 client
->error
= ENOMEM
;
1318 memcpy(client
->input_buffer
, header
, (size_t)len
);
1319 client
->input_buffer_offset
= (size_t)len
;
1325 /* Connection closed */
1326 drain_output(client
);
1331 if (errno
!= EWOULDBLOCK
)
1333 client
->error
= errno
;
1334 /* mark this client as terminated! */
1339 } while (more_data
);
1341 if (!drain_output(client
))
1346 return (client
->output
) ? READ_WRITE_EVENT
: READ_EVENT
;