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