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