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