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