summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrond Norbye <trond.norbye@gmail.com>2011-07-06 07:49:30 +0200
committerTrond Norbye <trond.norbye@gmail.com>2011-07-11 16:24:59 +0200
commite65697b627a8f52582672d0276b525d4eb8d4473 (patch)
tree0d32e86864d72ab82d5467c3828f10eed06fed9c
parent61cb72e0a6ae4a0ce3c20618193ef9dbb8b9cf76 (diff)
downloadmemcached-e65697b627a8f52582672d0276b525d4eb8d4473.tar.gz
Add support for binary protocol extensions
-rw-r--r--daemon/memcached.c100
-rw-r--r--daemon/memcached.h1
-rw-r--r--include/memcached/engine.h35
-rw-r--r--include/memcached/engine_common.h39
-rw-r--r--include/memcached/extension.h48
5 files changed, 175 insertions, 48 deletions
diff --git a/daemon/memcached.c b/daemon/memcached.c
index 7cc67e2..f4a328d 100644
--- a/daemon/memcached.c
+++ b/daemon/memcached.c
@@ -2583,6 +2583,37 @@ static void ship_tap_log(conn *c) {
}
}
+
+static ENGINE_ERROR_CODE default_unknown_command(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
+ ENGINE_HANDLE* handle,
+ const void* cookie,
+ protocol_binary_request_header *request,
+ ADD_RESPONSE response)
+{
+ return settings.engine.v1->unknown_command(handle, cookie, request, response);
+}
+
+struct request_lookup {
+ EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor;
+ BINARY_COMMAND_CALLBACK callback;
+};
+
+static struct request_lookup request_handlers[0x100];
+
+static void initialize_binary_lookup_map(void) {
+ for (int ii = 0; ii < 0x100; ++ii) {
+ request_handlers[ii].descriptor = NULL;
+ request_handlers[ii].callback = default_unknown_command;
+ }
+}
+
+static void setup_binary_lookup_cmd(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
+ uint8_t cmd,
+ BINARY_COMMAND_CALLBACK new_handler) {
+ request_handlers[cmd].descriptor = descriptor;
+ request_handlers[cmd].callback = new_handler;
+}
+
static void process_bin_unknown_packet(conn *c) {
void *packet = c->rcurr - (c->binary_header.request.bodylen +
sizeof(c->binary_header));
@@ -2592,24 +2623,31 @@ static void process_bin_unknown_packet(conn *c) {
c->ewouldblock = false;
if (ret == ENGINE_SUCCESS) {
- ret = settings.engine.v1->unknown_command(settings.engine.v0, c, packet,
- binary_response_handler);
+ struct request_lookup *rq = request_handlers + c->binary_header.request.opcode;
+ ret = rq->callback(rq->descriptor, settings.engine.v0, c, packet,
+ binary_response_handler);
}
- if (ret == ENGINE_SUCCESS) {
+ switch (ret) {
+ case ENGINE_SUCCESS:
if (c->dynamic_buffer.buffer != NULL) {
write_and_free(c, c->dynamic_buffer.buffer, c->dynamic_buffer.offset);
c->dynamic_buffer.buffer = NULL;
} else {
conn_set_state(c, conn_new_cmd);
}
- } else if (ret == ENGINE_ENOTSUP) {
- write_bin_packet(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, 0);
- } else if (ret == ENGINE_EWOULDBLOCK) {
+ break;
+ case ENGINE_EWOULDBLOCK:
c->ewouldblock = true;
- } else {
- /* FATAL ERROR, shut down connection */
+ break;
+ case ENGINE_DISCONNECT:
conn_set_state(c, conn_closing);
+ break;
+ default:
+ // Release the dynamic buffer.. it may be partial..
+ free(c->dynamic_buffer.buffer);
+ c->dynamic_buffer.buffer = NULL;
+ write_bin_packet(c, engine_error_2_protocol_error(ret), 0);
}
}
@@ -2835,8 +2873,6 @@ static void process_bin_packet(conn *c) {
}
}
-
-
typedef void (*RESPONSE_HANDLER)(conn*);
/**
* A map between the response packets op-code and the function to handle
@@ -3834,6 +3870,12 @@ static void process_stat_settings(ADD_STAT add_stats, void *c) {
ptr = ptr->next) {
APPEND_STAT("ascii_extension", "%s", ptr->get_name(ptr->cookie));
}
+
+ for (EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *ptr = settings.extensions.binary;
+ ptr != NULL;
+ ptr = ptr->next) {
+ APPEND_STAT("binary_extension", "%s", ptr->get_name());
+ }
}
static char *process_stat(conn *c, token_t *tokens, const size_t ntokens) {
@@ -6530,6 +6572,28 @@ static bool register_extension(extension_type_t type, void *extension)
}
return true;
+ case EXTENSION_BINARY_PROTOCOL:
+ if (settings.extensions.binary != NULL) {
+ EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *last;
+ for (last = settings.extensions.binary; last->next != NULL;
+ last = last->next) {
+ if (last == extension) {
+ return false;
+ }
+ }
+ if (last == extension) {
+ return false;
+ }
+ last->next = extension;
+ last->next->next = NULL;
+ } else {
+ settings.extensions.binary = extension;
+ settings.extensions.binary->next = NULL;
+ }
+
+ ((EXTENSION_BINARY_PROTOCOL_DESCRIPTOR*)extension)->setup(setup_binary_lookup_cmd);
+ return true;
+
default:
return false;
}
@@ -6592,6 +6656,13 @@ static void unregister_extension(extension_type_t type, void *extension)
}
break;
+
+ case EXTENSION_BINARY_PROTOCOL:
+ settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
+ "You can't unregister a binary command handler!");
+ abort();
+ break;
+
default:
;
}
@@ -6613,6 +6684,9 @@ static void* get_extension(extension_type_t type)
case EXTENSION_ASCII_PROTOCOL:
return settings.extensions.ascii;
+ case EXTENSION_BINARY_PROTOCOL:
+ return settings.extensions.binary;
+
default:
return NULL;
}
@@ -6866,6 +6940,8 @@ int main (int argc, char **argv) {
/* init settings */
settings_init();
+ initialize_binary_lookup_map();
+
if (memcached_initialize_stderr_logger(get_server_api) != EXTENSION_SUCCESS) {
fprintf(stderr, "Failed to initialize log system\n");
return EX_OSERR;
@@ -7345,11 +7421,11 @@ int main (int argc, char **argv) {
exit(EXIT_FAILURE);
}
- if(!init_engine(engine_handle,engine_config,settings.extensions.logger)) {
+ if (!init_engine(engine_handle,engine_config,settings.extensions.logger)) {
return false;
}
- if(settings.verbose > 0) {
+ if (settings.verbose > 0) {
log_engine_details(engine_handle,settings.extensions.logger);
}
settings.engine.v1 = (ENGINE_HANDLE_V1 *) engine_handle;
diff --git a/daemon/memcached.h b/daemon/memcached.h
index c8a0983..32697e0 100644
--- a/daemon/memcached.h
+++ b/daemon/memcached.h
@@ -209,6 +209,7 @@ struct settings {
EXTENSION_DAEMON_DESCRIPTOR *daemons;
EXTENSION_LOGGER_DESCRIPTOR *logger;
EXTENSION_ASCII_PROTOCOL_DESCRIPTOR *ascii;
+ EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *binary;
} extensions;
};
diff --git a/include/memcached/engine.h b/include/memcached/engine.h
index fa34410..6acea49 100644
--- a/include/memcached/engine.h
+++ b/include/memcached/engine.h
@@ -49,41 +49,6 @@ extern "C" {
#define ENGINE_INTERFACE_VERSION 1
- /**
- * Callback for any function producing stats.
- *
- * @param key the stat's key
- * @param klen length of the key
- * @param val the stat's value in an ascii form (e.g. text form of a number)
- * @param vlen length of the value
- * @param cookie magic callback cookie
- */
- typedef void (*ADD_STAT)(const char *key, const uint16_t klen,
- const char *val, const uint32_t vlen,
- const void *cookie);
-
- /**
- * Callback for adding a response backet
- * @param key The key to put in the response
- * @param keylen The length of the key
- * @param ext The data to put in the extended field in the response
- * @param extlen The number of bytes in the ext field
- * @param body The data body
- * @param bodylen The number of bytes in the body
- * @param datatype This is currently not used and should be set to 0
- * @param status The status code of the return packet (see in protocol_binary
- * for the legal values)
- * @param cas The cas to put in the return packet
- * @param cookie The cookie provided by the frontend
- * @return true if return message was successfully created, false if an
- * error occured that prevented the message from being sent
- */
- typedef bool (*ADD_RESPONSE)(const void *key, uint16_t keylen,
- const void *ext, uint8_t extlen,
- const void *body, uint32_t bodylen,
- uint8_t datatype, uint16_t status,
- uint64_t cas, const void *cookie);
-
/**
* Abstract interface to an engine.
diff --git a/include/memcached/engine_common.h b/include/memcached/engine_common.h
index e918be4..dea0e9f 100644
--- a/include/memcached/engine_common.h
+++ b/include/memcached/engine_common.h
@@ -1,6 +1,9 @@
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#ifndef ENGINE_COMMON_H
#define ENGINE_COMMON_H
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -9,6 +12,42 @@ extern "C" {
uint64_t interface; /**< The version number on the engine structure */
} ENGINE_HANDLE;
+ /**
+ * Callback for any function producing stats.
+ *
+ * @param key the stat's key
+ * @param klen length of the key
+ * @param val the stat's value in an ascii form (e.g. text form of a number)
+ * @param vlen length of the value
+ * @param cookie magic callback cookie
+ */
+ typedef void (*ADD_STAT)(const char *key, const uint16_t klen,
+ const char *val, const uint32_t vlen,
+ const void *cookie);
+
+ /**
+ * Callback for adding a response backet
+ * @param key The key to put in the response
+ * @param keylen The length of the key
+ * @param ext The data to put in the extended field in the response
+ * @param extlen The number of bytes in the ext field
+ * @param body The data body
+ * @param bodylen The number of bytes in the body
+ * @param datatype This is currently not used and should be set to 0
+ * @param status The status code of the return packet (see in protocol_binary
+ * for the legal values)
+ * @param cas The cas to put in the return packet
+ * @param cookie The cookie provided by the frontend
+ * @return true if return message was successfully created, false if an
+ * error occured that prevented the message from being sent
+ */
+ typedef bool (*ADD_RESPONSE)(const void *key, uint16_t keylen,
+ const void *ext, uint8_t extlen,
+ const void *body, uint32_t bodylen,
+ uint8_t datatype, uint16_t status,
+ uint64_t cas, const void *cookie);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/include/memcached/extension.h b/include/memcached/extension.h
index d978d75..6ec0040 100644
--- a/include/memcached/extension.h
+++ b/include/memcached/extension.h
@@ -3,6 +3,10 @@
#define MEMCACHED_EXTENSION_H
#include <stdbool.h>
+#include <stdint.h>
+#include <memcached/engine_common.h>
+#include <memcached/protocol_binary.h>
+#include <memcached/types.h>
#include <memcached/server_api.h>
#ifdef __cplusplus
@@ -43,7 +47,11 @@ extern "C" {
/**
* Command extension for the ASCII protocol
*/
- EXTENSION_ASCII_PROTOCOL
+ EXTENSION_ASCII_PROTOCOL,
+ /**
+ * Command extension for the binary protocol
+ */
+ EXTENSION_BINARY_PROTOCOL
} extension_type_t;
/**
@@ -203,6 +211,44 @@ extern "C" {
struct extension_ascii_protocol_descriptor *next;
} EXTENSION_ASCII_PROTOCOL_DESCRIPTOR;
+
+ typedef struct extension_binary_protocol_descriptor EXTENSION_BINARY_PROTOCOL_DESCRIPTOR;
+
+ typedef ENGINE_ERROR_CODE (*BINARY_COMMAND_CALLBACK)(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
+ ENGINE_HANDLE* handle,
+ const void* cookie,
+ protocol_binary_request_header *request,
+ ADD_RESPONSE response);
+
+ /**
+ * ASCII protocol extensions must provide the following descriptor to
+ * extend the capabilities of the ascii protocol. The memcached core
+ * will probe each command in the order they are registered, so you should
+ * register the most likely command to be used first (or you could register
+ * only one descriptor and do a better dispatch routine inside your own
+ * implementation of accept / execute).
+ */
+ struct extension_binary_protocol_descriptor {
+ /**
+ * Get the name of the descriptor. The memory area returned by this
+ * function has to be valid until the descriptor is unregistered.
+ */
+ const char* (*get_name)(void);
+
+ void (*setup)(void (*add)(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
+ uint8_t cmd,
+ BINARY_COMMAND_CALLBACK new_handler));
+
+ /**
+ * Deamon descriptors are stored in a linked list in the memcached
+ * core by using this pointer. Please do not modify this pointer
+ * by yourself until you have unregistered the descriptor.
+ * The <b>only</b> time it is safe for an extension to walk this
+ * list is during initialization of the modules.
+ */
+ struct extension_binary_protocol_descriptor *next;
+ };
+
/**
* The signature for the "memcached_extensions_initialize" function
* exported from the loadable module.