Add in MSG_DONTWAIT for recv().
[awesomized/libmemcached] / libmemcached / protocol / binary_handler.c
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
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
17 * distribution.
18 *
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
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.
34 *
35 */
36
37 #include <libmemcached/protocol/common.h>
38 #include <libmemcached/byteorder.h>
39
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <errno.h>
43 #include <stdbool.h>
44 #include <string.h>
45 #include <stdio.h>
46
47 /*
48 ** **********************************************************************
49 ** INTERNAL INTERFACE
50 ** **********************************************************************
51 */
52
53 /**
54 * Send a preformatted packet back to the client. If the connection is in
55 * pedantic mode, it will validate the packet and refuse to send it if it
56 * breaks the specification.
57 *
58 * @param cookie client identification
59 * @param request the original request packet
60 * @param response the packet to send
61 * @return The status of the operation
62 */
63 static protocol_binary_response_status
64 raw_response_handler(const void *cookie,
65 protocol_binary_request_header *request,
66 protocol_binary_response_header *response)
67 {
68 memcached_protocol_client_st *client= (void*)cookie;
69
70 if (client->root->pedantic &&
71 !memcached_binary_protocol_pedantic_check_response(request, response))
72 {
73 return PROTOCOL_BINARY_RESPONSE_EINVAL;
74 }
75
76 if (!client->root->drain(client))
77 {
78 return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
79 }
80
81 size_t len= sizeof(*response) + htonl(response->response.bodylen);
82 size_t offset= 0;
83 char *ptr= (void*)response;
84
85 if (client->output == NULL)
86 {
87 /* I can write directly to the socket.... */
88 do
89 {
90 size_t num_bytes= len - offset;
91 ssize_t nw= client->root->send(client,
92 client->sock,
93 ptr + offset,
94 num_bytes);
95 if (nw == -1)
96 {
97 if (get_socket_errno() == EWOULDBLOCK)
98 {
99 break;
100 }
101 else if (get_socket_errno() != EINTR)
102 {
103 client->error= errno;
104 return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
105 }
106 }
107 else
108 {
109 offset += (size_t)nw;
110 }
111 } while (offset < len);
112 }
113
114 return client->root->spool(client, ptr, len - offset);
115 }
116
117 /*
118 * Version 0 of the interface is really low level and protocol specific,
119 * while the version 1 of the interface is more API focused. We need a
120 * way to translate between the command codes on the wire and the
121 * application level interface in V1, so let's just use the V0 of the
122 * interface as a map instead of creating a huuuge switch :-)
123 */
124
125 /**
126 * Callback for the GET/GETQ/GETK and GETKQ responses
127 * @param cookie client identifier
128 * @param key the key for the item
129 * @param keylen the length of the key
130 * @param body the length of the body
131 * @param bodylen the length of the body
132 * @param flags the flags for the item
133 * @param cas the CAS id for the item
134 */
135 static protocol_binary_response_status
136 get_response_handler(const void *cookie,
137 const void *key,
138 uint16_t keylen,
139 const void *body,
140 uint32_t bodylen,
141 uint32_t flags,
142 uint64_t cas) {
143
144 memcached_protocol_client_st *client= (void*)cookie;
145 uint8_t opcode= client->current_command->request.opcode;
146
147 if (opcode == PROTOCOL_BINARY_CMD_GET || opcode == PROTOCOL_BINARY_CMD_GETQ)
148 {
149 keylen= 0;
150 }
151
152 protocol_binary_response_get response= {
153 .message.header.response= {
154 .magic= PROTOCOL_BINARY_RES,
155 .opcode= opcode,
156 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
157 .opaque= client->current_command->request.opaque,
158 .cas= htonll(cas),
159 .keylen= htons(keylen),
160 .extlen= 4,
161 .bodylen= htonl(bodylen + keylen + 4),
162 },
163 };
164
165 response.message.body.flags= htonl(flags);
166
167 protocol_binary_response_status rval;
168 const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS;
169 if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success ||
170 (rval= client->root->spool(client, key, keylen)) != success ||
171 (rval= client->root->spool(client, body, bodylen)) != success)
172 {
173 return rval;
174 }
175
176 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
177 }
178
179 /**
180 * Callback for the STAT responses
181 * @param cookie client identifier
182 * @param key the key for the item
183 * @param keylen the length of the key
184 * @param body the length of the body
185 * @param bodylen the length of the body
186 */
187 static protocol_binary_response_status
188 stat_response_handler(const void *cookie,
189 const void *key,
190 uint16_t keylen,
191 const void *body,
192 uint32_t bodylen) {
193
194 memcached_protocol_client_st *client= (void*)cookie;
195
196 protocol_binary_response_no_extras response= {
197 .message.header.response= {
198 .magic= PROTOCOL_BINARY_RES,
199 .opcode= client->current_command->request.opcode,
200 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
201 .opaque= client->current_command->request.opaque,
202 .keylen= htons(keylen),
203 .bodylen= htonl(bodylen + keylen),
204 },
205 };
206
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)
212 {
213 return rval;
214 }
215
216 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
217 }
218
219 /**
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
224 */
225 static protocol_binary_response_status
226 version_response_handler(const void *cookie,
227 const void *text,
228 uint32_t textlen) {
229
230 memcached_protocol_client_st *client= (void*)cookie;
231
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),
239 },
240 };
241
242 protocol_binary_response_status rval;
243 const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS;
244 if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success ||
245 (rval= client->root->spool(client, text, textlen)) != success)
246 {
247 return rval;
248 }
249
250 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
251 }
252
253 /**
254 * Callback for ADD and ADDQ
255 * @param cookie the calling client
256 * @param header the add/addq command
257 * @param response_handler not used
258 * @return the result of the operation
259 */
260 static protocol_binary_response_status
261 add_command_handler(const void *cookie,
262 protocol_binary_request_header *header,
263 memcached_binary_protocol_raw_response_handler response_handler)
264 {
265 protocol_binary_response_status rval;
266
267 memcached_protocol_client_st *client= (void*)cookie;
268 if (client->root->callback->interface.v1.add != NULL)
269 {
270 uint16_t keylen= ntohs(header->request.keylen);
271 uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8;
272 protocol_binary_request_add *request= (void*)header;
273 uint32_t flags= ntohl(request->message.body.flags);
274 uint32_t timeout= ntohl(request->message.body.expiration);
275 char *key= ((char*)header) + sizeof(*header) + 8;
276 char *data= key + keylen;
277 uint64_t cas;
278
279 rval= client->root->callback->interface.v1.add(cookie, key, keylen,
280 data, datalen, flags,
281 timeout, &cas);
282
283 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
284 header->request.opcode == PROTOCOL_BINARY_CMD_ADD)
285 {
286 /* Send a positive request */
287 protocol_binary_response_no_extras response= {
288 .message= {
289 .header.response= {
290 .magic= PROTOCOL_BINARY_RES,
291 .opcode= PROTOCOL_BINARY_CMD_ADD,
292 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
293 .opaque= header->request.opaque,
294 .cas= ntohll(cas)
295 }
296 }
297 };
298 rval= response_handler(cookie, header, (void*)&response);
299 }
300 }
301 else
302 {
303 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
304 }
305
306 return rval;
307 }
308
309 /**
310 * Callback for DECREMENT and DECREMENTQ
311 * @param cookie the calling client
312 * @param header the command
313 * @param response_handler not used
314 * @return the result of the operation
315 */
316 static protocol_binary_response_status
317 decrement_command_handler(const void *cookie,
318 protocol_binary_request_header *header,
319 memcached_binary_protocol_raw_response_handler response_handler)
320 {
321 (void)response_handler;
322 protocol_binary_response_status rval;
323
324 memcached_protocol_client_st *client= (void*)cookie;
325 if (client->root->callback->interface.v1.decrement != NULL)
326 {
327 uint16_t keylen= ntohs(header->request.keylen);
328 protocol_binary_request_decr *request= (void*)header;
329 uint64_t init= ntohll(request->message.body.initial);
330 uint64_t delta= ntohll(request->message.body.delta);
331 uint32_t timeout= ntohl(request->message.body.expiration);
332 void *key= request->bytes + sizeof(request->bytes);
333 uint64_t result;
334 uint64_t cas;
335
336 rval= client->root->callback->interface.v1.decrement(cookie, key, keylen,
337 delta, init, timeout,
338 &result, &cas);
339 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
340 header->request.opcode == PROTOCOL_BINARY_CMD_DECREMENT)
341 {
342 /* Send a positive request */
343 protocol_binary_response_decr response= {
344 .message= {
345 .header.response= {
346 .magic= PROTOCOL_BINARY_RES,
347 .opcode= PROTOCOL_BINARY_CMD_DECREMENT,
348 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
349 .opaque= header->request.opaque,
350 .cas= ntohll(cas),
351 .bodylen= htonl(8)
352 },
353 .body.value= htonll(result)
354 }
355 };
356 rval= response_handler(cookie, header, (void*)&response);
357 }
358 }
359 else
360 {
361 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
362 }
363
364 return rval;
365 }
366
367 /**
368 * Callback for DELETE and DELETEQ
369 * @param cookie the calling client
370 * @param header the command
371 * @param response_handler not used
372 * @return the result of the operation
373 */
374 static protocol_binary_response_status
375 delete_command_handler(const void *cookie,
376 protocol_binary_request_header *header,
377 memcached_binary_protocol_raw_response_handler response_handler)
378 {
379 (void)response_handler;
380 protocol_binary_response_status rval;
381
382 memcached_protocol_client_st *client= (void*)cookie;
383 if (client->root->callback->interface.v1.delete != NULL)
384 {
385 uint16_t keylen= ntohs(header->request.keylen);
386 void *key= (header + 1);
387 uint64_t cas= ntohll(header->request.cas);
388 rval= client->root->callback->interface.v1.delete(cookie, key, keylen, cas);
389 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
390 header->request.opcode == PROTOCOL_BINARY_CMD_DELETE)
391 {
392 /* Send a positive request */
393 protocol_binary_response_no_extras response= {
394 .message= {
395 .header.response= {
396 .magic= PROTOCOL_BINARY_RES,
397 .opcode= PROTOCOL_BINARY_CMD_DELETE,
398 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
399 .opaque= header->request.opaque,
400 }
401 }
402 };
403 rval= response_handler(cookie, header, (void*)&response);
404 }
405 }
406 else
407 {
408 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
409 }
410
411 return rval;
412 }
413
414 /**
415 * Callback for FLUSH and FLUSHQ
416 * @param cookie the calling client
417 * @param header the command
418 * @param response_handler not used
419 * @return the result of the operation
420 */
421 static protocol_binary_response_status
422 flush_command_handler(const void *cookie,
423 protocol_binary_request_header *header,
424 memcached_binary_protocol_raw_response_handler response_handler)
425 {
426 (void)response_handler;
427 protocol_binary_response_status rval;
428
429 memcached_protocol_client_st *client= (void*)cookie;
430 if (client->root->callback->interface.v1.flush != NULL)
431 {
432 protocol_binary_request_flush *flush= (void*)header;
433 uint32_t timeout= 0;
434 if (htonl(header->request.bodylen) == 4)
435 {
436 timeout= ntohl(flush->message.body.expiration);
437 }
438
439 rval= client->root->callback->interface.v1.flush(cookie, timeout);
440 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
441 header->request.opcode == PROTOCOL_BINARY_CMD_FLUSH)
442 {
443 /* Send a positive request */
444 protocol_binary_response_no_extras response= {
445 .message= {
446 .header.response= {
447 .magic= PROTOCOL_BINARY_RES,
448 .opcode= PROTOCOL_BINARY_CMD_FLUSH,
449 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
450 .opaque= header->request.opaque,
451 }
452 }
453 };
454 rval= response_handler(cookie, header, (void*)&response);
455 }
456 }
457 else
458 {
459 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
460 }
461
462 return rval;
463 }
464
465 /**
466 * Callback for GET, GETK, GETQ, GETKQ
467 * @param cookie the calling client
468 * @param header the command
469 * @param response_handler not used
470 * @return the result of the operation
471 */
472 static protocol_binary_response_status
473 get_command_handler(const void *cookie,
474 protocol_binary_request_header *header,
475 memcached_binary_protocol_raw_response_handler response_handler)
476 {
477 (void)response_handler;
478 protocol_binary_response_status rval;
479
480 memcached_protocol_client_st *client= (void*)cookie;
481 if (client->root->callback->interface.v1.get != NULL)
482 {
483 uint16_t keylen= ntohs(header->request.keylen);
484 void *key= (header + 1);
485 rval= client->root->callback->interface.v1.get(cookie, key, keylen,
486 get_response_handler);
487
488 if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT &&
489 (header->request.opcode == PROTOCOL_BINARY_CMD_GETQ ||
490 header->request.opcode == PROTOCOL_BINARY_CMD_GETKQ))
491 {
492 /* Quiet commands shouldn't respond on cache misses */
493 rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
494 }
495 }
496 else
497 {
498 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
499 }
500
501 return rval;
502 }
503
504 /**
505 * Callback for INCREMENT and INCREMENTQ
506 * @param cookie the calling client
507 * @param header the command
508 * @param response_handler not used
509 * @return the result of the operation
510 */
511 static protocol_binary_response_status
512 increment_command_handler(const void *cookie,
513 protocol_binary_request_header *header,
514 memcached_binary_protocol_raw_response_handler response_handler)
515 {
516 (void)response_handler;
517 protocol_binary_response_status rval;
518
519 memcached_protocol_client_st *client= (void*)cookie;
520 if (client->root->callback->interface.v1.increment != NULL)
521 {
522 uint16_t keylen= ntohs(header->request.keylen);
523 protocol_binary_request_incr *request= (void*)header;
524 uint64_t init= ntohll(request->message.body.initial);
525 uint64_t delta= ntohll(request->message.body.delta);
526 uint32_t timeout= ntohl(request->message.body.expiration);
527 void *key= request->bytes + sizeof(request->bytes);
528 uint64_t cas;
529 uint64_t result;
530
531 rval= client->root->callback->interface.v1.increment(cookie, key, keylen,
532 delta, init, timeout,
533 &result, &cas);
534 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
535 header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENT)
536 {
537 /* Send a positive request */
538 protocol_binary_response_incr response= {
539 .message= {
540 .header.response= {
541 .magic= PROTOCOL_BINARY_RES,
542 .opcode= PROTOCOL_BINARY_CMD_INCREMENT,
543 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
544 .opaque= header->request.opaque,
545 .cas= ntohll(cas),
546 .bodylen= htonl(8)
547 },
548 .body.value= htonll(result)
549 }
550 };
551
552 rval= response_handler(cookie, header, (void*)&response);
553 }
554 }
555 else
556 {
557 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
558 }
559
560 return rval;
561 }
562
563 /**
564 * Callback for noop. Inform the v1 interface about the noop packet, and
565 * create and send a packet back to the client
566 *
567 * @param cookie the calling client
568 * @param header the command
569 * @param response_handler the response handler
570 * @return the result of the operation
571 */
572 static protocol_binary_response_status
573 noop_command_handler(const void *cookie,
574 protocol_binary_request_header *header,
575 memcached_binary_protocol_raw_response_handler response_handler)
576 {
577 memcached_protocol_client_st *client= (void*)cookie;
578 if (client->root->callback->interface.v1.noop != NULL)
579 {
580 client->root->callback->interface.v1.noop(cookie);
581 }
582
583 protocol_binary_response_no_extras response= {
584 .message= {
585 .header.response= {
586 .magic= PROTOCOL_BINARY_RES,
587 .opcode= PROTOCOL_BINARY_CMD_NOOP,
588 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
589 .opaque= header->request.opaque,
590 }
591 }
592 };
593
594 return response_handler(cookie, header, (void*)&response);
595 }
596
597 /**
598 * Callback for APPEND and APPENDQ
599 * @param cookie the calling client
600 * @param header the command
601 * @param response_handler not used
602 * @return the result of the operation
603 */
604 static protocol_binary_response_status
605 append_command_handler(const void *cookie,
606 protocol_binary_request_header *header,
607 memcached_binary_protocol_raw_response_handler response_handler)
608 {
609 (void)response_handler;
610 protocol_binary_response_status rval;
611
612 memcached_protocol_client_st *client= (void*)cookie;
613 if (client->root->callback->interface.v1.append != NULL)
614 {
615 uint16_t keylen= ntohs(header->request.keylen);
616 uint32_t datalen= ntohl(header->request.bodylen) - keylen;
617 char *key= (void*)(header + 1);
618 char *data= key + keylen;
619 uint64_t cas= ntohll(header->request.cas);
620 uint64_t result_cas;
621
622 rval= client->root->callback->interface.v1.append(cookie, key, keylen,
623 data, datalen, cas,
624 &result_cas);
625 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
626 header->request.opcode == PROTOCOL_BINARY_CMD_APPEND)
627 {
628 /* Send a positive request */
629 protocol_binary_response_no_extras response= {
630 .message= {
631 .header.response= {
632 .magic= PROTOCOL_BINARY_RES,
633 .opcode= PROTOCOL_BINARY_CMD_APPEND,
634 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
635 .opaque= header->request.opaque,
636 .cas= ntohll(result_cas),
637 },
638 }
639 };
640 rval= response_handler(cookie, header, (void*)&response);
641 }
642 }
643 else
644 {
645 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
646 }
647
648 return rval;
649 }
650
651 /**
652 * Callback for PREPEND and PREPENDQ
653 * @param cookie the calling client
654 * @param header the command
655 * @param response_handler not used
656 * @return the result of the operation
657 */
658 static protocol_binary_response_status
659 prepend_command_handler(const void *cookie,
660 protocol_binary_request_header *header,
661 memcached_binary_protocol_raw_response_handler response_handler)
662 {
663 (void)response_handler;
664 protocol_binary_response_status rval;
665
666 memcached_protocol_client_st *client= (void*)cookie;
667 if (client->root->callback->interface.v1.prepend != NULL)
668 {
669 uint16_t keylen= ntohs(header->request.keylen);
670 uint32_t datalen= ntohl(header->request.bodylen) - keylen;
671 char *key= (char*)(header + 1);
672 char *data= key + keylen;
673 uint64_t cas= ntohll(header->request.cas);
674 uint64_t result_cas;
675 rval= client->root->callback->interface.v1.prepend(cookie, key, keylen,
676 data, datalen, cas,
677 &result_cas);
678 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
679 header->request.opcode == PROTOCOL_BINARY_CMD_PREPEND)
680 {
681 /* Send a positive request */
682 protocol_binary_response_no_extras response= {
683 .message= {
684 .header.response= {
685 .magic= PROTOCOL_BINARY_RES,
686 .opcode= PROTOCOL_BINARY_CMD_PREPEND,
687 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
688 .opaque= header->request.opaque,
689 .cas= ntohll(result_cas),
690 },
691 }
692 };
693 rval= response_handler(cookie, header, (void*)&response);
694 }
695 }
696 else
697 {
698 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
699 }
700
701 return rval;
702 }
703
704 /**
705 * Callback for QUIT and QUITQ. Notify the client and shut down the connection
706 * @param cookie the calling client
707 * @param header the command
708 * @param response_handler not used
709 * @return the result of the operation
710 */
711 static protocol_binary_response_status
712 quit_command_handler(const void *cookie,
713 protocol_binary_request_header *header,
714 memcached_binary_protocol_raw_response_handler response_handler)
715 {
716 memcached_protocol_client_st *client= (void*)cookie;
717 if (client->root->callback->interface.v1.quit != NULL)
718 {
719 client->root->callback->interface.v1.quit(cookie);
720 }
721
722 protocol_binary_response_no_extras response= {
723 .message= {
724 .header.response= {
725 .magic= PROTOCOL_BINARY_RES,
726 .opcode= PROTOCOL_BINARY_CMD_QUIT,
727 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
728 .opaque= header->request.opaque
729 }
730 }
731 };
732
733 if (header->request.opcode == PROTOCOL_BINARY_CMD_QUIT)
734 {
735 response_handler(cookie, header, (void*)&response);
736 }
737
738 /* I need a better way to signal to close the connection */
739 return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
740 }
741
742 /**
743 * Callback for REPLACE and REPLACEQ
744 * @param cookie the calling client
745 * @param header the command
746 * @param response_handler not used
747 * @return the result of the operation
748 */
749 static protocol_binary_response_status
750 replace_command_handler(const void *cookie,
751 protocol_binary_request_header *header,
752 memcached_binary_protocol_raw_response_handler response_handler)
753 {
754 (void)response_handler;
755 protocol_binary_response_status rval;
756
757 memcached_protocol_client_st *client= (void*)cookie;
758 if (client->root->callback->interface.v1.replace != NULL)
759 {
760 uint16_t keylen= ntohs(header->request.keylen);
761 uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8;
762 protocol_binary_request_replace *request= (void*)header;
763 uint32_t flags= ntohl(request->message.body.flags);
764 uint32_t timeout= ntohl(request->message.body.expiration);
765 char *key= ((char*)header) + sizeof(*header) + 8;
766 char *data= key + keylen;
767 uint64_t cas= ntohll(header->request.cas);
768 uint64_t result_cas;
769
770 rval= client->root->callback->interface.v1.replace(cookie, key, keylen,
771 data, datalen, flags,
772 timeout, cas,
773 &result_cas);
774 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
775 header->request.opcode == PROTOCOL_BINARY_CMD_REPLACE)
776 {
777 /* Send a positive request */
778 protocol_binary_response_no_extras response= {
779 .message= {
780 .header.response= {
781 .magic= PROTOCOL_BINARY_RES,
782 .opcode= PROTOCOL_BINARY_CMD_REPLACE,
783 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
784 .opaque= header->request.opaque,
785 .cas= ntohll(result_cas),
786 },
787 }
788 };
789 rval= response_handler(cookie, header, (void*)&response);
790 }
791 }
792 else
793 {
794 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
795 }
796
797 return rval;
798 }
799
800 /**
801 * Callback for SET and SETQ
802 * @param cookie the calling client
803 * @param header the command
804 * @param response_handler not used
805 * @return the result of the operation
806 */
807 static protocol_binary_response_status
808 set_command_handler(const void *cookie,
809 protocol_binary_request_header *header,
810 memcached_binary_protocol_raw_response_handler response_handler)
811 {
812 (void)response_handler;
813 protocol_binary_response_status rval;
814
815 memcached_protocol_client_st *client= (void*)cookie;
816 if (client->root->callback->interface.v1.set != NULL)
817 {
818 uint16_t keylen= ntohs(header->request.keylen);
819 uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8;
820 protocol_binary_request_replace *request= (void*)header;
821 uint32_t flags= ntohl(request->message.body.flags);
822 uint32_t timeout= ntohl(request->message.body.expiration);
823 char *key= ((char*)header) + sizeof(*header) + 8;
824 char *data= key + keylen;
825 uint64_t cas= ntohll(header->request.cas);
826 uint64_t result_cas;
827
828
829 rval= client->root->callback->interface.v1.set(cookie, key, keylen,
830 data, datalen, flags,
831 timeout, cas, &result_cas);
832 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
833 header->request.opcode == PROTOCOL_BINARY_CMD_SET)
834 {
835 /* Send a positive request */
836 protocol_binary_response_no_extras response= {
837 .message= {
838 .header.response= {
839 .magic= PROTOCOL_BINARY_RES,
840 .opcode= PROTOCOL_BINARY_CMD_SET,
841 .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
842 .opaque= header->request.opaque,
843 .cas= ntohll(result_cas),
844 },
845 }
846 };
847 rval= response_handler(cookie, header, (void*)&response);
848 }
849 }
850 else
851 {
852 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
853 }
854
855 return rval;
856 }
857
858 /**
859 * Callback for STAT
860 * @param cookie the calling client
861 * @param header the command
862 * @param response_handler not used
863 * @return the result of the operation
864 */
865 static protocol_binary_response_status
866 stat_command_handler(const void *cookie,
867 protocol_binary_request_header *header,
868 memcached_binary_protocol_raw_response_handler response_handler)
869 {
870 (void)response_handler;
871 protocol_binary_response_status rval;
872
873 memcached_protocol_client_st *client= (void*)cookie;
874 if (client->root->callback->interface.v1.stat != NULL)
875 {
876 uint16_t keylen= ntohs(header->request.keylen);
877
878 rval= client->root->callback->interface.v1.stat(cookie,
879 (void*)(header + 1),
880 keylen,
881 stat_response_handler);
882 }
883 else
884 {
885 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
886 }
887
888 return rval;
889 }
890
891 /**
892 * Callback for VERSION
893 * @param cookie the calling client
894 * @param header the command
895 * @param response_handler not used
896 * @return the result of the operation
897 */
898 static protocol_binary_response_status
899 version_command_handler(const void *cookie,
900 protocol_binary_request_header *header,
901 memcached_binary_protocol_raw_response_handler response_handler)
902 {
903 (void)response_handler;
904 (void)header;
905 protocol_binary_response_status rval;
906
907 memcached_protocol_client_st *client= (void*)cookie;
908 if (client->root->callback->interface.v1.version != NULL)
909 {
910 rval= client->root->callback->interface.v1.version(cookie,
911 version_response_handler);
912 }
913 else
914 {
915 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
916 }
917
918 return rval;
919 }
920
921 /**
922 * The map to remap between the com codes and the v1 logical setting
923 */
924 static memcached_binary_protocol_command_handler comcode_v0_v1_remap[256]= {
925 [PROTOCOL_BINARY_CMD_ADDQ]= add_command_handler,
926 [PROTOCOL_BINARY_CMD_ADD]= add_command_handler,
927 [PROTOCOL_BINARY_CMD_APPENDQ]= append_command_handler,
928 [PROTOCOL_BINARY_CMD_APPEND]= append_command_handler,
929 [PROTOCOL_BINARY_CMD_DECREMENTQ]= decrement_command_handler,
930 [PROTOCOL_BINARY_CMD_DECREMENT]= decrement_command_handler,
931 [PROTOCOL_BINARY_CMD_DELETEQ]= delete_command_handler,
932 [PROTOCOL_BINARY_CMD_DELETE]= delete_command_handler,
933 [PROTOCOL_BINARY_CMD_FLUSHQ]= flush_command_handler,
934 [PROTOCOL_BINARY_CMD_FLUSH]= flush_command_handler,
935 [PROTOCOL_BINARY_CMD_GETKQ]= get_command_handler,
936 [PROTOCOL_BINARY_CMD_GETK]= get_command_handler,
937 [PROTOCOL_BINARY_CMD_GETQ]= get_command_handler,
938 [PROTOCOL_BINARY_CMD_GET]= get_command_handler,
939 [PROTOCOL_BINARY_CMD_INCREMENTQ]= increment_command_handler,
940 [PROTOCOL_BINARY_CMD_INCREMENT]= increment_command_handler,
941 [PROTOCOL_BINARY_CMD_NOOP]= noop_command_handler,
942 [PROTOCOL_BINARY_CMD_PREPENDQ]= prepend_command_handler,
943 [PROTOCOL_BINARY_CMD_PREPEND]= prepend_command_handler,
944 [PROTOCOL_BINARY_CMD_QUITQ]= quit_command_handler,
945 [PROTOCOL_BINARY_CMD_QUIT]= quit_command_handler,
946 [PROTOCOL_BINARY_CMD_REPLACEQ]= replace_command_handler,
947 [PROTOCOL_BINARY_CMD_REPLACE]= replace_command_handler,
948 [PROTOCOL_BINARY_CMD_SETQ]= set_command_handler,
949 [PROTOCOL_BINARY_CMD_SET]= set_command_handler,
950 [PROTOCOL_BINARY_CMD_STAT]= stat_command_handler,
951 [PROTOCOL_BINARY_CMD_VERSION]= version_command_handler,
952 };
953
954 /**
955 * Try to execute a command. Fire the pre/post functions and the specialized
956 * handler function if it's set. If not, the unknown probe should be fired
957 * if it's present.
958 * @param client the client connection to operate on
959 * @param header the command to execute
960 * @return true if success or false if a fatal error occured so that the
961 * connection should be shut down.
962 */
963 static protocol_binary_response_status execute_command(memcached_protocol_client_st *client, protocol_binary_request_header *header)
964 {
965 if (client->root->pedantic &&
966 memcached_binary_protocol_pedantic_check_request(header))
967 {
968 /* @todo return invalid command packet */
969 }
970
971 /* we got all data available, execute the callback! */
972 if (client->root->callback->pre_execute != NULL)
973 {
974 client->root->callback->pre_execute(client, header);
975 }
976
977 protocol_binary_response_status rval;
978 rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
979 uint8_t cc= header->request.opcode;
980
981 switch (client->root->callback->interface_version)
982 {
983 case 0:
984 if (client->root->callback->interface.v0.comcode[cc] != NULL) {
985 rval= client->root->callback->interface.v0.comcode[cc](client, header, raw_response_handler);
986 }
987 break;
988 case 1:
989 if (comcode_v0_v1_remap[cc] != NULL) {
990 rval= comcode_v0_v1_remap[cc](client, header, raw_response_handler);
991 }
992 break;
993 default:
994 /* Unknown interface.
995 * It should be impossible to get here so I'll just call abort
996 * to avoid getting a compiler warning :-)
997 */
998 abort();
999 }
1000
1001
1002 if (rval == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND &&
1003 client->root->callback->unknown != NULL)
1004 {
1005 rval= client->root->callback->unknown(client, header, raw_response_handler);
1006 }
1007
1008 if (rval != PROTOCOL_BINARY_RESPONSE_SUCCESS &&
1009 rval != PROTOCOL_BINARY_RESPONSE_EINTERNAL &&
1010 rval != PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED)
1011 {
1012 protocol_binary_response_no_extras response= {
1013 .message= {
1014 .header.response= {
1015 .magic= PROTOCOL_BINARY_RES,
1016 .opcode= cc,
1017 .status= htons(rval),
1018 .opaque= header->request.opaque,
1019 },
1020 }
1021 };
1022 rval= raw_response_handler(client, header, (void*)&response);
1023 }
1024
1025 if (client->root->callback->post_execute != NULL)
1026 {
1027 client->root->callback->post_execute(client, header);
1028 }
1029
1030 return rval;
1031 }
1032
1033 /*
1034 ** **********************************************************************
1035 ** "PROTOECTED" INTERFACE
1036 ** **********************************************************************
1037 */
1038 memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr)
1039 {
1040 /* try to parse all of the received packets */
1041 protocol_binary_request_header *header;
1042 header= (void*)client->root->input_buffer;
1043 if (header->request.magic != (uint8_t)PROTOCOL_BINARY_REQ)
1044 {
1045 client->error= EINVAL;
1046 return MEMCACHED_PROTOCOL_ERROR_EVENT;
1047 }
1048 ssize_t len= *length;
1049
1050 while (len >= (ssize_t)sizeof(*header) &&
1051 (len >= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen))))
1052 {
1053 /* I have the complete package */
1054 client->current_command= header;
1055 protocol_binary_response_status rv= execute_command(client, header);
1056
1057 if (rv == PROTOCOL_BINARY_RESPONSE_EINTERNAL)
1058 {
1059 *length= len;
1060 *endptr= (void*)header;
1061 return MEMCACHED_PROTOCOL_ERROR_EVENT;
1062 } else if (rv == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED)
1063 return MEMCACHED_PROTOCOL_PAUSE_EVENT;
1064
1065 ssize_t total= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen));
1066 len -= total;
1067 if (len > 0)
1068 {
1069 intptr_t ptr= (intptr_t)header;
1070 ptr += total;
1071 if ((ptr % 8) == 0)
1072 {
1073 header= (void*)ptr;
1074 }
1075 else
1076 {
1077 /* Fix alignment */
1078 memmove(client->root->input_buffer, (void*)ptr, (size_t)len);
1079 header= (void*)client->root->input_buffer;
1080 }
1081 }
1082 *length= len;
1083 *endptr= (void*)header;
1084 }
1085
1086 return MEMCACHED_PROTOCOL_READ_EVENT;
1087 }
1088
1089 /*
1090 ** **********************************************************************
1091 ** PUBLIC INTERFACE
1092 ** **********************************************************************
1093 */
1094 memcached_binary_protocol_callback_st *memcached_binary_protocol_get_callbacks(memcached_protocol_st *instance)
1095 {
1096 return instance->callback;
1097 }
1098
1099 void memcached_binary_protocol_set_callbacks(memcached_protocol_st *instance, memcached_binary_protocol_callback_st *callback)
1100 {
1101 instance->callback= callback;
1102 }
1103
1104 memcached_binary_protocol_raw_response_handler memcached_binary_protocol_get_raw_response_handler(const void *cookie)
1105 {
1106 (void)cookie;
1107 return raw_response_handler;
1108 }
1109
1110 void memcached_binary_protocol_set_pedantic(memcached_protocol_st *instance, bool enable)
1111 {
1112 instance->pedantic= enable;
1113 }
1114
1115 bool memcached_binary_protocol_get_pedantic(memcached_protocol_st *instance)
1116 {
1117 return instance->pedantic;
1118 }
1119