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