From: Brian Aker Date: Tue, 4 Oct 2011 17:38:16 +0000 (-0700) Subject: Update all of the build/spec files. X-Git-Tag: 1.0.2~9^2~2^2~1 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=274778ed13cb7c38995dc1491d437b27db6339fb;p=awesomized%2Flibmemcached Update all of the build/spec files. --- diff --git a/ChangeLog b/ChangeLog index e8bf8630..a073b4fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ + +* Dropped libmemcached/memcached_util.h (undocumented header file) + 0.53 Mon Sep 26 20:50:33 PDT 2011 * Fix for FreeBSD/OpenBSD and -lm * Added memcached_exist() diff --git a/Makefile.am b/Makefile.am index 8a95b8c2..d19991ec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,19 +28,22 @@ EXTRA_DIST= \ libmemcached_libmemcached_la_LDFLAGS= +include libtest/include.am + +include libhashkit-1.0/include.am +include libmemcached-1.0/include.am +include libmemcachedprotocol-0.0/include.am +include libmemcachedutil-1.0/include.am + include clients/include.am include docs/include.am include example/include.am include libhashkit/include.am -include libmemcached-1.0/include.am -include libhashkit-1.0/include.am -include libmemcachedprotocol-0.0/include.am include libmemcached/include.am -include libmemcached/util/include.am -include libmemcached/protocol/include.am +include libmemcachedutil/include.am +include libmemcachedprotocol/include.am include libmemcachedinternal/include.am include libmemcachedinternal/util/include.am -include libtest/include.am include poll/include.am include support/include.am include tests/include.am diff --git a/docs/libmemcached.rst b/docs/libmemcached.rst index 80da8fe8..a97db699 100644 --- a/docs/libmemcached.rst +++ b/docs/libmemcached.rst @@ -12,7 +12,7 @@ Compile and link with -lmemcached ======= -libMemcached is an open source C/C++ client library and tools for the memcached server (http://danga.com/memcached). It has been designed to be light on memory usage, thread safe, and provide full access to server side methods. +libMemcached is an open source C/C++ client library and tools for the memcached server (http://memcached.org/). It has been designed to be light on memory usage, thread safe, and provide full access to server side methods. libMemcached was designed to provide the greatest number of options to use Memcached. Some of the features provided: @@ -31,7 +31,7 @@ DESCRIPTION "Memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web -applications by alleviating database load." `http://danga.com/memcached/ `_ +applications by alleviating database load." `http://memcached.org/ `_ :program:`libmemcached` is a small, thread-safe client library for the memcached protocol. The code has all been written to allow diff --git a/example/interface_v0.c b/example/interface_v0.c index 37c8a79a..ef5ba47b 100644 --- a/example/interface_v0.c +++ b/example/interface_v0.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include "example/storage.h" #include "example/memcached_light.h" diff --git a/example/interface_v1.c b/example/interface_v1.c index 88cb59f6..d2b20712 100644 --- a/example/interface_v1.c +++ b/example/interface_v1.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include "storage.h" diff --git a/example/memcached_light.c b/example/memcached_light.c index 6a650bba..ed892861 100644 --- a/example/memcached_light.c +++ b/example/memcached_light.c @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include "storage.h" #include "memcached_light.h" diff --git a/libhashkit-1.0/hashkit.hpp b/libhashkit-1.0/hashkit.hpp index 7ead63d0..a865bf96 100644 --- a/libhashkit-1.0/hashkit.hpp +++ b/libhashkit-1.0/hashkit.hpp @@ -37,7 +37,7 @@ #pragma once -#include +#include #include class Hashkit { diff --git a/libhashkit-1.0/include.am b/libhashkit-1.0/include.am index b9f7da0d..905e38fa 100644 --- a/libhashkit-1.0/include.am +++ b/libhashkit-1.0/include.am @@ -3,6 +3,9 @@ # All paths should be given relative to the root # +EXTRA_DIST+= \ + libhashkit-1.0/configure.h.in + nobase_include_HEADERS+= \ libhashkit-1.0/algorithm.h \ libhashkit-1.0/behavior.h \ diff --git a/libhashkit/common.h b/libhashkit/common.h index 4a239768..4dece00f 100644 --- a/libhashkit/common.h +++ b/libhashkit/common.h @@ -16,7 +16,7 @@ #include #include -#include +#include #include #ifdef __cplusplus diff --git a/libhashkit/include.am b/libhashkit/include.am index 7798f79a..d9b769f6 100644 --- a/libhashkit/include.am +++ b/libhashkit/include.am @@ -11,12 +11,13 @@ lib_LTLIBRARIES+= libhashkit/libhashkit.la -EXTRA_DIST+= \ - libhashkit/configure.h.in +noinst_HEADERS+= \ + libhashkit/common.h \ + libhashkit/is.h -noinst_HEADERS+= \ - libhashkit/common.h +nobase_include_HEADERS+= \ + libhashkit/hashkit.h libhashkit_libhashkit_la_SOURCES= \ libhashkit/algorithm.cc \ diff --git a/libmemcached-1.0/include.am b/libmemcached-1.0/include.am index edac5e71..e9658ad1 100644 --- a/libmemcached-1.0/include.am +++ b/libmemcached-1.0/include.am @@ -2,6 +2,8 @@ # included from Top Level Makefile.am # All paths should be given relative to the root +EXTRA_DIST+= \ + libmemcached-1.0/configure.h.in nobase_include_HEADERS+= \ libmemcached-1.0/allocators.h \ diff --git a/libmemcached-1.0/storage.h b/libmemcached-1.0/storage.h index 0854f164..e634235a 100644 --- a/libmemcached-1.0/storage.h +++ b/libmemcached-1.0/storage.h @@ -37,8 +37,6 @@ #pragma once -#include "libmemcached/memcached.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/libmemcached/common.h b/libmemcached/common.h index 6867490c..47735195 100644 --- a/libmemcached/common.h +++ b/libmemcached/common.h @@ -115,6 +115,7 @@ memcached_return_t memcached_server_execute(memcached_st *ptr, #include #include #include +#include #ifdef __cplusplus #include diff --git a/libmemcached/include.am b/libmemcached/include.am index 3097fdd0..03a327da 100644 --- a/libmemcached/include.am +++ b/libmemcached/include.am @@ -7,10 +7,14 @@ libmemcached_libmemcached_la_SOURCES = include libmemcached/csl/include.am EXTRA_DIST+= \ - libmemcached/configure.h.in \ libmemcached/libmemcached_probes.d \ libmemcached/memcached/README.txt +nobase_include_HEADERS+= \ + libmemcached/memcached.h \ + libmemcached/memcached.hpp \ + libmemcached/util.h + noinst_HEADERS+= \ libmemcached/array.h \ libmemcached/assert.hpp \ @@ -32,10 +36,6 @@ noinst_HEADERS+= \ libmemcached/memory.h \ libmemcached/namespace.h \ libmemcached/options.hpp \ - libmemcached/protocol/ascii_handler.h \ - libmemcached/protocol/binary_handler.h \ - libmemcached/protocol/cache.h \ - libmemcached/protocol/common.h \ libmemcached/response.h \ libmemcached/server.hpp \ libmemcached/server_instance.h \ diff --git a/libmemcached/memcached.hpp b/libmemcached/memcached.hpp new file mode 100644 index 00000000..d38e6d24 --- /dev/null +++ b/libmemcached/memcached.hpp @@ -0,0 +1,40 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include + diff --git a/libmemcached/memcached_util.h b/libmemcached/memcached_util.h deleted file mode 100644 index 8e952beb..00000000 --- a/libmemcached/memcached_util.h +++ /dev/null @@ -1,41 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2006-2009 Brian Aker All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#pragma once - -#include - diff --git a/libmemcached/protocol/ascii_handler.c b/libmemcached/protocol/ascii_handler.c deleted file mode 100644 index 2cceff22..00000000 --- a/libmemcached/protocol/ascii_handler.c +++ /dev/null @@ -1,961 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include -#include -#include -#include - -/** - * Try to parse a key from the string. - * @pointer start pointer to a pointer to the string (IN and OUT) - * @return length of the string of -1 if this was an illegal key (invalid - * characters or invalid length) - * @todo add length! - */ -static uint16_t parse_ascii_key(char **start) -{ - uint16_t len= 0; - char *c= *start; - /* Strip leading whitespaces */ - while (isspace(*c)) - { - ++c; - } - - *start= c; - - while (*c != '\0' && !isspace(*c) && !iscntrl(*c)) - { - ++c; - ++len; - } - - - if (len == 0 || len > 240 || (*c != '\0' && *c != '\r' && iscntrl(*c))) - { - return 0; - } - - return len; -} - -/** - * Spool a zero-terminated string - * @param client destination - * @param text the text to spool - * @return status of the spool operation - */ -static protocol_binary_response_status -spool_string(memcached_protocol_client_st *client, const char *text) -{ - return client->root->spool(client, text, strlen(text)); -} - -/** - * Send a "CLIENT_ERROR" message back to the client with the correct - * format of the command being sent - * @param client the client to send the message to - */ -static void send_command_usage(memcached_protocol_client_st *client) -{ - const char *errmsg[]= { - [GET_CMD]= "CLIENT_ERROR: Syntax error: get *\r\n", - [GETS_CMD]= "CLIENT_ERROR: Syntax error: gets *\r\n", - [SET_CMD]= "CLIENT_ERROR: Syntax error: set [noreply]\r\n", - [ADD_CMD]= "CLIENT_ERROR: Syntax error: add [noreply]\r\n", - [REPLACE_CMD]= "CLIENT_ERROR: Syntax error: replace [noreply]\r\n", - [CAS_CMD]= "CLIENT_ERROR: Syntax error: cas [noreply]\r\n", - [APPEND_CMD]= "CLIENT_ERROR: Syntax error: append [noreply]\r\n", - [PREPEND_CMD]= "CLIENT_ERROR: Syntax error: prepend [noreply]\r\n", - [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete [noreply]\r\n", - [INCR_CMD]= "CLIENT_ERROR: Syntax error: incr [noreply]\r\n", - [DECR_CMD]= "CLIENT_ERROR: Syntax error: decr [noreply]\r\n", - [STATS_CMD]= "CLIENT_ERROR: Syntax error: stats [key]\r\n", - [FLUSH_ALL_CMD]= "CLIENT_ERROR: Syntax error: flush_all [timeout] [noreply]\r\n", - [VERSION_CMD]= "CLIENT_ERROR: Syntax error: version\r\n", - [QUIT_CMD]="CLIENT_ERROR: Syntax error: quit\r\n", - - [VERBOSITY_CMD]= "CLIENT_ERROR: Syntax error: verbosity \r\n", - [UNKNOWN_CMD]= "CLIENT_ERROR: Unknown command\r\n", - }; - - client->mute = false; - spool_string(client, errmsg[client->ascii_command]); -} - -/** - * Callback for the VERSION responses - * @param cookie client identifier - * @param text the length of the body - * @param textlen the length of the body - */ -static protocol_binary_response_status -ascii_version_response_handler(const void *cookie, - const void *text, - uint32_t textlen) -{ - memcached_protocol_client_st *client= (memcached_protocol_client_st*)cookie; - spool_string(client, "VERSION "); - client->root->spool(client, text, textlen); - spool_string(client, "\r\n"); - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Callback for the GET/GETQ/GETK and GETKQ responses - * @param cookie client identifier - * @param key the key for the item - * @param keylen the length of the key - * @param body the length of the body - * @param bodylen the length of the body - * @param flags the flags for the item - * @param cas the CAS id for the item - */ -static protocol_binary_response_status -ascii_get_response_handler(const void *cookie, - const void *key, - uint16_t keylen, - const void *body, - uint32_t bodylen, - uint32_t flags, - uint64_t cas) -{ - memcached_protocol_client_st *client= (void*)cookie; - char buffer[300]; - strcpy(buffer, "VALUE "); - const char *source= key; - char *dest= buffer + 6; - - for (int x= 0; x < keylen; ++x) - { - if (*source != '\0' && !isspace(*source) && !iscntrl(*source)) - { - *dest= *source; - } - else - { - return PROTOCOL_BINARY_RESPONSE_EINVAL; /* key constraints in ascii */ - } - - ++dest; - ++source; - } - - size_t used= (size_t)(dest - buffer); - - if (client->ascii_command == GETS_CMD) - { - snprintf(dest, sizeof(buffer) - used, " %u %u %" PRIu64 "\r\n", flags, - bodylen, cas); - } - else - { - snprintf(dest, sizeof(buffer) - used, " %u %u\r\n", flags, bodylen); - } - - client->root->spool(client, buffer, strlen(buffer)); - client->root->spool(client, body, bodylen); - client->root->spool(client, "\r\n", 2); - - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Callback for the STAT responses - * @param cookie client identifier - * @param key the key for the item - * @param keylen the length of the key - * @param body the length of the body - * @param bodylen the length of the body - */ -static protocol_binary_response_status -ascii_stat_response_handler(const void *cookie, - const void *key, - uint16_t keylen, - const void *body, - uint32_t bodylen) -{ - - memcached_protocol_client_st *client= (void*)cookie; - - if (key != NULL) - { - spool_string(client, "STAT "); - client->root->spool(client, key, keylen); - spool_string(client, " "); - client->root->spool(client, body, bodylen); - spool_string(client, "\r\n"); - } - else - { - spool_string(client, "END\r\n"); - } - - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Process a get or a gets request. - * @param client the client handle - * @param buffer the complete get(s) command - * @param end the last character in the command - */ -static void ascii_process_gets(memcached_protocol_client_st *client, - char *buffer, char *end) -{ - char *key= buffer; - - /* Skip command */ - key += (client->ascii_command == GETS_CMD) ? 5 : 4; - - int num_keys= 0; - while (key < end) - { - uint16_t nkey= parse_ascii_key(&key); - if (nkey == 0) /* Invalid key... stop processing this line */ - { - break; - } - - (void)client->root->callback->interface.v1.get(client, key, nkey, - ascii_get_response_handler); - key += nkey; - ++num_keys; - } - - if (num_keys == 0) - { - send_command_usage(client); - } - else - client->root->spool(client, "END\r\n", 5); -} - -/** - * Try to split up the command line "asdf asdf asdf asdf\n" into an - * argument vector for easier parsing. - * @param start the first character in the command line - * @param end the last character in the command line ("\n") - * @param vec the vector to insert the pointers into - * @size the number of elements in the vector - * @return the number of tokens in the vector - */ -static int ascii_tokenize_command(char *str, char *end, char **vec, int size) -{ - int elem= 0; - - while (str < end) - { - /* Skip leading blanks */ - while (str < end && isspace(*str)) - { - ++str; - } - - if (str == end) - { - return elem; - } - - vec[elem++]= str; - /* find the next non-blank field */ - while (str < end && !isspace(*str)) - { - ++str; - } - - /* zero-terminate it for easier parsing later on */ - *str= '\0'; - ++str; - - /* Is the vector full? */ - if (elem == size) - { - break; - } - } - - return elem; -} - -/** - * If we for some reasons needs to push the line back to read more - * data we have to reverse the tokenization. Just do the brain-dead replace - * of all '\0' to ' ' and set the last character to '\n'. We could have used - * the vector we created, but then we would have to search for all of the - * spaces we ignored... - * @param start pointer to the first character in the buffer to recover - * @param end pointer to the last character in the buffer to recover - */ -static void recover_tokenize_command(char *start, char *end) -{ - while (start < end) - { - if (*start == '\0') - *start= ' '; - ++start; - } - - *end= '\n'; -} - -/** - * Convert the textual command into a comcode - */ -static enum ascii_cmd ascii_to_cmd(char *start, size_t length) -{ - struct { - const char *cmd; - size_t len; - enum ascii_cmd cc; - } commands[]= { - { .cmd= "get", .len= 3, .cc= GET_CMD }, - { .cmd= "gets", .len= 4, .cc= GETS_CMD }, - { .cmd= "set", .len= 3, .cc= SET_CMD }, - { .cmd= "add", .len= 3, .cc= ADD_CMD }, - { .cmd= "replace", .len= 7, .cc= REPLACE_CMD }, - { .cmd= "cas", .len= 3, .cc= CAS_CMD }, - { .cmd= "append", .len= 6, .cc= APPEND_CMD }, - { .cmd= "prepend", .len= 7, .cc= PREPEND_CMD }, - { .cmd= "delete", .len= 6, .cc= DELETE_CMD }, - { .cmd= "incr", .len= 4, .cc= INCR_CMD }, - { .cmd= "decr", .len= 4, .cc= DECR_CMD }, - { .cmd= "stats", .len= 5, .cc= STATS_CMD }, - { .cmd= "flush_all", .len= 9, .cc= FLUSH_ALL_CMD }, - { .cmd= "version", .len= 7, .cc= VERSION_CMD }, - { .cmd= "quit", .len= 4, .cc= QUIT_CMD }, - { .cmd= "verbosity", .len= 9, .cc= VERBOSITY_CMD }, - { .cmd= NULL, .len= 0, .cc= UNKNOWN_CMD }}; - - int x= 0; - while (commands[x].len > 0) { - if (length >= commands[x].len) - { - if (strncmp(start, commands[x].cmd, commands[x].len) == 0) - { - /* Potential hit */ - if (length == commands[x].len || isspace(*(start + commands[x].len))) - { - return commands[x].cc; - } - } - } - ++x; - } - - return UNKNOWN_CMD; -} - -/** - * Perform a delete operation. - * - * @param client client requesting the deletion - * @param tokens the command as a vector - * @param ntokens the number of items in the vector - */ -static void process_delete(memcached_protocol_client_st *client, - char **tokens, int ntokens) -{ - char *key= tokens[1]; - uint16_t nkey; - - if (ntokens != 2 || (nkey= parse_ascii_key(&key)) == 0) - { - send_command_usage(client); - return; - } - - if (client->root->callback->interface.v1.delete == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return; - } - - protocol_binary_response_status rval; - rval= client->root->callback->interface.v1.delete(client, key, nkey, 0); - - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) - { - spool_string(client, "DELETED\r\n"); - } - else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT) - { - spool_string(client, "NOT_FOUND\r\n"); - } - else - { - char msg[80]; - snprintf(msg, sizeof(msg), "SERVER_ERROR: delete failed %u\r\n",(uint32_t)rval); - spool_string(client, msg); - } -} - -static void process_arithmetic(memcached_protocol_client_st *client, - char **tokens, int ntokens) -{ - char *key= tokens[1]; - uint16_t nkey; - - if (ntokens != 3 || (nkey= parse_ascii_key(&key)) == 0) - { - send_command_usage(client); - return; - } - - uint64_t cas; - uint64_t result; - uint64_t delta= strtoull(tokens[2], NULL, 10); - - protocol_binary_response_status rval; - if (client->ascii_command == INCR_CMD) - { - if (client->root->callback->interface.v1.increment == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return; - } - rval= client->root->callback->interface.v1.increment(client, - key, nkey, - delta, 0, - 0, - &result, - &cas); - } - else - { - if (client->root->callback->interface.v1.decrement == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return; - } - rval= client->root->callback->interface.v1.decrement(client, - key, nkey, - delta, 0, - 0, - &result, - &cas); - } - - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) - { - char buffer[80]; - snprintf(buffer, sizeof(buffer), "%"PRIu64"\r\n", result); - spool_string(client, buffer); - } - else - { - spool_string(client, "NOT_FOUND\r\n"); - } -} - -/** - * Process the stats command (with or without a key specified) - * @param key pointer to the first character after "stats" - * @param end pointer to the "\n" - */ -static void process_stats(memcached_protocol_client_st *client, - char *key, char *end) -{ - if (client->root->callback->interface.v1.stat == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return; - } - - while (isspace(*key)) - key++; - - uint16_t nkey= (uint16_t)(end - key); - (void)client->root->callback->interface.v1.stat(client, key, nkey, - ascii_stat_response_handler); -} - -static void process_version(memcached_protocol_client_st *client, - char **tokens, int ntokens) -{ - (void)tokens; - if (ntokens != 1) - { - send_command_usage(client); - return; - } - - if (client->root->callback->interface.v1.version == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return; - } - - client->root->callback->interface.v1.version(client, - ascii_version_response_handler); -} - -static void process_flush(memcached_protocol_client_st *client, - char **tokens, int ntokens) -{ - if (ntokens > 2) - { - send_command_usage(client); - return; - } - - if (client->root->callback->interface.v1.flush == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return; - } - - uint32_t timeout= 0; - if (ntokens == 2) - { - timeout= (uint32_t)strtoul(tokens[1], NULL, 10); - } - - protocol_binary_response_status rval; - rval= client->root->callback->interface.v1.flush(client, timeout); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) - spool_string(client, "OK\r\n"); - else - spool_string(client, "SERVER_ERROR: internal error\r\n"); -} - -/** - * Process one of the storage commands - * @param client the client performing the operation - * @param tokens the command tokens - * @param ntokens the number of tokens - * @param start pointer to the first character in the line - * @param end pointer to the pointer where the last character of this - * command is (IN and OUT) - * @param length the number of bytes available - * @return -1 if an error occurs (and we should just terminate the connection - * because we are out of sync) - * 0 storage command completed, continue processing - * 1 We need more data, so just go ahead and wait for more! - */ -static inline int process_storage_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - (void)ntokens; /* already checked */ - char *key= tokens[1]; - uint16_t nkey= parse_ascii_key(&key); - if (nkey == 0) - { - /* return error */ - spool_string(client, "CLIENT_ERROR: bad key\r\n"); - return -1; - } - - uint32_t flags= (uint32_t)strtoul(tokens[2], NULL, 10); - uint32_t timeout= (uint32_t)strtoul(tokens[3], NULL, 10); - unsigned long nbytes= strtoul(tokens[4], NULL, 10); - - /* Do we have all data? */ - unsigned long need= nbytes + (unsigned long)((*end - start) + 1) + 2; /* \n\r\n */ - if ((ssize_t)need > length) - { - /* Keep on reading */ - recover_tokenize_command(start, *end); - return 1; - } - - void *data= (*end) + 1; - uint64_t cas= 0; - uint64_t result_cas; - protocol_binary_response_status rval; - switch (client->ascii_command) - { - case SET_CMD: - rval= client->root->callback->interface.v1.set(client, key, - (uint16_t)nkey, - data, - (uint32_t)nbytes, - flags, - timeout, cas, - &result_cas); - break; - case ADD_CMD: - rval= client->root->callback->interface.v1.add(client, key, - (uint16_t)nkey, - data, - (uint32_t)nbytes, - flags, - timeout, &result_cas); - break; - case CAS_CMD: - cas= strtoull(tokens[5], NULL, 10); - /* FALLTHROUGH */ - case REPLACE_CMD: - rval= client->root->callback->interface.v1.replace(client, key, - (uint16_t)nkey, - data, - (uint32_t)nbytes, - flags, - timeout, cas, - &result_cas); - break; - case APPEND_CMD: - rval= client->root->callback->interface.v1.append(client, key, - (uint16_t)nkey, - data, - (uint32_t)nbytes, - cas, - &result_cas); - break; - case PREPEND_CMD: - rval= client->root->callback->interface.v1.prepend(client, key, - (uint16_t)nkey, - data, - (uint32_t)nbytes, - cas, - &result_cas); - break; - - /* gcc complains if I don't put all of the enums in here.. */ - case GET_CMD: - case GETS_CMD: - case DELETE_CMD: - case DECR_CMD: - case INCR_CMD: - case STATS_CMD: - case FLUSH_ALL_CMD: - case VERSION_CMD: - case QUIT_CMD: - case VERBOSITY_CMD: - case UNKNOWN_CMD: - default: - abort(); /* impossible */ - } - - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) - { - spool_string(client, "STORED\r\n"); - } - else - { - if (client->ascii_command == CAS_CMD) - { - if (rval == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS) - { - spool_string(client, "EXISTS\r\n"); - } - else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT) - { - spool_string(client, "NOT_FOUND\r\n"); - } - else - { - spool_string(client, "NOT_STORED\r\n"); - } - } - else - { - spool_string(client, "NOT_STORED\r\n"); - } - } - - *end += nbytes + 2; - - return 0; -} - -static int process_cas_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - if (ntokens != 6) - { - send_command_usage(client); - return false; - } - - if (client->root->callback->interface.v1.replace == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return false; - } - - return process_storage_command(client, tokens, ntokens, start, end, length); -} - -static int process_set_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - if (ntokens != 5) - { - send_command_usage(client); - return false; - } - - if (client->root->callback->interface.v1.set == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return false; - } - - return process_storage_command(client, tokens, ntokens, start, end, length); -} - -static int process_add_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - if (ntokens != 5) - { - send_command_usage(client); - return false; - } - - if (client->root->callback->interface.v1.add == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return false; - } - - return process_storage_command(client, tokens, ntokens, start, end, length); -} - -static int process_replace_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - if (ntokens != 5) - { - send_command_usage(client); - return false; - } - - if (client->root->callback->interface.v1.replace == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return false; - } - - return process_storage_command(client, tokens, ntokens, start, end, length); -} - -static int process_append_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - if (ntokens != 5) - { - send_command_usage(client); - return false; - } - - if (client->root->callback->interface.v1.append == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return false; - } - - return process_storage_command(client, tokens, ntokens, start, end, length); -} - -static int process_prepend_command(memcached_protocol_client_st *client, - char **tokens, int ntokens, char *start, - char **end, ssize_t length) -{ - if (ntokens != 5) - { - send_command_usage(client); - return false; - } - - if (client->root->callback->interface.v1.prepend == NULL) - { - spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); - return false; - } - - return process_storage_command(client, tokens, ntokens, start, end, length); -} - -/** - * The ASCII protocol support is just one giant big hack. Instead of adding - * a optimal ascii support, I just convert the ASCII commands to the binary - * protocol and calls back into the command handlers for the binary protocol ;) - */ -memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr) -{ - char *ptr= (char*)client->root->input_buffer; - *endptr= ptr; - - do { - /* Do we have \n (indicating the command preamble)*/ - char *end= memchr(ptr, '\n', (size_t)*length); - if (end == NULL) - { - *endptr= ptr; - return MEMCACHED_PROTOCOL_READ_EVENT; - } - - client->ascii_command= ascii_to_cmd(ptr, (size_t)(*length)); - - /* A multiget lists all of the keys, and I don't want to have an - * avector of let's say 512 pointers to tokenize all of them, so let's - * just handle them immediately - */ - if (client->ascii_command == GET_CMD || - client->ascii_command == GETS_CMD) { - if (client->root->callback->interface.v1.get != NULL) - ascii_process_gets(client, ptr, end); - else - spool_string(client, "SERVER_ERROR: Command not implemented\n"); - } else { - /* None of the defined commands takes 10 parameters, so lets just use - * that as a maximum limit. - */ - char *tokens[10]; - int ntokens= ascii_tokenize_command(ptr, end, tokens, 10); - - if (ntokens < 10) - { - client->mute= strcmp(tokens[ntokens - 1], "noreply") == 0; - if (client->mute) - --ntokens; /* processed noreply token*/ - } - - int error= 0; - - switch (client->ascii_command) { - case SET_CMD: - error= process_set_command(client, tokens, ntokens, ptr, &end, *length); - break; - case ADD_CMD: - error= process_add_command(client, tokens, ntokens, ptr, &end, *length); - break; - case REPLACE_CMD: - error= process_replace_command(client, tokens, ntokens, - ptr, &end, *length); - break; - case CAS_CMD: - error= process_cas_command(client, tokens, ntokens, ptr, &end, *length); - break; - case APPEND_CMD: - error= process_append_command(client, tokens, ntokens, - ptr, &end, *length); - break; - case PREPEND_CMD: - error= process_prepend_command(client, tokens, ntokens, - ptr, &end, *length); - break; - case DELETE_CMD: - process_delete(client, tokens, ntokens); - break; - - case INCR_CMD: /* FALLTHROUGH */ - case DECR_CMD: - process_arithmetic(client, tokens, ntokens); - break; - case STATS_CMD: - if (client->mute) - { - send_command_usage(client); - } - else - { - recover_tokenize_command(ptr, end); - process_stats(client, ptr + 6, end); - } - break; - case FLUSH_ALL_CMD: - process_flush(client, tokens, ntokens); - break; - case VERSION_CMD: - if (client->mute) - { - send_command_usage(client); - } - else - { - process_version(client, tokens, ntokens); - } - break; - case QUIT_CMD: - if (ntokens != 1 || client->mute) - { - send_command_usage(client); - } - else - { - if (client->root->callback->interface.v1.quit != NULL) - client->root->callback->interface.v1.quit(client); - - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - break; - - case VERBOSITY_CMD: - if (ntokens != 2) - send_command_usage(client); - else - spool_string(client, "OK\r\n"); - break; - - case UNKNOWN_CMD: - send_command_usage(client); - break; - - case GET_CMD: - case GETS_CMD: - default: - /* Should already be handled */ - abort(); - } - - if (error == -1) - return MEMCACHED_PROTOCOL_ERROR_EVENT; - else if (error == 1) - return MEMCACHED_PROTOCOL_READ_EVENT; - } - - /* Move past \n */ - ++end; - *length -= end - ptr; - ptr= end; - } while (*length > 0); - - *endptr= ptr; - return MEMCACHED_PROTOCOL_READ_EVENT; -} diff --git a/libmemcached/protocol/ascii_handler.h b/libmemcached/protocol/ascii_handler.h deleted file mode 100644 index 02f8831e..00000000 --- a/libmemcached/protocol/ascii_handler.h +++ /dev/null @@ -1,40 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#pragma once - -LIBMEMCACHED_LOCAL -memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr); diff --git a/libmemcached/protocol/binary_handler.c b/libmemcached/protocol/binary_handler.c deleted file mode 100644 index 0997ebb0..00000000 --- a/libmemcached/protocol/binary_handler.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* -** ********************************************************************** -** INTERNAL INTERFACE -** ********************************************************************** -*/ - -/** - * Send a preformatted packet back to the client. If the connection is in - * pedantic mode, it will validate the packet and refuse to send it if it - * breaks the specification. - * - * @param cookie client identification - * @param request the original request packet - * @param response the packet to send - * @return The status of the operation - */ -static protocol_binary_response_status -raw_response_handler(const void *cookie, - protocol_binary_request_header *request, - protocol_binary_response_header *response) -{ - memcached_protocol_client_st *client= (void*)cookie; - - if (client->root->pedantic && - !memcached_binary_protocol_pedantic_check_response(request, response)) - { - return PROTOCOL_BINARY_RESPONSE_EINVAL; - } - - if (!client->root->drain(client)) - { - return PROTOCOL_BINARY_RESPONSE_EINTERNAL; - } - - size_t len= sizeof(*response) + htonl(response->response.bodylen); - size_t offset= 0; - char *ptr= (void*)response; - - if (client->output == NULL) - { - /* I can write directly to the socket.... */ - do - { - size_t num_bytes= len - offset; - ssize_t nw= client->root->send(client, - client->sock, - ptr + offset, - num_bytes); - if (nw == -1) - { - if (get_socket_errno() == EWOULDBLOCK) - { - break; - } - else if (get_socket_errno() != EINTR) - { - client->error= errno; - return PROTOCOL_BINARY_RESPONSE_EINTERNAL; - } - } - else - { - offset += (size_t)nw; - } - } while (offset < len); - } - - return client->root->spool(client, ptr, len - offset); -} - -/* - * Version 0 of the interface is really low level and protocol specific, - * while the version 1 of the interface is more API focused. We need a - * way to translate between the command codes on the wire and the - * application level interface in V1, so let's just use the V0 of the - * interface as a map instead of creating a huuuge switch :-) - */ - -/** - * Callback for the GET/GETQ/GETK and GETKQ responses - * @param cookie client identifier - * @param key the key for the item - * @param keylen the length of the key - * @param body the length of the body - * @param bodylen the length of the body - * @param flags the flags for the item - * @param cas the CAS id for the item - */ -static protocol_binary_response_status -get_response_handler(const void *cookie, - const void *key, - uint16_t keylen, - const void *body, - uint32_t bodylen, - uint32_t flags, - uint64_t cas) { - - memcached_protocol_client_st *client= (void*)cookie; - uint8_t opcode= client->current_command->request.opcode; - - if (opcode == PROTOCOL_BINARY_CMD_GET || opcode == PROTOCOL_BINARY_CMD_GETQ) - { - keylen= 0; - } - - protocol_binary_response_get response= { - .message.header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= opcode, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= client->current_command->request.opaque, - .cas= memcached_htonll(cas), - .keylen= htons(keylen), - .extlen= 4, - .bodylen= htonl(bodylen + keylen + 4), - }, - }; - - response.message.body.flags= htonl(flags); - - protocol_binary_response_status rval; - const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS; - if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success || - (rval= client->root->spool(client, key, keylen)) != success || - (rval= client->root->spool(client, body, bodylen)) != success) - { - return rval; - } - - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Callback for the STAT responses - * @param cookie client identifier - * @param key the key for the item - * @param keylen the length of the key - * @param body the length of the body - * @param bodylen the length of the body - */ -static protocol_binary_response_status stat_response_handler(const void *cookie, - const void *key, - uint16_t keylen, - const void *body, - uint32_t bodylen) -{ - - memcached_protocol_client_st *client= (void*)cookie; - - protocol_binary_response_no_extras response= { - .message.header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= client->current_command->request.opcode, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= client->current_command->request.opaque, - .keylen= htons(keylen), - .bodylen= htonl(bodylen + keylen), - .cas= 0 - }, - }; - - protocol_binary_response_status rval; - const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS; - if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success || - (rval= client->root->spool(client, key, keylen)) != success || - (rval= client->root->spool(client, body, bodylen)) != success) - { - return rval; - } - - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Callback for the VERSION responses - * @param cookie client identifier - * @param text the length of the body - * @param textlen the length of the body - */ -static protocol_binary_response_status -version_response_handler(const void *cookie, - const void *text, - uint32_t textlen) { - - memcached_protocol_client_st *client= (void*)cookie; - - protocol_binary_response_no_extras response= { - .message.header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= client->current_command->request.opcode, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= client->current_command->request.opaque, - .bodylen= htonl(textlen), - .cas= 0 - }, - }; - - protocol_binary_response_status rval; - const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS; - if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success || - (rval= client->root->spool(client, text, textlen)) != success) - { - return rval; - } - - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Callback for ADD and ADDQ - * @param cookie the calling client - * @param header the add/addq command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -add_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.add != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8; - protocol_binary_request_add *request= (void*)header; - uint32_t flags= ntohl(request->message.body.flags); - uint32_t timeout= ntohl(request->message.body.expiration); - char *key= ((char*)header) + sizeof(*header) + 8; - char *data= key + keylen; - uint64_t cas; - - rval= client->root->callback->interface.v1.add(cookie, key, keylen, - data, datalen, flags, - timeout, &cas); - - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_ADD) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_ADD, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(cas) - } - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for DECREMENT and DECREMENTQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -decrement_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.decrement != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - protocol_binary_request_decr *request= (void*)header; - uint64_t init= memcached_ntohll(request->message.body.initial); - uint64_t delta= memcached_ntohll(request->message.body.delta); - uint32_t timeout= ntohl(request->message.body.expiration); - void *key= request->bytes + sizeof(request->bytes); - uint64_t result; - uint64_t cas; - - rval= client->root->callback->interface.v1.decrement(cookie, key, keylen, - delta, init, timeout, - &result, &cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_DECREMENT) - { - /* Send a positive request */ - protocol_binary_response_decr response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_DECREMENT, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(cas), - .bodylen= htonl(8) - }, - .body.value= memcached_htonll(result) - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for DELETE and DELETEQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -delete_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.delete != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - void *key= (header +1); - uint64_t cas= memcached_ntohll(header->request.cas); - rval= client->root->callback->interface.v1.delete(cookie, key, keylen, cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_DELETE) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_DELETE, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - } - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for FLUSH and FLUSHQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -flush_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.flush != NULL) - { - protocol_binary_request_flush *flush= (void*)header; - uint32_t timeout= 0; - if (htonl(header->request.bodylen) == 4) - { - timeout= ntohl(flush->message.body.expiration); - } - - rval= client->root->callback->interface.v1.flush(cookie, timeout); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_FLUSH) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_FLUSH, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - } - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for GET, GETK, GETQ, GETKQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -get_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.get != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - void *key= (header + 1); - rval= client->root->callback->interface.v1.get(cookie, key, keylen, - get_response_handler); - - if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT && - (header->request.opcode == PROTOCOL_BINARY_CMD_GETQ || - header->request.opcode == PROTOCOL_BINARY_CMD_GETKQ)) - { - /* Quiet commands shouldn't respond on cache misses */ - rval= PROTOCOL_BINARY_RESPONSE_SUCCESS; - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for INCREMENT and INCREMENTQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -increment_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.increment != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - protocol_binary_request_incr *request= (void*)header; - uint64_t init= memcached_ntohll(request->message.body.initial); - uint64_t delta= memcached_ntohll(request->message.body.delta); - uint32_t timeout= ntohl(request->message.body.expiration); - void *key= request->bytes + sizeof(request->bytes); - uint64_t cas; - uint64_t result; - - rval= client->root->callback->interface.v1.increment(cookie, key, keylen, - delta, init, timeout, - &result, &cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENT) - { - /* Send a positive request */ - protocol_binary_response_incr response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_INCREMENT, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(cas), - .bodylen= htonl(8) - }, - .body.value= memcached_htonll(result) - } - }; - - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for noop. Inform the v1 interface about the noop packet, and - * create and send a packet back to the client - * - * @param cookie the calling client - * @param header the command - * @param response_handler the response handler - * @return the result of the operation - */ -static protocol_binary_response_status -noop_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.noop != NULL) - { - client->root->callback->interface.v1.noop(cookie); - } - - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_NOOP, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - } - } - }; - - return response_handler(cookie, header, (void*)&response); -} - -/** - * Callback for APPEND and APPENDQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -append_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.append != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - uint32_t datalen= ntohl(header->request.bodylen) - keylen; - char *key= (void*)(header +1); - char *data= key +keylen; - uint64_t cas= memcached_ntohll(header->request.cas); - uint64_t result_cas; - - rval= client->root->callback->interface.v1.append(cookie, key, keylen, - data, datalen, cas, - &result_cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_APPEND) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_APPEND, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(result_cas), - }, - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for PREPEND and PREPENDQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -prepend_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.prepend != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - uint32_t datalen= ntohl(header->request.bodylen) - keylen; - char *key= (char*)(header + 1); - char *data= key + keylen; - uint64_t cas= memcached_ntohll(header->request.cas); - uint64_t result_cas; - rval= client->root->callback->interface.v1.prepend(cookie, key, keylen, - data, datalen, cas, - &result_cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_PREPEND) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_PREPEND, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(result_cas), - }, - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for QUIT and QUITQ. Notify the client and shut down the connection - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -quit_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.quit != NULL) - { - client->root->callback->interface.v1.quit(cookie); - } - - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_QUIT, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque - } - } - }; - - if (header->request.opcode == PROTOCOL_BINARY_CMD_QUIT) - { - response_handler(cookie, header, (void*)&response); - } - - /* I need a better way to signal to close the connection */ - return PROTOCOL_BINARY_RESPONSE_EINTERNAL; -} - -/** - * Callback for REPLACE and REPLACEQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -replace_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.replace != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8; - protocol_binary_request_replace *request= (void*)header; - uint32_t flags= ntohl(request->message.body.flags); - uint32_t timeout= ntohl(request->message.body.expiration); - char *key= ((char*)header) + sizeof(*header) + 8; - char *data= key + keylen; - uint64_t cas= memcached_ntohll(header->request.cas); - uint64_t result_cas; - - rval= client->root->callback->interface.v1.replace(cookie, key, keylen, - data, datalen, flags, - timeout, cas, - &result_cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_REPLACE) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_REPLACE, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(result_cas), - }, - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for SET and SETQ - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -set_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.set != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8; - protocol_binary_request_replace *request= (void*)header; - uint32_t flags= ntohl(request->message.body.flags); - uint32_t timeout= ntohl(request->message.body.expiration); - char *key= ((char*)header) + sizeof(*header) + 8; - char *data= key + keylen; - uint64_t cas= memcached_ntohll(header->request.cas); - uint64_t result_cas; - - - rval= client->root->callback->interface.v1.set(cookie, key, keylen, - data, datalen, flags, - timeout, cas, &result_cas); - if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && - header->request.opcode == PROTOCOL_BINARY_CMD_SET) - { - /* Send a positive request */ - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= PROTOCOL_BINARY_CMD_SET, - .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), - .opaque= header->request.opaque, - .cas= memcached_ntohll(result_cas), - }, - } - }; - rval= response_handler(cookie, header, (void*)&response); - } - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for STAT - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -stat_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.stat != NULL) - { - uint16_t keylen= ntohs(header->request.keylen); - - rval= client->root->callback->interface.v1.stat(cookie, - (void*)(header + 1), - keylen, - stat_response_handler); - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * Callback for VERSION - * @param cookie the calling client - * @param header the command - * @param response_handler not used - * @return the result of the operation - */ -static protocol_binary_response_status -version_command_handler(const void *cookie, - protocol_binary_request_header *header, - memcached_binary_protocol_raw_response_handler response_handler) -{ - (void)response_handler; - (void)header; - protocol_binary_response_status rval; - - memcached_protocol_client_st *client= (void*)cookie; - if (client->root->callback->interface.v1.version != NULL) - { - rval= client->root->callback->interface.v1.version(cookie, - version_response_handler); - } - else - { - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - } - - return rval; -} - -/** - * The map to remap between the com codes and the v1 logical setting - */ -static memcached_binary_protocol_command_handler comcode_v0_v1_remap[256]= { - [PROTOCOL_BINARY_CMD_ADDQ]= add_command_handler, - [PROTOCOL_BINARY_CMD_ADD]= add_command_handler, - [PROTOCOL_BINARY_CMD_APPENDQ]= append_command_handler, - [PROTOCOL_BINARY_CMD_APPEND]= append_command_handler, - [PROTOCOL_BINARY_CMD_DECREMENTQ]= decrement_command_handler, - [PROTOCOL_BINARY_CMD_DECREMENT]= decrement_command_handler, - [PROTOCOL_BINARY_CMD_DELETEQ]= delete_command_handler, - [PROTOCOL_BINARY_CMD_DELETE]= delete_command_handler, - [PROTOCOL_BINARY_CMD_FLUSHQ]= flush_command_handler, - [PROTOCOL_BINARY_CMD_FLUSH]= flush_command_handler, - [PROTOCOL_BINARY_CMD_GETKQ]= get_command_handler, - [PROTOCOL_BINARY_CMD_GETK]= get_command_handler, - [PROTOCOL_BINARY_CMD_GETQ]= get_command_handler, - [PROTOCOL_BINARY_CMD_GET]= get_command_handler, - [PROTOCOL_BINARY_CMD_INCREMENTQ]= increment_command_handler, - [PROTOCOL_BINARY_CMD_INCREMENT]= increment_command_handler, - [PROTOCOL_BINARY_CMD_NOOP]= noop_command_handler, - [PROTOCOL_BINARY_CMD_PREPENDQ]= prepend_command_handler, - [PROTOCOL_BINARY_CMD_PREPEND]= prepend_command_handler, - [PROTOCOL_BINARY_CMD_QUITQ]= quit_command_handler, - [PROTOCOL_BINARY_CMD_QUIT]= quit_command_handler, - [PROTOCOL_BINARY_CMD_REPLACEQ]= replace_command_handler, - [PROTOCOL_BINARY_CMD_REPLACE]= replace_command_handler, - [PROTOCOL_BINARY_CMD_SETQ]= set_command_handler, - [PROTOCOL_BINARY_CMD_SET]= set_command_handler, - [PROTOCOL_BINARY_CMD_STAT]= stat_command_handler, - [PROTOCOL_BINARY_CMD_VERSION]= version_command_handler, -}; - -/** - * Try to execute a command. Fire the pre/post functions and the specialized - * handler function if it's set. If not, the unknown probe should be fired - * if it's present. - * @param client the client connection to operate on - * @param header the command to execute - * @return true if success or false if a fatal error occured so that the - * connection should be shut down. - */ -static protocol_binary_response_status execute_command(memcached_protocol_client_st *client, protocol_binary_request_header *header) -{ - if (client->root->pedantic && - memcached_binary_protocol_pedantic_check_request(header)) - { - /* @todo return invalid command packet */ - } - - /* we got all data available, execute the callback! */ - if (client->root->callback->pre_execute != NULL) - { - client->root->callback->pre_execute(client, header); - } - - protocol_binary_response_status rval; - rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - uint8_t cc= header->request.opcode; - - switch (client->root->callback->interface_version) - { - case 0: - if (client->root->callback->interface.v0.comcode[cc] != NULL) { - rval= client->root->callback->interface.v0.comcode[cc](client, header, raw_response_handler); - } - break; - case 1: - if (comcode_v0_v1_remap[cc] != NULL) { - rval= comcode_v0_v1_remap[cc](client, header, raw_response_handler); - } - break; - default: - /* Unknown interface. - * It should be impossible to get here so I'll just call abort - * to avoid getting a compiler warning :-) - */ - abort(); - } - - - if (rval == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND && - client->root->callback->unknown != NULL) - { - rval= client->root->callback->unknown(client, header, raw_response_handler); - } - - if (rval != PROTOCOL_BINARY_RESPONSE_SUCCESS && - rval != PROTOCOL_BINARY_RESPONSE_EINTERNAL && - rval != PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) - { - protocol_binary_response_no_extras response= { - .message= { - .header.response= { - .magic= PROTOCOL_BINARY_RES, - .opcode= cc, - .status= htons(rval), - .opaque= header->request.opaque, - }, - } - }; - rval= raw_response_handler(client, header, (void*)&response); - } - - if (client->root->callback->post_execute != NULL) - { - client->root->callback->post_execute(client, header); - } - - return rval; -} - -/* -** ********************************************************************** -** "PROTOECTED" INTERFACE -** ********************************************************************** -*/ -memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr) -{ - /* try to parse all of the received packets */ - protocol_binary_request_header *header; - header= (void*)client->root->input_buffer; - if (header->request.magic != (uint8_t)PROTOCOL_BINARY_REQ) - { - client->error= EINVAL; - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - ssize_t len= *length; - - while (len >= (ssize_t)sizeof(*header) && - (len >= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen)))) - { - /* I have the complete package */ - client->current_command= header; - protocol_binary_response_status rv= execute_command(client, header); - - if (rv == PROTOCOL_BINARY_RESPONSE_EINTERNAL) - { - *length= len; - *endptr= (void*)header; - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } else if (rv == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) - return MEMCACHED_PROTOCOL_PAUSE_EVENT; - - ssize_t total= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen)); - len -= total; - if (len > 0) - { - intptr_t ptr= (intptr_t)header; - ptr += total; - if ((ptr % 8) == 0) - { - header= (void*)ptr; - } - else - { - /* Fix alignment */ - memmove(client->root->input_buffer, (void*)ptr, (size_t)len); - header= (void*)client->root->input_buffer; - } - } - *length= len; - *endptr= (void*)header; - } - - return MEMCACHED_PROTOCOL_READ_EVENT; -} - -/* -** ********************************************************************** -** PUBLIC INTERFACE -** ********************************************************************** -*/ -memcached_binary_protocol_callback_st *memcached_binary_protocol_get_callbacks(memcached_protocol_st *instance) -{ - return instance->callback; -} - -void memcached_binary_protocol_set_callbacks(memcached_protocol_st *instance, memcached_binary_protocol_callback_st *callback) -{ - instance->callback= callback; -} - -memcached_binary_protocol_raw_response_handler memcached_binary_protocol_get_raw_response_handler(const void *cookie) -{ - (void)cookie; - return raw_response_handler; -} - -void memcached_binary_protocol_set_pedantic(memcached_protocol_st *instance, bool enable) -{ - instance->pedantic= enable; -} - -bool memcached_binary_protocol_get_pedantic(memcached_protocol_st *instance) -{ - return instance->pedantic; -} - diff --git a/libmemcached/protocol/binary_handler.h b/libmemcached/protocol/binary_handler.h deleted file mode 100644 index d5a74e78..00000000 --- a/libmemcached/protocol/binary_handler.h +++ /dev/null @@ -1,47 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#pragma once - -LIBMEMCACHED_LOCAL -bool memcached_binary_protocol_pedantic_check_request(const protocol_binary_request_header *request); - -LIBMEMCACHED_LOCAL -bool memcached_binary_protocol_pedantic_check_response(const protocol_binary_request_header *request, - const protocol_binary_response_header *response); - -LIBMEMCACHED_LOCAL -memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr); diff --git a/libmemcached/protocol/cache.c b/libmemcached/protocol/cache.c deleted file mode 100644 index f7e3bc06..00000000 --- a/libmemcached/protocol/cache.c +++ /dev/null @@ -1,187 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -#include -#include -#include - -#ifndef NDEBUG -#include -#endif - -#include - -#ifndef NDEBUG -const uint64_t redzone_pattern = 0xdeadbeefcafebabe; -int cache_error = 0; -#endif - -const size_t initial_pool_size = 64; - -cache_t* cache_create(const char *name, size_t bufsize, size_t align, - cache_constructor_t* constructor, - cache_destructor_t* destructor) { - cache_t* ret = calloc(1, sizeof(cache_t)); - size_t name_length= strlen(name); - char* nm= calloc(1, (sizeof(char) * name_length) +1); - memcpy(nm, name, name_length); - void** ptr = calloc(initial_pool_size, bufsize); - if (ret == NULL || nm == NULL || ptr == NULL || - pthread_mutex_init(&ret->mutex, NULL) == -1) { - free(ret); - free(nm); - free(ptr); - return NULL; - } - - ret->name = nm; - ret->ptr = ptr; - ret->freetotal = initial_pool_size; - ret->constructor = constructor; - ret->destructor = destructor; - -#ifndef NDEBUG - ret->bufsize = bufsize + 2 * sizeof(redzone_pattern); -#else - ret->bufsize = bufsize; -#endif - - (void)align; - - return ret; -} - -static inline void* get_object(void *ptr) { -#ifndef NDEBUG - uint64_t *pre = ptr; - return pre + 1; -#else - return ptr; -#endif -} - -void cache_destroy(cache_t *cache) { - while (cache->freecurr > 0) { - void *ptr = cache->ptr[--cache->freecurr]; - if (cache->destructor) { - cache->destructor(get_object(ptr), NULL); - } - free(ptr); - } - free(cache->name); - free(cache->ptr); - pthread_mutex_destroy(&cache->mutex); -} - -void* cache_alloc(cache_t *cache) { - void *ret; - void *object; - pthread_mutex_lock(&cache->mutex); - if (cache->freecurr > 0) { - ret = cache->ptr[--cache->freecurr]; - object = get_object(ret); - } else { - object = ret = malloc(cache->bufsize); - if (ret != NULL) { - object = get_object(ret); - - if (cache->constructor != NULL && - cache->constructor(object, NULL, 0) != 0) { - free(ret); - object = NULL; - } - } - } - pthread_mutex_unlock(&cache->mutex); - -#ifndef NDEBUG - if (object != NULL) { - /* add a simple form of buffer-check */ - uint64_t *pre = ret; - *pre = redzone_pattern; - ret = pre+1; - memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)), - &redzone_pattern, sizeof(redzone_pattern)); - } -#endif - - return object; -} - -void cache_free(cache_t *cache, void *ptr) { - pthread_mutex_lock(&cache->mutex); - -#ifndef NDEBUG - /* validate redzone... */ - if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)), - &redzone_pattern, sizeof(redzone_pattern)) != 0) { - raise(SIGABRT); - cache_error = 1; - pthread_mutex_unlock(&cache->mutex); - return; - } - uint64_t *pre = ptr; - --pre; - if (*pre != redzone_pattern) { - raise(SIGABRT); - cache_error = -1; - pthread_mutex_unlock(&cache->mutex); - return; - } - ptr = pre; -#endif - if (cache->freecurr < cache->freetotal) { - cache->ptr[cache->freecurr++] = ptr; - } else { - /* try to enlarge free connections array */ - size_t newtotal = cache->freetotal * 2; - void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal); - if (new_free) { - cache->freetotal = newtotal; - cache->ptr = new_free; - cache->ptr[cache->freecurr++] = ptr; - } else { - if (cache->destructor) { - cache->destructor(ptr, NULL); - } - free(ptr); - - } - } - pthread_mutex_unlock(&cache->mutex); -} - diff --git a/libmemcached/protocol/cache.h b/libmemcached/protocol/cache.h deleted file mode 100644 index 6f84feaa..00000000 --- a/libmemcached/protocol/cache.h +++ /dev/null @@ -1,148 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -#pragma once - -#include - -#ifdef HAVE_UMEM_H -# include -# define cache_t umem_cache_t -# define cache_alloc(a) umem_cache_alloc(a, UMEM_DEFAULT) -# define cache_free(a, b) umem_cache_free(a, b) -# define cache_create(a,b,c,d,e) umem_cache_create((char*)a, b, c, d, e, NULL, NULL, NULL, 0) -# define cache_destroy(a) umem_cache_destroy(a); -#else -# ifndef NDEBUG -/* may be used for debug purposes */ -extern int cache_error; -# endif - -/** - * Constructor used to initialize allocated objects - * - * @param obj pointer to the object to initialized. - * @param notused1 This parameter is currently not used. - * @param notused2 This parameter is currently not used. - * @return you should return 0, but currently this is not checked - */ -typedef int cache_constructor_t(void* obj, void* notused1, int notused2); -/** - * Destructor used to clean up allocated objects before they are - * returned to the operating system. - * - * @param obj pointer to the object to initialized. - * @param notused1 This parameter is currently not used. - * @param notused2 This parameter is currently not used. - * @return you should return 0, but currently this is not checked - */ -typedef void cache_destructor_t(void* obj, void* notused); - -/** - * Definition of the structure to keep track of the internal details of - * the cache allocator. Touching any of these variables results in - * undefined behavior. - */ -typedef struct { - /** Mutex to protect access to the structure */ - pthread_mutex_t mutex; - /** Name of the cache objects in this cache (provided by the caller) */ - char *name; - /** List of pointers to available buffers in this cache */ - void **ptr; - /** The size of each element in this cache */ - size_t bufsize; - /** The capacity of the list of elements */ - size_t freetotal; - /** The current number of free elements */ - size_t freecurr; - /** The constructor to be called each time we allocate more memory */ - cache_constructor_t* constructor; - /** The destructor to be called each time before we release memory */ - cache_destructor_t* destructor; -} cache_t; - -/** - * Create an object cache. - * - * The object cache will let you allocate objects of the same size. It is fully - * MT safe, so you may allocate objects from multiple threads without having to - * do any syncrhonization in the application code. - * - * @param name the name of the object cache. This name may be used for debug purposes - * and may help you track down what kind of object you have problems with - * (buffer overruns, leakage etc) - * @param bufsize the size of each object in the cache - * @param align the alignment requirements of the objects in the cache. - * @param constructor the function to be called to initialize memory when we need - * to allocate more memory from the os. - * @param destructor the function to be called before we release the memory back - * to the os. - * @return a handle to an object cache if successful, NULL otherwise. - */ -cache_t* cache_create(const char* name, size_t bufsize, size_t align, - cache_constructor_t* constructor, - cache_destructor_t* destructor); -/** - * Destroy an object cache. - * - * Destroy and invalidate an object cache. You should return all buffers allocated - * with cache_alloc by using cache_free before calling this function. Not doing - * so results in undefined behavior (the buffers may or may not be invalidated) - * - * @param handle the handle to the object cache to destroy. - */ -void cache_destroy(cache_t* handle); -/** - * Allocate an object from the cache. - * - * @param handle the handle to the object cache to allocate from - * @return a pointer to an initialized object from the cache, or NULL if - * the allocation cannot be satisfied. - */ -void* cache_alloc(cache_t* handle); -/** - * Return an object back to the cache. - * - * The caller should return the object in an initialized state so that - * the object may be returned in an expected state from cache_alloc. - * - * @param handle handle to the object cache to return the object to - * @param ptr pointer to the object to return. - */ -void cache_free(cache_t* handle, void* ptr); -#endif // HAVE_UMEM_H diff --git a/libmemcached/protocol/common.h b/libmemcached/protocol/common.h deleted file mode 100644 index e8df93e9..00000000 --- a/libmemcached/protocol/common.h +++ /dev/null @@ -1,162 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#pragma once - -#include "config.h" -#if !defined(__cplusplus) -# include -#endif -#include - -#include -#include - -/* - * I don't really need the following two functions as function pointers - * in the instance handle, but I don't want to put them in the global - * namespace for those linking statically (personally I don't like that, - * but some people still do). If it ever shows up as a performance thing - * I'll look into optimizing this ;-) - */ -typedef bool (*drain_func)(memcached_protocol_client_st *client); -typedef protocol_binary_response_status (*spool_func)(memcached_protocol_client_st *client, - const void *data, - size_t length); - -/** - * Definition of the per instance structure. - */ -struct memcached_protocol_st { - memcached_binary_protocol_callback_st *callback; - memcached_protocol_recv_func recv; - memcached_protocol_send_func send; - - /* - * I really don't need these as funciton pointers, but I don't want - * to clutter the namespace if someone links statically. - */ - drain_func drain; - spool_func spool; - - /* - * To avoid keeping a buffer in each client all the time I have a - * bigger buffer in the instance that I read to initially, and then - * I try to parse and execute as much from the buffer. If I wasn't able - * to process all data I'll keep that in a per-connection buffer until - * the next time I can read from the socket. - */ - uint8_t *input_buffer; - size_t input_buffer_size; - - bool pedantic; - /* @todo use multiple sized buffers */ - cache_t *buffer_cache; -}; - -struct chunk_st { - /* Pointer to the data */ - char *data; - /* The offset to the first byte into the buffer that is used */ - size_t offset; - /* The offset into the buffer for the first free byte */ - size_t nbytes; - /* The number of bytes in the buffer */ - size_t size; - /* Pointer to the next buffer in the chain */ - struct chunk_st *next; -}; - -#define CHUNK_BUFFERSIZE 2048 - -typedef memcached_protocol_event_t (*process_data)(struct memcached_protocol_client_st *client, ssize_t *length, void **endptr); - -enum ascii_cmd { - GET_CMD, - GETS_CMD, - SET_CMD, - ADD_CMD, - REPLACE_CMD, - CAS_CMD, - APPEND_CMD, - PREPEND_CMD, - DELETE_CMD, - INCR_CMD, - DECR_CMD, - STATS_CMD, - FLUSH_ALL_CMD, - VERSION_CMD, - QUIT_CMD, - VERBOSITY_CMD, - UNKNOWN_CMD -}; - -struct memcached_protocol_client_st { - memcached_protocol_st *root; - memcached_socket_t sock; - int error; - - /* Linked list of data to send */ - struct chunk_st *output; - struct chunk_st *output_tail; - - /* - * While we process input data, this is where we spool incomplete commands - * if we need to receive more data.... - * @todo use the buffercace - */ - uint8_t *input_buffer; - size_t input_buffer_size; - size_t input_buffer_offset; - - /* The callback to the protocol handler to use (ascii or binary) */ - process_data work; - - /* - * Should the spool data discard the data to send or not? (aka noreply in - * the ascii protocol.. - */ - bool mute; - - /* Members used by the binary protocol */ - protocol_binary_request_header *current_command; - - /* Members used by the ascii protocol */ - enum ascii_cmd ascii_command; -}; - -#include "ascii_handler.h" -#include "binary_handler.h" diff --git a/libmemcached/protocol/include.am b/libmemcached/protocol/include.am deleted file mode 100644 index d5bd9c46..00000000 --- a/libmemcached/protocol/include.am +++ /dev/null @@ -1,28 +0,0 @@ -# vim:ft=automake -# included from Top Level Makefile.am -# All paths should be given relative to the root - - -lib_LTLIBRARIES+= libmemcached/libmemcachedprotocol.la -libmemcached_libmemcachedprotocol_la_SOURCES= \ - libmemcached/byteorder.cc \ - libmemcached/protocol/ascii_handler.c \ - libmemcached/protocol/binary_handler.c \ - libmemcached/protocol/cache.c \ - libmemcached/protocol/pedantic.c \ - libmemcached/protocol/protocol_handler.c - -libmemcached_libmemcachedprotocol_la_CFLAGS= \ - ${AM_CFLAGS} \ - ${NO_CONVERSION} \ - -DBUILDING_LIBMEMCACHED -libmemcached_libmemcachedprotocol_la_CFLAGS+= ${PTHREAD_CFLAGS} - -libmemcached_libmemcachedprotocol_la_CXXFLAGS= \ - ${AM_CXXFLAGS} \ - -DBUILDING_LIBMEMCACHED -libmemcached_libmemcachedprotocol_la_CXXFLAGS+= ${PTHREAD_CFLAGS} - -libmemcached_libmemcachedprotocol_la_LIBADD= ${PTHREAD_LIBS} -libmemcached_libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} -libmemcached_libmemcachedprotocol_la_LDFLAGS+= -version-info ${MEMCACHED_PROTOCAL_LIBRARY_VERSION} diff --git a/libmemcached/protocol/pedantic.c b/libmemcached/protocol/pedantic.c deleted file mode 100644 index f275b39a..00000000 --- a/libmemcached/protocol/pedantic.c +++ /dev/null @@ -1,238 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include - -#include -#include - -#define ensure(a) if (!(a)) { return false; } - -bool memcached_binary_protocol_pedantic_check_request(const protocol_binary_request_header *request) -{ - ensure(request->request.magic == PROTOCOL_BINARY_REQ); - ensure(request->request.datatype == PROTOCOL_BINARY_RAW_BYTES); - - ensure(request->bytes[6] == 0); - ensure(request->bytes[7] == 0); - - uint8_t opcode= request->request.opcode; - uint16_t keylen= ntohs(request->request.keylen); - uint8_t extlen= request->request.extlen; - uint32_t bodylen= ntohl(request->request.bodylen); - - ensure(bodylen >= (keylen + extlen)); - - switch (opcode) { - case PROTOCOL_BINARY_CMD_GET: - case PROTOCOL_BINARY_CMD_GETK: - case PROTOCOL_BINARY_CMD_GETKQ: - case PROTOCOL_BINARY_CMD_GETQ: - ensure(extlen == 0); - ensure(keylen > 0); - ensure(keylen == bodylen); - ensure(request->request.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_ADD: - case PROTOCOL_BINARY_CMD_ADDQ: - /* it makes no sense to run add with a cas value */ - ensure(request->request.cas == 0); - /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_SET: - case PROTOCOL_BINARY_CMD_SETQ: - case PROTOCOL_BINARY_CMD_REPLACE: - case PROTOCOL_BINARY_CMD_REPLACEQ: - ensure(keylen > 0); - ensure(extlen == 8); - break; - - case PROTOCOL_BINARY_CMD_DELETE: - case PROTOCOL_BINARY_CMD_DELETEQ: - ensure(extlen == 0); - ensure(keylen > 0); - ensure(keylen == bodylen); - break; - - case PROTOCOL_BINARY_CMD_INCREMENT: - case PROTOCOL_BINARY_CMD_INCREMENTQ: - case PROTOCOL_BINARY_CMD_DECREMENT: - case PROTOCOL_BINARY_CMD_DECREMENTQ: - ensure(extlen == 20); - ensure(keylen > 0); - ensure(keylen + extlen == bodylen); - break; - - case PROTOCOL_BINARY_CMD_QUIT: - case PROTOCOL_BINARY_CMD_QUITQ: - case PROTOCOL_BINARY_CMD_NOOP: - case PROTOCOL_BINARY_CMD_VERSION: - ensure(extlen == 0); - ensure(keylen == 0); - ensure(bodylen == 0); - break; - - case PROTOCOL_BINARY_CMD_FLUSH: - case PROTOCOL_BINARY_CMD_FLUSHQ: - ensure(extlen == 0 || extlen == 4); - ensure(keylen == 0); - ensure(bodylen == extlen); - break; - - case PROTOCOL_BINARY_CMD_STAT: - ensure(extlen == 0); - /* May have key, but not value */ - ensure(keylen == bodylen); - break; - - case PROTOCOL_BINARY_CMD_APPEND: - case PROTOCOL_BINARY_CMD_APPENDQ: - case PROTOCOL_BINARY_CMD_PREPEND: - case PROTOCOL_BINARY_CMD_PREPENDQ: - ensure(extlen == 0); - ensure(keylen > 0); - break; - default: - /* Unknown command */ - ; - } - - return true; -} - -bool memcached_binary_protocol_pedantic_check_response(const protocol_binary_request_header *request, - const protocol_binary_response_header *response) -{ - ensure(response->response.magic == PROTOCOL_BINARY_RES); - ensure(response->response.datatype == PROTOCOL_BINARY_RAW_BYTES); - ensure(response->response.opaque == request->request.opaque); - - uint16_t status= ntohs(response->response.status); - uint8_t opcode= response->response.opcode; - - if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) - { - switch (opcode) { - case PROTOCOL_BINARY_CMD_ADDQ: - case PROTOCOL_BINARY_CMD_APPENDQ: - case PROTOCOL_BINARY_CMD_DECREMENTQ: - case PROTOCOL_BINARY_CMD_DELETEQ: - case PROTOCOL_BINARY_CMD_FLUSHQ: - case PROTOCOL_BINARY_CMD_INCREMENTQ: - case PROTOCOL_BINARY_CMD_PREPENDQ: - case PROTOCOL_BINARY_CMD_QUITQ: - case PROTOCOL_BINARY_CMD_REPLACEQ: - case PROTOCOL_BINARY_CMD_SETQ: - /* Quiet command shouldn't return on success */ - return false; - default: - break; - } - - switch (opcode) { - case PROTOCOL_BINARY_CMD_ADD: - case PROTOCOL_BINARY_CMD_REPLACE: - case PROTOCOL_BINARY_CMD_SET: - case PROTOCOL_BINARY_CMD_APPEND: - case PROTOCOL_BINARY_CMD_PREPEND: - ensure(response->response.keylen == 0); - ensure(response->response.extlen == 0); - ensure(response->response.bodylen == 0); - ensure(response->response.cas != 0); - break; - case PROTOCOL_BINARY_CMD_FLUSH: - case PROTOCOL_BINARY_CMD_NOOP: - case PROTOCOL_BINARY_CMD_QUIT: - case PROTOCOL_BINARY_CMD_DELETE: - ensure(response->response.keylen == 0); - ensure(response->response.extlen == 0); - ensure(response->response.bodylen == 0); - ensure(response->response.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_DECREMENT: - case PROTOCOL_BINARY_CMD_INCREMENT: - ensure(response->response.keylen == 0); - ensure(response->response.extlen == 0); - ensure(ntohl(response->response.bodylen) == 8); - ensure(response->response.cas != 0); - break; - - case PROTOCOL_BINARY_CMD_STAT: - ensure(response->response.extlen == 0); - /* key and value exists in all packets except in the terminating */ - ensure(response->response.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_VERSION: - ensure(response->response.keylen == 0); - ensure(response->response.extlen == 0); - ensure(response->response.bodylen != 0); - ensure(response->response.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_GET: - case PROTOCOL_BINARY_CMD_GETQ: - ensure(response->response.keylen == 0); - ensure(response->response.extlen == 4); - ensure(response->response.cas != 0); - break; - - case PROTOCOL_BINARY_CMD_GETK: - case PROTOCOL_BINARY_CMD_GETKQ: - ensure(response->response.keylen != 0); - ensure(response->response.extlen == 4); - ensure(response->response.cas != 0); - break; - - default: - /* Undefined command code */ - break; - } - } - else - { - ensure(response->response.cas == 0); - ensure(response->response.extlen == 0); - if (opcode != PROTOCOL_BINARY_CMD_GETK) - { - ensure(response->response.keylen == 0); - } - } - - return true; -} diff --git a/libmemcached/protocol/protocol_handler.c b/libmemcached/protocol/protocol_handler.c deleted file mode 100644 index 99b4ff2f..00000000 --- a/libmemcached/protocol/protocol_handler.c +++ /dev/null @@ -1,401 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* -** ********************************************************************** -** INTERNAL INTERFACE -** ********************************************************************** -*/ - -/** - * The default function to receive data from the client. This function - * just wraps the recv function to receive from a socket. - * See man -s3socket recv for more information. - * - * @param cookie cookie indentifying a client, not used - * @param sock socket to read from - * @param buf the destination buffer - * @param nbytes the number of bytes to read - * @return the number of bytes transferred of -1 upon error - */ -static ssize_t default_recv(const void *cookie, - memcached_socket_t sock, - void *buf, - size_t nbytes) -{ - (void)cookie; - return recv(sock, buf, nbytes, 0); -} - -/** - * The default function to send data to the server. This function - * just wraps the send function to send through a socket. - * See man -s3socket send for more information. - * - * @param cookie cookie indentifying a client, not used - * @param sock socket to send to - * @param buf the source buffer - * @param nbytes the number of bytes to send - * @return the number of bytes transferred of -1 upon error - */ -static ssize_t default_send(const void *cookie, - memcached_socket_t fd, - const void *buf, - size_t nbytes) -{ - (void)cookie; - return send(fd, buf, nbytes, 0); -} - -/** - * Try to drain the output buffers without blocking - * - * @param client the client to drain - * @return false if an error occured (connection should be shut down) - * true otherwise (please note that there may be more data to - * left in the buffer to send) - */ -static bool drain_output(struct memcached_protocol_client_st *client) -{ - ssize_t len; - - /* Do we have pending data to send? */ - while (client->output != NULL) - { - len= client->root->send(client, - client->sock, - client->output->data + client->output->offset, - client->output->nbytes - client->output->offset); - - if (len == -1) - { - if (get_socket_errno() == EWOULDBLOCK) - { - return true; - } - else if (get_socket_errno() != EINTR) - { - client->error= get_socket_errno(); - return false; - } - } - else - { - client->output->offset += (size_t)len; - if (client->output->offset == client->output->nbytes) - { - /* This was the complete buffer */ - struct chunk_st *old= client->output; - client->output= client->output->next; - if (client->output == NULL) - { - client->output_tail= NULL; - } - cache_free(client->root->buffer_cache, old); - } - } - } - - return true; -} - -/** - * Allocate an output buffer and chain it into the output list - * - * @param client the client that needs the buffer - * @return pointer to the new chunk if the allocation succeeds, NULL otherwise - */ -static struct chunk_st *allocate_output_chunk(struct memcached_protocol_client_st *client) -{ - struct chunk_st *ret= cache_alloc(client->root->buffer_cache); - - if (ret == NULL) - { - return NULL; - } - - ret->offset= ret->nbytes= 0; - ret->next= NULL; - ret->size= CHUNK_BUFFERSIZE; - ret->data= (void*)(ret + 1); - if (client->output == NULL) - { - client->output= client->output_tail= ret; - } - else - { - client->output_tail->next= ret; - client->output_tail= ret; - } - - return ret; -} - -/** - * Spool data into the send-buffer for a client. - * - * @param client the client to spool the data for - * @param data the data to spool - * @param length the number of bytes of data to spool - * @return PROTOCOL_BINARY_RESPONSE_SUCCESS if success, - * PROTOCOL_BINARY_RESPONSE_ENOMEM if we failed to allocate memory - */ -static protocol_binary_response_status spool_output(struct memcached_protocol_client_st *client, - const void *data, - size_t length) -{ - if (client->mute) - { - return PROTOCOL_BINARY_RESPONSE_SUCCESS; - } - - size_t offset= 0; - - struct chunk_st *chunk= client->output; - while (offset < length) - { - if (chunk == NULL || (chunk->size - chunk->nbytes) == 0) - { - if ((chunk= allocate_output_chunk(client)) == NULL) - { - return PROTOCOL_BINARY_RESPONSE_ENOMEM; - } - } - - size_t bulk= length - offset; - if (bulk > chunk->size - chunk->nbytes) - { - bulk= chunk->size - chunk->nbytes; - } - - memcpy(chunk->data + chunk->nbytes, data, bulk); - chunk->nbytes += bulk; - offset += bulk; - } - - return PROTOCOL_BINARY_RESPONSE_SUCCESS; -} - -/** - * Try to determine the protocol used on this connection. - * If the first byte contains the magic byte PROTOCOL_BINARY_REQ we should - * be using the binary protocol on the connection. I implemented the support - * for the ASCII protocol by wrapping into the simple interface (aka v1), - * so the implementors needs to provide an implementation of that interface - * - */ -static memcached_protocol_event_t determine_protocol(struct memcached_protocol_client_st *client, ssize_t *length, void **endptr) -{ - if (*client->root->input_buffer == (uint8_t)PROTOCOL_BINARY_REQ) - { - client->work= memcached_binary_protocol_process_data; - } - else if (client->root->callback->interface_version == 1) - { - /* - * The ASCII protocol can only be used if the implementors provide - * an implementation for the version 1 of the interface.. - * - * @todo I should allow the implementors to provide an implementation - * for version 0 and 1 at the same time and set the preferred - * interface to use... - */ - client->work= memcached_ascii_protocol_process_data; - } - else - { - /* Let's just output a warning the way it is supposed to look like - * in the ASCII protocol... - */ - const char *err= "CLIENT_ERROR: Unsupported protocol\r\n"; - client->root->spool(client, err, strlen(err)); - client->root->drain(client); - return MEMCACHED_PROTOCOL_ERROR_EVENT; /* Unsupported protocol */ - } - - return client->work(client, length, endptr); -} - -/* -** ********************************************************************** -** * PUBLIC INTERFACE -** * See protocol_handler.h for function description -** ********************************************************************** -*/ -struct memcached_protocol_st *memcached_protocol_create_instance(void) -{ - struct memcached_protocol_st *ret= calloc(1, sizeof(*ret)); - if (ret != NULL) - { - ret->recv= default_recv; - ret->send= default_send; - ret->drain= drain_output; - ret->spool= spool_output; - ret->input_buffer_size= 1 * 1024 * 1024; - ret->input_buffer= malloc(ret->input_buffer_size); - if (ret->input_buffer == NULL) - { - free(ret); - ret= NULL; - return NULL; - } - - ret->buffer_cache= cache_create("protocol_handler", - CHUNK_BUFFERSIZE + sizeof(struct chunk_st), - 0, NULL, NULL); - if (ret->buffer_cache == NULL) - { - free(ret->input_buffer); - free(ret); - } - } - - return ret; -} - -void memcached_protocol_destroy_instance(struct memcached_protocol_st *instance) -{ - cache_destroy(instance->buffer_cache); - free(instance->input_buffer); - free(instance); -} - -struct memcached_protocol_client_st *memcached_protocol_create_client(struct memcached_protocol_st *instance, memcached_socket_t sock) -{ - struct memcached_protocol_client_st *ret= calloc(1, sizeof(*ret)); - if (ret != NULL) - { - ret->root= instance; - ret->sock= sock; - ret->work= determine_protocol; - } - - return ret; -} - -void memcached_protocol_client_destroy(struct memcached_protocol_client_st *client) -{ - free(client); -} - -memcached_protocol_event_t memcached_protocol_client_work(struct memcached_protocol_client_st *client) -{ - /* Try to send data and read from the socket */ - bool more_data= true; - do - { - ssize_t len= client->root->recv(client, - client->sock, - client->root->input_buffer + client->input_buffer_offset, - client->root->input_buffer_size - client->input_buffer_offset); - - if (len > 0) - { - /* Do we have the complete packet? */ - if (client->input_buffer_offset > 0) - { - memcpy(client->root->input_buffer, client->input_buffer, - client->input_buffer_offset); - len += (ssize_t)client->input_buffer_offset; - - /* @todo use buffer-cache! */ - free(client->input_buffer); - client->input_buffer_offset= 0; - } - - void *endptr; - memcached_protocol_event_t events= client->work(client, &len, &endptr); - if (events == MEMCACHED_PROTOCOL_ERROR_EVENT) - { - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - - if (len > 0) - { - /* save the data for later on */ - /* @todo use buffer-cache */ - client->input_buffer= malloc((size_t)len); - if (client->input_buffer == NULL) - { - client->error= ENOMEM; - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - memcpy(client->input_buffer, endptr, (size_t)len); - client->input_buffer_offset= (size_t)len; - more_data= false; - } - } - else if (len == 0) - { - /* Connection closed */ - drain_output(client); - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - else - { - if (get_socket_errno() != EWOULDBLOCK) - { - client->error= get_socket_errno(); - /* mark this client as terminated! */ - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - more_data= false; - } - } while (more_data); - - if (!drain_output(client)) - { - return MEMCACHED_PROTOCOL_ERROR_EVENT; - } - - memcached_protocol_event_t ret= MEMCACHED_PROTOCOL_READ_EVENT; - if (client->output) - ret|= MEMCACHED_PROTOCOL_READ_EVENT; - - return ret; -} diff --git a/libmemcached/util/flush.cc b/libmemcached/util/flush.cc deleted file mode 100644 index 17131f7f..00000000 --- a/libmemcached/util/flush.cc +++ /dev/null @@ -1,61 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Summary: connects to a host, and then flushes it (memcached_flush(3)). - * - */ - -#include -#include - - -bool libmemcached_util_flush(const char *hostname, in_port_t port, memcached_return_t *ret) -{ - memcached_st *memc_ptr= memcached_create(NULL); - - memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); - if (memcached_success(rc)) - { - rc= memcached_flush(memc_ptr, 0); - } - - memcached_free(memc_ptr); - - if (ret) - { - *ret= rc; - } - - return memcached_success(rc); -} diff --git a/libmemcached/util/include.am b/libmemcached/util/include.am deleted file mode 100644 index 3aa95a5a..00000000 --- a/libmemcached/util/include.am +++ /dev/null @@ -1,23 +0,0 @@ -# vim:ft=automake -# included from Top Level Makefile.am -# All paths should be given relative to the root - -lib_LTLIBRARIES+= libmemcached/libmemcachedutil.la - -libmemcached_libmemcachedutil_la_SOURCES= \ - libmemcached/backtrace.cc \ - libmemcached/util/flush.cc \ - libmemcached/util/pid.cc \ - libmemcached/util/ping.cc \ - libmemcached/util/pool.cc \ - libmemcached/util/version.cc -libmemcached_libmemcachedutil_la_CXXFLAGS= \ - ${AM_CXXFLAGS} \ - ${NO_CONVERSION} \ - -DBUILDING_LIBMEMCACHED -libmemcached_libmemcachedutil_la_CXXFLAGS+= ${PTHREAD_CFLAGS} -libmemcached_libmemcachedutil_la_LIBADD= libmemcached/libmemcached.la -libmemcached_libmemcachedutil_la_LIBADD+= ${PTHREAD_LIBS} -libmemcached_libmemcachedutil_la_LDFLAGS= ${AM_LDFLAGS} -version-info ${MEMCACHED_UTIL_LIBRARY_VERSION} -libmemcached_libmemcachedutil_la_DEPENDENCIES= libmemcached/libmemcached.la - diff --git a/libmemcached/util/pid.cc b/libmemcached/util/pid.cc deleted file mode 100644 index 0ac3d4f3..00000000 --- a/libmemcached/util/pid.cc +++ /dev/null @@ -1,161 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2010 Brian Aker All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Summary: connects to a host, and determines what its pid is - * - */ - -#include -#include - - -// Never look at the stat object directly. - - -pid_t libmemcached_util_getpid(const char *hostname, in_port_t port, memcached_return_t *ret) -{ - pid_t pid= -1; - - memcached_return_t unused; - if (not ret) - ret= &unused; - - memcached_st *memc_ptr= memcached_create(NULL); - if (not memc_ptr) - { - *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return -1; - } - - memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); - if (memcached_success(rc)) - { - memcached_stat_st *stat= memcached_stat(memc_ptr, NULL, &rc); - if (memcached_success(rc) and stat and stat->pid != -1) - { - pid= stat->pid; - } - else if (memcached_success(rc)) - { - rc= MEMCACHED_UNKNOWN_STAT_KEY; // Something went wrong if this happens - } - else if (rc == MEMCACHED_SOME_ERRORS) // Generic answer, we will now find the specific reason (if one exists) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(memc_ptr, 0); - - assert_msg(instance and instance->error_messages, " "); - if (instance and instance->error_messages) - { - rc= memcached_server_error_return(instance); - } - } - - memcached_stat_free(memc_ptr, stat); - } - memcached_free(memc_ptr); - - *ret= rc; - - return pid; -} - -pid_t libmemcached_util_getpid2(const char *hostname, in_port_t port, const char *username, const char *password, memcached_return_t *ret) -{ - if (username == NULL) - { - return libmemcached_util_getpid(hostname, port, ret); - } - - pid_t pid= -1; - - memcached_return_t unused; - if (not ret) - ret= &unused; - - if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0) - { - *ret= MEMCACHED_NOT_SUPPORTED; - return pid; - } - - memcached_st *memc_ptr= memcached_create(NULL); - if (not memc_ptr) - { - *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return -1; - } - - if (memcached_failed(*ret= memcached_set_sasl_auth_data(memc_ptr, username, password))) - { - memcached_free(memc_ptr); - return false; - } - - - memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); - if (memcached_success(rc)) - { - memcached_stat_st *stat= memcached_stat(memc_ptr, NULL, &rc); - if (memcached_success(rc) and stat and stat->pid != -1) - { - pid= stat->pid; - } - else if (memcached_success(rc)) - { - rc= MEMCACHED_UNKNOWN_STAT_KEY; // Something went wrong if this happens - } - else if (rc == MEMCACHED_SOME_ERRORS) // Generic answer, we will now find the specific reason (if one exists) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(memc_ptr, 0); - -#if 0 - assert_msg(instance and instance->error_messages, " "); -#endif - if (instance and instance->error_messages) - { - rc= memcached_server_error_return(instance); - } - } - - memcached_stat_free(memc_ptr, stat); - } - memcached_free(memc_ptr); - - *ret= rc; - - return pid; -} diff --git a/libmemcached/util/ping.cc b/libmemcached/util/ping.cc deleted file mode 100644 index 66b1df77..00000000 --- a/libmemcached/util/ping.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2010 Brian Aker All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Summary: connects to a host, and makes sure it is alive. - * - */ - -#include -#include - -bool libmemcached_util_ping(const char *hostname, in_port_t port, memcached_return_t *ret) -{ - memcached_return_t unused; - if (not ret) - ret= &unused; - - memcached_st *memc_ptr= memcached_create(NULL); - if (not memc_ptr) - { - *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return false; - } - - memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); - if (memcached_success(rc)) - { - rc= memcached_version(memc_ptr); - } - - if (memcached_failed(rc) and rc == MEMCACHED_SOME_ERRORS) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(memc_ptr, 0); - - assert_msg(instance and instance->error_messages, " "); - if (instance and instance->error_messages) - { - rc= memcached_server_error_return(instance); - } - } - memcached_free(memc_ptr); - - *ret= rc; - - return memcached_success(rc); -} - -bool libmemcached_util_ping2(const char *hostname, in_port_t port, const char *username, const char *password, memcached_return_t *ret) -{ - if (username == NULL) - { - return libmemcached_util_ping(hostname, port, ret); - } - - memcached_return_t unused; - if (not ret) - ret= &unused; - - if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0) - { - *ret= MEMCACHED_NOT_SUPPORTED; - return false; - } - - memcached_st *memc_ptr= memcached_create(NULL); - if (not memc_ptr) - { - *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return false; - } - - if (memcached_failed(*ret= memcached_set_sasl_auth_data(memc_ptr, username, password))) - { - memcached_free(memc_ptr); - return false; - } - - memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); - if (memcached_success(rc)) - { - rc= memcached_version(memc_ptr); - } - - if (memcached_failed(rc) and rc == MEMCACHED_SOME_ERRORS) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(memc_ptr, 0); - - assert_msg(instance and instance->error_messages, " "); - if (instance and instance->error_messages) - { - rc= memcached_server_error_return(instance); - } - } - memcached_free(memc_ptr); - - *ret= rc; - - return memcached_success(rc); -} diff --git a/libmemcached/util/pool.cc b/libmemcached/util/pool.cc deleted file mode 100644 index 47f46eec..00000000 --- a/libmemcached/util/pool.cc +++ /dev/null @@ -1,485 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2010 Brian Aker All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - - -#include -#include - -#include - -#include -#include -#include -#include - -struct memcached_pool_st -{ - pthread_mutex_t mutex; - pthread_cond_t cond; - memcached_st *master; - memcached_st **server_pool; - int firstfree; - const uint32_t size; - uint32_t current_size; - bool _owns_master; - struct timespec _timeout; - - memcached_pool_st(memcached_st *master_arg, size_t max_arg) : - master(master_arg), - server_pool(NULL), - firstfree(-1), - size(max_arg), - current_size(0), - _owns_master(false) - { - pthread_mutex_init(&mutex, NULL); - pthread_cond_init(&cond, NULL); - _timeout.tv_sec= 5; - _timeout.tv_nsec= 0; - } - - const struct timespec& timeout() const - { - return _timeout; - } - - bool release(memcached_st*, memcached_return_t& rc); - - memcached_st *fetch(memcached_return_t& rc); - memcached_st *fetch(const struct timespec&, memcached_return_t& rc); - - bool init(uint32_t initial); - - ~memcached_pool_st() - { - for (int x= 0; x <= firstfree; ++x) - { - memcached_free(server_pool[x]); - server_pool[x] = NULL; - } - - pthread_mutex_destroy(&mutex); - pthread_cond_destroy(&cond); - delete [] server_pool; - if (_owns_master) - { - memcached_free(master); - } - } - - void increment_version() - { - ++master->configure.version; - } - - bool compare_version(const memcached_st *arg) const - { - return (arg->configure.version == version()); - } - - int32_t version() const - { - return master->configure.version; - } -}; - - -/** - * Grow the connection pool by creating a connection structure and clone the - * original memcached handle. - */ -static bool grow_pool(memcached_pool_st* pool) -{ - assert(pool); - - memcached_st *obj; - if (not (obj= memcached_clone(NULL, pool->master))) - { - return false; - } - - pool->server_pool[++pool->firstfree]= obj; - pool->current_size++; - obj->configure.version= pool->version(); - - return true; -} - -bool memcached_pool_st::init(uint32_t initial) -{ - server_pool= new (std::nothrow) memcached_st *[size]; - if (not server_pool) - return false; - - /* - Try to create the initial size of the pool. An allocation failure at - this time is not fatal.. - */ - for (unsigned int x= 0; x < initial; ++x) - { - if (grow_pool(this) == false) - { - break; - } - } - - return true; -} - - -static inline memcached_pool_st *_pool_create(memcached_st* master, uint32_t initial, uint32_t max) -{ - if (initial == 0 or max == 0 or (initial > max)) - { - return NULL; - } - - memcached_pool_st *object= new (std::nothrow) memcached_pool_st(master, max); - if (object == NULL) - { - return NULL; - } - - /* - Try to create the initial size of the pool. An allocation failure at - this time is not fatal.. - */ - if (not object->init(initial)) - { - delete object; - return NULL; - } - - return object; -} - -memcached_pool_st *memcached_pool_create(memcached_st* master, uint32_t initial, uint32_t max) -{ - return _pool_create(master, initial, max); -} - -memcached_pool_st * memcached_pool(const char *option_string, size_t option_string_length) -{ - memcached_st *memc= memcached(option_string, option_string_length); - - if (memc == NULL) - { - return NULL; - } - - memcached_pool_st *self= memcached_pool_create(memc, memc->configure.initial_pool_size, memc->configure.max_pool_size); - if (self == NULL) - { - memcached_free(memc); - return NULL; - } - - self->_owns_master= true; - - return self; -} - -memcached_st* memcached_pool_destroy(memcached_pool_st* pool) -{ - if (pool == NULL) - { - return NULL; - } - - // Legacy that we return the original structure - memcached_st *ret= NULL; - if (pool->_owns_master) - { } - else - { - ret= pool->master; - } - - delete pool; - - return ret; -} - -memcached_st* memcached_pool_st::fetch(memcached_return_t& rc) -{ - static struct timespec relative_time= { 0, 0 }; - return fetch(relative_time, rc); -} - -memcached_st* memcached_pool_st::fetch(const struct timespec& relative_time, memcached_return_t& rc) -{ - rc= MEMCACHED_SUCCESS; - - if (pthread_mutex_lock(&mutex)) - { - rc= MEMCACHED_IN_PROGRESS; - return NULL; - } - - memcached_st *ret= NULL; - do - { - if (firstfree > -1) - { - ret= server_pool[firstfree--]; - } - else if (current_size == size) - { - if (relative_time.tv_sec == 0 and relative_time.tv_nsec == 0) - { - pthread_mutex_unlock(&mutex); - rc= MEMCACHED_NOTFOUND; - - return NULL; - } - - struct timespec time_to_wait= {0, 0}; - time_to_wait.tv_sec= time(NULL) +relative_time.tv_sec; - time_to_wait.tv_nsec= relative_time.tv_nsec; - - int thread_ret; - if ((thread_ret= pthread_cond_timedwait(&cond, &mutex, &time_to_wait)) != 0) - { - pthread_mutex_unlock(&mutex); - - if (thread_ret == ETIMEDOUT) - { - rc= MEMCACHED_TIMEOUT; - } - else - { - errno= thread_ret; - rc= MEMCACHED_ERRNO; - } - - return NULL; - } - } - else if (grow_pool(this) == false) - { - (void)pthread_mutex_unlock(&mutex); - return NULL; - } - } while (ret == NULL); - - pthread_mutex_unlock(&mutex); - - return ret; -} - -bool memcached_pool_st::release(memcached_st *released, memcached_return_t& rc) -{ - rc= MEMCACHED_SUCCESS; - if (released == NULL) - { - rc= MEMCACHED_INVALID_ARGUMENTS; - return false; - } - - if (pthread_mutex_lock(&mutex)) - { - rc= MEMCACHED_IN_PROGRESS; - return false; - } - - /* - Someone updated the behavior on the object, so we clone a new memcached_st with the new settings. If we fail to clone, we keep the old one around. - */ - if (compare_version(released) == false) - { - memcached_st *memc; - if ((memc= memcached_clone(NULL, master))) - { - memcached_free(released); - released= memc; - } - } - - server_pool[++firstfree]= released; - - if (firstfree == 0 and current_size == size) - { - /* we might have people waiting for a connection.. wake them up :-) */ - pthread_cond_broadcast(&cond); - } - - (void)pthread_mutex_unlock(&mutex); - - return true; -} - -memcached_st* memcached_pool_fetch(memcached_pool_st* pool, struct timespec* relative_time, memcached_return_t* rc) -{ - if (pool == NULL) - { - return NULL; - } - - memcached_return_t unused; - if (rc == NULL) - { - rc= &unused; - } - - if (relative_time == NULL) - { - return pool->fetch(*rc); - } - - return pool->fetch(*relative_time, *rc); -} - -memcached_st* memcached_pool_pop(memcached_pool_st* pool, - bool block, - memcached_return_t *rc) -{ - if (pool == NULL) - { - return NULL; - } - - memcached_return_t unused; - if (rc == NULL) - { - rc= &unused; - } - - memcached_st *memc; - if (block) - { - memc= pool->fetch(pool->timeout(), *rc); - } - else - { - memc= pool->fetch(*rc); - } - - return memc; -} - -memcached_return_t memcached_pool_release(memcached_pool_st* pool, memcached_st *released) -{ - if (pool == NULL) - { - return MEMCACHED_INVALID_ARGUMENTS; - } - - memcached_return_t rc; - - (void) pool->release(released, rc); - - return rc; -} - -memcached_return_t memcached_pool_push(memcached_pool_st* pool, memcached_st *released) -{ - return memcached_pool_release(pool, released); -} - - -memcached_return_t memcached_pool_behavior_set(memcached_pool_st *pool, - memcached_behavior_t flag, - uint64_t data) -{ - if (pool == NULL) - { - return MEMCACHED_INVALID_ARGUMENTS; - } - - if (pthread_mutex_lock(&pool->mutex)) - { - return MEMCACHED_IN_PROGRESS; - } - - /* update the master */ - memcached_return_t rc= memcached_behavior_set(pool->master, flag, data); - if (memcached_failed(rc)) - { - (void)pthread_mutex_unlock(&pool->mutex); - return rc; - } - - pool->increment_version(); - /* update the clones */ - for (int xx= 0; xx <= pool->firstfree; ++xx) - { - if (memcached_success(memcached_behavior_set(pool->server_pool[xx], flag, data))) - { - pool->server_pool[xx]->configure.version= pool->version(); - } - else - { - memcached_st *memc; - if ((memc= memcached_clone(NULL, pool->master))) - { - memcached_free(pool->server_pool[xx]); - pool->server_pool[xx]= memc; - /* I'm not sure what to do in this case.. this would happen - if we fail to push the server list inside the client.. - I should add a testcase for this, but I believe the following - would work, except that you would add a hole in the pool list.. - in theory you could end up with an empty pool.... - */ - } - } - } - - (void)pthread_mutex_unlock(&pool->mutex); - - return rc; -} - -memcached_return_t memcached_pool_behavior_get(memcached_pool_st *pool, - memcached_behavior_t flag, - uint64_t *value) -{ - if (pool == NULL) - { - return MEMCACHED_INVALID_ARGUMENTS; - } - - if (pthread_mutex_lock(&pool->mutex)) - { - return MEMCACHED_IN_PROGRESS; - } - - *value= memcached_behavior_get(pool->master, flag); - - (void)pthread_mutex_unlock(&pool->mutex); - - return MEMCACHED_SUCCESS; -} diff --git a/libmemcached/util/version.cc b/libmemcached/util/version.cc deleted file mode 100644 index a8208298..00000000 --- a/libmemcached/util/version.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2010 Brian Aker All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - - -#include -#include - -struct local_context -{ - uint8_t major_version; - uint8_t minor_version; - uint8_t micro_version; - - bool truth; -}; - -static memcached_return_t check_server_version(const memcached_st *ptr, - const memcached_server_st *instance, - void *context) -{ - /* Do Nothing */ - struct local_context *check= (struct local_context *)context; - (void)ptr; - - if (instance->major_version != UINT8_MAX && - instance->major_version >= check->major_version && - instance->minor_version >= check->minor_version && - instance->micro_version >= check->micro_version ) - { - return MEMCACHED_SUCCESS; - } - - check->truth= false; - - return MEMCACHED_FAILURE; -} - -bool libmemcached_util_version_check(memcached_st *memc, - uint8_t major_version, - uint8_t minor_version, - uint8_t micro_version) -{ - if (memcached_version(memc) != MEMCACHED_SUCCESS) - return false; - - struct local_context check= { major_version, minor_version, micro_version, true }; - - memcached_server_fn callbacks[1]; - callbacks[0]= check_server_version; - memcached_server_cursor(memc, callbacks, (void *)&check, 1); - - return check.truth; -} diff --git a/libmemcached/virtual_bucket.c b/libmemcached/virtual_bucket.c index 74ec10ef..f91bf454 100644 --- a/libmemcached/virtual_bucket.c +++ b/libmemcached/virtual_bucket.c @@ -36,8 +36,7 @@ */ #include -#include -#include +#include struct bucket_t { uint32_t master; @@ -58,7 +57,9 @@ memcached_return_t memcached_virtual_bucket_create(memcached_st *self, const uint32_t replicas) { if (! self || ! host_map || ! buckets) + { return MEMCACHED_INVALID_ARGUMENTS; + } memcached_virtual_bucket_free(self); diff --git a/libmemcachedprotocol-0.0/binary.h b/libmemcachedprotocol-0.0/binary.h new file mode 100644 index 00000000..73cd35b1 --- /dev/null +++ b/libmemcachedprotocol-0.0/binary.h @@ -0,0 +1,726 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright (c) <2008>, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Summary: Constants used by to implement the binary protocol. + * + * Copy: See Copyright for the status of this software. + * + * Author: Trond Norbye + */ + +#ifndef PROTOCOL_BINARY_H +#define PROTOCOL_BINARY_H + +#include + +/** + * \addtogroup Protocol + * @{ + */ + +/** + * This file contains definitions of the constants and packet formats + * defined in the binary specification. Please note that you _MUST_ remember + * to convert each multibyte field to / from network byte order to / from + * host order. + */ +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Definition of the legal "magic" values used in a packet. + * See section 3.1 Magic byte + */ + typedef enum { + PROTOCOL_BINARY_REQ = 0x80, + PROTOCOL_BINARY_RES = 0x81 + } protocol_binary_magic; + + /** + * Definition of the valid response status numbers. + * See section 3.2 Response Status + */ + typedef enum { + PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00, + PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01, + PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02, + PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03, + PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04, + PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05, + PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06, + PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET = 0x07, + PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20, + PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21, + PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81, + PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82, + PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED = 0x83, + PROTOCOL_BINARY_RESPONSE_EINTERNAL = 0x84, + PROTOCOL_BINARY_RESPONSE_EBUSY = 0x85, + PROTOCOL_BINARY_RESPONSE_ETMPFAIL = 0x86 + } protocol_binary_response_status; + + /** + * Defintion of the different command opcodes. + * See section 3.3 Command Opcodes + */ + typedef enum { + PROTOCOL_BINARY_CMD_GET = 0x00, + PROTOCOL_BINARY_CMD_SET = 0x01, + PROTOCOL_BINARY_CMD_ADD = 0x02, + PROTOCOL_BINARY_CMD_REPLACE = 0x03, + PROTOCOL_BINARY_CMD_DELETE = 0x04, + PROTOCOL_BINARY_CMD_INCREMENT = 0x05, + PROTOCOL_BINARY_CMD_DECREMENT = 0x06, + PROTOCOL_BINARY_CMD_QUIT = 0x07, + PROTOCOL_BINARY_CMD_FLUSH = 0x08, + PROTOCOL_BINARY_CMD_GETQ = 0x09, + PROTOCOL_BINARY_CMD_NOOP = 0x0a, + PROTOCOL_BINARY_CMD_VERSION = 0x0b, + PROTOCOL_BINARY_CMD_GETK = 0x0c, + PROTOCOL_BINARY_CMD_GETKQ = 0x0d, + PROTOCOL_BINARY_CMD_APPEND = 0x0e, + PROTOCOL_BINARY_CMD_PREPEND = 0x0f, + PROTOCOL_BINARY_CMD_STAT = 0x10, + PROTOCOL_BINARY_CMD_SETQ = 0x11, + PROTOCOL_BINARY_CMD_ADDQ = 0x12, + PROTOCOL_BINARY_CMD_REPLACEQ = 0x13, + PROTOCOL_BINARY_CMD_DELETEQ = 0x14, + PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15, + PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16, + PROTOCOL_BINARY_CMD_QUITQ = 0x17, + PROTOCOL_BINARY_CMD_FLUSHQ = 0x18, + PROTOCOL_BINARY_CMD_APPENDQ = 0x19, + PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a, + PROTOCOL_BINARY_CMD_VERBOSITY = 0x1b, + PROTOCOL_BINARY_CMD_TOUCH = 0x1c, + PROTOCOL_BINARY_CMD_GAT = 0x1d, + PROTOCOL_BINARY_CMD_GATQ = 0x1e, + + PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20, + PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21, + PROTOCOL_BINARY_CMD_SASL_STEP = 0x22, + + /* These commands are used for range operations and exist within + * this header for use in other projects. Range operations are + * not expected to be implemented in the memcached server itself. + */ + PROTOCOL_BINARY_CMD_RGET = 0x30, + PROTOCOL_BINARY_CMD_RSET = 0x31, + PROTOCOL_BINARY_CMD_RSETQ = 0x32, + PROTOCOL_BINARY_CMD_RAPPEND = 0x33, + PROTOCOL_BINARY_CMD_RAPPENDQ = 0x34, + PROTOCOL_BINARY_CMD_RPREPEND = 0x35, + PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36, + PROTOCOL_BINARY_CMD_RDELETE = 0x37, + PROTOCOL_BINARY_CMD_RDELETEQ = 0x38, + PROTOCOL_BINARY_CMD_RINCR = 0x39, + PROTOCOL_BINARY_CMD_RINCRQ = 0x3a, + PROTOCOL_BINARY_CMD_RDECR = 0x3b, + PROTOCOL_BINARY_CMD_RDECRQ = 0x3c, + /* End Range operations */ + + /* VBucket commands */ + PROTOCOL_BINARY_CMD_SET_VBUCKET = 0x3d, + PROTOCOL_BINARY_CMD_GET_VBUCKET = 0x3e, + PROTOCOL_BINARY_CMD_DEL_VBUCKET = 0x3f, + /* End VBucket commands */ + + /* TAP commands */ + PROTOCOL_BINARY_CMD_TAP_CONNECT = 0x40, + PROTOCOL_BINARY_CMD_TAP_MUTATION = 0x41, + PROTOCOL_BINARY_CMD_TAP_DELETE = 0x42, + PROTOCOL_BINARY_CMD_TAP_FLUSH = 0x43, + PROTOCOL_BINARY_CMD_TAP_OPAQUE = 0x44, + PROTOCOL_BINARY_CMD_TAP_VBUCKET_SET = 0x45, + PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_START = 0x46, + PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_END = 0x47, + /* End TAP */ + + PROTOCOL_BINARY_CMD_LAST_RESERVED = 0xef, + + /* Scrub the data */ + PROTOCOL_BINARY_CMD_SCRUB = 0xf0 + } protocol_binary_command; + + /** + * Definition of the data types in the packet + * See section 3.4 Data Types + */ + typedef enum { + PROTOCOL_BINARY_RAW_BYTES = 0x00 + } protocol_binary_datatypes; + + /** + * Definition of the header structure for a request packet. + * See section 2 + */ + typedef union { + struct { + uint8_t magic; + uint8_t opcode; + uint16_t keylen; + uint8_t extlen; + uint8_t datatype; + uint16_t vbucket; + uint32_t bodylen; + uint32_t opaque; + uint64_t cas; + } request; + uint8_t bytes[24]; + } protocol_binary_request_header; + + /** + * Definition of the header structure for a response packet. + * See section 2 + */ + typedef union { + struct { + uint8_t magic; + uint8_t opcode; + uint16_t keylen; + uint8_t extlen; + uint8_t datatype; + uint16_t status; + uint32_t bodylen; + uint32_t opaque; + uint64_t cas; + } response; + uint8_t bytes[24]; + } protocol_binary_response_header; + + /** + * Definition of a request-packet containing no extras + */ + union protocol_binary_request_no_extras { + struct { + protocol_binary_request_header header; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header)]; + }; + typedef union protocol_binary_request_no_extras protocol_binary_request_no_extras; + + /** + * Definition of a response-packet containing no extras + */ + typedef union { + struct { + protocol_binary_response_header header; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header)]; + } protocol_binary_response_no_extras; + + /** + * Definition of the packet used by the get, getq, getk and getkq command. + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get; + typedef protocol_binary_request_no_extras protocol_binary_request_getq; + typedef protocol_binary_request_no_extras protocol_binary_request_getk; + typedef protocol_binary_request_no_extras protocol_binary_request_getkq; + + /** + * Definition of the packet returned from a successful get, getq, getk and + * getkq. + * See section 4 + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + uint32_t flags; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header) + 4]; + } protocol_binary_response_get; + + typedef protocol_binary_response_get protocol_binary_response_getq; + typedef protocol_binary_response_get protocol_binary_response_getk; + typedef protocol_binary_response_get protocol_binary_response_getkq; + + /** + * Definition of the packet used by the delete command + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_delete; + + /** + * Definition of the packet returned by the delete command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_delete; + + /** + * Definition of the packet used by the flush command + * See section 4 + * Please note that the expiration field is optional, so remember to see + * check the header.bodysize to see if it is present. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_flush; + + /** + * Definition of the packet returned by the flush command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_flush; + + /** + * Definition of the packet used by set, add and replace + * See section 4 + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t flags; + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_set; + typedef protocol_binary_request_set protocol_binary_request_add; + typedef protocol_binary_request_set protocol_binary_request_replace; + + /** + * Definition of the packet returned by set, add and replace + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_set; + typedef protocol_binary_response_no_extras protocol_binary_response_add; + typedef protocol_binary_response_no_extras protocol_binary_response_replace; + + /** + * Definition of the noop packet + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_noop; + + /** + * Definition of the packet returned by the noop command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_noop; + + /** + * Definition of the structure used by the increment and decrement + * command. + * See section 4 + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t delta; + uint64_t initial; + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 20]; + } protocol_binary_request_incr; + typedef protocol_binary_request_incr protocol_binary_request_decr; + + /** + * Definition of the response from an incr or decr command + * command. + * See section 4 + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + uint64_t value; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header) + 8]; + } protocol_binary_response_incr; + typedef protocol_binary_response_incr protocol_binary_response_decr; + + /** + * Definition of the quit + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_quit; + + /** + * Definition of the packet returned by the quit command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_quit; + + /** + * Definition of the packet used by append and prepend command + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_append; + typedef protocol_binary_request_no_extras protocol_binary_request_prepend; + + /** + * Definition of the packet returned from a successful append or prepend + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_append; + typedef protocol_binary_response_no_extras protocol_binary_response_prepend; + + /** + * Definition of the packet used by the version command + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_version; + + /** + * Definition of the packet returned from a successful version command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_version; + + + /** + * Definition of the packet used by the stats command. + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_stats; + + /** + * Definition of the packet returned from a successful stats command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_stats; + + /** + * Definition of the packet used by the verbosity command + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t level; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_verbosity; + + /** + * Definition of the packet returned from the verbosity command + */ + typedef protocol_binary_response_no_extras protocol_binary_response_verbosity; + + /** + * Definition of the packet used by the touch command. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_touch; + + /** + * Definition of the packet returned from the touch command + */ + typedef protocol_binary_response_no_extras protocol_binary_response_touch; + + /** + * Definition of the packet used by the GAT(Q) command. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_gat; + + typedef protocol_binary_request_gat protocol_binary_request_gatq; + + /** + * Definition of the packet returned from the GAT(Q) + */ + typedef protocol_binary_response_get protocol_binary_response_gat; + typedef protocol_binary_response_get protocol_binary_response_gatq; + + + /** + * Definition of a request for a range operation. + * See http://code.google.com/p/memcached/wiki/RangeOps + * + * These types are used for range operations and exist within + * this header for use in other projects. Range operations are + * not expected to be implemented in the memcached server itself. + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + uint16_t size; + uint8_t reserved; + uint8_t flags; + uint32_t max_results; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_rangeop; + + typedef protocol_binary_request_rangeop protocol_binary_request_rget; + typedef protocol_binary_request_rangeop protocol_binary_request_rset; + typedef protocol_binary_request_rangeop protocol_binary_request_rsetq; + typedef protocol_binary_request_rangeop protocol_binary_request_rappend; + typedef protocol_binary_request_rangeop protocol_binary_request_rappendq; + typedef protocol_binary_request_rangeop protocol_binary_request_rprepend; + typedef protocol_binary_request_rangeop protocol_binary_request_rprependq; + typedef protocol_binary_request_rangeop protocol_binary_request_rdelete; + typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq; + typedef protocol_binary_request_rangeop protocol_binary_request_rincr; + typedef protocol_binary_request_rangeop protocol_binary_request_rincrq; + typedef protocol_binary_request_rangeop protocol_binary_request_rdecr; + typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq; + + + /** + * Definition of tap commands + * See To be written + * + */ + + typedef union { + struct { + protocol_binary_request_header header; + struct { + /** + * flags is a bitmask used to set properties for the + * the connection. Please In order to be forward compatible + * you should set all undefined bits to 0. + * + * If the bit require extra userdata, it will be stored + * in the user-data field of the body (passed to the engine + * as enginespeciffic). That means that when you parse the + * flags and the engine-specific data, you have to work your + * way from bit 0 and upwards to find the correct offset for + * the data. + * + */ + uint32_t flags; + + /** + * Backfill age + * + * By using this flag you can limit the amount of data being + * transmitted. If you don't specify a backfill age, the + * server will transmit everything it contains. + * + * The first 8 bytes in the engine specific data contains + * the oldest entry (from epoc) you're interested in. + * Specifying a time in the future (for the server you are + * connecting to), will cause it to start streaming current + * changes. + */ +#define TAP_CONNECT_FLAG_BACKFILL 0x01 + /** + * Dump will cause the server to send the data stored on the + * server, but disconnect when the keys stored in the server + * are transmitted. + */ +#define TAP_CONNECT_FLAG_DUMP 0x02 + /** + * The body contains a list of 16 bits words in network byte + * order specifying the vbucket ids to monitor. The first 16 + * bit word contains the number of buckets. The number of 0 + * means "all buckets" + */ +#define TAP_CONNECT_FLAG_LIST_VBUCKETS 0x04 + /** + * The responsibility of the vbuckets is to be transferred + * over to the caller when all items are transferred. + */ +#define TAP_CONNECT_FLAG_TAKEOVER_VBUCKETS 0x08 + /** + * The tap consumer supports ack'ing of tap messages + */ +#define TAP_CONNECT_SUPPORT_ACK 0x10 + /** + * The tap consumer would prefer to just get the keys + * back. If the engine supports this it will set + * the TAP_FLAG_NO_VALUE flag in each of the + * tap packets returned. + */ +#define TAP_CONNECT_REQUEST_KEYS_ONLY 0x20 + /** + * The body contains a list of (vbucket_id, last_checkpoint_id) + * pairs. This provides the checkpoint support in TAP streams. + * The last checkpoint id represents the last checkpoint that + * was successfully persisted. + */ +#define TAP_CONNECT_CHECKPOINT 0x40 + /** + * The tap consumer is a registered tap client, which means that + * the tap server will maintain its checkpoint cursor permanently. + */ +#define TAP_CONNECT_REGISTERED_CLIENT 0x80 + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_tap_connect; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + struct { + uint16_t enginespecific_length; + /* + * The flag section support the following flags + */ + /** + * Request that the consumer send a response packet + * for this packet. The opaque field must be preserved + * in the response. + */ +#define TAP_FLAG_ACK 0x01 + /** + * The value for the key is not included in the packet + */ +#define TAP_FLAG_NO_VALUE 0x02 + uint16_t flags; + uint8_t ttl; + uint8_t res1; + uint8_t res2; + uint8_t res3; + } tap; + struct { + uint32_t flags; + uint32_t expiration; + } item; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 16]; + } protocol_binary_request_tap_mutation; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + struct { + uint16_t enginespecific_length; + /** + * See the definition of the flags for + * protocol_binary_request_tap_mutation for a description + * of the available flags. + */ + uint16_t flags; + uint8_t ttl; + uint8_t res1; + uint8_t res2; + uint8_t res3; + } tap; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_tap_no_extras; + + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_delete; + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_flush; + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_opaque; + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_vbucket_set; + + + /** + * Definition of the packet used by the scrub. + */ + typedef protocol_binary_request_no_extras protocol_binary_request_scrub; + + /** + * Definition of the packet returned from scrub. + */ + typedef protocol_binary_response_no_extras protocol_binary_response_scrub; + + + /** + * Definition of the packet used by set vbucket + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + vbucket_state_t state; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + sizeof(vbucket_state_t)]; + } protocol_binary_request_set_vbucket; + /** + * Definition of the packet returned from set vbucket + */ + typedef protocol_binary_response_no_extras protocol_binary_response_set_vbucket; + /** + * Definition of the packet used by del vbucket + */ + typedef protocol_binary_request_no_extras protocol_binary_request_del_vbucket; + /** + * Definition of the packet returned from del vbucket + */ + typedef protocol_binary_response_no_extras protocol_binary_response_del_vbucket; + + /** + * Definition of the packet used by get vbucket + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get_vbucket; + + /** + * Definition of the packet returned from get vbucket + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + vbucket_state_t state; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header) + sizeof(vbucket_state_t)]; + } protocol_binary_response_get_vbucket; + + + /** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* PROTOCOL_BINARY_H */ diff --git a/libmemcachedprotocol-0.0/handler.h b/libmemcachedprotocol-0.0/handler.h new file mode 100644 index 00000000..f1cfdc03 --- /dev/null +++ b/libmemcachedprotocol-0.0/handler.h @@ -0,0 +1,215 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Definition of the callback interface to the protocol handler + * + * Author: Trond Norbye + * + */ + +#pragma once + +#include +#if !defined(__cplusplus) +# include +#endif + +#include +#include +#include +#include + +/* Forward declarations */ +/* + * You should only access memcached_protocol_st from one thread!, + * and never assume anything about the internal layout / sizes of the + * structures. + */ +typedef struct memcached_protocol_st memcached_protocol_st; +typedef struct memcached_protocol_client_st memcached_protocol_client_st; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Function the protocol handler should call to receive data. + * This function should behave exactly like read(2) + * + * @param cookie a cookie used to represent a given client + * @param fd the filedescriptor associated with the client + * @param buf destination buffer + * @param nbuf number of bytes to receive + * @return the number of bytes copied into buf + * or -1 upon error (errno should contain more information) + */ +typedef ssize_t (*memcached_protocol_recv_func)(const void *cookie, + memcached_socket_t fd, + void *buf, + size_t nbuf); + +/** + * Function the protocol handler should call to send data. + * This function should behave exactly like write(2) + * + * @param cookie a cookie used to represent a given client + * @param fd the filedescriptor associated with the client + * @param buf the source buffer + * @param nbuf number of bytes to send + * @return the number of bytes sent + * or -1 upon error (errno should contain more information) + */ +typedef ssize_t (*memcached_protocol_send_func)(const void *cookie, + memcached_socket_t fd, + const void *buf, + size_t nbuf); + +/** + * Create an instance of the protocol handler + * + * @return NULL if allocation of an instance fails + */ +LIBMEMCACHED_API +memcached_protocol_st *memcached_protocol_create_instance(void); + +/** + * Get the callbacks associated with a protocol handler instance + * @return the callbacks currently used + */ +LIBMEMCACHED_API +memcached_binary_protocol_callback_st *memcached_binary_protocol_get_callbacks(memcached_protocol_st *instance); + +/** + * Set the callbacks to be used by the given protocol handler instance + * @param instance the instance to update + * @param callback the callbacks to use + */ +LIBMEMCACHED_API +void memcached_binary_protocol_set_callbacks(memcached_protocol_st *instance, memcached_binary_protocol_callback_st *callback); + +/** + * Should the library inspect the packages being sent and received and verify + * that they are according to the specification? If it encounters an invalid + * packet, it will return an EINVAL packet. + * + * @param instance the instance to update + * @param enable true if you want the library to check packages, false otherwise + */ +LIBMEMCACHED_API +void memcached_binary_protocol_set_pedantic(memcached_protocol_st *instance, bool enable); + +/** + * Is the library inpecting each package? + * @param instance the instance to check + * @return true it the library is inspecting each package, false otherwise + */ +LIBMEMCACHED_API +bool memcached_binary_protocol_get_pedantic(memcached_protocol_st *instance); + +/** + * Destroy an instance of the protocol handler + * + * @param instance The instance to destroy + */ +LIBMEMCACHED_API +void memcached_protocol_destroy_instance(memcached_protocol_st *instance); + +/** + * Set the IO functions used by the instance to send and receive data. The + * functions should behave like recv(3socket) and send(3socket). + * + * @param instance the instance to specify the IO functions for + * @param recv the function to call for reciving data + * @param send the function to call for sending data + */ +LIBMEMCACHED_API +void memached_protocol_set_io_functions(memcached_protocol_st *instance, + memcached_protocol_recv_func recv, + memcached_protocol_send_func send); + + +/** + * Create a new client instance and associate it with a socket + * @param instance the protocol instance to bind the client to + * @param sock the client socket + * @return NULL if allocation fails, otherwise an instance + */ +LIBMEMCACHED_API +memcached_protocol_client_st *memcached_protocol_create_client(memcached_protocol_st *instance, memcached_socket_t sock); + +/** + * Destroy a client handle. + * The caller needs to close the socket accociated with the client + * before calling this function. This function invalidates the + * client memory area. + * + * @param client the client to destroy + */ +LIBMEMCACHED_API +void memcached_protocol_client_destroy(memcached_protocol_client_st *client); + +/** + * Error event means that the client encountered an error with the + * connection so you should shut it down + */ +#define MEMCACHED_PROTOCOL_ERROR_EVENT 1 +/** + * Please notify when there is more data available to read + */ +#define MEMCACHED_PROTOCOL_READ_EVENT 2 +/** + * Please notify when it is possible to send more data + */ +#define MEMCACHED_PROTOCOL_WRITE_EVENT 4 +/** + * Backed paused the execution for this client + */ +#define MEMCACHED_PROTOCOL_PAUSE_EVENT 8 + +/** + * The different events the client is interested in. This is a bitmask of + * the constants defined above. + */ +typedef uint32_t memcached_protocol_event_t; + +/** + * Let the client do some work. This might involve reading / sending data + * to/from the client, or perform callbacks to execute a command. + * @param client the client structure to work on + * @return The next event the protocol handler will be notified for + */ +LIBMEMCACHED_API +memcached_protocol_event_t memcached_protocol_client_work(memcached_protocol_client_st *client); + +/** + * Get the socket attached to a client handle + * @param client the client to query + * @return the socket handle + */ +LIBMEMCACHED_API +memcached_socket_t memcached_protocol_client_get_socket(memcached_protocol_client_st *client); + +/** + * Get the error id socket attached to a client handle + * @param client the client to query for an error code + * @return the OS error code from the client + */ +LIBMEMCACHED_API +int memcached_protocol_client_get_errno(memcached_protocol_client_st *client); + +/** + * Get a raw response handler for the given cookie + * @param cookie the cookie passed along into the callback + * @return the raw reponse handler you may use if you find + * the generic callback too limiting + */ +LIBMEMCACHED_API +memcached_binary_protocol_raw_response_handler memcached_binary_protocol_get_raw_response_handler(const void *cookie); + +#ifdef __cplusplus +} +#endif diff --git a/libmemcachedprotocol-0.0/include.am b/libmemcachedprotocol-0.0/include.am index 0588b0a5..3683c0f1 100644 --- a/libmemcachedprotocol-0.0/include.am +++ b/libmemcachedprotocol-0.0/include.am @@ -3,5 +3,7 @@ # All paths should be given relative to the root nobase_include_HEADERS+= \ + libmemcachedprotocol-0.0/binary.h \ libmemcachedprotocol-0.0/callback.h \ - libmemcachedprotocol-0.0/protocol_handler.h + libmemcachedprotocol-0.0/handler.h \ + libmemcachedprotocol-0.0/vbucket.h diff --git a/libmemcachedprotocol-0.0/protocol_binary.h b/libmemcachedprotocol-0.0/protocol_binary.h deleted file mode 100644 index 73cd35b1..00000000 --- a/libmemcachedprotocol-0.0/protocol_binary.h +++ /dev/null @@ -1,726 +0,0 @@ -/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -/* - * Copyright (c) <2008>, Sun Microsystems, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Summary: Constants used by to implement the binary protocol. - * - * Copy: See Copyright for the status of this software. - * - * Author: Trond Norbye - */ - -#ifndef PROTOCOL_BINARY_H -#define PROTOCOL_BINARY_H - -#include - -/** - * \addtogroup Protocol - * @{ - */ - -/** - * This file contains definitions of the constants and packet formats - * defined in the binary specification. Please note that you _MUST_ remember - * to convert each multibyte field to / from network byte order to / from - * host order. - */ -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * Definition of the legal "magic" values used in a packet. - * See section 3.1 Magic byte - */ - typedef enum { - PROTOCOL_BINARY_REQ = 0x80, - PROTOCOL_BINARY_RES = 0x81 - } protocol_binary_magic; - - /** - * Definition of the valid response status numbers. - * See section 3.2 Response Status - */ - typedef enum { - PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00, - PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01, - PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02, - PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03, - PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04, - PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05, - PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06, - PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET = 0x07, - PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20, - PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21, - PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81, - PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82, - PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED = 0x83, - PROTOCOL_BINARY_RESPONSE_EINTERNAL = 0x84, - PROTOCOL_BINARY_RESPONSE_EBUSY = 0x85, - PROTOCOL_BINARY_RESPONSE_ETMPFAIL = 0x86 - } protocol_binary_response_status; - - /** - * Defintion of the different command opcodes. - * See section 3.3 Command Opcodes - */ - typedef enum { - PROTOCOL_BINARY_CMD_GET = 0x00, - PROTOCOL_BINARY_CMD_SET = 0x01, - PROTOCOL_BINARY_CMD_ADD = 0x02, - PROTOCOL_BINARY_CMD_REPLACE = 0x03, - PROTOCOL_BINARY_CMD_DELETE = 0x04, - PROTOCOL_BINARY_CMD_INCREMENT = 0x05, - PROTOCOL_BINARY_CMD_DECREMENT = 0x06, - PROTOCOL_BINARY_CMD_QUIT = 0x07, - PROTOCOL_BINARY_CMD_FLUSH = 0x08, - PROTOCOL_BINARY_CMD_GETQ = 0x09, - PROTOCOL_BINARY_CMD_NOOP = 0x0a, - PROTOCOL_BINARY_CMD_VERSION = 0x0b, - PROTOCOL_BINARY_CMD_GETK = 0x0c, - PROTOCOL_BINARY_CMD_GETKQ = 0x0d, - PROTOCOL_BINARY_CMD_APPEND = 0x0e, - PROTOCOL_BINARY_CMD_PREPEND = 0x0f, - PROTOCOL_BINARY_CMD_STAT = 0x10, - PROTOCOL_BINARY_CMD_SETQ = 0x11, - PROTOCOL_BINARY_CMD_ADDQ = 0x12, - PROTOCOL_BINARY_CMD_REPLACEQ = 0x13, - PROTOCOL_BINARY_CMD_DELETEQ = 0x14, - PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15, - PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16, - PROTOCOL_BINARY_CMD_QUITQ = 0x17, - PROTOCOL_BINARY_CMD_FLUSHQ = 0x18, - PROTOCOL_BINARY_CMD_APPENDQ = 0x19, - PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a, - PROTOCOL_BINARY_CMD_VERBOSITY = 0x1b, - PROTOCOL_BINARY_CMD_TOUCH = 0x1c, - PROTOCOL_BINARY_CMD_GAT = 0x1d, - PROTOCOL_BINARY_CMD_GATQ = 0x1e, - - PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20, - PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21, - PROTOCOL_BINARY_CMD_SASL_STEP = 0x22, - - /* These commands are used for range operations and exist within - * this header for use in other projects. Range operations are - * not expected to be implemented in the memcached server itself. - */ - PROTOCOL_BINARY_CMD_RGET = 0x30, - PROTOCOL_BINARY_CMD_RSET = 0x31, - PROTOCOL_BINARY_CMD_RSETQ = 0x32, - PROTOCOL_BINARY_CMD_RAPPEND = 0x33, - PROTOCOL_BINARY_CMD_RAPPENDQ = 0x34, - PROTOCOL_BINARY_CMD_RPREPEND = 0x35, - PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36, - PROTOCOL_BINARY_CMD_RDELETE = 0x37, - PROTOCOL_BINARY_CMD_RDELETEQ = 0x38, - PROTOCOL_BINARY_CMD_RINCR = 0x39, - PROTOCOL_BINARY_CMD_RINCRQ = 0x3a, - PROTOCOL_BINARY_CMD_RDECR = 0x3b, - PROTOCOL_BINARY_CMD_RDECRQ = 0x3c, - /* End Range operations */ - - /* VBucket commands */ - PROTOCOL_BINARY_CMD_SET_VBUCKET = 0x3d, - PROTOCOL_BINARY_CMD_GET_VBUCKET = 0x3e, - PROTOCOL_BINARY_CMD_DEL_VBUCKET = 0x3f, - /* End VBucket commands */ - - /* TAP commands */ - PROTOCOL_BINARY_CMD_TAP_CONNECT = 0x40, - PROTOCOL_BINARY_CMD_TAP_MUTATION = 0x41, - PROTOCOL_BINARY_CMD_TAP_DELETE = 0x42, - PROTOCOL_BINARY_CMD_TAP_FLUSH = 0x43, - PROTOCOL_BINARY_CMD_TAP_OPAQUE = 0x44, - PROTOCOL_BINARY_CMD_TAP_VBUCKET_SET = 0x45, - PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_START = 0x46, - PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_END = 0x47, - /* End TAP */ - - PROTOCOL_BINARY_CMD_LAST_RESERVED = 0xef, - - /* Scrub the data */ - PROTOCOL_BINARY_CMD_SCRUB = 0xf0 - } protocol_binary_command; - - /** - * Definition of the data types in the packet - * See section 3.4 Data Types - */ - typedef enum { - PROTOCOL_BINARY_RAW_BYTES = 0x00 - } protocol_binary_datatypes; - - /** - * Definition of the header structure for a request packet. - * See section 2 - */ - typedef union { - struct { - uint8_t magic; - uint8_t opcode; - uint16_t keylen; - uint8_t extlen; - uint8_t datatype; - uint16_t vbucket; - uint32_t bodylen; - uint32_t opaque; - uint64_t cas; - } request; - uint8_t bytes[24]; - } protocol_binary_request_header; - - /** - * Definition of the header structure for a response packet. - * See section 2 - */ - typedef union { - struct { - uint8_t magic; - uint8_t opcode; - uint16_t keylen; - uint8_t extlen; - uint8_t datatype; - uint16_t status; - uint32_t bodylen; - uint32_t opaque; - uint64_t cas; - } response; - uint8_t bytes[24]; - } protocol_binary_response_header; - - /** - * Definition of a request-packet containing no extras - */ - union protocol_binary_request_no_extras { - struct { - protocol_binary_request_header header; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header)]; - }; - typedef union protocol_binary_request_no_extras protocol_binary_request_no_extras; - - /** - * Definition of a response-packet containing no extras - */ - typedef union { - struct { - protocol_binary_response_header header; - } message; - uint8_t bytes[sizeof(protocol_binary_response_header)]; - } protocol_binary_response_no_extras; - - /** - * Definition of the packet used by the get, getq, getk and getkq command. - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_get; - typedef protocol_binary_request_no_extras protocol_binary_request_getq; - typedef protocol_binary_request_no_extras protocol_binary_request_getk; - typedef protocol_binary_request_no_extras protocol_binary_request_getkq; - - /** - * Definition of the packet returned from a successful get, getq, getk and - * getkq. - * See section 4 - */ - typedef union { - struct { - protocol_binary_response_header header; - struct { - uint32_t flags; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_response_header) + 4]; - } protocol_binary_response_get; - - typedef protocol_binary_response_get protocol_binary_response_getq; - typedef protocol_binary_response_get protocol_binary_response_getk; - typedef protocol_binary_response_get protocol_binary_response_getkq; - - /** - * Definition of the packet used by the delete command - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_delete; - - /** - * Definition of the packet returned by the delete command - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_delete; - - /** - * Definition of the packet used by the flush command - * See section 4 - * Please note that the expiration field is optional, so remember to see - * check the header.bodysize to see if it is present. - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - uint32_t expiration; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; - } protocol_binary_request_flush; - - /** - * Definition of the packet returned by the flush command - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_flush; - - /** - * Definition of the packet used by set, add and replace - * See section 4 - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - uint32_t flags; - uint32_t expiration; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; - } protocol_binary_request_set; - typedef protocol_binary_request_set protocol_binary_request_add; - typedef protocol_binary_request_set protocol_binary_request_replace; - - /** - * Definition of the packet returned by set, add and replace - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_set; - typedef protocol_binary_response_no_extras protocol_binary_response_add; - typedef protocol_binary_response_no_extras protocol_binary_response_replace; - - /** - * Definition of the noop packet - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_noop; - - /** - * Definition of the packet returned by the noop command - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_noop; - - /** - * Definition of the structure used by the increment and decrement - * command. - * See section 4 - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - uint64_t delta; - uint64_t initial; - uint32_t expiration; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 20]; - } protocol_binary_request_incr; - typedef protocol_binary_request_incr protocol_binary_request_decr; - - /** - * Definition of the response from an incr or decr command - * command. - * See section 4 - */ - typedef union { - struct { - protocol_binary_response_header header; - struct { - uint64_t value; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_response_header) + 8]; - } protocol_binary_response_incr; - typedef protocol_binary_response_incr protocol_binary_response_decr; - - /** - * Definition of the quit - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_quit; - - /** - * Definition of the packet returned by the quit command - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_quit; - - /** - * Definition of the packet used by append and prepend command - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_append; - typedef protocol_binary_request_no_extras protocol_binary_request_prepend; - - /** - * Definition of the packet returned from a successful append or prepend - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_append; - typedef protocol_binary_response_no_extras protocol_binary_response_prepend; - - /** - * Definition of the packet used by the version command - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_version; - - /** - * Definition of the packet returned from a successful version command - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_version; - - - /** - * Definition of the packet used by the stats command. - * See section 4 - */ - typedef protocol_binary_request_no_extras protocol_binary_request_stats; - - /** - * Definition of the packet returned from a successful stats command - * See section 4 - */ - typedef protocol_binary_response_no_extras protocol_binary_response_stats; - - /** - * Definition of the packet used by the verbosity command - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - uint32_t level; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; - } protocol_binary_request_verbosity; - - /** - * Definition of the packet returned from the verbosity command - */ - typedef protocol_binary_response_no_extras protocol_binary_response_verbosity; - - /** - * Definition of the packet used by the touch command. - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - uint32_t expiration; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; - } protocol_binary_request_touch; - - /** - * Definition of the packet returned from the touch command - */ - typedef protocol_binary_response_no_extras protocol_binary_response_touch; - - /** - * Definition of the packet used by the GAT(Q) command. - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - uint32_t expiration; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; - } protocol_binary_request_gat; - - typedef protocol_binary_request_gat protocol_binary_request_gatq; - - /** - * Definition of the packet returned from the GAT(Q) - */ - typedef protocol_binary_response_get protocol_binary_response_gat; - typedef protocol_binary_response_get protocol_binary_response_gatq; - - - /** - * Definition of a request for a range operation. - * See http://code.google.com/p/memcached/wiki/RangeOps - * - * These types are used for range operations and exist within - * this header for use in other projects. Range operations are - * not expected to be implemented in the memcached server itself. - */ - typedef union { - struct { - protocol_binary_response_header header; - struct { - uint16_t size; - uint8_t reserved; - uint8_t flags; - uint32_t max_results; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; - } protocol_binary_request_rangeop; - - typedef protocol_binary_request_rangeop protocol_binary_request_rget; - typedef protocol_binary_request_rangeop protocol_binary_request_rset; - typedef protocol_binary_request_rangeop protocol_binary_request_rsetq; - typedef protocol_binary_request_rangeop protocol_binary_request_rappend; - typedef protocol_binary_request_rangeop protocol_binary_request_rappendq; - typedef protocol_binary_request_rangeop protocol_binary_request_rprepend; - typedef protocol_binary_request_rangeop protocol_binary_request_rprependq; - typedef protocol_binary_request_rangeop protocol_binary_request_rdelete; - typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq; - typedef protocol_binary_request_rangeop protocol_binary_request_rincr; - typedef protocol_binary_request_rangeop protocol_binary_request_rincrq; - typedef protocol_binary_request_rangeop protocol_binary_request_rdecr; - typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq; - - - /** - * Definition of tap commands - * See To be written - * - */ - - typedef union { - struct { - protocol_binary_request_header header; - struct { - /** - * flags is a bitmask used to set properties for the - * the connection. Please In order to be forward compatible - * you should set all undefined bits to 0. - * - * If the bit require extra userdata, it will be stored - * in the user-data field of the body (passed to the engine - * as enginespeciffic). That means that when you parse the - * flags and the engine-specific data, you have to work your - * way from bit 0 and upwards to find the correct offset for - * the data. - * - */ - uint32_t flags; - - /** - * Backfill age - * - * By using this flag you can limit the amount of data being - * transmitted. If you don't specify a backfill age, the - * server will transmit everything it contains. - * - * The first 8 bytes in the engine specific data contains - * the oldest entry (from epoc) you're interested in. - * Specifying a time in the future (for the server you are - * connecting to), will cause it to start streaming current - * changes. - */ -#define TAP_CONNECT_FLAG_BACKFILL 0x01 - /** - * Dump will cause the server to send the data stored on the - * server, but disconnect when the keys stored in the server - * are transmitted. - */ -#define TAP_CONNECT_FLAG_DUMP 0x02 - /** - * The body contains a list of 16 bits words in network byte - * order specifying the vbucket ids to monitor. The first 16 - * bit word contains the number of buckets. The number of 0 - * means "all buckets" - */ -#define TAP_CONNECT_FLAG_LIST_VBUCKETS 0x04 - /** - * The responsibility of the vbuckets is to be transferred - * over to the caller when all items are transferred. - */ -#define TAP_CONNECT_FLAG_TAKEOVER_VBUCKETS 0x08 - /** - * The tap consumer supports ack'ing of tap messages - */ -#define TAP_CONNECT_SUPPORT_ACK 0x10 - /** - * The tap consumer would prefer to just get the keys - * back. If the engine supports this it will set - * the TAP_FLAG_NO_VALUE flag in each of the - * tap packets returned. - */ -#define TAP_CONNECT_REQUEST_KEYS_ONLY 0x20 - /** - * The body contains a list of (vbucket_id, last_checkpoint_id) - * pairs. This provides the checkpoint support in TAP streams. - * The last checkpoint id represents the last checkpoint that - * was successfully persisted. - */ -#define TAP_CONNECT_CHECKPOINT 0x40 - /** - * The tap consumer is a registered tap client, which means that - * the tap server will maintain its checkpoint cursor permanently. - */ -#define TAP_CONNECT_REGISTERED_CLIENT 0x80 - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; - } protocol_binary_request_tap_connect; - - typedef union { - struct { - protocol_binary_request_header header; - struct { - struct { - uint16_t enginespecific_length; - /* - * The flag section support the following flags - */ - /** - * Request that the consumer send a response packet - * for this packet. The opaque field must be preserved - * in the response. - */ -#define TAP_FLAG_ACK 0x01 - /** - * The value for the key is not included in the packet - */ -#define TAP_FLAG_NO_VALUE 0x02 - uint16_t flags; - uint8_t ttl; - uint8_t res1; - uint8_t res2; - uint8_t res3; - } tap; - struct { - uint32_t flags; - uint32_t expiration; - } item; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 16]; - } protocol_binary_request_tap_mutation; - - typedef union { - struct { - protocol_binary_request_header header; - struct { - struct { - uint16_t enginespecific_length; - /** - * See the definition of the flags for - * protocol_binary_request_tap_mutation for a description - * of the available flags. - */ - uint16_t flags; - uint8_t ttl; - uint8_t res1; - uint8_t res2; - uint8_t res3; - } tap; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; - } protocol_binary_request_tap_no_extras; - - typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_delete; - typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_flush; - typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_opaque; - typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_vbucket_set; - - - /** - * Definition of the packet used by the scrub. - */ - typedef protocol_binary_request_no_extras protocol_binary_request_scrub; - - /** - * Definition of the packet returned from scrub. - */ - typedef protocol_binary_response_no_extras protocol_binary_response_scrub; - - - /** - * Definition of the packet used by set vbucket - */ - typedef union { - struct { - protocol_binary_request_header header; - struct { - vbucket_state_t state; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_request_header) + sizeof(vbucket_state_t)]; - } protocol_binary_request_set_vbucket; - /** - * Definition of the packet returned from set vbucket - */ - typedef protocol_binary_response_no_extras protocol_binary_response_set_vbucket; - /** - * Definition of the packet used by del vbucket - */ - typedef protocol_binary_request_no_extras protocol_binary_request_del_vbucket; - /** - * Definition of the packet returned from del vbucket - */ - typedef protocol_binary_response_no_extras protocol_binary_response_del_vbucket; - - /** - * Definition of the packet used by get vbucket - */ - typedef protocol_binary_request_no_extras protocol_binary_request_get_vbucket; - - /** - * Definition of the packet returned from get vbucket - */ - typedef union { - struct { - protocol_binary_response_header header; - struct { - vbucket_state_t state; - } body; - } message; - uint8_t bytes[sizeof(protocol_binary_response_header) + sizeof(vbucket_state_t)]; - } protocol_binary_response_get_vbucket; - - - /** - * @} - */ - -#ifdef __cplusplus -} -#endif -#endif /* PROTOCOL_BINARY_H */ diff --git a/libmemcachedprotocol-0.0/protocol_handler.h b/libmemcachedprotocol-0.0/protocol_handler.h deleted file mode 100644 index 1930b026..00000000 --- a/libmemcachedprotocol-0.0/protocol_handler.h +++ /dev/null @@ -1,215 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Definition of the callback interface to the protocol handler - * - * Author: Trond Norbye - * - */ - -#pragma once - -#include -#if !defined(__cplusplus) -# include -#endif - -#include -#include -#include -#include - -/* Forward declarations */ -/* - * You should only access memcached_protocol_st from one thread!, - * and never assume anything about the internal layout / sizes of the - * structures. - */ -typedef struct memcached_protocol_st memcached_protocol_st; -typedef struct memcached_protocol_client_st memcached_protocol_client_st; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Function the protocol handler should call to receive data. - * This function should behave exactly like read(2) - * - * @param cookie a cookie used to represent a given client - * @param fd the filedescriptor associated with the client - * @param buf destination buffer - * @param nbuf number of bytes to receive - * @return the number of bytes copied into buf - * or -1 upon error (errno should contain more information) - */ -typedef ssize_t (*memcached_protocol_recv_func)(const void *cookie, - memcached_socket_t fd, - void *buf, - size_t nbuf); - -/** - * Function the protocol handler should call to send data. - * This function should behave exactly like write(2) - * - * @param cookie a cookie used to represent a given client - * @param fd the filedescriptor associated with the client - * @param buf the source buffer - * @param nbuf number of bytes to send - * @return the number of bytes sent - * or -1 upon error (errno should contain more information) - */ -typedef ssize_t (*memcached_protocol_send_func)(const void *cookie, - memcached_socket_t fd, - const void *buf, - size_t nbuf); - -/** - * Create an instance of the protocol handler - * - * @return NULL if allocation of an instance fails - */ -LIBMEMCACHED_API -memcached_protocol_st *memcached_protocol_create_instance(void); - -/** - * Get the callbacks associated with a protocol handler instance - * @return the callbacks currently used - */ -LIBMEMCACHED_API -memcached_binary_protocol_callback_st *memcached_binary_protocol_get_callbacks(memcached_protocol_st *instance); - -/** - * Set the callbacks to be used by the given protocol handler instance - * @param instance the instance to update - * @param callback the callbacks to use - */ -LIBMEMCACHED_API -void memcached_binary_protocol_set_callbacks(memcached_protocol_st *instance, memcached_binary_protocol_callback_st *callback); - -/** - * Should the library inspect the packages being sent and received and verify - * that they are according to the specification? If it encounters an invalid - * packet, it will return an EINVAL packet. - * - * @param instance the instance to update - * @param enable true if you want the library to check packages, false otherwise - */ -LIBMEMCACHED_API -void memcached_binary_protocol_set_pedantic(memcached_protocol_st *instance, bool enable); - -/** - * Is the library inpecting each package? - * @param instance the instance to check - * @return true it the library is inspecting each package, false otherwise - */ -LIBMEMCACHED_API -bool memcached_binary_protocol_get_pedantic(memcached_protocol_st *instance); - -/** - * Destroy an instance of the protocol handler - * - * @param instance The instance to destroy - */ -LIBMEMCACHED_API -void memcached_protocol_destroy_instance(memcached_protocol_st *instance); - -/** - * Set the IO functions used by the instance to send and receive data. The - * functions should behave like recv(3socket) and send(3socket). - * - * @param instance the instance to specify the IO functions for - * @param recv the function to call for reciving data - * @param send the function to call for sending data - */ -LIBMEMCACHED_API -void memached_protocol_set_io_functions(memcached_protocol_st *instance, - memcached_protocol_recv_func recv, - memcached_protocol_send_func send); - - -/** - * Create a new client instance and associate it with a socket - * @param instance the protocol instance to bind the client to - * @param sock the client socket - * @return NULL if allocation fails, otherwise an instance - */ -LIBMEMCACHED_API -memcached_protocol_client_st *memcached_protocol_create_client(memcached_protocol_st *instance, memcached_socket_t sock); - -/** - * Destroy a client handle. - * The caller needs to close the socket accociated with the client - * before calling this function. This function invalidates the - * client memory area. - * - * @param client the client to destroy - */ -LIBMEMCACHED_API -void memcached_protocol_client_destroy(memcached_protocol_client_st *client); - -/** - * Error event means that the client encountered an error with the - * connection so you should shut it down - */ -#define MEMCACHED_PROTOCOL_ERROR_EVENT 1 -/** - * Please notify when there is more data available to read - */ -#define MEMCACHED_PROTOCOL_READ_EVENT 2 -/** - * Please notify when it is possible to send more data - */ -#define MEMCACHED_PROTOCOL_WRITE_EVENT 4 -/** - * Backed paused the execution for this client - */ -#define MEMCACHED_PROTOCOL_PAUSE_EVENT 8 - -/** - * The different events the client is interested in. This is a bitmask of - * the constants defined above. - */ -typedef uint32_t memcached_protocol_event_t; - -/** - * Let the client do some work. This might involve reading / sending data - * to/from the client, or perform callbacks to execute a command. - * @param client the client structure to work on - * @return The next event the protocol handler will be notified for - */ -LIBMEMCACHED_API -memcached_protocol_event_t memcached_protocol_client_work(memcached_protocol_client_st *client); - -/** - * Get the socket attached to a client handle - * @param client the client to query - * @return the socket handle - */ -LIBMEMCACHED_API -memcached_socket_t memcached_protocol_client_get_socket(memcached_protocol_client_st *client); - -/** - * Get the error id socket attached to a client handle - * @param client the client to query for an error code - * @return the OS error code from the client - */ -LIBMEMCACHED_API -int memcached_protocol_client_get_errno(memcached_protocol_client_st *client); - -/** - * Get a raw response handler for the given cookie - * @param cookie the cookie passed along into the callback - * @return the raw reponse handler you may use if you find - * the generic callback too limiting - */ -LIBMEMCACHED_API -memcached_binary_protocol_raw_response_handler memcached_binary_protocol_get_raw_response_handler(const void *cookie); - -#ifdef __cplusplus -} -#endif diff --git a/libmemcachedprotocol-0.0/vbucket.h b/libmemcachedprotocol-0.0/vbucket.h index c6c4d8ca..e2cc563e 100644 --- a/libmemcachedprotocol-0.0/vbucket.h +++ b/libmemcachedprotocol-0.0/vbucket.h @@ -1,6 +1,40 @@ -/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -#ifndef MEMCACHED_VBUCKET_H -#define MEMCACHED_VBUCKET_H 1 +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once #ifdef __cplusplus extern "C" @@ -23,4 +57,3 @@ typedef enum { #ifdef __cplusplus } #endif -#endif diff --git a/libmemcachedprotocol/ascii_handler.c b/libmemcachedprotocol/ascii_handler.c new file mode 100644 index 00000000..f6134b3c --- /dev/null +++ b/libmemcachedprotocol/ascii_handler.c @@ -0,0 +1,960 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include +#include + +/** + * Try to parse a key from the string. + * @pointer start pointer to a pointer to the string (IN and OUT) + * @return length of the string of -1 if this was an illegal key (invalid + * characters or invalid length) + * @todo add length! + */ +static uint16_t parse_ascii_key(char **start) +{ + uint16_t len= 0; + char *c= *start; + /* Strip leading whitespaces */ + while (isspace(*c)) + { + ++c; + } + + *start= c; + + while (*c != '\0' && !isspace(*c) && !iscntrl(*c)) + { + ++c; + ++len; + } + + + if (len == 0 || len > 240 || (*c != '\0' && *c != '\r' && iscntrl(*c))) + { + return 0; + } + + return len; +} + +/** + * Spool a zero-terminated string + * @param client destination + * @param text the text to spool + * @return status of the spool operation + */ +static protocol_binary_response_status +spool_string(memcached_protocol_client_st *client, const char *text) +{ + return client->root->spool(client, text, strlen(text)); +} + +/** + * Send a "CLIENT_ERROR" message back to the client with the correct + * format of the command being sent + * @param client the client to send the message to + */ +static void send_command_usage(memcached_protocol_client_st *client) +{ + const char *errmsg[]= { + [GET_CMD]= "CLIENT_ERROR: Syntax error: get *\r\n", + [GETS_CMD]= "CLIENT_ERROR: Syntax error: gets *\r\n", + [SET_CMD]= "CLIENT_ERROR: Syntax error: set [noreply]\r\n", + [ADD_CMD]= "CLIENT_ERROR: Syntax error: add [noreply]\r\n", + [REPLACE_CMD]= "CLIENT_ERROR: Syntax error: replace [noreply]\r\n", + [CAS_CMD]= "CLIENT_ERROR: Syntax error: cas [noreply]\r\n", + [APPEND_CMD]= "CLIENT_ERROR: Syntax error: append [noreply]\r\n", + [PREPEND_CMD]= "CLIENT_ERROR: Syntax error: prepend [noreply]\r\n", + [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete [noreply]\r\n", + [INCR_CMD]= "CLIENT_ERROR: Syntax error: incr [noreply]\r\n", + [DECR_CMD]= "CLIENT_ERROR: Syntax error: decr [noreply]\r\n", + [STATS_CMD]= "CLIENT_ERROR: Syntax error: stats [key]\r\n", + [FLUSH_ALL_CMD]= "CLIENT_ERROR: Syntax error: flush_all [timeout] [noreply]\r\n", + [VERSION_CMD]= "CLIENT_ERROR: Syntax error: version\r\n", + [QUIT_CMD]="CLIENT_ERROR: Syntax error: quit\r\n", + + [VERBOSITY_CMD]= "CLIENT_ERROR: Syntax error: verbosity \r\n", + [UNKNOWN_CMD]= "CLIENT_ERROR: Unknown command\r\n", + }; + + client->mute = false; + spool_string(client, errmsg[client->ascii_command]); +} + +/** + * Callback for the VERSION responses + * @param cookie client identifier + * @param text the length of the body + * @param textlen the length of the body + */ +static protocol_binary_response_status +ascii_version_response_handler(const void *cookie, + const void *text, + uint32_t textlen) +{ + memcached_protocol_client_st *client= (memcached_protocol_client_st*)cookie; + spool_string(client, "VERSION "); + client->root->spool(client, text, textlen); + spool_string(client, "\r\n"); + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Callback for the GET/GETQ/GETK and GETKQ responses + * @param cookie client identifier + * @param key the key for the item + * @param keylen the length of the key + * @param body the length of the body + * @param bodylen the length of the body + * @param flags the flags for the item + * @param cas the CAS id for the item + */ +static protocol_binary_response_status +ascii_get_response_handler(const void *cookie, + const void *key, + uint16_t keylen, + const void *body, + uint32_t bodylen, + uint32_t flags, + uint64_t cas) +{ + memcached_protocol_client_st *client= (void*)cookie; + char buffer[300]; + strcpy(buffer, "VALUE "); + const char *source= key; + char *dest= buffer + 6; + + for (int x= 0; x < keylen; ++x) + { + if (*source != '\0' && !isspace(*source) && !iscntrl(*source)) + { + *dest= *source; + } + else + { + return PROTOCOL_BINARY_RESPONSE_EINVAL; /* key constraints in ascii */ + } + + ++dest; + ++source; + } + + size_t used= (size_t)(dest - buffer); + + if (client->ascii_command == GETS_CMD) + { + snprintf(dest, sizeof(buffer) - used, " %u %u %" PRIu64 "\r\n", flags, + bodylen, cas); + } + else + { + snprintf(dest, sizeof(buffer) - used, " %u %u\r\n", flags, bodylen); + } + + client->root->spool(client, buffer, strlen(buffer)); + client->root->spool(client, body, bodylen); + client->root->spool(client, "\r\n", 2); + + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Callback for the STAT responses + * @param cookie client identifier + * @param key the key for the item + * @param keylen the length of the key + * @param body the length of the body + * @param bodylen the length of the body + */ +static protocol_binary_response_status +ascii_stat_response_handler(const void *cookie, + const void *key, + uint16_t keylen, + const void *body, + uint32_t bodylen) +{ + + memcached_protocol_client_st *client= (void*)cookie; + + if (key != NULL) + { + spool_string(client, "STAT "); + client->root->spool(client, key, keylen); + spool_string(client, " "); + client->root->spool(client, body, bodylen); + spool_string(client, "\r\n"); + } + else + { + spool_string(client, "END\r\n"); + } + + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Process a get or a gets request. + * @param client the client handle + * @param buffer the complete get(s) command + * @param end the last character in the command + */ +static void ascii_process_gets(memcached_protocol_client_st *client, + char *buffer, char *end) +{ + char *key= buffer; + + /* Skip command */ + key += (client->ascii_command == GETS_CMD) ? 5 : 4; + + int num_keys= 0; + while (key < end) + { + uint16_t nkey= parse_ascii_key(&key); + if (nkey == 0) /* Invalid key... stop processing this line */ + { + break; + } + + (void)client->root->callback->interface.v1.get(client, key, nkey, + ascii_get_response_handler); + key += nkey; + ++num_keys; + } + + if (num_keys == 0) + { + send_command_usage(client); + } + else + client->root->spool(client, "END\r\n", 5); +} + +/** + * Try to split up the command line "asdf asdf asdf asdf\n" into an + * argument vector for easier parsing. + * @param start the first character in the command line + * @param end the last character in the command line ("\n") + * @param vec the vector to insert the pointers into + * @size the number of elements in the vector + * @return the number of tokens in the vector + */ +static int ascii_tokenize_command(char *str, char *end, char **vec, int size) +{ + int elem= 0; + + while (str < end) + { + /* Skip leading blanks */ + while (str < end && isspace(*str)) + { + ++str; + } + + if (str == end) + { + return elem; + } + + vec[elem++]= str; + /* find the next non-blank field */ + while (str < end && !isspace(*str)) + { + ++str; + } + + /* zero-terminate it for easier parsing later on */ + *str= '\0'; + ++str; + + /* Is the vector full? */ + if (elem == size) + { + break; + } + } + + return elem; +} + +/** + * If we for some reasons needs to push the line back to read more + * data we have to reverse the tokenization. Just do the brain-dead replace + * of all '\0' to ' ' and set the last character to '\n'. We could have used + * the vector we created, but then we would have to search for all of the + * spaces we ignored... + * @param start pointer to the first character in the buffer to recover + * @param end pointer to the last character in the buffer to recover + */ +static void recover_tokenize_command(char *start, char *end) +{ + while (start < end) + { + if (*start == '\0') + *start= ' '; + ++start; + } + + *end= '\n'; +} + +/** + * Convert the textual command into a comcode + */ +static enum ascii_cmd ascii_to_cmd(char *start, size_t length) +{ + struct { + const char *cmd; + size_t len; + enum ascii_cmd cc; + } commands[]= { + { .cmd= "get", .len= 3, .cc= GET_CMD }, + { .cmd= "gets", .len= 4, .cc= GETS_CMD }, + { .cmd= "set", .len= 3, .cc= SET_CMD }, + { .cmd= "add", .len= 3, .cc= ADD_CMD }, + { .cmd= "replace", .len= 7, .cc= REPLACE_CMD }, + { .cmd= "cas", .len= 3, .cc= CAS_CMD }, + { .cmd= "append", .len= 6, .cc= APPEND_CMD }, + { .cmd= "prepend", .len= 7, .cc= PREPEND_CMD }, + { .cmd= "delete", .len= 6, .cc= DELETE_CMD }, + { .cmd= "incr", .len= 4, .cc= INCR_CMD }, + { .cmd= "decr", .len= 4, .cc= DECR_CMD }, + { .cmd= "stats", .len= 5, .cc= STATS_CMD }, + { .cmd= "flush_all", .len= 9, .cc= FLUSH_ALL_CMD }, + { .cmd= "version", .len= 7, .cc= VERSION_CMD }, + { .cmd= "quit", .len= 4, .cc= QUIT_CMD }, + { .cmd= "verbosity", .len= 9, .cc= VERBOSITY_CMD }, + { .cmd= NULL, .len= 0, .cc= UNKNOWN_CMD }}; + + int x= 0; + while (commands[x].len > 0) { + if (length >= commands[x].len) + { + if (strncmp(start, commands[x].cmd, commands[x].len) == 0) + { + /* Potential hit */ + if (length == commands[x].len || isspace(*(start + commands[x].len))) + { + return commands[x].cc; + } + } + } + ++x; + } + + return UNKNOWN_CMD; +} + +/** + * Perform a delete operation. + * + * @param client client requesting the deletion + * @param tokens the command as a vector + * @param ntokens the number of items in the vector + */ +static void process_delete(memcached_protocol_client_st *client, + char **tokens, int ntokens) +{ + char *key= tokens[1]; + uint16_t nkey; + + if (ntokens != 2 || (nkey= parse_ascii_key(&key)) == 0) + { + send_command_usage(client); + return; + } + + if (client->root->callback->interface.v1.delete == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return; + } + + protocol_binary_response_status rval; + rval= client->root->callback->interface.v1.delete(client, key, nkey, 0); + + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) + { + spool_string(client, "DELETED\r\n"); + } + else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT) + { + spool_string(client, "NOT_FOUND\r\n"); + } + else + { + char msg[80]; + snprintf(msg, sizeof(msg), "SERVER_ERROR: delete failed %u\r\n",(uint32_t)rval); + spool_string(client, msg); + } +} + +static void process_arithmetic(memcached_protocol_client_st *client, + char **tokens, int ntokens) +{ + char *key= tokens[1]; + uint16_t nkey; + + if (ntokens != 3 || (nkey= parse_ascii_key(&key)) == 0) + { + send_command_usage(client); + return; + } + + uint64_t cas; + uint64_t result; + uint64_t delta= strtoull(tokens[2], NULL, 10); + + protocol_binary_response_status rval; + if (client->ascii_command == INCR_CMD) + { + if (client->root->callback->interface.v1.increment == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return; + } + rval= client->root->callback->interface.v1.increment(client, + key, nkey, + delta, 0, + 0, + &result, + &cas); + } + else + { + if (client->root->callback->interface.v1.decrement == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return; + } + rval= client->root->callback->interface.v1.decrement(client, + key, nkey, + delta, 0, + 0, + &result, + &cas); + } + + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) + { + char buffer[80]; + snprintf(buffer, sizeof(buffer), "%"PRIu64"\r\n", result); + spool_string(client, buffer); + } + else + { + spool_string(client, "NOT_FOUND\r\n"); + } +} + +/** + * Process the stats command (with or without a key specified) + * @param key pointer to the first character after "stats" + * @param end pointer to the "\n" + */ +static void process_stats(memcached_protocol_client_st *client, + char *key, char *end) +{ + if (client->root->callback->interface.v1.stat == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return; + } + + while (isspace(*key)) + key++; + + uint16_t nkey= (uint16_t)(end - key); + (void)client->root->callback->interface.v1.stat(client, key, nkey, + ascii_stat_response_handler); +} + +static void process_version(memcached_protocol_client_st *client, + char **tokens, int ntokens) +{ + (void)tokens; + if (ntokens != 1) + { + send_command_usage(client); + return; + } + + if (client->root->callback->interface.v1.version == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return; + } + + client->root->callback->interface.v1.version(client, + ascii_version_response_handler); +} + +static void process_flush(memcached_protocol_client_st *client, + char **tokens, int ntokens) +{ + if (ntokens > 2) + { + send_command_usage(client); + return; + } + + if (client->root->callback->interface.v1.flush == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return; + } + + uint32_t timeout= 0; + if (ntokens == 2) + { + timeout= (uint32_t)strtoul(tokens[1], NULL, 10); + } + + protocol_binary_response_status rval; + rval= client->root->callback->interface.v1.flush(client, timeout); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) + spool_string(client, "OK\r\n"); + else + spool_string(client, "SERVER_ERROR: internal error\r\n"); +} + +/** + * Process one of the storage commands + * @param client the client performing the operation + * @param tokens the command tokens + * @param ntokens the number of tokens + * @param start pointer to the first character in the line + * @param end pointer to the pointer where the last character of this + * command is (IN and OUT) + * @param length the number of bytes available + * @return -1 if an error occurs (and we should just terminate the connection + * because we are out of sync) + * 0 storage command completed, continue processing + * 1 We need more data, so just go ahead and wait for more! + */ +static inline int process_storage_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + (void)ntokens; /* already checked */ + char *key= tokens[1]; + uint16_t nkey= parse_ascii_key(&key); + if (nkey == 0) + { + /* return error */ + spool_string(client, "CLIENT_ERROR: bad key\r\n"); + return -1; + } + + uint32_t flags= (uint32_t)strtoul(tokens[2], NULL, 10); + uint32_t timeout= (uint32_t)strtoul(tokens[3], NULL, 10); + unsigned long nbytes= strtoul(tokens[4], NULL, 10); + + /* Do we have all data? */ + unsigned long need= nbytes + (unsigned long)((*end - start) + 1) + 2; /* \n\r\n */ + if ((ssize_t)need > length) + { + /* Keep on reading */ + recover_tokenize_command(start, *end); + return 1; + } + + void *data= (*end) + 1; + uint64_t cas= 0; + uint64_t result_cas; + protocol_binary_response_status rval; + switch (client->ascii_command) + { + case SET_CMD: + rval= client->root->callback->interface.v1.set(client, key, + (uint16_t)nkey, + data, + (uint32_t)nbytes, + flags, + timeout, cas, + &result_cas); + break; + case ADD_CMD: + rval= client->root->callback->interface.v1.add(client, key, + (uint16_t)nkey, + data, + (uint32_t)nbytes, + flags, + timeout, &result_cas); + break; + case CAS_CMD: + cas= strtoull(tokens[5], NULL, 10); + /* FALLTHROUGH */ + case REPLACE_CMD: + rval= client->root->callback->interface.v1.replace(client, key, + (uint16_t)nkey, + data, + (uint32_t)nbytes, + flags, + timeout, cas, + &result_cas); + break; + case APPEND_CMD: + rval= client->root->callback->interface.v1.append(client, key, + (uint16_t)nkey, + data, + (uint32_t)nbytes, + cas, + &result_cas); + break; + case PREPEND_CMD: + rval= client->root->callback->interface.v1.prepend(client, key, + (uint16_t)nkey, + data, + (uint32_t)nbytes, + cas, + &result_cas); + break; + + /* gcc complains if I don't put all of the enums in here.. */ + case GET_CMD: + case GETS_CMD: + case DELETE_CMD: + case DECR_CMD: + case INCR_CMD: + case STATS_CMD: + case FLUSH_ALL_CMD: + case VERSION_CMD: + case QUIT_CMD: + case VERBOSITY_CMD: + case UNKNOWN_CMD: + default: + abort(); /* impossible */ + } + + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS) + { + spool_string(client, "STORED\r\n"); + } + else + { + if (client->ascii_command == CAS_CMD) + { + if (rval == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS) + { + spool_string(client, "EXISTS\r\n"); + } + else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT) + { + spool_string(client, "NOT_FOUND\r\n"); + } + else + { + spool_string(client, "NOT_STORED\r\n"); + } + } + else + { + spool_string(client, "NOT_STORED\r\n"); + } + } + + *end += nbytes + 2; + + return 0; +} + +static int process_cas_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + if (ntokens != 6) + { + send_command_usage(client); + return false; + } + + if (client->root->callback->interface.v1.replace == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return false; + } + + return process_storage_command(client, tokens, ntokens, start, end, length); +} + +static int process_set_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + if (ntokens != 5) + { + send_command_usage(client); + return false; + } + + if (client->root->callback->interface.v1.set == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return false; + } + + return process_storage_command(client, tokens, ntokens, start, end, length); +} + +static int process_add_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + if (ntokens != 5) + { + send_command_usage(client); + return false; + } + + if (client->root->callback->interface.v1.add == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return false; + } + + return process_storage_command(client, tokens, ntokens, start, end, length); +} + +static int process_replace_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + if (ntokens != 5) + { + send_command_usage(client); + return false; + } + + if (client->root->callback->interface.v1.replace == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return false; + } + + return process_storage_command(client, tokens, ntokens, start, end, length); +} + +static int process_append_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + if (ntokens != 5) + { + send_command_usage(client); + return false; + } + + if (client->root->callback->interface.v1.append == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return false; + } + + return process_storage_command(client, tokens, ntokens, start, end, length); +} + +static int process_prepend_command(memcached_protocol_client_st *client, + char **tokens, int ntokens, char *start, + char **end, ssize_t length) +{ + if (ntokens != 5) + { + send_command_usage(client); + return false; + } + + if (client->root->callback->interface.v1.prepend == NULL) + { + spool_string(client, "SERVER_ERROR: callback not implemented\r\n"); + return false; + } + + return process_storage_command(client, tokens, ntokens, start, end, length); +} + +/** + * The ASCII protocol support is just one giant big hack. Instead of adding + * a optimal ascii support, I just convert the ASCII commands to the binary + * protocol and calls back into the command handlers for the binary protocol ;) + */ +memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr) +{ + char *ptr= (char*)client->root->input_buffer; + *endptr= ptr; + + do { + /* Do we have \n (indicating the command preamble)*/ + char *end= memchr(ptr, '\n', (size_t)*length); + if (end == NULL) + { + *endptr= ptr; + return MEMCACHED_PROTOCOL_READ_EVENT; + } + + client->ascii_command= ascii_to_cmd(ptr, (size_t)(*length)); + + /* A multiget lists all of the keys, and I don't want to have an + * avector of let's say 512 pointers to tokenize all of them, so let's + * just handle them immediately + */ + if (client->ascii_command == GET_CMD || + client->ascii_command == GETS_CMD) { + if (client->root->callback->interface.v1.get != NULL) + ascii_process_gets(client, ptr, end); + else + spool_string(client, "SERVER_ERROR: Command not implemented\n"); + } else { + /* None of the defined commands takes 10 parameters, so lets just use + * that as a maximum limit. + */ + char *tokens[10]; + int ntokens= ascii_tokenize_command(ptr, end, tokens, 10); + + if (ntokens < 10) + { + client->mute= strcmp(tokens[ntokens - 1], "noreply") == 0; + if (client->mute) + --ntokens; /* processed noreply token*/ + } + + int error= 0; + + switch (client->ascii_command) { + case SET_CMD: + error= process_set_command(client, tokens, ntokens, ptr, &end, *length); + break; + case ADD_CMD: + error= process_add_command(client, tokens, ntokens, ptr, &end, *length); + break; + case REPLACE_CMD: + error= process_replace_command(client, tokens, ntokens, + ptr, &end, *length); + break; + case CAS_CMD: + error= process_cas_command(client, tokens, ntokens, ptr, &end, *length); + break; + case APPEND_CMD: + error= process_append_command(client, tokens, ntokens, + ptr, &end, *length); + break; + case PREPEND_CMD: + error= process_prepend_command(client, tokens, ntokens, + ptr, &end, *length); + break; + case DELETE_CMD: + process_delete(client, tokens, ntokens); + break; + + case INCR_CMD: /* FALLTHROUGH */ + case DECR_CMD: + process_arithmetic(client, tokens, ntokens); + break; + case STATS_CMD: + if (client->mute) + { + send_command_usage(client); + } + else + { + recover_tokenize_command(ptr, end); + process_stats(client, ptr + 6, end); + } + break; + case FLUSH_ALL_CMD: + process_flush(client, tokens, ntokens); + break; + case VERSION_CMD: + if (client->mute) + { + send_command_usage(client); + } + else + { + process_version(client, tokens, ntokens); + } + break; + case QUIT_CMD: + if (ntokens != 1 || client->mute) + { + send_command_usage(client); + } + else + { + if (client->root->callback->interface.v1.quit != NULL) + client->root->callback->interface.v1.quit(client); + + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + break; + + case VERBOSITY_CMD: + if (ntokens != 2) + send_command_usage(client); + else + spool_string(client, "OK\r\n"); + break; + + case UNKNOWN_CMD: + send_command_usage(client); + break; + + case GET_CMD: + case GETS_CMD: + default: + /* Should already be handled */ + abort(); + } + + if (error == -1) + return MEMCACHED_PROTOCOL_ERROR_EVENT; + else if (error == 1) + return MEMCACHED_PROTOCOL_READ_EVENT; + } + + /* Move past \n */ + ++end; + *length -= end - ptr; + ptr= end; + } while (*length > 0); + + *endptr= ptr; + return MEMCACHED_PROTOCOL_READ_EVENT; +} diff --git a/libmemcachedprotocol/ascii_handler.h b/libmemcachedprotocol/ascii_handler.h new file mode 100644 index 00000000..02f8831e --- /dev/null +++ b/libmemcachedprotocol/ascii_handler.h @@ -0,0 +1,40 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +LIBMEMCACHED_LOCAL +memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr); diff --git a/libmemcachedprotocol/binary_handler.c b/libmemcachedprotocol/binary_handler.c new file mode 100644 index 00000000..c2fd4414 --- /dev/null +++ b/libmemcachedprotocol/binary_handler.c @@ -0,0 +1,1120 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +/* +** ********************************************************************** +** INTERNAL INTERFACE +** ********************************************************************** +*/ + +/** + * Send a preformatted packet back to the client. If the connection is in + * pedantic mode, it will validate the packet and refuse to send it if it + * breaks the specification. + * + * @param cookie client identification + * @param request the original request packet + * @param response the packet to send + * @return The status of the operation + */ +static protocol_binary_response_status +raw_response_handler(const void *cookie, + protocol_binary_request_header *request, + protocol_binary_response_header *response) +{ + memcached_protocol_client_st *client= (void*)cookie; + + if (client->root->pedantic && + !memcached_binary_protocol_pedantic_check_response(request, response)) + { + return PROTOCOL_BINARY_RESPONSE_EINVAL; + } + + if (!client->root->drain(client)) + { + return PROTOCOL_BINARY_RESPONSE_EINTERNAL; + } + + size_t len= sizeof(*response) + htonl(response->response.bodylen); + size_t offset= 0; + char *ptr= (void*)response; + + if (client->output == NULL) + { + /* I can write directly to the socket.... */ + do + { + size_t num_bytes= len - offset; + ssize_t nw= client->root->send(client, + client->sock, + ptr + offset, + num_bytes); + if (nw == -1) + { + if (get_socket_errno() == EWOULDBLOCK) + { + break; + } + else if (get_socket_errno() != EINTR) + { + client->error= errno; + return PROTOCOL_BINARY_RESPONSE_EINTERNAL; + } + } + else + { + offset += (size_t)nw; + } + } while (offset < len); + } + + return client->root->spool(client, ptr, len - offset); +} + +/* + * Version 0 of the interface is really low level and protocol specific, + * while the version 1 of the interface is more API focused. We need a + * way to translate between the command codes on the wire and the + * application level interface in V1, so let's just use the V0 of the + * interface as a map instead of creating a huuuge switch :-) + */ + +/** + * Callback for the GET/GETQ/GETK and GETKQ responses + * @param cookie client identifier + * @param key the key for the item + * @param keylen the length of the key + * @param body the length of the body + * @param bodylen the length of the body + * @param flags the flags for the item + * @param cas the CAS id for the item + */ +static protocol_binary_response_status +get_response_handler(const void *cookie, + const void *key, + uint16_t keylen, + const void *body, + uint32_t bodylen, + uint32_t flags, + uint64_t cas) { + + memcached_protocol_client_st *client= (void*)cookie; + uint8_t opcode= client->current_command->request.opcode; + + if (opcode == PROTOCOL_BINARY_CMD_GET || opcode == PROTOCOL_BINARY_CMD_GETQ) + { + keylen= 0; + } + + protocol_binary_response_get response= { + .message.header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= opcode, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= client->current_command->request.opaque, + .cas= memcached_htonll(cas), + .keylen= htons(keylen), + .extlen= 4, + .bodylen= htonl(bodylen + keylen + 4), + }, + }; + + response.message.body.flags= htonl(flags); + + protocol_binary_response_status rval; + const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS; + if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success || + (rval= client->root->spool(client, key, keylen)) != success || + (rval= client->root->spool(client, body, bodylen)) != success) + { + return rval; + } + + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Callback for the STAT responses + * @param cookie client identifier + * @param key the key for the item + * @param keylen the length of the key + * @param body the length of the body + * @param bodylen the length of the body + */ +static protocol_binary_response_status stat_response_handler(const void *cookie, + const void *key, + uint16_t keylen, + const void *body, + uint32_t bodylen) +{ + + memcached_protocol_client_st *client= (void*)cookie; + + protocol_binary_response_no_extras response= { + .message.header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= client->current_command->request.opcode, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= client->current_command->request.opaque, + .keylen= htons(keylen), + .bodylen= htonl(bodylen + keylen), + .cas= 0 + }, + }; + + protocol_binary_response_status rval; + const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS; + if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success || + (rval= client->root->spool(client, key, keylen)) != success || + (rval= client->root->spool(client, body, bodylen)) != success) + { + return rval; + } + + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Callback for the VERSION responses + * @param cookie client identifier + * @param text the length of the body + * @param textlen the length of the body + */ +static protocol_binary_response_status +version_response_handler(const void *cookie, + const void *text, + uint32_t textlen) { + + memcached_protocol_client_st *client= (void*)cookie; + + protocol_binary_response_no_extras response= { + .message.header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= client->current_command->request.opcode, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= client->current_command->request.opaque, + .bodylen= htonl(textlen), + .cas= 0 + }, + }; + + protocol_binary_response_status rval; + const protocol_binary_response_status success= PROTOCOL_BINARY_RESPONSE_SUCCESS; + if ((rval= client->root->spool(client, response.bytes, sizeof(response.bytes))) != success || + (rval= client->root->spool(client, text, textlen)) != success) + { + return rval; + } + + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Callback for ADD and ADDQ + * @param cookie the calling client + * @param header the add/addq command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +add_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.add != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8; + protocol_binary_request_add *request= (void*)header; + uint32_t flags= ntohl(request->message.body.flags); + uint32_t timeout= ntohl(request->message.body.expiration); + char *key= ((char*)header) + sizeof(*header) + 8; + char *data= key + keylen; + uint64_t cas; + + rval= client->root->callback->interface.v1.add(cookie, key, keylen, + data, datalen, flags, + timeout, &cas); + + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_ADD) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_ADD, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(cas) + } + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for DECREMENT and DECREMENTQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +decrement_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.decrement != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + protocol_binary_request_decr *request= (void*)header; + uint64_t init= memcached_ntohll(request->message.body.initial); + uint64_t delta= memcached_ntohll(request->message.body.delta); + uint32_t timeout= ntohl(request->message.body.expiration); + void *key= request->bytes + sizeof(request->bytes); + uint64_t result; + uint64_t cas; + + rval= client->root->callback->interface.v1.decrement(cookie, key, keylen, + delta, init, timeout, + &result, &cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_DECREMENT) + { + /* Send a positive request */ + protocol_binary_response_decr response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_DECREMENT, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(cas), + .bodylen= htonl(8) + }, + .body.value= memcached_htonll(result) + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for DELETE and DELETEQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +delete_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.delete != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + void *key= (header +1); + uint64_t cas= memcached_ntohll(header->request.cas); + rval= client->root->callback->interface.v1.delete(cookie, key, keylen, cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_DELETE) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_DELETE, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + } + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for FLUSH and FLUSHQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +flush_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.flush != NULL) + { + protocol_binary_request_flush *flush= (void*)header; + uint32_t timeout= 0; + if (htonl(header->request.bodylen) == 4) + { + timeout= ntohl(flush->message.body.expiration); + } + + rval= client->root->callback->interface.v1.flush(cookie, timeout); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_FLUSH) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_FLUSH, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + } + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for GET, GETK, GETQ, GETKQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +get_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.get != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + void *key= (header + 1); + rval= client->root->callback->interface.v1.get(cookie, key, keylen, + get_response_handler); + + if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT && + (header->request.opcode == PROTOCOL_BINARY_CMD_GETQ || + header->request.opcode == PROTOCOL_BINARY_CMD_GETKQ)) + { + /* Quiet commands shouldn't respond on cache misses */ + rval= PROTOCOL_BINARY_RESPONSE_SUCCESS; + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for INCREMENT and INCREMENTQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +increment_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.increment != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + protocol_binary_request_incr *request= (void*)header; + uint64_t init= memcached_ntohll(request->message.body.initial); + uint64_t delta= memcached_ntohll(request->message.body.delta); + uint32_t timeout= ntohl(request->message.body.expiration); + void *key= request->bytes + sizeof(request->bytes); + uint64_t cas; + uint64_t result; + + rval= client->root->callback->interface.v1.increment(cookie, key, keylen, + delta, init, timeout, + &result, &cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENT) + { + /* Send a positive request */ + protocol_binary_response_incr response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_INCREMENT, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(cas), + .bodylen= htonl(8) + }, + .body.value= memcached_htonll(result) + } + }; + + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for noop. Inform the v1 interface about the noop packet, and + * create and send a packet back to the client + * + * @param cookie the calling client + * @param header the command + * @param response_handler the response handler + * @return the result of the operation + */ +static protocol_binary_response_status +noop_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.noop != NULL) + { + client->root->callback->interface.v1.noop(cookie); + } + + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_NOOP, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + } + } + }; + + return response_handler(cookie, header, (void*)&response); +} + +/** + * Callback for APPEND and APPENDQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +append_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.append != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + uint32_t datalen= ntohl(header->request.bodylen) - keylen; + char *key= (void*)(header +1); + char *data= key +keylen; + uint64_t cas= memcached_ntohll(header->request.cas); + uint64_t result_cas; + + rval= client->root->callback->interface.v1.append(cookie, key, keylen, + data, datalen, cas, + &result_cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_APPEND) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_APPEND, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(result_cas), + }, + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for PREPEND and PREPENDQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +prepend_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.prepend != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + uint32_t datalen= ntohl(header->request.bodylen) - keylen; + char *key= (char*)(header + 1); + char *data= key + keylen; + uint64_t cas= memcached_ntohll(header->request.cas); + uint64_t result_cas; + rval= client->root->callback->interface.v1.prepend(cookie, key, keylen, + data, datalen, cas, + &result_cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_PREPEND) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_PREPEND, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(result_cas), + }, + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for QUIT and QUITQ. Notify the client and shut down the connection + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +quit_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.quit != NULL) + { + client->root->callback->interface.v1.quit(cookie); + } + + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_QUIT, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque + } + } + }; + + if (header->request.opcode == PROTOCOL_BINARY_CMD_QUIT) + { + response_handler(cookie, header, (void*)&response); + } + + /* I need a better way to signal to close the connection */ + return PROTOCOL_BINARY_RESPONSE_EINTERNAL; +} + +/** + * Callback for REPLACE and REPLACEQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +replace_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.replace != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8; + protocol_binary_request_replace *request= (void*)header; + uint32_t flags= ntohl(request->message.body.flags); + uint32_t timeout= ntohl(request->message.body.expiration); + char *key= ((char*)header) + sizeof(*header) + 8; + char *data= key + keylen; + uint64_t cas= memcached_ntohll(header->request.cas); + uint64_t result_cas; + + rval= client->root->callback->interface.v1.replace(cookie, key, keylen, + data, datalen, flags, + timeout, cas, + &result_cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_REPLACE) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_REPLACE, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(result_cas), + }, + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for SET and SETQ + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +set_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.set != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + uint32_t datalen= ntohl(header->request.bodylen) - keylen - 8; + protocol_binary_request_replace *request= (void*)header; + uint32_t flags= ntohl(request->message.body.flags); + uint32_t timeout= ntohl(request->message.body.expiration); + char *key= ((char*)header) + sizeof(*header) + 8; + char *data= key + keylen; + uint64_t cas= memcached_ntohll(header->request.cas); + uint64_t result_cas; + + + rval= client->root->callback->interface.v1.set(cookie, key, keylen, + data, datalen, flags, + timeout, cas, &result_cas); + if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS && + header->request.opcode == PROTOCOL_BINARY_CMD_SET) + { + /* Send a positive request */ + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= PROTOCOL_BINARY_CMD_SET, + .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS), + .opaque= header->request.opaque, + .cas= memcached_ntohll(result_cas), + }, + } + }; + rval= response_handler(cookie, header, (void*)&response); + } + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for STAT + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +stat_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.stat != NULL) + { + uint16_t keylen= ntohs(header->request.keylen); + + rval= client->root->callback->interface.v1.stat(cookie, + (void*)(header + 1), + keylen, + stat_response_handler); + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * Callback for VERSION + * @param cookie the calling client + * @param header the command + * @param response_handler not used + * @return the result of the operation + */ +static protocol_binary_response_status +version_command_handler(const void *cookie, + protocol_binary_request_header *header, + memcached_binary_protocol_raw_response_handler response_handler) +{ + (void)response_handler; + (void)header; + protocol_binary_response_status rval; + + memcached_protocol_client_st *client= (void*)cookie; + if (client->root->callback->interface.v1.version != NULL) + { + rval= client->root->callback->interface.v1.version(cookie, + version_response_handler); + } + else + { + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + } + + return rval; +} + +/** + * The map to remap between the com codes and the v1 logical setting + */ +static memcached_binary_protocol_command_handler comcode_v0_v1_remap[256]= { + [PROTOCOL_BINARY_CMD_ADDQ]= add_command_handler, + [PROTOCOL_BINARY_CMD_ADD]= add_command_handler, + [PROTOCOL_BINARY_CMD_APPENDQ]= append_command_handler, + [PROTOCOL_BINARY_CMD_APPEND]= append_command_handler, + [PROTOCOL_BINARY_CMD_DECREMENTQ]= decrement_command_handler, + [PROTOCOL_BINARY_CMD_DECREMENT]= decrement_command_handler, + [PROTOCOL_BINARY_CMD_DELETEQ]= delete_command_handler, + [PROTOCOL_BINARY_CMD_DELETE]= delete_command_handler, + [PROTOCOL_BINARY_CMD_FLUSHQ]= flush_command_handler, + [PROTOCOL_BINARY_CMD_FLUSH]= flush_command_handler, + [PROTOCOL_BINARY_CMD_GETKQ]= get_command_handler, + [PROTOCOL_BINARY_CMD_GETK]= get_command_handler, + [PROTOCOL_BINARY_CMD_GETQ]= get_command_handler, + [PROTOCOL_BINARY_CMD_GET]= get_command_handler, + [PROTOCOL_BINARY_CMD_INCREMENTQ]= increment_command_handler, + [PROTOCOL_BINARY_CMD_INCREMENT]= increment_command_handler, + [PROTOCOL_BINARY_CMD_NOOP]= noop_command_handler, + [PROTOCOL_BINARY_CMD_PREPENDQ]= prepend_command_handler, + [PROTOCOL_BINARY_CMD_PREPEND]= prepend_command_handler, + [PROTOCOL_BINARY_CMD_QUITQ]= quit_command_handler, + [PROTOCOL_BINARY_CMD_QUIT]= quit_command_handler, + [PROTOCOL_BINARY_CMD_REPLACEQ]= replace_command_handler, + [PROTOCOL_BINARY_CMD_REPLACE]= replace_command_handler, + [PROTOCOL_BINARY_CMD_SETQ]= set_command_handler, + [PROTOCOL_BINARY_CMD_SET]= set_command_handler, + [PROTOCOL_BINARY_CMD_STAT]= stat_command_handler, + [PROTOCOL_BINARY_CMD_VERSION]= version_command_handler, +}; + +/** + * Try to execute a command. Fire the pre/post functions and the specialized + * handler function if it's set. If not, the unknown probe should be fired + * if it's present. + * @param client the client connection to operate on + * @param header the command to execute + * @return true if success or false if a fatal error occured so that the + * connection should be shut down. + */ +static protocol_binary_response_status execute_command(memcached_protocol_client_st *client, protocol_binary_request_header *header) +{ + if (client->root->pedantic && + memcached_binary_protocol_pedantic_check_request(header)) + { + /* @todo return invalid command packet */ + } + + /* we got all data available, execute the callback! */ + if (client->root->callback->pre_execute != NULL) + { + client->root->callback->pre_execute(client, header); + } + + protocol_binary_response_status rval; + rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; + uint8_t cc= header->request.opcode; + + switch (client->root->callback->interface_version) + { + case 0: + if (client->root->callback->interface.v0.comcode[cc] != NULL) { + rval= client->root->callback->interface.v0.comcode[cc](client, header, raw_response_handler); + } + break; + case 1: + if (comcode_v0_v1_remap[cc] != NULL) { + rval= comcode_v0_v1_remap[cc](client, header, raw_response_handler); + } + break; + default: + /* Unknown interface. + * It should be impossible to get here so I'll just call abort + * to avoid getting a compiler warning :-) + */ + abort(); + } + + + if (rval == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND && + client->root->callback->unknown != NULL) + { + rval= client->root->callback->unknown(client, header, raw_response_handler); + } + + if (rval != PROTOCOL_BINARY_RESPONSE_SUCCESS && + rval != PROTOCOL_BINARY_RESPONSE_EINTERNAL && + rval != PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) + { + protocol_binary_response_no_extras response= { + .message= { + .header.response= { + .magic= PROTOCOL_BINARY_RES, + .opcode= cc, + .status= htons(rval), + .opaque= header->request.opaque, + }, + } + }; + rval= raw_response_handler(client, header, (void*)&response); + } + + if (client->root->callback->post_execute != NULL) + { + client->root->callback->post_execute(client, header); + } + + return rval; +} + +/* +** ********************************************************************** +** "PROTOECTED" INTERFACE +** ********************************************************************** +*/ +memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr) +{ + /* try to parse all of the received packets */ + protocol_binary_request_header *header; + header= (void*)client->root->input_buffer; + if (header->request.magic != (uint8_t)PROTOCOL_BINARY_REQ) + { + client->error= EINVAL; + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + ssize_t len= *length; + + while (len >= (ssize_t)sizeof(*header) && + (len >= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen)))) + { + /* I have the complete package */ + client->current_command= header; + protocol_binary_response_status rv= execute_command(client, header); + + if (rv == PROTOCOL_BINARY_RESPONSE_EINTERNAL) + { + *length= len; + *endptr= (void*)header; + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } else if (rv == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) + return MEMCACHED_PROTOCOL_PAUSE_EVENT; + + ssize_t total= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen)); + len -= total; + if (len > 0) + { + intptr_t ptr= (intptr_t)header; + ptr += total; + if ((ptr % 8) == 0) + { + header= (void*)ptr; + } + else + { + /* Fix alignment */ + memmove(client->root->input_buffer, (void*)ptr, (size_t)len); + header= (void*)client->root->input_buffer; + } + } + *length= len; + *endptr= (void*)header; + } + + return MEMCACHED_PROTOCOL_READ_EVENT; +} + +/* +** ********************************************************************** +** PUBLIC INTERFACE +** ********************************************************************** +*/ +memcached_binary_protocol_callback_st *memcached_binary_protocol_get_callbacks(memcached_protocol_st *instance) +{ + return instance->callback; +} + +void memcached_binary_protocol_set_callbacks(memcached_protocol_st *instance, memcached_binary_protocol_callback_st *callback) +{ + instance->callback= callback; +} + +memcached_binary_protocol_raw_response_handler memcached_binary_protocol_get_raw_response_handler(const void *cookie) +{ + (void)cookie; + return raw_response_handler; +} + +void memcached_binary_protocol_set_pedantic(memcached_protocol_st *instance, bool enable) +{ + instance->pedantic= enable; +} + +bool memcached_binary_protocol_get_pedantic(memcached_protocol_st *instance) +{ + return instance->pedantic; +} + diff --git a/libmemcachedprotocol/binary_handler.h b/libmemcachedprotocol/binary_handler.h new file mode 100644 index 00000000..d5a74e78 --- /dev/null +++ b/libmemcachedprotocol/binary_handler.h @@ -0,0 +1,47 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +LIBMEMCACHED_LOCAL +bool memcached_binary_protocol_pedantic_check_request(const protocol_binary_request_header *request); + +LIBMEMCACHED_LOCAL +bool memcached_binary_protocol_pedantic_check_response(const protocol_binary_request_header *request, + const protocol_binary_response_header *response); + +LIBMEMCACHED_LOCAL +memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr); diff --git a/libmemcachedprotocol/cache.c b/libmemcachedprotocol/cache.c new file mode 100644 index 00000000..ed669f4f --- /dev/null +++ b/libmemcachedprotocol/cache.c @@ -0,0 +1,187 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +#include + +#ifndef NDEBUG +const uint64_t redzone_pattern = 0xdeadbeefcafebabe; +int cache_error = 0; +#endif + +const size_t initial_pool_size = 64; + +cache_t* cache_create(const char *name, size_t bufsize, size_t align, + cache_constructor_t* constructor, + cache_destructor_t* destructor) { + cache_t* ret = calloc(1, sizeof(cache_t)); + size_t name_length= strlen(name); + char* nm= calloc(1, (sizeof(char) * name_length) +1); + memcpy(nm, name, name_length); + void** ptr = calloc(initial_pool_size, bufsize); + if (ret == NULL || nm == NULL || ptr == NULL || + pthread_mutex_init(&ret->mutex, NULL) == -1) { + free(ret); + free(nm); + free(ptr); + return NULL; + } + + ret->name = nm; + ret->ptr = ptr; + ret->freetotal = initial_pool_size; + ret->constructor = constructor; + ret->destructor = destructor; + +#ifndef NDEBUG + ret->bufsize = bufsize + 2 * sizeof(redzone_pattern); +#else + ret->bufsize = bufsize; +#endif + + (void)align; + + return ret; +} + +static inline void* get_object(void *ptr) { +#ifndef NDEBUG + uint64_t *pre = ptr; + return pre + 1; +#else + return ptr; +#endif +} + +void cache_destroy(cache_t *cache) { + while (cache->freecurr > 0) { + void *ptr = cache->ptr[--cache->freecurr]; + if (cache->destructor) { + cache->destructor(get_object(ptr), NULL); + } + free(ptr); + } + free(cache->name); + free(cache->ptr); + pthread_mutex_destroy(&cache->mutex); +} + +void* cache_alloc(cache_t *cache) { + void *ret; + void *object; + pthread_mutex_lock(&cache->mutex); + if (cache->freecurr > 0) { + ret = cache->ptr[--cache->freecurr]; + object = get_object(ret); + } else { + object = ret = malloc(cache->bufsize); + if (ret != NULL) { + object = get_object(ret); + + if (cache->constructor != NULL && + cache->constructor(object, NULL, 0) != 0) { + free(ret); + object = NULL; + } + } + } + pthread_mutex_unlock(&cache->mutex); + +#ifndef NDEBUG + if (object != NULL) { + /* add a simple form of buffer-check */ + uint64_t *pre = ret; + *pre = redzone_pattern; + ret = pre+1; + memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)), + &redzone_pattern, sizeof(redzone_pattern)); + } +#endif + + return object; +} + +void cache_free(cache_t *cache, void *ptr) { + pthread_mutex_lock(&cache->mutex); + +#ifndef NDEBUG + /* validate redzone... */ + if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)), + &redzone_pattern, sizeof(redzone_pattern)) != 0) { + raise(SIGABRT); + cache_error = 1; + pthread_mutex_unlock(&cache->mutex); + return; + } + uint64_t *pre = ptr; + --pre; + if (*pre != redzone_pattern) { + raise(SIGABRT); + cache_error = -1; + pthread_mutex_unlock(&cache->mutex); + return; + } + ptr = pre; +#endif + if (cache->freecurr < cache->freetotal) { + cache->ptr[cache->freecurr++] = ptr; + } else { + /* try to enlarge free connections array */ + size_t newtotal = cache->freetotal * 2; + void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal); + if (new_free) { + cache->freetotal = newtotal; + cache->ptr = new_free; + cache->ptr[cache->freecurr++] = ptr; + } else { + if (cache->destructor) { + cache->destructor(ptr, NULL); + } + free(ptr); + + } + } + pthread_mutex_unlock(&cache->mutex); +} + diff --git a/libmemcachedprotocol/cache.h b/libmemcachedprotocol/cache.h new file mode 100644 index 00000000..6f84feaa --- /dev/null +++ b/libmemcachedprotocol/cache.h @@ -0,0 +1,148 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#pragma once + +#include + +#ifdef HAVE_UMEM_H +# include +# define cache_t umem_cache_t +# define cache_alloc(a) umem_cache_alloc(a, UMEM_DEFAULT) +# define cache_free(a, b) umem_cache_free(a, b) +# define cache_create(a,b,c,d,e) umem_cache_create((char*)a, b, c, d, e, NULL, NULL, NULL, 0) +# define cache_destroy(a) umem_cache_destroy(a); +#else +# ifndef NDEBUG +/* may be used for debug purposes */ +extern int cache_error; +# endif + +/** + * Constructor used to initialize allocated objects + * + * @param obj pointer to the object to initialized. + * @param notused1 This parameter is currently not used. + * @param notused2 This parameter is currently not used. + * @return you should return 0, but currently this is not checked + */ +typedef int cache_constructor_t(void* obj, void* notused1, int notused2); +/** + * Destructor used to clean up allocated objects before they are + * returned to the operating system. + * + * @param obj pointer to the object to initialized. + * @param notused1 This parameter is currently not used. + * @param notused2 This parameter is currently not used. + * @return you should return 0, but currently this is not checked + */ +typedef void cache_destructor_t(void* obj, void* notused); + +/** + * Definition of the structure to keep track of the internal details of + * the cache allocator. Touching any of these variables results in + * undefined behavior. + */ +typedef struct { + /** Mutex to protect access to the structure */ + pthread_mutex_t mutex; + /** Name of the cache objects in this cache (provided by the caller) */ + char *name; + /** List of pointers to available buffers in this cache */ + void **ptr; + /** The size of each element in this cache */ + size_t bufsize; + /** The capacity of the list of elements */ + size_t freetotal; + /** The current number of free elements */ + size_t freecurr; + /** The constructor to be called each time we allocate more memory */ + cache_constructor_t* constructor; + /** The destructor to be called each time before we release memory */ + cache_destructor_t* destructor; +} cache_t; + +/** + * Create an object cache. + * + * The object cache will let you allocate objects of the same size. It is fully + * MT safe, so you may allocate objects from multiple threads without having to + * do any syncrhonization in the application code. + * + * @param name the name of the object cache. This name may be used for debug purposes + * and may help you track down what kind of object you have problems with + * (buffer overruns, leakage etc) + * @param bufsize the size of each object in the cache + * @param align the alignment requirements of the objects in the cache. + * @param constructor the function to be called to initialize memory when we need + * to allocate more memory from the os. + * @param destructor the function to be called before we release the memory back + * to the os. + * @return a handle to an object cache if successful, NULL otherwise. + */ +cache_t* cache_create(const char* name, size_t bufsize, size_t align, + cache_constructor_t* constructor, + cache_destructor_t* destructor); +/** + * Destroy an object cache. + * + * Destroy and invalidate an object cache. You should return all buffers allocated + * with cache_alloc by using cache_free before calling this function. Not doing + * so results in undefined behavior (the buffers may or may not be invalidated) + * + * @param handle the handle to the object cache to destroy. + */ +void cache_destroy(cache_t* handle); +/** + * Allocate an object from the cache. + * + * @param handle the handle to the object cache to allocate from + * @return a pointer to an initialized object from the cache, or NULL if + * the allocation cannot be satisfied. + */ +void* cache_alloc(cache_t* handle); +/** + * Return an object back to the cache. + * + * The caller should return the object in an initialized state so that + * the object may be returned in an expected state from cache_alloc. + * + * @param handle handle to the object cache to return the object to + * @param ptr pointer to the object to return. + */ +void cache_free(cache_t* handle, void* ptr); +#endif // HAVE_UMEM_H diff --git a/libmemcachedprotocol/common.h b/libmemcachedprotocol/common.h new file mode 100644 index 00000000..398e7ee2 --- /dev/null +++ b/libmemcachedprotocol/common.h @@ -0,0 +1,163 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include "config.h" +#if !defined(__cplusplus) +# include +#endif +#include + +#include +#include +#include + +/* + * I don't really need the following two functions as function pointers + * in the instance handle, but I don't want to put them in the global + * namespace for those linking statically (personally I don't like that, + * but some people still do). If it ever shows up as a performance thing + * I'll look into optimizing this ;-) + */ +typedef bool (*drain_func)(memcached_protocol_client_st *client); +typedef protocol_binary_response_status (*spool_func)(memcached_protocol_client_st *client, + const void *data, + size_t length); + +/** + * Definition of the per instance structure. + */ +struct memcached_protocol_st { + memcached_binary_protocol_callback_st *callback; + memcached_protocol_recv_func recv; + memcached_protocol_send_func send; + + /* + * I really don't need these as funciton pointers, but I don't want + * to clutter the namespace if someone links statically. + */ + drain_func drain; + spool_func spool; + + /* + * To avoid keeping a buffer in each client all the time I have a + * bigger buffer in the instance that I read to initially, and then + * I try to parse and execute as much from the buffer. If I wasn't able + * to process all data I'll keep that in a per-connection buffer until + * the next time I can read from the socket. + */ + uint8_t *input_buffer; + size_t input_buffer_size; + + bool pedantic; + /* @todo use multiple sized buffers */ + cache_t *buffer_cache; +}; + +struct chunk_st { + /* Pointer to the data */ + char *data; + /* The offset to the first byte into the buffer that is used */ + size_t offset; + /* The offset into the buffer for the first free byte */ + size_t nbytes; + /* The number of bytes in the buffer */ + size_t size; + /* Pointer to the next buffer in the chain */ + struct chunk_st *next; +}; + +#define CHUNK_BUFFERSIZE 2048 + +typedef memcached_protocol_event_t (*process_data)(struct memcached_protocol_client_st *client, ssize_t *length, void **endptr); + +enum ascii_cmd { + GET_CMD, + GETS_CMD, + SET_CMD, + ADD_CMD, + REPLACE_CMD, + CAS_CMD, + APPEND_CMD, + PREPEND_CMD, + DELETE_CMD, + INCR_CMD, + DECR_CMD, + STATS_CMD, + FLUSH_ALL_CMD, + VERSION_CMD, + QUIT_CMD, + VERBOSITY_CMD, + UNKNOWN_CMD +}; + +struct memcached_protocol_client_st { + memcached_protocol_st *root; + memcached_socket_t sock; + int error; + + /* Linked list of data to send */ + struct chunk_st *output; + struct chunk_st *output_tail; + + /* + * While we process input data, this is where we spool incomplete commands + * if we need to receive more data.... + * @todo use the buffercace + */ + uint8_t *input_buffer; + size_t input_buffer_size; + size_t input_buffer_offset; + + /* The callback to the protocol handler to use (ascii or binary) */ + process_data work; + + /* + * Should the spool data discard the data to send or not? (aka noreply in + * the ascii protocol.. + */ + bool mute; + + /* Members used by the binary protocol */ + protocol_binary_request_header *current_command; + + /* Members used by the ascii protocol */ + enum ascii_cmd ascii_command; +}; + +#include "ascii_handler.h" +#include "binary_handler.h" diff --git a/libmemcachedprotocol/handler.c b/libmemcachedprotocol/handler.c new file mode 100644 index 00000000..9ce927a6 --- /dev/null +++ b/libmemcachedprotocol/handler.c @@ -0,0 +1,401 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* +** ********************************************************************** +** INTERNAL INTERFACE +** ********************************************************************** +*/ + +/** + * The default function to receive data from the client. This function + * just wraps the recv function to receive from a socket. + * See man -s3socket recv for more information. + * + * @param cookie cookie indentifying a client, not used + * @param sock socket to read from + * @param buf the destination buffer + * @param nbytes the number of bytes to read + * @return the number of bytes transferred of -1 upon error + */ +static ssize_t default_recv(const void *cookie, + memcached_socket_t sock, + void *buf, + size_t nbytes) +{ + (void)cookie; + return recv(sock, buf, nbytes, 0); +} + +/** + * The default function to send data to the server. This function + * just wraps the send function to send through a socket. + * See man -s3socket send for more information. + * + * @param cookie cookie indentifying a client, not used + * @param sock socket to send to + * @param buf the source buffer + * @param nbytes the number of bytes to send + * @return the number of bytes transferred of -1 upon error + */ +static ssize_t default_send(const void *cookie, + memcached_socket_t fd, + const void *buf, + size_t nbytes) +{ + (void)cookie; + return send(fd, buf, nbytes, 0); +} + +/** + * Try to drain the output buffers without blocking + * + * @param client the client to drain + * @return false if an error occured (connection should be shut down) + * true otherwise (please note that there may be more data to + * left in the buffer to send) + */ +static bool drain_output(struct memcached_protocol_client_st *client) +{ + ssize_t len; + + /* Do we have pending data to send? */ + while (client->output != NULL) + { + len= client->root->send(client, + client->sock, + client->output->data + client->output->offset, + client->output->nbytes - client->output->offset); + + if (len == -1) + { + if (get_socket_errno() == EWOULDBLOCK) + { + return true; + } + else if (get_socket_errno() != EINTR) + { + client->error= get_socket_errno(); + return false; + } + } + else + { + client->output->offset += (size_t)len; + if (client->output->offset == client->output->nbytes) + { + /* This was the complete buffer */ + struct chunk_st *old= client->output; + client->output= client->output->next; + if (client->output == NULL) + { + client->output_tail= NULL; + } + cache_free(client->root->buffer_cache, old); + } + } + } + + return true; +} + +/** + * Allocate an output buffer and chain it into the output list + * + * @param client the client that needs the buffer + * @return pointer to the new chunk if the allocation succeeds, NULL otherwise + */ +static struct chunk_st *allocate_output_chunk(struct memcached_protocol_client_st *client) +{ + struct chunk_st *ret= cache_alloc(client->root->buffer_cache); + + if (ret == NULL) + { + return NULL; + } + + ret->offset= ret->nbytes= 0; + ret->next= NULL; + ret->size= CHUNK_BUFFERSIZE; + ret->data= (void*)(ret + 1); + if (client->output == NULL) + { + client->output= client->output_tail= ret; + } + else + { + client->output_tail->next= ret; + client->output_tail= ret; + } + + return ret; +} + +/** + * Spool data into the send-buffer for a client. + * + * @param client the client to spool the data for + * @param data the data to spool + * @param length the number of bytes of data to spool + * @return PROTOCOL_BINARY_RESPONSE_SUCCESS if success, + * PROTOCOL_BINARY_RESPONSE_ENOMEM if we failed to allocate memory + */ +static protocol_binary_response_status spool_output(struct memcached_protocol_client_st *client, + const void *data, + size_t length) +{ + if (client->mute) + { + return PROTOCOL_BINARY_RESPONSE_SUCCESS; + } + + size_t offset= 0; + + struct chunk_st *chunk= client->output; + while (offset < length) + { + if (chunk == NULL || (chunk->size - chunk->nbytes) == 0) + { + if ((chunk= allocate_output_chunk(client)) == NULL) + { + return PROTOCOL_BINARY_RESPONSE_ENOMEM; + } + } + + size_t bulk= length - offset; + if (bulk > chunk->size - chunk->nbytes) + { + bulk= chunk->size - chunk->nbytes; + } + + memcpy(chunk->data + chunk->nbytes, data, bulk); + chunk->nbytes += bulk; + offset += bulk; + } + + return PROTOCOL_BINARY_RESPONSE_SUCCESS; +} + +/** + * Try to determine the protocol used on this connection. + * If the first byte contains the magic byte PROTOCOL_BINARY_REQ we should + * be using the binary protocol on the connection. I implemented the support + * for the ASCII protocol by wrapping into the simple interface (aka v1), + * so the implementors needs to provide an implementation of that interface + * + */ +static memcached_protocol_event_t determine_protocol(struct memcached_protocol_client_st *client, ssize_t *length, void **endptr) +{ + if (*client->root->input_buffer == (uint8_t)PROTOCOL_BINARY_REQ) + { + client->work= memcached_binary_protocol_process_data; + } + else if (client->root->callback->interface_version == 1) + { + /* + * The ASCII protocol can only be used if the implementors provide + * an implementation for the version 1 of the interface.. + * + * @todo I should allow the implementors to provide an implementation + * for version 0 and 1 at the same time and set the preferred + * interface to use... + */ + client->work= memcached_ascii_protocol_process_data; + } + else + { + /* Let's just output a warning the way it is supposed to look like + * in the ASCII protocol... + */ + const char *err= "CLIENT_ERROR: Unsupported protocol\r\n"; + client->root->spool(client, err, strlen(err)); + client->root->drain(client); + return MEMCACHED_PROTOCOL_ERROR_EVENT; /* Unsupported protocol */ + } + + return client->work(client, length, endptr); +} + +/* +** ********************************************************************** +** * PUBLIC INTERFACE +** * See protocol_handler.h for function description +** ********************************************************************** +*/ +struct memcached_protocol_st *memcached_protocol_create_instance(void) +{ + struct memcached_protocol_st *ret= calloc(1, sizeof(*ret)); + if (ret != NULL) + { + ret->recv= default_recv; + ret->send= default_send; + ret->drain= drain_output; + ret->spool= spool_output; + ret->input_buffer_size= 1 * 1024 * 1024; + ret->input_buffer= malloc(ret->input_buffer_size); + if (ret->input_buffer == NULL) + { + free(ret); + ret= NULL; + return NULL; + } + + ret->buffer_cache= cache_create("protocol_handler", + CHUNK_BUFFERSIZE + sizeof(struct chunk_st), + 0, NULL, NULL); + if (ret->buffer_cache == NULL) + { + free(ret->input_buffer); + free(ret); + } + } + + return ret; +} + +void memcached_protocol_destroy_instance(struct memcached_protocol_st *instance) +{ + cache_destroy(instance->buffer_cache); + free(instance->input_buffer); + free(instance); +} + +struct memcached_protocol_client_st *memcached_protocol_create_client(struct memcached_protocol_st *instance, memcached_socket_t sock) +{ + struct memcached_protocol_client_st *ret= calloc(1, sizeof(*ret)); + if (ret != NULL) + { + ret->root= instance; + ret->sock= sock; + ret->work= determine_protocol; + } + + return ret; +} + +void memcached_protocol_client_destroy(struct memcached_protocol_client_st *client) +{ + free(client); +} + +memcached_protocol_event_t memcached_protocol_client_work(struct memcached_protocol_client_st *client) +{ + /* Try to send data and read from the socket */ + bool more_data= true; + do + { + ssize_t len= client->root->recv(client, + client->sock, + client->root->input_buffer + client->input_buffer_offset, + client->root->input_buffer_size - client->input_buffer_offset); + + if (len > 0) + { + /* Do we have the complete packet? */ + if (client->input_buffer_offset > 0) + { + memcpy(client->root->input_buffer, client->input_buffer, + client->input_buffer_offset); + len += (ssize_t)client->input_buffer_offset; + + /* @todo use buffer-cache! */ + free(client->input_buffer); + client->input_buffer_offset= 0; + } + + void *endptr; + memcached_protocol_event_t events= client->work(client, &len, &endptr); + if (events == MEMCACHED_PROTOCOL_ERROR_EVENT) + { + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + + if (len > 0) + { + /* save the data for later on */ + /* @todo use buffer-cache */ + client->input_buffer= malloc((size_t)len); + if (client->input_buffer == NULL) + { + client->error= ENOMEM; + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + memcpy(client->input_buffer, endptr, (size_t)len); + client->input_buffer_offset= (size_t)len; + more_data= false; + } + } + else if (len == 0) + { + /* Connection closed */ + drain_output(client); + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + else + { + if (get_socket_errno() != EWOULDBLOCK) + { + client->error= get_socket_errno(); + /* mark this client as terminated! */ + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + more_data= false; + } + } while (more_data); + + if (!drain_output(client)) + { + return MEMCACHED_PROTOCOL_ERROR_EVENT; + } + + memcached_protocol_event_t ret= MEMCACHED_PROTOCOL_READ_EVENT; + if (client->output) + ret|= MEMCACHED_PROTOCOL_READ_EVENT; + + return ret; +} diff --git a/libmemcachedprotocol/include.am b/libmemcachedprotocol/include.am new file mode 100644 index 00000000..58aed623 --- /dev/null +++ b/libmemcachedprotocol/include.am @@ -0,0 +1,35 @@ +# vim:ft=automake +# included from Top Level Makefile.am +# All paths should be given relative to the root + + +lib_LTLIBRARIES+= libmemcached/libmemcachedprotocol.la + +noinst_HEADERS+= \ + libmemcachedprotocol/ascii_handler.h \ + libmemcachedprotocol/binary_handler.h \ + libmemcachedprotocol/cache.h \ + libmemcachedprotocol/common.h + +libmemcached_libmemcachedprotocol_la_SOURCES= \ + libmemcached/byteorder.cc \ + libmemcachedprotocol/ascii_handler.c \ + libmemcachedprotocol/binary_handler.c \ + libmemcachedprotocol/cache.c \ + libmemcachedprotocol/handler.c \ + libmemcachedprotocol/pedantic.c + +libmemcached_libmemcachedprotocol_la_CFLAGS= \ + ${AM_CFLAGS} \ + ${NO_CONVERSION} \ + -DBUILDING_LIBMEMCACHED +libmemcached_libmemcachedprotocol_la_CFLAGS+= ${PTHREAD_CFLAGS} + +libmemcached_libmemcachedprotocol_la_CXXFLAGS= \ + ${AM_CXXFLAGS} \ + -DBUILDING_LIBMEMCACHED +libmemcached_libmemcachedprotocol_la_CXXFLAGS+= ${PTHREAD_CFLAGS} + +libmemcached_libmemcachedprotocol_la_LIBADD= ${PTHREAD_LIBS} +libmemcached_libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} +libmemcached_libmemcachedprotocol_la_LDFLAGS+= -version-info ${MEMCACHED_PROTOCAL_LIBRARY_VERSION} diff --git a/libmemcachedprotocol/pedantic.c b/libmemcachedprotocol/pedantic.c new file mode 100644 index 00000000..54a2add0 --- /dev/null +++ b/libmemcachedprotocol/pedantic.c @@ -0,0 +1,237 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#include + +#include + +#define ensure(a) if (!(a)) { return false; } + +bool memcached_binary_protocol_pedantic_check_request(const protocol_binary_request_header *request) +{ + ensure(request->request.magic == PROTOCOL_BINARY_REQ); + ensure(request->request.datatype == PROTOCOL_BINARY_RAW_BYTES); + + ensure(request->bytes[6] == 0); + ensure(request->bytes[7] == 0); + + uint8_t opcode= request->request.opcode; + uint16_t keylen= ntohs(request->request.keylen); + uint8_t extlen= request->request.extlen; + uint32_t bodylen= ntohl(request->request.bodylen); + + ensure(bodylen >= (keylen + extlen)); + + switch (opcode) { + case PROTOCOL_BINARY_CMD_GET: + case PROTOCOL_BINARY_CMD_GETK: + case PROTOCOL_BINARY_CMD_GETKQ: + case PROTOCOL_BINARY_CMD_GETQ: + ensure(extlen == 0); + ensure(keylen > 0); + ensure(keylen == bodylen); + ensure(request->request.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_ADD: + case PROTOCOL_BINARY_CMD_ADDQ: + /* it makes no sense to run add with a cas value */ + ensure(request->request.cas == 0); + /* FALLTHROUGH */ + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_SETQ: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_REPLACEQ: + ensure(keylen > 0); + ensure(extlen == 8); + break; + + case PROTOCOL_BINARY_CMD_DELETE: + case PROTOCOL_BINARY_CMD_DELETEQ: + ensure(extlen == 0); + ensure(keylen > 0); + ensure(keylen == bodylen); + break; + + case PROTOCOL_BINARY_CMD_INCREMENT: + case PROTOCOL_BINARY_CMD_INCREMENTQ: + case PROTOCOL_BINARY_CMD_DECREMENT: + case PROTOCOL_BINARY_CMD_DECREMENTQ: + ensure(extlen == 20); + ensure(keylen > 0); + ensure(keylen + extlen == bodylen); + break; + + case PROTOCOL_BINARY_CMD_QUIT: + case PROTOCOL_BINARY_CMD_QUITQ: + case PROTOCOL_BINARY_CMD_NOOP: + case PROTOCOL_BINARY_CMD_VERSION: + ensure(extlen == 0); + ensure(keylen == 0); + ensure(bodylen == 0); + break; + + case PROTOCOL_BINARY_CMD_FLUSH: + case PROTOCOL_BINARY_CMD_FLUSHQ: + ensure(extlen == 0 || extlen == 4); + ensure(keylen == 0); + ensure(bodylen == extlen); + break; + + case PROTOCOL_BINARY_CMD_STAT: + ensure(extlen == 0); + /* May have key, but not value */ + ensure(keylen == bodylen); + break; + + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_APPENDQ: + case PROTOCOL_BINARY_CMD_PREPEND: + case PROTOCOL_BINARY_CMD_PREPENDQ: + ensure(extlen == 0); + ensure(keylen > 0); + break; + default: + /* Unknown command */ + ; + } + + return true; +} + +bool memcached_binary_protocol_pedantic_check_response(const protocol_binary_request_header *request, + const protocol_binary_response_header *response) +{ + ensure(response->response.magic == PROTOCOL_BINARY_RES); + ensure(response->response.datatype == PROTOCOL_BINARY_RAW_BYTES); + ensure(response->response.opaque == request->request.opaque); + + uint16_t status= ntohs(response->response.status); + uint8_t opcode= response->response.opcode; + + if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) + { + switch (opcode) { + case PROTOCOL_BINARY_CMD_ADDQ: + case PROTOCOL_BINARY_CMD_APPENDQ: + case PROTOCOL_BINARY_CMD_DECREMENTQ: + case PROTOCOL_BINARY_CMD_DELETEQ: + case PROTOCOL_BINARY_CMD_FLUSHQ: + case PROTOCOL_BINARY_CMD_INCREMENTQ: + case PROTOCOL_BINARY_CMD_PREPENDQ: + case PROTOCOL_BINARY_CMD_QUITQ: + case PROTOCOL_BINARY_CMD_REPLACEQ: + case PROTOCOL_BINARY_CMD_SETQ: + /* Quiet command shouldn't return on success */ + return false; + default: + break; + } + + switch (opcode) { + case PROTOCOL_BINARY_CMD_ADD: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_PREPEND: + ensure(response->response.keylen == 0); + ensure(response->response.extlen == 0); + ensure(response->response.bodylen == 0); + ensure(response->response.cas != 0); + break; + case PROTOCOL_BINARY_CMD_FLUSH: + case PROTOCOL_BINARY_CMD_NOOP: + case PROTOCOL_BINARY_CMD_QUIT: + case PROTOCOL_BINARY_CMD_DELETE: + ensure(response->response.keylen == 0); + ensure(response->response.extlen == 0); + ensure(response->response.bodylen == 0); + ensure(response->response.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_DECREMENT: + case PROTOCOL_BINARY_CMD_INCREMENT: + ensure(response->response.keylen == 0); + ensure(response->response.extlen == 0); + ensure(ntohl(response->response.bodylen) == 8); + ensure(response->response.cas != 0); + break; + + case PROTOCOL_BINARY_CMD_STAT: + ensure(response->response.extlen == 0); + /* key and value exists in all packets except in the terminating */ + ensure(response->response.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_VERSION: + ensure(response->response.keylen == 0); + ensure(response->response.extlen == 0); + ensure(response->response.bodylen != 0); + ensure(response->response.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_GET: + case PROTOCOL_BINARY_CMD_GETQ: + ensure(response->response.keylen == 0); + ensure(response->response.extlen == 4); + ensure(response->response.cas != 0); + break; + + case PROTOCOL_BINARY_CMD_GETK: + case PROTOCOL_BINARY_CMD_GETKQ: + ensure(response->response.keylen != 0); + ensure(response->response.extlen == 4); + ensure(response->response.cas != 0); + break; + + default: + /* Undefined command code */ + break; + } + } + else + { + ensure(response->response.cas == 0); + ensure(response->response.extlen == 0); + if (opcode != PROTOCOL_BINARY_CMD_GETK) + { + ensure(response->response.keylen == 0); + } + } + + return true; +} diff --git a/libmemcachedutil-1.0/include.am b/libmemcachedutil-1.0/include.am index c60028c7..c5576058 100644 --- a/libmemcachedutil-1.0/include.am +++ b/libmemcachedutil-1.0/include.am @@ -5,7 +5,6 @@ nobase_include_HEADERS+= \ libmemcachedutil-1.0/flush.h \ - libmemcachedutil-1.0/memcached_util.h \ libmemcachedutil-1.0/pid.h \ libmemcachedutil-1.0/ping.h \ libmemcachedutil-1.0/pool.h \ diff --git a/libmemcachedutil-1.0/pid.h b/libmemcachedutil-1.0/pid.h index 4101c11f..01738acc 100644 --- a/libmemcachedutil-1.0/pid.h +++ b/libmemcachedutil-1.0/pid.h @@ -36,6 +36,12 @@ #pragma once +#ifndef WIN32 +#include +#endif + +#include + #ifdef __cplusplus extern "C" { #endif diff --git a/libmemcachedutil-1.0/pool.h b/libmemcachedutil-1.0/pool.h index eba97ec2..682aed5f 100644 --- a/libmemcachedutil-1.0/pool.h +++ b/libmemcachedutil-1.0/pool.h @@ -38,7 +38,7 @@ #pragma once -#include +#include #ifdef __cplusplus extern "C" { diff --git a/libmemcachedutil-1.0/util.h b/libmemcachedutil-1.0/util.h index 09053a99..d3620988 100644 --- a/libmemcachedutil-1.0/util.h +++ b/libmemcachedutil-1.0/util.h @@ -37,6 +37,8 @@ #pragma once +#include + #include #include #include diff --git a/libmemcachedutil/common.h b/libmemcachedutil/common.h new file mode 100644 index 00000000..5c7b59f4 --- /dev/null +++ b/libmemcachedutil/common.h @@ -0,0 +1,45 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached Utility library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Summary: connects to a host, and then flushes it (memcached_flush(3)). + * + */ + +#pragma once + +#include + +#include +#include +#include diff --git a/libmemcachedutil/flush.cc b/libmemcachedutil/flush.cc new file mode 100644 index 00000000..6a99603f --- /dev/null +++ b/libmemcachedutil/flush.cc @@ -0,0 +1,60 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Summary: connects to a host, and then flushes it (memcached_flush(3)). + * + */ + +#include + + +bool libmemcached_util_flush(const char *hostname, in_port_t port, memcached_return_t *ret) +{ + memcached_st *memc_ptr= memcached_create(NULL); + + memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); + if (memcached_success(rc)) + { + rc= memcached_flush(memc_ptr, 0); + } + + memcached_free(memc_ptr); + + if (ret) + { + *ret= rc; + } + + return memcached_success(rc); +} diff --git a/libmemcachedutil/include.am b/libmemcachedutil/include.am new file mode 100644 index 00000000..8c9b7a50 --- /dev/null +++ b/libmemcachedutil/include.am @@ -0,0 +1,26 @@ +# vim:ft=automake +# included from Top Level Makefile.am +# All paths should be given relative to the root + +lib_LTLIBRARIES+= libmemcached/libmemcachedutil.la + +noinst_HEADERS+= \ + libmemcachedutil/common.h + +libmemcached_libmemcachedutil_la_SOURCES= \ + libmemcached/backtrace.cc \ + libmemcachedutil/flush.cc \ + libmemcachedutil/pid.cc \ + libmemcachedutil/ping.cc \ + libmemcachedutil/pool.cc \ + libmemcachedutil/version.cc +libmemcached_libmemcachedutil_la_CXXFLAGS= \ + ${AM_CXXFLAGS} \ + ${NO_CONVERSION} \ + -DBUILDING_LIBMEMCACHED +libmemcached_libmemcachedutil_la_CXXFLAGS+= ${PTHREAD_CFLAGS} +libmemcached_libmemcachedutil_la_LIBADD= libmemcached/libmemcached.la +libmemcached_libmemcachedutil_la_LIBADD+= ${PTHREAD_LIBS} +libmemcached_libmemcachedutil_la_LDFLAGS= ${AM_LDFLAGS} -version-info ${MEMCACHED_UTIL_LIBRARY_VERSION} +libmemcached_libmemcachedutil_la_DEPENDENCIES= libmemcached/libmemcached.la + diff --git a/libmemcachedutil/pid.cc b/libmemcachedutil/pid.cc new file mode 100644 index 00000000..7ff665b6 --- /dev/null +++ b/libmemcachedutil/pid.cc @@ -0,0 +1,160 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Summary: connects to a host, and determines what its pid is + * + */ + +#include + + +// Never look at the stat object directly. + + +pid_t libmemcached_util_getpid(const char *hostname, in_port_t port, memcached_return_t *ret) +{ + pid_t pid= -1; + + memcached_return_t unused; + if (not ret) + ret= &unused; + + memcached_st *memc_ptr= memcached_create(NULL); + if (not memc_ptr) + { + *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return -1; + } + + memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); + if (memcached_success(rc)) + { + memcached_stat_st *stat= memcached_stat(memc_ptr, NULL, &rc); + if (memcached_success(rc) and stat and stat->pid != -1) + { + pid= stat->pid; + } + else if (memcached_success(rc)) + { + rc= MEMCACHED_UNKNOWN_STAT_KEY; // Something went wrong if this happens + } + else if (rc == MEMCACHED_SOME_ERRORS) // Generic answer, we will now find the specific reason (if one exists) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc_ptr, 0); + + assert_msg(instance and instance->error_messages, " "); + if (instance and instance->error_messages) + { + rc= memcached_server_error_return(instance); + } + } + + memcached_stat_free(memc_ptr, stat); + } + memcached_free(memc_ptr); + + *ret= rc; + + return pid; +} + +pid_t libmemcached_util_getpid2(const char *hostname, in_port_t port, const char *username, const char *password, memcached_return_t *ret) +{ + if (username == NULL) + { + return libmemcached_util_getpid(hostname, port, ret); + } + + pid_t pid= -1; + + memcached_return_t unused; + if (not ret) + ret= &unused; + + if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0) + { + *ret= MEMCACHED_NOT_SUPPORTED; + return pid; + } + + memcached_st *memc_ptr= memcached_create(NULL); + if (not memc_ptr) + { + *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return -1; + } + + if (memcached_failed(*ret= memcached_set_sasl_auth_data(memc_ptr, username, password))) + { + memcached_free(memc_ptr); + return false; + } + + + memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); + if (memcached_success(rc)) + { + memcached_stat_st *stat= memcached_stat(memc_ptr, NULL, &rc); + if (memcached_success(rc) and stat and stat->pid != -1) + { + pid= stat->pid; + } + else if (memcached_success(rc)) + { + rc= MEMCACHED_UNKNOWN_STAT_KEY; // Something went wrong if this happens + } + else if (rc == MEMCACHED_SOME_ERRORS) // Generic answer, we will now find the specific reason (if one exists) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc_ptr, 0); + +#if 0 + assert_msg(instance and instance->error_messages, " "); +#endif + if (instance and instance->error_messages) + { + rc= memcached_server_error_return(instance); + } + } + + memcached_stat_free(memc_ptr, stat); + } + memcached_free(memc_ptr); + + *ret= rc; + + return pid; +} diff --git a/libmemcachedutil/ping.cc b/libmemcachedutil/ping.cc new file mode 100644 index 00000000..82d891a9 --- /dev/null +++ b/libmemcachedutil/ping.cc @@ -0,0 +1,131 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Summary: connects to a host, and makes sure it is alive. + * + */ + +#include + +bool libmemcached_util_ping(const char *hostname, in_port_t port, memcached_return_t *ret) +{ + memcached_return_t unused; + if (not ret) + ret= &unused; + + memcached_st *memc_ptr= memcached_create(NULL); + if (not memc_ptr) + { + *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return false; + } + + memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); + if (memcached_success(rc)) + { + rc= memcached_version(memc_ptr); + } + + if (memcached_failed(rc) and rc == MEMCACHED_SOME_ERRORS) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc_ptr, 0); + + assert_msg(instance and instance->error_messages, " "); + if (instance and instance->error_messages) + { + rc= memcached_server_error_return(instance); + } + } + memcached_free(memc_ptr); + + *ret= rc; + + return memcached_success(rc); +} + +bool libmemcached_util_ping2(const char *hostname, in_port_t port, const char *username, const char *password, memcached_return_t *ret) +{ + if (username == NULL) + { + return libmemcached_util_ping(hostname, port, ret); + } + + memcached_return_t unused; + if (not ret) + ret= &unused; + + if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0) + { + *ret= MEMCACHED_NOT_SUPPORTED; + return false; + } + + memcached_st *memc_ptr= memcached_create(NULL); + if (not memc_ptr) + { + *ret= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return false; + } + + if (memcached_failed(*ret= memcached_set_sasl_auth_data(memc_ptr, username, password))) + { + memcached_free(memc_ptr); + return false; + } + + memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); + if (memcached_success(rc)) + { + rc= memcached_version(memc_ptr); + } + + if (memcached_failed(rc) and rc == MEMCACHED_SOME_ERRORS) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc_ptr, 0); + + assert_msg(instance and instance->error_messages, " "); + if (instance and instance->error_messages) + { + rc= memcached_server_error_return(instance); + } + } + memcached_free(memc_ptr); + + *ret= rc; + + return memcached_success(rc); +} diff --git a/libmemcachedutil/pool.cc b/libmemcachedutil/pool.cc new file mode 100644 index 00000000..a0f30c51 --- /dev/null +++ b/libmemcachedutil/pool.cc @@ -0,0 +1,482 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include + +#include +#include +#include +#include + +struct memcached_pool_st +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + memcached_st *master; + memcached_st **server_pool; + int firstfree; + const uint32_t size; + uint32_t current_size; + bool _owns_master; + struct timespec _timeout; + + memcached_pool_st(memcached_st *master_arg, size_t max_arg) : + master(master_arg), + server_pool(NULL), + firstfree(-1), + size(max_arg), + current_size(0), + _owns_master(false) + { + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); + _timeout.tv_sec= 5; + _timeout.tv_nsec= 0; + } + + const struct timespec& timeout() const + { + return _timeout; + } + + bool release(memcached_st*, memcached_return_t& rc); + + memcached_st *fetch(memcached_return_t& rc); + memcached_st *fetch(const struct timespec&, memcached_return_t& rc); + + bool init(uint32_t initial); + + ~memcached_pool_st() + { + for (int x= 0; x <= firstfree; ++x) + { + memcached_free(server_pool[x]); + server_pool[x] = NULL; + } + + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); + delete [] server_pool; + if (_owns_master) + { + memcached_free(master); + } + } + + void increment_version() + { + ++master->configure.version; + } + + bool compare_version(const memcached_st *arg) const + { + return (arg->configure.version == version()); + } + + int32_t version() const + { + return master->configure.version; + } +}; + + +/** + * Grow the connection pool by creating a connection structure and clone the + * original memcached handle. + */ +static bool grow_pool(memcached_pool_st* pool) +{ + assert(pool); + + memcached_st *obj; + if (not (obj= memcached_clone(NULL, pool->master))) + { + return false; + } + + pool->server_pool[++pool->firstfree]= obj; + pool->current_size++; + obj->configure.version= pool->version(); + + return true; +} + +bool memcached_pool_st::init(uint32_t initial) +{ + server_pool= new (std::nothrow) memcached_st *[size]; + if (not server_pool) + return false; + + /* + Try to create the initial size of the pool. An allocation failure at + this time is not fatal.. + */ + for (unsigned int x= 0; x < initial; ++x) + { + if (grow_pool(this) == false) + { + break; + } + } + + return true; +} + + +static inline memcached_pool_st *_pool_create(memcached_st* master, uint32_t initial, uint32_t max) +{ + if (initial == 0 or max == 0 or (initial > max)) + { + return NULL; + } + + memcached_pool_st *object= new (std::nothrow) memcached_pool_st(master, max); + if (object == NULL) + { + return NULL; + } + + /* + Try to create the initial size of the pool. An allocation failure at + this time is not fatal.. + */ + if (not object->init(initial)) + { + delete object; + return NULL; + } + + return object; +} + +memcached_pool_st *memcached_pool_create(memcached_st* master, uint32_t initial, uint32_t max) +{ + return _pool_create(master, initial, max); +} + +memcached_pool_st * memcached_pool(const char *option_string, size_t option_string_length) +{ + memcached_st *memc= memcached(option_string, option_string_length); + + if (memc == NULL) + { + return NULL; + } + + memcached_pool_st *self= memcached_pool_create(memc, memc->configure.initial_pool_size, memc->configure.max_pool_size); + if (self == NULL) + { + memcached_free(memc); + return NULL; + } + + self->_owns_master= true; + + return self; +} + +memcached_st* memcached_pool_destroy(memcached_pool_st* pool) +{ + if (pool == NULL) + { + return NULL; + } + + // Legacy that we return the original structure + memcached_st *ret= NULL; + if (pool->_owns_master) + { } + else + { + ret= pool->master; + } + + delete pool; + + return ret; +} + +memcached_st* memcached_pool_st::fetch(memcached_return_t& rc) +{ + static struct timespec relative_time= { 0, 0 }; + return fetch(relative_time, rc); +} + +memcached_st* memcached_pool_st::fetch(const struct timespec& relative_time, memcached_return_t& rc) +{ + rc= MEMCACHED_SUCCESS; + + if (pthread_mutex_lock(&mutex)) + { + rc= MEMCACHED_IN_PROGRESS; + return NULL; + } + + memcached_st *ret= NULL; + do + { + if (firstfree > -1) + { + ret= server_pool[firstfree--]; + } + else if (current_size == size) + { + if (relative_time.tv_sec == 0 and relative_time.tv_nsec == 0) + { + pthread_mutex_unlock(&mutex); + rc= MEMCACHED_NOTFOUND; + + return NULL; + } + + struct timespec time_to_wait= {0, 0}; + time_to_wait.tv_sec= time(NULL) +relative_time.tv_sec; + time_to_wait.tv_nsec= relative_time.tv_nsec; + + int thread_ret; + if ((thread_ret= pthread_cond_timedwait(&cond, &mutex, &time_to_wait)) != 0) + { + pthread_mutex_unlock(&mutex); + + if (thread_ret == ETIMEDOUT) + { + rc= MEMCACHED_TIMEOUT; + } + else + { + errno= thread_ret; + rc= MEMCACHED_ERRNO; + } + + return NULL; + } + } + else if (grow_pool(this) == false) + { + (void)pthread_mutex_unlock(&mutex); + return NULL; + } + } while (ret == NULL); + + pthread_mutex_unlock(&mutex); + + return ret; +} + +bool memcached_pool_st::release(memcached_st *released, memcached_return_t& rc) +{ + rc= MEMCACHED_SUCCESS; + if (released == NULL) + { + rc= MEMCACHED_INVALID_ARGUMENTS; + return false; + } + + if (pthread_mutex_lock(&mutex)) + { + rc= MEMCACHED_IN_PROGRESS; + return false; + } + + /* + Someone updated the behavior on the object, so we clone a new memcached_st with the new settings. If we fail to clone, we keep the old one around. + */ + if (compare_version(released) == false) + { + memcached_st *memc; + if ((memc= memcached_clone(NULL, master))) + { + memcached_free(released); + released= memc; + } + } + + server_pool[++firstfree]= released; + + if (firstfree == 0 and current_size == size) + { + /* we might have people waiting for a connection.. wake them up :-) */ + pthread_cond_broadcast(&cond); + } + + (void)pthread_mutex_unlock(&mutex); + + return true; +} + +memcached_st* memcached_pool_fetch(memcached_pool_st* pool, struct timespec* relative_time, memcached_return_t* rc) +{ + if (pool == NULL) + { + return NULL; + } + + memcached_return_t unused; + if (rc == NULL) + { + rc= &unused; + } + + if (relative_time == NULL) + { + return pool->fetch(*rc); + } + + return pool->fetch(*relative_time, *rc); +} + +memcached_st* memcached_pool_pop(memcached_pool_st* pool, + bool block, + memcached_return_t *rc) +{ + if (pool == NULL) + { + return NULL; + } + + memcached_return_t unused; + if (rc == NULL) + { + rc= &unused; + } + + memcached_st *memc; + if (block) + { + memc= pool->fetch(pool->timeout(), *rc); + } + else + { + memc= pool->fetch(*rc); + } + + return memc; +} + +memcached_return_t memcached_pool_release(memcached_pool_st* pool, memcached_st *released) +{ + if (pool == NULL) + { + return MEMCACHED_INVALID_ARGUMENTS; + } + + memcached_return_t rc; + + (void) pool->release(released, rc); + + return rc; +} + +memcached_return_t memcached_pool_push(memcached_pool_st* pool, memcached_st *released) +{ + return memcached_pool_release(pool, released); +} + + +memcached_return_t memcached_pool_behavior_set(memcached_pool_st *pool, + memcached_behavior_t flag, + uint64_t data) +{ + if (pool == NULL) + { + return MEMCACHED_INVALID_ARGUMENTS; + } + + if (pthread_mutex_lock(&pool->mutex)) + { + return MEMCACHED_IN_PROGRESS; + } + + /* update the master */ + memcached_return_t rc= memcached_behavior_set(pool->master, flag, data); + if (memcached_failed(rc)) + { + (void)pthread_mutex_unlock(&pool->mutex); + return rc; + } + + pool->increment_version(); + /* update the clones */ + for (int xx= 0; xx <= pool->firstfree; ++xx) + { + if (memcached_success(memcached_behavior_set(pool->server_pool[xx], flag, data))) + { + pool->server_pool[xx]->configure.version= pool->version(); + } + else + { + memcached_st *memc; + if ((memc= memcached_clone(NULL, pool->master))) + { + memcached_free(pool->server_pool[xx]); + pool->server_pool[xx]= memc; + /* I'm not sure what to do in this case.. this would happen + if we fail to push the server list inside the client.. + I should add a testcase for this, but I believe the following + would work, except that you would add a hole in the pool list.. + in theory you could end up with an empty pool.... + */ + } + } + } + + (void)pthread_mutex_unlock(&pool->mutex); + + return rc; +} + +memcached_return_t memcached_pool_behavior_get(memcached_pool_st *pool, + memcached_behavior_t flag, + uint64_t *value) +{ + if (pool == NULL) + { + return MEMCACHED_INVALID_ARGUMENTS; + } + + if (pthread_mutex_lock(&pool->mutex)) + { + return MEMCACHED_IN_PROGRESS; + } + + *value= memcached_behavior_get(pool->master, flag); + + (void)pthread_mutex_unlock(&pool->mutex); + + return MEMCACHED_SUCCESS; +} diff --git a/libmemcachedutil/version.cc b/libmemcachedutil/version.cc new file mode 100644 index 00000000..abfd47f7 --- /dev/null +++ b/libmemcachedutil/version.cc @@ -0,0 +1,85 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include + +struct local_context +{ + uint8_t major_version; + uint8_t minor_version; + uint8_t micro_version; + + bool truth; +}; + +static memcached_return_t check_server_version(const memcached_st *, + const memcached_server_st *instance, + void *context) +{ + /* Do Nothing */ + struct local_context *check= (struct local_context *)context; + + if (instance->major_version != UINT8_MAX && + instance->major_version >= check->major_version && + instance->minor_version >= check->minor_version && + instance->micro_version >= check->micro_version ) + { + return MEMCACHED_SUCCESS; + } + + check->truth= false; + + return MEMCACHED_FAILURE; +} + +bool libmemcached_util_version_check(memcached_st *memc, + uint8_t major_version, + uint8_t minor_version, + uint8_t micro_version) +{ + if (memcached_version(memc) != MEMCACHED_SUCCESS) + return false; + + struct local_context check= { major_version, minor_version, micro_version, true }; + + memcached_server_fn callbacks[1]; + callbacks[0]= check_server_version; + memcached_server_cursor(memc, callbacks, (void *)&check, 1); + + return check.truth; +} diff --git a/support/libmemcached.spec.in b/support/libmemcached.spec.in index 077ffd22..c6a6e727 100644 --- a/support/libmemcached.spec.in +++ b/support/libmemcached.spec.in @@ -109,70 +109,74 @@ you will need to install %{name}-devel. %files devel %defattr (-,root,root,-) %doc examples -%{_includedir}/libhashkit/algorithm.h -%{_includedir}/libhashkit/behavior.h -%{_includedir}/libhashkit/configure.h -%{_includedir}/libhashkit/digest.h -%{_includedir}/libhashkit/function.h -%{_includedir}/libhashkit/has.h %{_includedir}/libhashkit/hashkit.h -%{_includedir}/libhashkit/hashkit.hpp -%{_includedir}/libhashkit/str_algorithm.h -%{_includedir}/libhashkit/strerror.h -%{_includedir}/libhashkit/types.h -%{_includedir}/libhashkit/visibility.h - -%{_includedir}/libmemcached/allocators.h -%{_includedir}/libmemcached/analyze.h -%{_includedir}/libmemcached/array.h -%{_includedir}/libmemcached/auto.h -%{_includedir}/libmemcached/basic_string.h -%{_includedir}/libmemcached/behavior.h -%{_includedir}/libmemcached/callback.h -%{_includedir}/libmemcached/configure.h -%{_includedir}/libmemcached/constants.h -%{_includedir}/libmemcached/delete.h -%{_includedir}/libmemcached/dump.h -%{_includedir}/libmemcached/error.h -%{_includedir}/libmemcached/exist.h -%{_includedir}/libmemcached/exception.hpp -%{_includedir}/libmemcached/fetch.h -%{_includedir}/libmemcached/flush.h -%{_includedir}/libmemcached/flush_buffers.h -%{_includedir}/libmemcached/get.h -%{_includedir}/libmemcached/hash.h +%{_includedir}/libhashkit-1.0/algorithm.h +%{_includedir}/libhashkit-1.0/behavior.h +%{_includedir}/libhashkit-1.0/configure.h +%{_includedir}/libhashkit-1.0/digest.h +%{_includedir}/libhashkit-1.0/function.h +%{_includedir}/libhashkit-1.0/has.h +%{_includedir}/libhashkit-1.0/hashkit.h +%{_includedir}/libhashkit-1.0/hashkit.hpp +%{_includedir}/libhashkit-1.0/str_algorithm.h +%{_includedir}/libhashkit-1.0/strerror.h +%{_includedir}/libhashkit-1.0/types.h +%{_includedir}/libhashkit-1.0/visibility.h + +%{_includedir}/libmemcachedprotocol-0.0/binary.h +%{_includedir}/libmemcachedprotocol-0.0/callback.h +%{_includedir}/libmemcachedprotocol-0.0/handler.h +%{_includedir}/libmemcachedprotocol-0.0/vbucket.h + +%{_includedir}/libmemcachedutil-1.0/util.h +%{_includedir}/libmemcachedutil-1.0/flush.h +%{_includedir}/libmemcachedutil-1.0/pid.h +%{_includedir}/libmemcachedutil-1.0/ping.h +%{_includedir}/libmemcachedutil-1.0/pool.h +%{_includedir}/libmemcachedutil-1.0/version.h + %{_includedir}/libmemcached/memcached.h %{_includedir}/libmemcached/memcached.hpp -%{_includedir}/libmemcached/memcached/protocol_binary.h -%{_includedir}/libmemcached/memcached/vbucket.h -%{_includedir}/libmemcached/memcached_util.h -%{_includedir}/libmemcached/namespace.h -%{_includedir}/libmemcached/options.h -%{_includedir}/libmemcached/parse.h -%{_includedir}/libmemcached/platform.h -%{_includedir}/libmemcached/protocol/cache.h -%{_includedir}/libmemcached/protocol/callback.h -%{_includedir}/libmemcached/protocol_handler.h -%{_includedir}/libmemcached/quit.h -%{_includedir}/libmemcached/result.h -%{_includedir}/libmemcached/return.h -%{_includedir}/libmemcached/sasl.h -%{_includedir}/libmemcached/server.h -%{_includedir}/libmemcached/server_list.h -%{_includedir}/libmemcached/stats.h -%{_includedir}/libmemcached/storage.h -%{_includedir}/libmemcached/strerror.h -%{_includedir}/libmemcached/string.h -%{_includedir}/libmemcached/types.h %{_includedir}/libmemcached/util.h -%{_includedir}/libmemcached/util/flush.h -%{_includedir}/libmemcached/util/pid.h -%{_includedir}/libmemcached/util/ping.h -%{_includedir}/libmemcached/util/pool.h -%{_includedir}/libmemcached/util/version.h -%{_includedir}/libmemcached/verbosity.h -%{_includedir}/libmemcached/version.h -%{_includedir}/libmemcached/visibility.h + +%{_includedir}/libmemcached-1.0/allocators.h +%{_includedir}/libmemcached-1.0/analyze.h +%{_includedir}/libmemcached-1.0/auto.h +%{_includedir}/libmemcached-1.0/basic_string.h +%{_includedir}/libmemcached-1.0/behavior.h +%{_includedir}/libmemcached-1.0/callback.h +%{_includedir}/libmemcached-1.0/configure.h +%{_includedir}/libmemcached-1.0/constants.h +%{_includedir}/libmemcached-1.0/delete.h +%{_includedir}/libmemcached-1.0/dump.h +%{_includedir}/libmemcached-1.0/error.h +%{_includedir}/libmemcached-1.0/exist.h +%{_includedir}/libmemcached-1.0/exception.hpp +%{_includedir}/libmemcached-1.0/fetch.h +%{_includedir}/libmemcached-1.0/flush.h +%{_includedir}/libmemcached-1.0/flush_buffers.h +%{_includedir}/libmemcached-1.0/get.h +%{_includedir}/libmemcached-1.0/hash.h +%{_includedir}/libmemcached-1.0/memcached.h +%{_includedir}/libmemcached-1.0/memcached.hpp +%{_includedir}/libmemcached-1.0/options.h +%{_includedir}/libmemcached-1.0/parse.h +%{_includedir}/libmemcached-1.0/platform.h +%{_includedir}/libmemcached-1.0/quit.h +%{_includedir}/libmemcached-1.0/result.h +%{_includedir}/libmemcached-1.0/return.h +%{_includedir}/libmemcached-1.0/sasl.h +%{_includedir}/libmemcached-1.0/server.h +%{_includedir}/libmemcached-1.0/server_list.h +%{_includedir}/libmemcached-1.0/stats.h +%{_includedir}/libmemcached-1.0/storage.h +%{_includedir}/libmemcached-1.0/strerror.h +%{_includedir}/libmemcached-1.0/string.h +%{_includedir}/libmemcached-1.0/types.h +%{_includedir}/libmemcached-1.0/verbosity.h +%{_includedir}/libmemcached-1.0/version.h +%{_includedir}/libmemcached-1.0/visibility.h + %{_libdir}/libhashkit.so %{_libdir}/libmemcached.so %{_libdir}/libmemcachedprotocol.so diff --git a/tests/hashkit_functions.cc b/tests/hashkit_functions.cc index 6a823cdd..908ad960 100644 --- a/tests/hashkit_functions.cc +++ b/tests/hashkit_functions.cc @@ -45,7 +45,7 @@ using namespace libtest; #include #include -#include +#include #include #include "tests/hash_results.h" diff --git a/tests/mem_functions.cc b/tests/mem_functions.cc index 18e092e4..3d2869ce 100644 --- a/tests/mem_functions.cc +++ b/tests/mem_functions.cc @@ -42,11 +42,11 @@ Test cases */ -#include +#include #include #include -#include +#include #include #include @@ -87,7 +87,7 @@ using namespace libtest; -#include +#include #include "hash_results.h" diff --git a/win32/include.am b/win32/include.am index f81ae3e3..43e8d9b1 100644 --- a/win32/include.am +++ b/win32/include.am @@ -4,8 +4,8 @@ noinst_HEADERS+= win32/wrappers.h if BUILD_WIN32_WRAPPERS +libhashkit_libhashkit_la_LDFLAGS+=-no-undefined libmemcached_libmemcached_la_LDFLAGS+=-no-undefined libmemcached_libmemcachedprotocol_la_LDFLAGS+=-no-undefined libmemcached_libmemcachedutil_la_LDFLAGS+=-no-undefined -libhashkit_libhashkit_la_LDFLAGS+=-no-undefined endif