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