1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
3 * This file contains an implementation of the callback interface for level 0
4 * in the protocol library. You might want to have your copy of the protocol
5 * specification next to your coffee ;-)
9 #include <sys/socket.h>
11 #include <netinet/tcp.h>
19 #include <libmemcached/protocol_handler.h>
20 #include <libmemcached/byteorder.h>
23 static protocol_binary_response_status
noop_command_handler(const void *cookie
,
24 protocol_binary_request_header
*header
,
25 memcached_binary_protocol_raw_response_handler response_handler
)
27 protocol_binary_response_no_extras response
= {
28 .message
.header
.response
= {
29 .magic
= PROTOCOL_BINARY_RES
,
30 .opcode
= PROTOCOL_BINARY_CMD_NOOP
,
31 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
32 .opaque
= header
->request
.opaque
36 return response_handler(cookie
, header
, (void*)&response
);
39 static protocol_binary_response_status
quit_command_handler(const void *cookie
,
40 protocol_binary_request_header
*header
,
41 memcached_binary_protocol_raw_response_handler response_handler
)
43 protocol_binary_response_no_extras response
= {
44 .message
.header
.response
= {
45 .magic
= PROTOCOL_BINARY_RES
,
46 .opcode
= PROTOCOL_BINARY_CMD_QUIT
,
47 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
48 .opaque
= header
->request
.opaque
52 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_QUIT
)
53 response_handler(cookie
, header
, (void*)&response
);
55 /* I need a better way to signal to close the connection */
56 return PROTOCOL_BINARY_RESPONSE_EIO
;
59 static protocol_binary_response_status
get_command_handler(const void *cookie
,
60 protocol_binary_request_header
*header
,
61 memcached_binary_protocol_raw_response_handler response_handler
)
63 uint8_t opcode
= header
->request
.opcode
;
65 protocol_binary_response_get response
;
68 .response
.message
.header
.response
= {
69 .magic
= PROTOCOL_BINARY_RES
,
71 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
72 .opaque
= header
->request
.opaque
76 struct item
*item
= get_item(header
+ 1, ntohs(header
->request
.keylen
));
79 msg
.response
.message
.body
.flags
= htonl(item
->flags
);
80 char *ptr
= (char*)(msg
.response
.bytes
+ sizeof(*header
) + 4);
82 msg
.response
.message
.header
.response
.cas
= htonll(item
->cas
);
83 if (opcode
== PROTOCOL_BINARY_CMD_GETK
|| opcode
== PROTOCOL_BINARY_CMD_GETKQ
)
85 memcpy(ptr
, item
->key
, item
->nkey
);
86 msg
.response
.message
.header
.response
.keylen
= htons((uint16_t)item
->nkey
);
88 bodysize
+= (uint32_t)item
->nkey
;
90 memcpy(ptr
, item
->data
, item
->size
);
91 bodysize
+= (uint32_t)item
->size
;
92 msg
.response
.message
.header
.response
.bodylen
= htonl(bodysize
);
93 msg
.response
.message
.header
.response
.extlen
= 4;
96 return response_handler(cookie
, header
, (void*)&msg
);
98 else if (opcode
== PROTOCOL_BINARY_CMD_GET
|| opcode
== PROTOCOL_BINARY_CMD_GETK
)
100 msg
.response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
);
101 return response_handler(cookie
, header
, (void*)&msg
);
104 /* Q shouldn't report a miss ;-) */
105 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
108 static protocol_binary_response_status
delete_command_handler(const void *cookie
,
109 protocol_binary_request_header
*header
,
110 memcached_binary_protocol_raw_response_handler response_handler
)
112 size_t keylen
= ntohs(header
->request
.keylen
);
113 char *key
= ((char*)header
) + sizeof(*header
);
114 protocol_binary_response_no_extras response
= {
115 .message
.header
.response
= {
116 .magic
= PROTOCOL_BINARY_RES
,
117 .opcode
= header
->request
.opcode
,
118 .opaque
= header
->request
.opaque
122 if (!delete_item(key
, keylen
))
124 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
);
125 return response_handler(cookie
, header
, (void*)&response
);
127 else if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
129 /* DELETEQ doesn't want success response */
130 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
);
131 return response_handler(cookie
, header
, (void*)&response
);
134 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
137 static protocol_binary_response_status
flush_command_handler(const void *cookie
,
138 protocol_binary_request_header
*header
,
139 memcached_binary_protocol_raw_response_handler response_handler
)
141 uint8_t opcode
= header
->request
.opcode
;
143 /* @fixme sett inn when! */
146 if (opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
148 protocol_binary_response_no_extras response
= {
149 .message
.header
.response
= {
150 .magic
= PROTOCOL_BINARY_RES
,
152 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
153 .opaque
= header
->request
.opaque
156 return response_handler(cookie
, header
, (void*)&response
);
159 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
162 static protocol_binary_response_status
arithmetic_command_handler(const void *cookie
,
163 protocol_binary_request_header
*header
,
164 memcached_binary_protocol_raw_response_handler response_handler
)
166 protocol_binary_request_incr
*req
= (void*)header
;
167 protocol_binary_response_incr response
= {
168 .message
.header
.response
= {
169 .magic
= PROTOCOL_BINARY_RES
,
170 .opcode
= header
->request
.opcode
,
171 .opaque
= header
->request
.opaque
,
175 uint16_t keylen
= ntohs(header
->request
.keylen
);
176 uint64_t initial
= ntohll(req
->message
.body
.initial
);
177 uint64_t delta
= ntohll(req
->message
.body
.delta
);
178 uint32_t expiration
= ntohl(req
->message
.body
.expiration
);
180 void *key
= req
->bytes
+ sizeof(req
->bytes
);
181 protocol_binary_response_status rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
183 uint64_t value
= initial
;
185 struct item
*item
= get_item(key
, keylen
);
188 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
||
189 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENTQ
)
191 value
= (*(uint64_t*)item
->data
) + delta
;
195 if (delta
> *(uint64_t*)item
->data
)
201 value
= *(uint64_t*)item
->data
- delta
;
204 expiration
= (uint32_t)item
->exp
;
208 delete_item(key
, keylen
);
211 item
= create_item(key
, keylen
, NULL
, sizeof(value
), flags
, (time_t)expiration
);
214 rval
= PROTOCOL_BINARY_RESPONSE_ENOMEM
;
218 memcpy(item
->data
, &value
, sizeof(value
));
222 response
.message
.header
.response
.status
= htons(rval
);
223 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
225 response
.message
.header
.response
.bodylen
= ntohl(8);
226 response
.message
.body
.value
= ntohll((*(uint64_t*)item
->data
));
227 response
.message
.header
.response
.cas
= ntohll(item
->cas
);
230 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENTQ
||
231 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENTQ
)
233 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
237 return response_handler(cookie
, header
, (void*)&response
);
240 static protocol_binary_response_status
version_command_handler(const void *cookie
,
241 protocol_binary_request_header
*header
,
242 memcached_binary_protocol_raw_response_handler response_handler
)
244 const char *versionstring
= "1.0.0";
246 protocol_binary_response_header packet
;
250 .magic
= PROTOCOL_BINARY_RES
,
251 .opcode
= PROTOCOL_BINARY_CMD_VERSION
,
252 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
253 .opaque
= header
->request
.opaque
,
254 .bodylen
= htonl((uint32_t)strlen(versionstring
))
258 memcpy(response
.buffer
+ sizeof(response
.packet
), versionstring
, strlen(versionstring
));
260 return response_handler(cookie
, header
, (void*)&response
);
263 static protocol_binary_response_status
concat_command_handler(const void *cookie
,
264 protocol_binary_request_header
*header
,
265 memcached_binary_protocol_raw_response_handler response_handler
)
267 protocol_binary_response_status rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
268 uint16_t keylen
= ntohs(header
->request
.keylen
);
269 uint64_t cas
= ntohll(header
->request
.cas
);
270 void *key
= header
+ 1;
271 uint32_t vallen
= ntohl(header
->request
.bodylen
) - keylen
;
272 void *val
= (char*)key
+ keylen
;
274 struct item
*item
= get_item(key
, keylen
);
275 struct item
*nitem
= NULL
;
279 rval
= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
;
281 else if (cas
!= 0 && cas
!= item
->cas
)
283 rval
= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
;
285 else if ((nitem
= create_item(key
, keylen
, NULL
, item
->size
+ vallen
,
286 item
->flags
, item
->exp
)) == NULL
)
289 rval
= PROTOCOL_BINARY_RESPONSE_ENOMEM
;
293 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
||
294 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPENDQ
)
296 memcpy(nitem
->data
, item
->data
, item
->size
);
297 memcpy(((char*)(nitem
->data
)) + item
->size
, val
, vallen
);
301 memcpy(nitem
->data
, val
, vallen
);
302 memcpy(((char*)(nitem
->data
)) + vallen
, item
->data
, item
->size
);
305 delete_item(key
, keylen
);
310 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
||
311 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
313 protocol_binary_response_no_extras response
= {
316 .magic
= PROTOCOL_BINARY_RES
,
317 .opcode
= header
->request
.opcode
,
318 .status
= htons(rval
),
319 .opaque
= header
->request
.opaque
,
324 return response_handler(cookie
, header
, (void*)&response
);
331 static protocol_binary_response_status
set_command_handler(const void *cookie
,
332 protocol_binary_request_header
*header
,
333 memcached_binary_protocol_raw_response_handler response_handler
)
335 size_t keylen
= ntohs(header
->request
.keylen
);
336 size_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
337 protocol_binary_request_replace
*request
= (void*)header
;
338 uint32_t flags
= ntohl(request
->message
.body
.flags
);
339 time_t timeout
= (time_t)ntohl(request
->message
.body
.expiration
);
340 char *key
= ((char*)header
) + sizeof(*header
) + 8;
341 char *data
= key
+ keylen
;
343 protocol_binary_response_no_extras response
= {
346 .magic
= PROTOCOL_BINARY_RES
,
347 .opcode
= header
->request
.opcode
,
348 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
349 .opaque
= header
->request
.opaque
354 if (header
->request
.cas
!= 0)
357 struct item
* item
= get_item(key
, keylen
);
360 if (item
->cas
!= ntohll(header
->request
.cas
))
363 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
);
364 return response_handler(cookie
, header
, (void*)&response
);
370 delete_item(key
, keylen
);
371 struct item
* item
= create_item(key
, keylen
, data
, datalen
, flags
, timeout
);
374 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM
);
379 /* SETQ shouldn't return a message */
380 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
382 response
.message
.header
.response
.cas
= htonll(item
->cas
);
384 return response_handler(cookie
, header
, (void*)&response
);
388 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
391 return response_handler(cookie
, header
, (void*)&response
);
394 static protocol_binary_response_status
add_command_handler(const void *cookie
,
395 protocol_binary_request_header
*header
,
396 memcached_binary_protocol_raw_response_handler response_handler
)
398 size_t keylen
= ntohs(header
->request
.keylen
);
399 size_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
400 protocol_binary_request_add
*request
= (void*)header
;
401 uint32_t flags
= ntohl(request
->message
.body
.flags
);
402 time_t timeout
= (time_t)ntohl(request
->message
.body
.expiration
);
403 char *key
= ((char*)header
) + sizeof(*header
) + 8;
404 char *data
= key
+ keylen
;
406 protocol_binary_response_no_extras response
= {
409 .magic
= PROTOCOL_BINARY_RES
,
410 .opcode
= header
->request
.opcode
,
411 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
412 .opaque
= header
->request
.opaque
417 struct item
* item
= get_item(key
, keylen
);
420 item
= create_item(key
, keylen
, data
, datalen
, flags
, timeout
);
422 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM
);
426 /* ADDQ shouldn't return a message */
427 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
429 response
.message
.header
.response
.cas
= htonll(item
->cas
);
431 return response_handler(cookie
, header
, (void*)&response
);
434 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
440 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
);
443 return response_handler(cookie
, header
, (void*)&response
);
446 static protocol_binary_response_status
replace_command_handler(const void *cookie
,
447 protocol_binary_request_header
*header
,
448 memcached_binary_protocol_raw_response_handler response_handler
)
450 size_t keylen
= ntohs(header
->request
.keylen
);
451 size_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
452 protocol_binary_request_replace
*request
= (void*)header
;
453 uint32_t flags
= ntohl(request
->message
.body
.flags
);
454 time_t timeout
= (time_t)ntohl(request
->message
.body
.expiration
);
455 char *key
= ((char*)header
) + sizeof(*header
) + 8;
456 char *data
= key
+ keylen
;
458 protocol_binary_response_no_extras response
= {
461 .magic
= PROTOCOL_BINARY_RES
,
462 .opcode
= header
->request
.opcode
,
463 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
464 .opaque
= header
->request
.opaque
469 struct item
* item
= get_item(key
, keylen
);
471 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
);
472 else if (header
->request
.cas
== 0 || ntohll(header
->request
.cas
) == item
->cas
)
475 delete_item(key
, keylen
);
476 item
= create_item(key
, keylen
, data
, datalen
, flags
, timeout
);
478 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM
);
482 /* REPLACEQ shouldn't return a message */
483 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
485 response
.message
.header
.response
.cas
= htonll(item
->cas
);
487 return response_handler(cookie
, header
, (void*)&response
);
490 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
495 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
);
499 return response_handler(cookie
, header
, (void*)&response
);
502 static protocol_binary_response_status
stat_command_handler(const void *cookie
,
503 protocol_binary_request_header
*header
,
504 memcached_binary_protocol_raw_response_handler response_handler
)
506 /* Just send the terminating packet*/
507 protocol_binary_response_no_extras response
= {
510 .magic
= PROTOCOL_BINARY_RES
,
511 .opcode
= PROTOCOL_BINARY_CMD_STAT
,
512 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
513 .opaque
= header
->request
.opaque
518 return response_handler(cookie
, header
, (void*)&response
);
521 struct memcached_binary_protocol_callback_st interface_v0_impl
= {
522 .interface_version
= 0,
523 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
524 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
525 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
526 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
527 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
528 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_INCREMENT
]= arithmetic_command_handler
,
529 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DECREMENT
]= arithmetic_command_handler
,
530 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
531 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
532 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
533 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
534 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
535 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
536 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
537 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_APPEND
]= concat_command_handler
,
538 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_PREPEND
]= concat_command_handler
,
539 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
540 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
541 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
542 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
543 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
544 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_INCREMENTQ
]= arithmetic_command_handler
,
545 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DECREMENTQ
]= arithmetic_command_handler
,
546 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
547 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
548 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_APPENDQ
]= concat_command_handler
,
549 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_PREPENDQ
]= concat_command_handler
,