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;
95 return response_handler(cookie
, header
, (void*)&msg
);
97 else if (opcode
== PROTOCOL_BINARY_CMD_GET
|| opcode
== PROTOCOL_BINARY_CMD_GETK
)
99 msg
.response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
);
100 return response_handler(cookie
, header
, (void*)&msg
);
103 /* Q shouldn't report a miss ;-) */
104 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
107 static protocol_binary_response_status
delete_command_handler(const void *cookie
,
108 protocol_binary_request_header
*header
,
109 memcached_binary_protocol_raw_response_handler response_handler
)
111 size_t keylen
= ntohs(header
->request
.keylen
);
112 char *key
= ((char*)header
) + sizeof(*header
);
113 protocol_binary_response_no_extras response
= {
114 .message
.header
.response
= {
115 .magic
= PROTOCOL_BINARY_RES
,
116 .opcode
= header
->request
.opcode
,
117 .opaque
= header
->request
.opaque
121 if (!delete_item(key
, keylen
))
123 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
);
124 return response_handler(cookie
, header
, (void*)&response
);
126 else if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_DELETE
)
128 /* DELETEQ doesn't want success response */
129 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
);
130 return response_handler(cookie
, header
, (void*)&response
);
133 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
136 static protocol_binary_response_status
flush_command_handler(const void *cookie
,
137 protocol_binary_request_header
*header
,
138 memcached_binary_protocol_raw_response_handler response_handler
)
140 uint8_t opcode
= header
->request
.opcode
;
142 /* @fixme sett inn when! */
145 if (opcode
== PROTOCOL_BINARY_CMD_FLUSH
)
147 protocol_binary_response_no_extras response
= {
148 .message
.header
.response
= {
149 .magic
= PROTOCOL_BINARY_RES
,
151 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
152 .opaque
= header
->request
.opaque
155 return response_handler(cookie
, header
, (void*)&response
);
158 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
161 static protocol_binary_response_status
arithmetic_command_handler(const void *cookie
,
162 protocol_binary_request_header
*header
,
163 memcached_binary_protocol_raw_response_handler response_handler
)
165 protocol_binary_request_incr
*req
= (void*)header
;
166 protocol_binary_response_incr response
= {
167 .message
.header
.response
= {
168 .magic
= PROTOCOL_BINARY_RES
,
169 .opcode
= header
->request
.opcode
,
170 .opaque
= header
->request
.opaque
,
174 uint16_t keylen
= ntohs(header
->request
.keylen
);
175 uint64_t initial
= ntohll(req
->message
.body
.initial
);
176 uint64_t delta
= ntohll(req
->message
.body
.delta
);
177 uint32_t expiration
= ntohl(req
->message
.body
.expiration
);
178 void *key
= req
->bytes
+ sizeof(req
->bytes
);
179 protocol_binary_response_status rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
181 struct item
*item
= get_item(key
, keylen
);
184 item
= create_item(key
, keylen
, NULL
, sizeof(initial
), 0, expiration
);
187 rval
= PROTOCOL_BINARY_RESPONSE_ENOMEM
;
191 memcpy(item
->data
, &initial
, sizeof(initial
));
197 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENT
||
198 header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENTQ
)
200 (*(uint64_t*)item
->data
) += delta
;
204 if (delta
> *(uint64_t*)item
->data
)
206 *(uint64_t*)item
->data
= 0;
210 *(uint64_t*)item
->data
-= delta
;
216 response
.message
.header
.response
.status
= htons(rval
);
217 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
219 response
.message
.header
.response
.bodylen
= ntohl(8);
220 response
.message
.body
.value
= ntohll((*(uint64_t*)item
->data
));
221 response
.message
.header
.response
.cas
= ntohll(item
->cas
);
223 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_INCREMENTQ
||
224 header
->request
.opcode
== PROTOCOL_BINARY_CMD_DECREMENTQ
)
226 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
230 return response_handler(cookie
, header
, (void*)&response
);
233 static protocol_binary_response_status
version_command_handler(const void *cookie
,
234 protocol_binary_request_header
*header
,
235 memcached_binary_protocol_raw_response_handler response_handler
)
237 const char *versionstring
= "1.0.0";
239 protocol_binary_response_header packet
;
243 .magic
= PROTOCOL_BINARY_RES
,
244 .opcode
= PROTOCOL_BINARY_CMD_VERSION
,
245 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
246 .opaque
= header
->request
.opaque
,
247 .bodylen
= htonl((uint32_t)strlen(versionstring
))
251 memcpy(response
.buffer
+ sizeof(response
.packet
), versionstring
, strlen(versionstring
));
253 return response_handler(cookie
, header
, (void*)&response
);
256 static protocol_binary_response_status
concat_command_handler(const void *cookie
,
257 protocol_binary_request_header
*header
,
258 memcached_binary_protocol_raw_response_handler response_handler
)
260 protocol_binary_response_status rval
= PROTOCOL_BINARY_RESPONSE_SUCCESS
;
261 uint16_t keylen
= ntohs(header
->request
.keylen
);
262 uint64_t cas
= ntohll(header
->request
.cas
);
263 void *key
= header
+ 1;
264 uint32_t vallen
= ntohl(header
->request
.bodylen
) - keylen
;
265 void *val
= (char*)key
+ keylen
;
267 struct item
*item
= get_item(key
, keylen
);
271 rval
= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
;
273 else if (cas
!= 0 && cas
!= item
->cas
)
275 rval
= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
;
277 else if ((nitem
= create_item(key
, keylen
, NULL
, item
->size
+ vallen
,
278 item
->flags
, item
->exp
)) == NULL
)
280 rval
= PROTOCOL_BINARY_RESPONSE_ENOMEM
;
284 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
||
285 header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPENDQ
)
287 memcpy(nitem
->data
, item
->data
, item
->size
);
288 memcpy(((char*)(nitem
->data
)) + item
->size
, val
, vallen
);
292 memcpy(nitem
->data
, val
, vallen
);
293 memcpy(((char*)(nitem
->data
)) + vallen
, item
->data
, item
->size
);
295 delete_item(key
, keylen
);
297 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_APPEND
||
298 header
->request
.opcode
== PROTOCOL_BINARY_CMD_PREPEND
)
300 protocol_binary_response_no_extras response
= {
303 .magic
= PROTOCOL_BINARY_RES
,
304 .opcode
= header
->request
.opcode
,
305 .status
= htons(rval
),
306 .opaque
= header
->request
.opaque
,
307 .cas
= htonll(nitem
->cas
),
311 return response_handler(cookie
, header
, (void*)&response
);
318 static protocol_binary_response_status
set_command_handler(const void *cookie
,
319 protocol_binary_request_header
*header
,
320 memcached_binary_protocol_raw_response_handler response_handler
)
322 size_t keylen
= ntohs(header
->request
.keylen
);
323 size_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
324 protocol_binary_request_replace
*request
= (void*)header
;
325 uint32_t flags
= ntohl(request
->message
.body
.flags
);
326 time_t timeout
= ntohl(request
->message
.body
.expiration
);
327 char *key
= ((char*)header
) + sizeof(*header
) + 8;
328 char *data
= key
+ keylen
;
330 protocol_binary_response_no_extras response
= {
333 .magic
= PROTOCOL_BINARY_RES
,
334 .opcode
= header
->request
.opcode
,
335 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
336 .opaque
= header
->request
.opaque
341 if (header
->request
.cas
!= 0)
344 struct item
* item
= get_item(key
, keylen
);
345 if (item
!= NULL
&& item
->cas
!= ntohll(header
->request
.cas
))
347 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
);
348 return response_handler(cookie
, header
, (void*)&response
);
352 delete_item(key
, keylen
);
353 struct item
* item
= create_item(key
, keylen
, data
, datalen
, flags
, timeout
);
356 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM
);
361 /* SETQ shouldn't return a message */
362 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_SET
)
364 response
.message
.header
.response
.cas
= htonll(item
->cas
);
365 return response_handler(cookie
, header
, (void*)&response
);
367 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
370 return response_handler(cookie
, header
, (void*)&response
);
373 static protocol_binary_response_status
add_command_handler(const void *cookie
,
374 protocol_binary_request_header
*header
,
375 memcached_binary_protocol_raw_response_handler response_handler
)
377 size_t keylen
= ntohs(header
->request
.keylen
);
378 size_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
379 protocol_binary_request_add
*request
= (void*)header
;
380 uint32_t flags
= ntohl(request
->message
.body
.flags
);
381 time_t timeout
= ntohl(request
->message
.body
.expiration
);
382 char *key
= ((char*)header
) + sizeof(*header
) + 8;
383 char *data
= key
+ keylen
;
385 protocol_binary_response_no_extras response
= {
388 .magic
= PROTOCOL_BINARY_RES
,
389 .opcode
= header
->request
.opcode
,
390 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
391 .opaque
= header
->request
.opaque
396 struct item
* item
= get_item(key
, keylen
);
399 item
= create_item(key
, keylen
, data
, datalen
, flags
, timeout
);
401 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM
);
405 /* ADDQ shouldn't return a message */
406 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_ADD
)
408 response
.message
.header
.response
.cas
= htonll(item
->cas
);
409 return response_handler(cookie
, header
, (void*)&response
);
411 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
416 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
);
419 return response_handler(cookie
, header
, (void*)&response
);
422 static protocol_binary_response_status
replace_command_handler(const void *cookie
,
423 protocol_binary_request_header
*header
,
424 memcached_binary_protocol_raw_response_handler response_handler
)
426 size_t keylen
= ntohs(header
->request
.keylen
);
427 size_t datalen
= ntohl(header
->request
.bodylen
) - keylen
- 8;
428 protocol_binary_request_replace
*request
= (void*)header
;
429 uint32_t flags
= ntohl(request
->message
.body
.flags
);
430 time_t timeout
= ntohl(request
->message
.body
.expiration
);
431 char *key
= ((char*)header
) + sizeof(*header
) + 8;
432 char *data
= key
+ keylen
;
434 protocol_binary_response_no_extras response
= {
437 .magic
= PROTOCOL_BINARY_RES
,
438 .opcode
= header
->request
.opcode
,
439 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
440 .opaque
= header
->request
.opaque
445 struct item
* item
= get_item(key
, keylen
);
447 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
);
448 else if (header
->request
.cas
== 0 || ntohll(header
->request
.cas
) == item
->cas
)
450 delete_item(key
, keylen
);
451 item
= create_item(key
, keylen
, data
, datalen
, flags
, timeout
);
453 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM
);
457 /* REPLACEQ shouldn't return a message */
458 if (header
->request
.opcode
== PROTOCOL_BINARY_CMD_REPLACE
)
460 response
.message
.header
.response
.cas
= htonll(item
->cas
);
461 return response_handler(cookie
, header
, (void*)&response
);
463 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
467 response
.message
.header
.response
.status
= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
);
469 return response_handler(cookie
, header
, (void*)&response
);
472 static protocol_binary_response_status
stat_command_handler(const void *cookie
,
473 protocol_binary_request_header
*header
,
474 memcached_binary_protocol_raw_response_handler response_handler
)
476 /* Just send the terminating packet*/
477 protocol_binary_response_no_extras response
= {
480 .magic
= PROTOCOL_BINARY_RES
,
481 .opcode
= PROTOCOL_BINARY_CMD_STAT
,
482 .status
= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS
),
483 .opaque
= header
->request
.opaque
488 return response_handler(cookie
, header
, (void*)&response
);
491 struct memcached_binary_protocol_callback_st interface_v0_impl
= {
492 .interface_version
= 0,
493 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GET
]= get_command_handler
,
494 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_SET
]= set_command_handler
,
495 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_ADD
]= add_command_handler
,
496 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_REPLACE
]= replace_command_handler
,
497 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DELETE
]= delete_command_handler
,
498 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_INCREMENT
]= arithmetic_command_handler
,
499 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DECREMENT
]= arithmetic_command_handler
,
500 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_QUIT
]= quit_command_handler
,
501 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_FLUSH
]= flush_command_handler
,
502 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GETQ
]= get_command_handler
,
503 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_NOOP
]= noop_command_handler
,
504 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_VERSION
]= version_command_handler
,
505 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GETK
]= get_command_handler
,
506 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_GETKQ
]= get_command_handler
,
507 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_APPEND
]= concat_command_handler
,
508 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_PREPEND
]= concat_command_handler
,
509 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_STAT
]= stat_command_handler
,
510 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_SETQ
]= set_command_handler
,
511 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_ADDQ
]= add_command_handler
,
512 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_REPLACEQ
]= replace_command_handler
,
513 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DELETEQ
]= delete_command_handler
,
514 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_INCREMENTQ
]= arithmetic_command_handler
,
515 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_DECREMENTQ
]= arithmetic_command_handler
,
516 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_QUITQ
]= quit_command_handler
,
517 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_FLUSHQ
]= flush_command_handler
,
518 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_APPENDQ
]= concat_command_handler
,
519 .interface
.v0
.comcode
[PROTOCOL_BINARY_CMD_PREPENDQ
]= concat_command_handler
,