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