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