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