mirror of
https://github.com/php-win-ext/pecl-memcache.git
synced 2026-03-24 00:52:07 +01:00
Updated binary protocol to run against memcached 1.3.0
Fixed bug #13592 error in opcode Fixed request #13588 minor inefficiency in binary protocol implementation
This commit is contained in:
106
memcache.c
106
memcache.c
@@ -509,35 +509,6 @@ static int mmc_deleted_handler(mmc_t *mmc, mmc_request_t *request, int response,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int mmc_numeric_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /*
|
||||
parses a numeric response line, param is a zval pointer to store result into {{{ */
|
||||
{
|
||||
/* must contain digit(s) */
|
||||
if (message_len < 1) {
|
||||
return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* append return value to result array */
|
||||
if (param != NULL) {
|
||||
if (Z_TYPE_P((zval *)param) == IS_NULL) {
|
||||
array_init((zval *)param);
|
||||
}
|
||||
|
||||
if (Z_TYPE_P((zval *)param) == IS_ARRAY) {
|
||||
zval *result;
|
||||
MAKE_STD_ZVAL(result);
|
||||
ZVAL_LONG(result, atol(message));
|
||||
add_assoc_zval_ex((zval *)param, request->key, request->key_len + 1, result);
|
||||
}
|
||||
else {
|
||||
ZVAL_LONG((zval *)param, atol(message));
|
||||
}
|
||||
}
|
||||
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int invert) /*
|
||||
sends one or several commands which have a single optional numeric parameter (incr, decr, delete) {{{ */
|
||||
{
|
||||
@@ -548,7 +519,8 @@ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int inver
|
||||
long value = 1, defval = 0, exptime = 0;
|
||||
mmc_request_t *request;
|
||||
mmc_request_response_handler response_handler;
|
||||
|
||||
void *value_handler_param[3];
|
||||
|
||||
if (mmc_object == NULL) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|lll", &mmc_object, memcache_pool_ce, &keys, &value, &defval, &exptime) == FAILURE) {
|
||||
return;
|
||||
@@ -567,16 +539,16 @@ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int inver
|
||||
if (deleted) {
|
||||
response_handler = mmc_deleted_handler;
|
||||
}
|
||||
else {
|
||||
response_handler = mmc_numeric_handler;
|
||||
}
|
||||
|
||||
ZVAL_NULL(return_value);
|
||||
value_handler_param[0] = return_value;
|
||||
value_handler_param[1] = NULL;
|
||||
value_handler_param[2] = NULL;
|
||||
|
||||
if (Z_TYPE_P(keys) == IS_ARRAY) {
|
||||
zval **key;
|
||||
HashPosition pos;
|
||||
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
|
||||
|
||||
RETVAL_NULL();
|
||||
|
||||
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&key, &pos) == SUCCESS) {
|
||||
zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
|
||||
@@ -593,6 +565,9 @@ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int inver
|
||||
mmc_pool_failover_handler, NULL TSRMLS_CC);
|
||||
}
|
||||
|
||||
request->value_handler = mmc_value_handler_multi;
|
||||
request->value_handler_param = value_handler_param;
|
||||
|
||||
if (mmc_prepare_key(*key, request->key, &(request->key_len)) != MMC_OK) {
|
||||
mmc_pool_release(pool, request);
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key");
|
||||
@@ -603,7 +578,7 @@ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int inver
|
||||
pool->protocol->delete(request, request->key, request->key_len, value);
|
||||
}
|
||||
else {
|
||||
pool->protocol->mutate(request, request->key, request->key_len, invert ? -value : value, defval, exptime);
|
||||
pool->protocol->mutate(request, *key, request->key, request->key_len, invert ? -value : value, defval, exptime);
|
||||
}
|
||||
|
||||
/* schedule request */
|
||||
@@ -627,6 +602,9 @@ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int inver
|
||||
request = mmc_pool_request(pool, MMC_PROTO_TCP,
|
||||
response_handler, return_value, mmc_pool_failover_handler, NULL TSRMLS_CC);
|
||||
|
||||
request->value_handler = mmc_value_handler_single;
|
||||
request->value_handler_param = value_handler_param;
|
||||
|
||||
if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) {
|
||||
mmc_pool_release(pool, request);
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key");
|
||||
@@ -637,7 +615,7 @@ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int inver
|
||||
pool->protocol->delete(request, request->key, request->key_len, value);
|
||||
}
|
||||
else {
|
||||
pool->protocol->mutate(request, request->key, request->key_len, invert ? -value : value, defval, exptime);
|
||||
pool->protocol->mutate(request, keys, request->key, request->key_len, invert ? -value : value, defval, exptime);
|
||||
}
|
||||
|
||||
/* schedule request */
|
||||
@@ -725,20 +703,6 @@ static mmc_t *php_mmc_pool_addserver(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* lazy initialization of server struct */
|
||||
if (persistent && status) {
|
||||
mmc = mmc_find_persistent(host, host_len, tcp_port, udp_port, timeout, retry_interval TSRMLS_CC);
|
||||
}
|
||||
else {
|
||||
mmc = mmc_server_new(host, host_len, tcp_port, udp_port, 0, timeout, retry_interval TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* add server in failed mode */
|
||||
if (!status) {
|
||||
mmc->tcp.status = MMC_STATUS_FAILED;
|
||||
mmc->udp.status = MMC_STATUS_FAILED;
|
||||
}
|
||||
|
||||
/* initialize pool if need be */
|
||||
if (zend_hash_find(Z_OBJPROP_P(mmc_object), "connection", sizeof("connection"), (void **)&connection) == FAILURE) {
|
||||
pool = mmc_pool_new(TSRMLS_C);
|
||||
@@ -753,6 +717,26 @@ static mmc_t *php_mmc_pool_addserver(
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* binary protocol isn't support over UDP yet */
|
||||
if (udp_port && pool->protocol == &mmc_binary_protocol) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "binary protocol isn't support over UDP, defaulting to TCP");
|
||||
udp_port = 0;
|
||||
}
|
||||
|
||||
/* lazy initialization of server struct */
|
||||
if (persistent && status) {
|
||||
mmc = mmc_find_persistent(host, host_len, tcp_port, udp_port, timeout, retry_interval TSRMLS_CC);
|
||||
}
|
||||
else {
|
||||
mmc = mmc_server_new(host, host_len, tcp_port, udp_port, 0, timeout, retry_interval TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* add server in failed mode */
|
||||
if (!status) {
|
||||
mmc->tcp.status = MMC_STATUS_FAILED;
|
||||
mmc->udp.status = MMC_STATUS_FAILED;
|
||||
}
|
||||
|
||||
mmc_pool_add(pool, mmc, weight);
|
||||
|
||||
@@ -1403,14 +1387,14 @@ PHP_FUNCTION(memcache_prepend)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int mmc_value_handler_multi(
|
||||
const char *key, unsigned int key_len, void *value, unsigned int value_len,
|
||||
int mmc_value_handler_multi(
|
||||
const char *key, unsigned int key_len, zval *value,
|
||||
unsigned int flags, unsigned long cas, void *param TSRMLS_DC) /*
|
||||
receives a multiple values, param is a zval** array to store value and flags in {{{ */
|
||||
{
|
||||
zval *arrval, **result = (zval **)param;
|
||||
ALLOC_ZVAL(arrval);
|
||||
*((zval *)arrval) = *((zval *)value);
|
||||
*((zval *)arrval) = *value;
|
||||
|
||||
/* add value to result */
|
||||
if (Z_TYPE_P(result[0]) != IS_ARRAY) {
|
||||
@@ -1434,18 +1418,17 @@ static int mmc_value_handler_multi(
|
||||
add_assoc_long_ex(result[2], (char *)key, key_len + 1, cas);
|
||||
}
|
||||
|
||||
/* request more data (more values or END line) */
|
||||
return MMC_REQUEST_AGAIN;
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int mmc_value_handler_single(
|
||||
const char *key, unsigned int key_len, void *value, unsigned int value_len,
|
||||
const char *key, unsigned int key_len, zval *value,
|
||||
unsigned int flags, unsigned long cas, void *param TSRMLS_DC) /*
|
||||
receives a single value, param is a zval pointer to store value to {{{ */
|
||||
{
|
||||
zval **result = (zval **)param;
|
||||
*(result[0]) = *((zval *)value);
|
||||
*(result[0]) = *value;
|
||||
|
||||
if (result[1] != NULL) {
|
||||
ZVAL_LONG(result[1], flags);
|
||||
@@ -1455,8 +1438,7 @@ int mmc_value_handler_single(
|
||||
ZVAL_LONG(result[2], cas);
|
||||
}
|
||||
|
||||
/* request more data (END line) */
|
||||
return MMC_REQUEST_AGAIN;
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -1574,6 +1556,10 @@ static int mmc_stats_handler(mmc_t *mmc, mmc_request_t *request, int response, c
|
||||
{
|
||||
char *line = (char *)message;
|
||||
|
||||
if(!message_len) {
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
|
||||
if (mmc_str_left(line, "RESET", message_len, sizeof("RESET")-1)) {
|
||||
ZVAL_TRUE((zval *)param);
|
||||
return MMC_REQUEST_DONE;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
typedef struct mmc_ascii_request {
|
||||
mmc_request_t base; /* enable cast to mmc_request_t */
|
||||
struct { /* stores value info while the body is being read */
|
||||
char key[MMC_MAX_KEY_LEN + 1]; /* key buffer to use on failover of single-key requests */
|
||||
char key[MMC_MAX_KEY_LEN + 1];
|
||||
unsigned int flags;
|
||||
unsigned long length;
|
||||
unsigned long cas; /* CAS counter */
|
||||
@@ -104,8 +104,32 @@ static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request TSRMLS_
|
||||
}
|
||||
/* }}}*/
|
||||
|
||||
static int mmc_request_parse_mutate(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the <long-value> response header {{{ */
|
||||
{
|
||||
char *line;
|
||||
int line_len;
|
||||
|
||||
line_len = mmc_stream_get_line(request->io, &line TSRMLS_CC);
|
||||
if (line_len > 0) {
|
||||
long lval;
|
||||
zval value;
|
||||
|
||||
if (sscanf(line, "%lu", &lval) < 1) {
|
||||
return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0 TSRMLS_CC);
|
||||
}
|
||||
|
||||
INIT_PZVAL(&value);
|
||||
ZVAL_LONG(&value, lval);
|
||||
return request->value_handler(request->key, request->key_len, &value, 0, 0, request->value_handler_param TSRMLS_CC);
|
||||
}
|
||||
|
||||
return MMC_REQUEST_MORE;
|
||||
}
|
||||
/* }}}*/
|
||||
|
||||
static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the VALUE <key> <flags> <size> <cas> header and then reads the value body {{{ */
|
||||
reads and parses the VALUE <key> <flags> <size> <cas> response header and then reads the value body {{{ */
|
||||
{
|
||||
char *line;
|
||||
int line_len;
|
||||
@@ -152,6 +176,11 @@ static int mmc_server_read_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /
|
||||
mmc, request, &(request->readbuf), req->value.key, strlen(req->value.key),
|
||||
req->value.flags, req->value.cas, req->value.length TSRMLS_CC);
|
||||
|
||||
/* request more data (END line) */
|
||||
if (result == MMC_REQUEST_DONE) {
|
||||
return MMC_REQUEST_AGAIN;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -167,6 +196,10 @@ static mmc_request_t *mmc_ascii_create_request() /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_ascii_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */
|
||||
{}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_ascii_reset_request(mmc_request_t *request) /* {{{ */
|
||||
{
|
||||
mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
|
||||
@@ -287,9 +320,9 @@ static void mmc_ascii_delete(mmc_request_t *request, const char *key, unsigned i
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_ascii_mutate(mmc_request_t *request, const char *key, unsigned int key_len, long value, long defval, unsigned int exptime) /* {{{ */
|
||||
static void mmc_ascii_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, unsigned int exptime) /* {{{ */
|
||||
{
|
||||
request->parse = mmc_request_parse_response;
|
||||
request->parse = mmc_request_parse_mutate;
|
||||
|
||||
if (value >= 0) {
|
||||
smart_str_appendl(&(request->sendbuf.value), "incr", sizeof("incr")-1);
|
||||
@@ -350,6 +383,7 @@ static void mmc_ascii_stats(mmc_request_t *request, const char *type, long slabi
|
||||
|
||||
mmc_protocol_t mmc_ascii_protocol = {
|
||||
mmc_ascii_create_request,
|
||||
mmc_ascii_clone_request,
|
||||
mmc_ascii_reset_request,
|
||||
mmc_request_free,
|
||||
mmc_ascii_get,
|
||||
|
||||
@@ -40,51 +40,56 @@
|
||||
# error "Could not determine byte order: __BYTE_ORDER uncorrectly defined"
|
||||
#endif
|
||||
|
||||
#define MMC_REQUEST_MAGIC 0x0f
|
||||
#define MMC_RESPONSE_MAGIC 0xf0
|
||||
#define MMC_REQUEST_MAGIC 0x80
|
||||
#define MMC_RESPONSE_MAGIC 0x81
|
||||
|
||||
#define MMC_OP_GET 0x00
|
||||
#define MMC_OP_SET 0x01
|
||||
#define MMC_OP_ADD 0x02
|
||||
#define MMC_OP_REPLACE 0x03
|
||||
#define MMC_OP_DELETE 0x04
|
||||
#define MMC_OP_MUTATE 0x05
|
||||
#define MMC_OP_FLUSH 0x07
|
||||
#define MMC_OP_GETQ 0x08
|
||||
#define MMC_OP_NOOP 0x09
|
||||
#define MMC_OP_VERSION 0x0a
|
||||
#define MMC_OP_INCR 0x05
|
||||
#define MMC_OP_DECR 0x06
|
||||
#define MMC_OP_QUIT 0x07
|
||||
#define MMC_OP_FLUSH 0x08
|
||||
#define MMC_OP_GETQ 0x09
|
||||
#define MMC_OP_NOOP 0x0a
|
||||
#define MMC_OP_VERSION 0x0b
|
||||
|
||||
typedef struct mmc_binary_request {
|
||||
mmc_request_t base; /* enable cast to mmc_request_t */
|
||||
mmc_queue_t keys; /* mmc_queue_t<zval *>, reqid -> key mappings */
|
||||
mmc_request_t base; /* enable cast to mmc_request_t */
|
||||
mmc_request_parser next_parse_handler; /* next payload parser state */
|
||||
mmc_queue_t keys; /* mmc_queue_t<zval *>, reqid -> key mappings */
|
||||
struct {
|
||||
uint8_t cmdid;
|
||||
uint8_t error; /* error received in current request */
|
||||
uint32_t reqid; /* current reqid being processed */
|
||||
uint8_t opcode;
|
||||
uint8_t error; /* error received in current request */
|
||||
uint32_t reqid; /* current reqid being processed */
|
||||
} command;
|
||||
struct { /* stores value info while the body is being read */
|
||||
struct { /* stores value info while the body is being read */
|
||||
unsigned int flags;
|
||||
unsigned long length;
|
||||
uint64_t cas; /* CAS counter */
|
||||
uint64_t cas; /* CAS counter */
|
||||
} value;
|
||||
} mmc_binary_request_t;
|
||||
|
||||
typedef struct mmc_request_header {
|
||||
uint8_t magic;
|
||||
uint8_t cmdid;
|
||||
uint8_t key_len;
|
||||
uint8_t _reserved;
|
||||
uint32_t reqid; /* opaque request id */
|
||||
uint32_t length; /* trailing body length (not including this header) */
|
||||
uint8_t opcode;
|
||||
uint16_t key_len;
|
||||
uint8_t extras_len;
|
||||
uint8_t datatype;
|
||||
uint16_t _reserved;
|
||||
uint32_t length; /* trailing body length (not including this header) */
|
||||
uint32_t reqid; /* opaque request id */
|
||||
} mmc_request_header_t;
|
||||
|
||||
typedef struct mmc_store_request_header {
|
||||
mmc_request_header_t base;
|
||||
uint64_t cas;
|
||||
uint32_t flags;
|
||||
uint32_t exptime;
|
||||
} mmc_store_request_header_t;
|
||||
|
||||
typedef struct mmc_cas_request_header {
|
||||
mmc_store_request_header_t base;
|
||||
uint64_t cas;
|
||||
} mmc_cas_request_header_t;
|
||||
|
||||
typedef struct mmc_delete_request_header {
|
||||
mmc_request_header_t base;
|
||||
uint32_t exptime;
|
||||
@@ -92,37 +97,33 @@ typedef struct mmc_delete_request_header {
|
||||
|
||||
typedef struct mmc_mutate_request_header {
|
||||
mmc_request_header_t base;
|
||||
int64_t value;
|
||||
int64_t defval;
|
||||
uint64_t value;
|
||||
uint64_t defval;
|
||||
uint32_t exptime;
|
||||
} mmc_mutate_request_header_t;
|
||||
|
||||
typedef struct mmc_flush_request_header {
|
||||
mmc_request_header_t base;
|
||||
uint32_t exptime;
|
||||
} mmc_flush_request_header_t;
|
||||
|
||||
typedef struct mmc_response_header {
|
||||
uint8_t magic;
|
||||
uint8_t cmdid;
|
||||
uint8_t error;
|
||||
uint8_t _reserved;
|
||||
uint32_t reqid; /* echo'ed from request */
|
||||
uint8_t opcode;
|
||||
uint16_t error;
|
||||
uint8_t extras_len;
|
||||
uint8_t datatype;
|
||||
uint16_t _reserved;
|
||||
uint32_t length; /* trailing body length (not including this header) */
|
||||
uint32_t reqid; /* echo'ed from request */
|
||||
} mmc_response_header_t;
|
||||
|
||||
typedef struct mmc_get_response_header {
|
||||
uint64_t cas;
|
||||
uint32_t flags;
|
||||
} mmc_get_response_header_t;
|
||||
|
||||
typedef struct mmc_gets_response_header {
|
||||
uint32_t flags;
|
||||
uint64_t cas;
|
||||
} mmc_gets_response_header_t;
|
||||
typedef struct mmc_mutate_response_header {
|
||||
uint64_t value;
|
||||
} mmc_mutate_response_header_t;
|
||||
|
||||
static int mmc_request_read_response(mmc_t *, mmc_request_t * TSRMLS_DC);
|
||||
static int mmc_request_parse_value(mmc_t *, mmc_request_t * TSRMLS_DC);
|
||||
static int mmc_request_parse_value_cas(mmc_t *, mmc_request_t * TSRMLS_DC);
|
||||
static int mmc_request_read_value(mmc_t *, mmc_request_t * TSRMLS_DC);
|
||||
|
||||
static inline char *mmc_stream_get(mmc_stream_t *io, size_t bytes TSRMLS_DC) /*
|
||||
@@ -139,39 +140,46 @@ static inline char *mmc_stream_get(mmc_stream_t *io, size_t bytes TSRMLS_DC) /*
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static inline void mmc_decode_response_header(mmc_response_header_t *header) /* {{{ */
|
||||
{
|
||||
header->reqid = ntohl(header->reqid);
|
||||
header->length = ntohl(header->length);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads a generic response header and reads the response body {{{ */
|
||||
{
|
||||
mmc_response_header_t *header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
header = (mmc_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC);
|
||||
|
||||
if (header != NULL) {
|
||||
mmc_decode_response_header(header);
|
||||
if (header->magic != MMC_RESPONSE_MAGIC) {
|
||||
return mmc_server_failure(mmc, request->io, "Malformed server response (invalid magic byte)", 0 TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (header->length == 0) {
|
||||
return request->response_handler(mmc, request, header->error, "", 0, request->response_handler_param TSRMLS_CC);
|
||||
if (header->opcode == MMC_OP_NOOP) {
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
|
||||
req->command.error = header->error;
|
||||
req->value.length = header->length;
|
||||
|
||||
/* memory for data + \0 */
|
||||
mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
|
||||
req->command.opcode = header->opcode;
|
||||
req->command.error = ntohs(header->error);
|
||||
req->command.reqid = ntohl(header->reqid);
|
||||
req->value.length = ntohl(header->length);
|
||||
|
||||
if (req->value.length == 0) {
|
||||
return request->response_handler(mmc, request, req->command.error, "", 0, request->response_handler_param TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* allow read_response handler to read the response body */
|
||||
request->parse = mmc_request_read_response;
|
||||
|
||||
if (req->command.error) {
|
||||
request->parse = mmc_request_read_response;
|
||||
mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
|
||||
}
|
||||
else {
|
||||
request->parse = req->next_parse_handler;
|
||||
|
||||
if (req->value.length >= header->extras_len) {
|
||||
req->value.length -= header->extras_len;
|
||||
}
|
||||
|
||||
mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
|
||||
}
|
||||
|
||||
/* read more, php streams buffer input which must be read if available */
|
||||
return MMC_REQUEST_AGAIN;
|
||||
}
|
||||
@@ -180,8 +188,14 @@ static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request TSRMLS_
|
||||
}
|
||||
/* }}}*/
|
||||
|
||||
static int mmc_request_parse_null(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
always returns MMC_REQUEST_DONE {{{ */
|
||||
{
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
|
||||
static int mmc_request_read_response(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
read the response body into the buffer {{{ */
|
||||
read the response body into the buffer and delegates to response_handler {{{ */
|
||||
{
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
request->readbuf.idx +=
|
||||
@@ -189,6 +203,7 @@ static int mmc_request_read_response(mmc_t *mmc, mmc_request_t *request TSRMLS_D
|
||||
|
||||
/* done reading? */
|
||||
if (request->readbuf.idx >= req->value.length) {
|
||||
request->readbuf.value.c[req->value.length] = '\0';
|
||||
return request->response_handler(mmc, request, req->command.error, request->readbuf.value.c, req->value.length, request->response_handler_param TSRMLS_CC);
|
||||
}
|
||||
|
||||
@@ -196,98 +211,60 @@ static int mmc_request_read_response(mmc_t *mmc, mmc_request_t *request TSRMLS_D
|
||||
}
|
||||
/* }}}*/
|
||||
|
||||
static int mmc_request_parse_value_header(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the header and then reads the value header {{{ */
|
||||
static int mmc_request_read_mutate(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the mutate response header {{{ */
|
||||
{
|
||||
mmc_response_header_t *header;
|
||||
mmc_mutate_response_header_t *header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
header = (mmc_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC);
|
||||
header = (mmc_mutate_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC);
|
||||
if (header != NULL) {
|
||||
mmc_decode_response_header(header);
|
||||
if (header->magic != MMC_RESPONSE_MAGIC) {
|
||||
return mmc_server_failure(mmc, request->io, "Malformed server response (invalid magic byte)", 0 TSRMLS_CC);
|
||||
}
|
||||
int result;
|
||||
zval *key, value;
|
||||
|
||||
/* convert remembered key to string and unpack value */
|
||||
key = (zval *)mmc_queue_item(&(req->keys), req->command.reqid);
|
||||
|
||||
/* read response body in case of error */
|
||||
if (header->error != 0) {
|
||||
if (header->length == 0) {
|
||||
return request->response_handler(mmc, request, header->error, "", 0, request->response_handler_param TSRMLS_CC);
|
||||
}
|
||||
INIT_PZVAL(&value);
|
||||
ZVAL_LONG(&value, ntohll(header->value));
|
||||
|
||||
if (Z_TYPE_P(key) != IS_STRING) {
|
||||
zval keytmp = *key;
|
||||
|
||||
req->command.error = header->error;
|
||||
req->value.length = header->length;
|
||||
zval_copy_ctor(&keytmp);
|
||||
INIT_PZVAL(&keytmp);
|
||||
convert_to_string(&keytmp);
|
||||
|
||||
/* memory for data + \0 */
|
||||
mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
|
||||
|
||||
/* allow read_response handler to read the response body */
|
||||
request->parse = mmc_request_read_response;
|
||||
result = request->value_handler(
|
||||
Z_STRVAL(keytmp), Z_STRLEN(keytmp), &value,
|
||||
req->value.flags, req->value.cas, request->value_handler_param TSRMLS_CC);
|
||||
|
||||
zval_dtor(&keytmp);
|
||||
}
|
||||
else {
|
||||
/* last command in multi-get */
|
||||
if (header->cmdid == MMC_OP_NOOP) {
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
|
||||
req->command.reqid = header->reqid;
|
||||
|
||||
/* allow parse_value_ handler to read the value specific header */
|
||||
if (header->cmdid == MMC_OP_GETS) {
|
||||
request->parse = mmc_request_parse_value_cas;
|
||||
req->value.length = header->length - sizeof(mmc_gets_response_header_t);
|
||||
}
|
||||
else {
|
||||
request->parse = mmc_request_parse_value;
|
||||
req->value.length = header->length - sizeof(mmc_get_response_header_t);
|
||||
}
|
||||
result = request->value_handler(
|
||||
Z_STRVAL_P(key), Z_STRLEN_P(key), &value,
|
||||
req->value.flags, req->value.cas, request->value_handler_param TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* read more, php streams buffer input which must be read if available */
|
||||
return MMC_REQUEST_AGAIN;
|
||||
}
|
||||
|
||||
return MMC_REQUEST_MORE;
|
||||
}
|
||||
|
||||
static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the value header and then reads the value body {{{ */
|
||||
{
|
||||
mmc_get_response_header_t *header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
header = (mmc_get_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC);
|
||||
if (header != NULL) {
|
||||
req->value.flags = ntohl(header->flags);
|
||||
|
||||
/* memory for data + \0 */
|
||||
mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
|
||||
|
||||
/* allow read_value handler to read the value body */
|
||||
request->parse = mmc_request_read_value;
|
||||
|
||||
/* read more, php streams buffer input which must be read if available */
|
||||
return MMC_REQUEST_AGAIN;
|
||||
return result;
|
||||
}
|
||||
|
||||
return MMC_REQUEST_MORE;
|
||||
}
|
||||
/* }}}*/
|
||||
|
||||
static int mmc_request_parse_value_cas(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the value header and then reads the value body {{{ */
|
||||
static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /*
|
||||
reads and parses the value response header and then reads the value body {{{ */
|
||||
{
|
||||
mmc_gets_response_header_t *header;
|
||||
mmc_get_response_header_t *header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
header = (mmc_gets_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC);
|
||||
header = (mmc_get_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC);
|
||||
if (header != NULL) {
|
||||
req->value.flags = ntohl(header->flags);
|
||||
req->value.cas = ntohll(header->cas);
|
||||
req->value.flags = ntohl(header->flags);
|
||||
|
||||
/* memory for data + \0 */
|
||||
mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
|
||||
|
||||
/* allow read_value handler to read the value body */
|
||||
request->parse = mmc_request_read_value;
|
||||
|
||||
@@ -311,34 +288,57 @@ static int mmc_request_read_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC)
|
||||
zval *key;
|
||||
int result;
|
||||
|
||||
/* allow parse_value to read next VALUE or NOOP */
|
||||
request->parse = mmc_request_parse_value_header;
|
||||
/* allow parse_value to read next VALUE or NOOP, done here to ensure reentrancy */
|
||||
if (req->command.opcode == MMC_OP_GET) {
|
||||
request->parse = mmc_request_parse_null;
|
||||
}
|
||||
else {
|
||||
request->parse = mmc_request_parse_response;
|
||||
}
|
||||
mmc_buffer_reset(&(request->readbuf));
|
||||
|
||||
/* convert remembered key to string and unpack value */
|
||||
key = (zval *)mmc_queue_item(&(req->keys), req->command.reqid);
|
||||
result = mmc_unpack_value(
|
||||
mmc, request, &(request->readbuf), Z_STRVAL_P(key), Z_STRLEN_P(key),
|
||||
req->value.flags, req->value.cas, req->value.length TSRMLS_CC);
|
||||
if (Z_TYPE_P(key) != IS_STRING) {
|
||||
zval keytmp = *key;
|
||||
|
||||
zval_copy_ctor(&keytmp);
|
||||
INIT_PZVAL(&keytmp);
|
||||
convert_to_string(&keytmp);
|
||||
|
||||
result = mmc_unpack_value(
|
||||
mmc, request, &(request->readbuf), Z_STRVAL(keytmp), Z_STRLEN(keytmp),
|
||||
req->value.flags, req->value.cas, req->value.length TSRMLS_CC);
|
||||
|
||||
zval_dtor(&keytmp);
|
||||
}
|
||||
else {
|
||||
result = mmc_unpack_value(
|
||||
mmc, request, &(request->readbuf), Z_STRVAL_P(key), Z_STRLEN_P(key),
|
||||
req->value.flags, req->value.cas, req->value.length TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (result == MMC_REQUEST_AGAIN && req->command.reqid >= req->keys.len) {
|
||||
if (result == MMC_REQUEST_DONE && (req->command.opcode == MMC_OP_GET || req->command.reqid >= req->keys.len)) {
|
||||
return MMC_REQUEST_DONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
return MMC_REQUEST_AGAIN;
|
||||
}
|
||||
|
||||
return MMC_REQUEST_MORE;
|
||||
}
|
||||
/* }}}*/
|
||||
|
||||
static inline void mmc_pack_header(mmc_request_header_t *header, uint8_t cmdid, unsigned int key_len, unsigned int reqid, unsigned int length) /* {{{ */
|
||||
static inline void mmc_pack_header(mmc_request_header_t *header, uint8_t opcode, unsigned int reqid, unsigned int key_len, unsigned int extras_len, unsigned int length) /* {{{ */
|
||||
{
|
||||
header->magic = MMC_REQUEST_MAGIC;
|
||||
header->cmdid = cmdid;
|
||||
header->key_len = key_len & 0xff;
|
||||
header->opcode = opcode;
|
||||
header->key_len = htons(key_len);
|
||||
header->extras_len = extras_len;
|
||||
header->datatype = 0;
|
||||
header->_reserved = 0;
|
||||
header->length = htonl(key_len + extras_len + length);
|
||||
header->reqid = htonl(reqid);
|
||||
header->length = htonl(length);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -350,6 +350,16 @@ static mmc_request_t *mmc_binary_create_request() /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_binary_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */
|
||||
{
|
||||
mmc_binary_request_t *rcl = (mmc_binary_request_t *)clone;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
rcl->next_parse_handler = req->next_parse_handler;
|
||||
mmc_queue_copy(&(rcl->keys), &(req->keys));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_binary_reset_request(mmc_request_t *request) /* {{{ */
|
||||
{
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
@@ -369,9 +379,9 @@ static void mmc_binary_free_request(mmc_request_t *request) /* {{{ */
|
||||
|
||||
static void mmc_binary_begin_get(mmc_request_t *request, int op) /* {{{ */
|
||||
{
|
||||
request->parse = mmc_request_parse_value_header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
req->command.cmdid = (op == MMC_OP_GET ? MMC_OP_GETQ : MMC_OP_GETS);
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_parse_value;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -381,7 +391,7 @@ static void mmc_binary_append_get(mmc_request_t *request, zval *zkey, const char
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
/* reqid/opaque is the index into the collection of key pointers */
|
||||
mmc_pack_header(&header, req->command.cmdid, key_len, req->keys.len, key_len);
|
||||
mmc_pack_header(&header, MMC_OP_GETQ, req->keys.len, key_len, 0, 0);
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
smart_str_appendl(&(request->sendbuf.value), key, key_len);
|
||||
|
||||
@@ -394,16 +404,25 @@ static void mmc_binary_end_get(mmc_request_t *request) /* {{{ */
|
||||
{
|
||||
mmc_request_header_t header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
mmc_pack_header(&header, MMC_OP_NOOP, 0, req->keys.len, 0);
|
||||
mmc_pack_header(&header, MMC_OP_NOOP, req->keys.len, 0, 0, 0);
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_binary_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
|
||||
{
|
||||
mmc_binary_begin_get(request, op);
|
||||
mmc_binary_append_get(request, zkey, key, key_len);
|
||||
mmc_binary_end_get(request);
|
||||
mmc_request_header_t header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_parse_value;
|
||||
|
||||
/* reqid/opaque is the index into the collection of key pointers */
|
||||
mmc_pack_header(&header, MMC_OP_GET, req->keys.len, key_len, 0, 0);
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
smart_str_appendl(&(request->sendbuf.value), key, key_len);
|
||||
|
||||
/* store key to be used by the response handler */
|
||||
mmc_queue_push(&(req->keys), zkey);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -411,24 +430,23 @@ static int mmc_binary_store(
|
||||
mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len,
|
||||
unsigned int flags, unsigned int exptime, unsigned long cas, zval *value TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
int status, prevlen;
|
||||
int status, prevlen, valuelen;
|
||||
mmc_store_request_header_t *header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
request->parse = mmc_request_parse_response;
|
||||
prevlen = request->sendbuf.value.len;
|
||||
req->next_parse_handler = mmc_request_read_response;
|
||||
|
||||
prevlen = request->sendbuf.value.len;
|
||||
|
||||
/* allocate space for header */
|
||||
if (op == MMC_OP_CAS) {
|
||||
mmc_buffer_alloc(&(request->sendbuf), sizeof(mmc_cas_request_header_t));
|
||||
request->sendbuf.value.len += sizeof(mmc_cas_request_header_t);
|
||||
}
|
||||
else {
|
||||
mmc_buffer_alloc(&(request->sendbuf), sizeof(*header));
|
||||
request->sendbuf.value.len += sizeof(*header);
|
||||
}
|
||||
mmc_buffer_alloc(&(request->sendbuf), sizeof(*header));
|
||||
request->sendbuf.value.len += sizeof(*header);
|
||||
|
||||
/* append key and data */
|
||||
smart_str_appendl(&(request->sendbuf.value), key, key_len);
|
||||
|
||||
valuelen = request->sendbuf.value.len;
|
||||
status = mmc_pack_value(pool, &(request->sendbuf), value, &flags TSRMLS_CC);
|
||||
|
||||
if (status != MMC_OK) {
|
||||
@@ -437,15 +455,24 @@ static int mmc_binary_store(
|
||||
|
||||
/* initialize header */
|
||||
header = (mmc_store_request_header_t *)(request->sendbuf.value.c + prevlen);
|
||||
mmc_pack_header(&(header->base), op, key_len, 0, request->sendbuf.value.len - prevlen - sizeof(header->base));
|
||||
|
||||
switch (op) {
|
||||
case MMC_OP_CAS:
|
||||
op = MMC_OP_SET;
|
||||
break;
|
||||
|
||||
case MMC_OP_APPEND:
|
||||
case MMC_OP_PREPEND:
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Binary protocol doesn't support append/prepend");
|
||||
return MMC_REQUEST_FAILURE;
|
||||
}
|
||||
|
||||
mmc_pack_header(&(header->base), op, 0, key_len, sizeof(*header) - sizeof(header->base), request->sendbuf.value.len - valuelen);
|
||||
|
||||
header->cas = htonll(cas);
|
||||
header->flags = htonl(flags);
|
||||
header->exptime = htonl(exptime);
|
||||
|
||||
if (op == MMC_OP_CAS) {
|
||||
((mmc_cas_request_header_t *)header)->cas = htonll(cas);
|
||||
}
|
||||
|
||||
return MMC_OK;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -453,9 +480,12 @@ static int mmc_binary_store(
|
||||
static void mmc_binary_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */
|
||||
{
|
||||
mmc_delete_request_header_t header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_read_response;
|
||||
|
||||
mmc_pack_header(&(header.base), MMC_OP_DELETE, key_len, 0, sizeof(header) - sizeof(header.base) + key_len);
|
||||
mmc_pack_header(&(header.base), MMC_OP_DELETE, 0, key_len, sizeof(header) - sizeof(header.base), 0);
|
||||
header.exptime = htonl(exptime);
|
||||
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
@@ -463,29 +493,43 @@ static void mmc_binary_delete(mmc_request_t *request, const char *key, unsigned
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_binary_mutate(mmc_request_t *request, const char *key, unsigned int key_len, long value, long defval, unsigned int exptime) /* {{{ */
|
||||
static void mmc_binary_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, unsigned int exptime) /* {{{ */
|
||||
{
|
||||
mmc_mutate_request_header_t header;
|
||||
request->parse = mmc_request_parse_response;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
mmc_pack_header(&(header.base), MMC_OP_MUTATE, key_len, 0, sizeof(header) - sizeof(header.base) + key_len);
|
||||
header.value = htonll(value);
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_read_mutate;
|
||||
|
||||
if (value >= 0) {
|
||||
mmc_pack_header(&(header.base), MMC_OP_INCR, req->keys.len, key_len, sizeof(header) - sizeof(header.base), 0);
|
||||
header.value = htonll(value);
|
||||
}
|
||||
else {
|
||||
mmc_pack_header(&(header.base), MMC_OP_DECR, req->keys.len, key_len, sizeof(header) - sizeof(header.base), 0);
|
||||
header.value = htonll(-value);
|
||||
}
|
||||
|
||||
header.defval = htonll(defval);
|
||||
header.exptime = htonl(exptime);
|
||||
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
smart_str_appendl(&(request->sendbuf.value), key, key_len);
|
||||
|
||||
/* store key to be used by the response handler */
|
||||
mmc_queue_push(&(req->keys), zkey);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void mmc_binary_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */
|
||||
{
|
||||
mmc_flush_request_header_t header;
|
||||
mmc_request_header_t header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_read_response;
|
||||
|
||||
mmc_pack_header(&(header.base), MMC_OP_FLUSH, 0, 0, sizeof(header) - sizeof(header.base));
|
||||
header.exptime = htonl(exptime);
|
||||
|
||||
mmc_pack_header(&header, MMC_OP_FLUSH, 0, 0, 0, 0);
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
}
|
||||
/* }}} */
|
||||
@@ -493,9 +537,12 @@ static void mmc_binary_flush(mmc_request_t *request, unsigned int exptime) /* {{
|
||||
static void mmc_binary_version(mmc_request_t *request) /* {{{ */
|
||||
{
|
||||
mmc_request_header_t header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_read_response;
|
||||
|
||||
mmc_pack_header(&header, MMC_OP_VERSION, 0, 0, 0);
|
||||
mmc_pack_header(&header, MMC_OP_VERSION, 0, 0, 0, 0);
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
}
|
||||
/* }}} */
|
||||
@@ -504,15 +551,19 @@ static void mmc_binary_stats(mmc_request_t *request, const char *type, long slab
|
||||
{
|
||||
/* stats not supported */
|
||||
mmc_request_header_t header;
|
||||
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
|
||||
|
||||
request->parse = mmc_request_parse_response;
|
||||
req->next_parse_handler = mmc_request_read_response;
|
||||
|
||||
mmc_pack_header(&header, MMC_OP_NOOP, 0, 0, 0);
|
||||
mmc_pack_header(&header, MMC_OP_NOOP, 0, 0, 0, 0);
|
||||
smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
mmc_protocol_t mmc_binary_protocol = {
|
||||
mmc_binary_create_request,
|
||||
mmc_binary_clone_request,
|
||||
mmc_binary_reset_request,
|
||||
mmc_binary_free_request,
|
||||
mmc_binary_get,
|
||||
|
||||
@@ -422,10 +422,9 @@ int mmc_unpack_value(
|
||||
}
|
||||
|
||||
/* delegate to value handler */
|
||||
return value_handler(key_tmp, key_len, object, 0, flags, cas, value_handler_param TSRMLS_CC);
|
||||
return value_handler(key_tmp, key_len, object, flags, cas, value_handler_param TSRMLS_CC);
|
||||
}
|
||||
else {
|
||||
/* room for \0 since buffer contains trailing \r\n and uncompress allocates + 1 */
|
||||
data[data_len] = '\0';
|
||||
ZVAL_STRINGL(&value, data, data_len, 0);
|
||||
|
||||
@@ -434,7 +433,7 @@ int mmc_unpack_value(
|
||||
}
|
||||
|
||||
/* delegate to value handler */
|
||||
return request->value_handler(key, key_len, &value, 0, flags, cas, request->value_handler_param TSRMLS_CC);
|
||||
return request->value_handler(key, key_len, &value, flags, cas, request->value_handler_param TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
/* }}}*/
|
||||
@@ -577,11 +576,8 @@ int mmc_server_failure(mmc_t *mmc, mmc_stream_t *io, const char *error, int errn
|
||||
int mmc_request_failure(mmc_t *mmc, mmc_stream_t *io, const char *message, unsigned int message_len, int errnum TSRMLS_DC) /*
|
||||
checks for a valid server generated error message and calls mmc_server_failure() {{{ */
|
||||
{
|
||||
if (mmc_str_left(message, "ERROR", message_len, sizeof("ERROR")-1) ||
|
||||
mmc_str_left(message, "CLIENT_ERROR", message_len, sizeof("CLIENT_ERROR")-1) ||
|
||||
mmc_str_left(message, "SERVER_ERROR", message_len, sizeof("SERVER_ERROR")-1))
|
||||
{
|
||||
return mmc_server_failure(mmc, io, message, errnum TSRMLS_CC);
|
||||
if (message_len) {
|
||||
return mmc_server_failure(mmc, io, message, errnum TSRMLS_CC);;
|
||||
}
|
||||
|
||||
return mmc_server_failure(mmc, io, "Malformed server response", errnum TSRMLS_CC);
|
||||
@@ -1018,7 +1014,7 @@ mmc_request_t *mmc_pool_clone_request(mmc_pool_t *pool, mmc_request_t *request T
|
||||
|
||||
/* copy payload parser */
|
||||
clone->parse = request->parse;
|
||||
|
||||
|
||||
/* copy key */
|
||||
memcpy(clone->key, request->key, request->key_len);
|
||||
clone->key_len = request->key_len;
|
||||
@@ -1028,6 +1024,9 @@ mmc_request_t *mmc_pool_clone_request(mmc_pool_t *pool, mmc_request_t *request T
|
||||
memcpy(clone->sendbuf.value.c, request->sendbuf.value.c, request->sendbuf.value.len);
|
||||
clone->sendbuf.value.len = request->sendbuf.value.len;
|
||||
|
||||
/* copy protocol specific values */
|
||||
pool->protocol->clone_request(clone, request);
|
||||
|
||||
return clone;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1172,7 +1171,7 @@ int mmc_pool_schedule_get(
|
||||
failover_handler, failover_handler_param TSRMLS_CC);
|
||||
|
||||
if (failed_request != NULL) {
|
||||
mmc_queue_copy(&(failed_request->failed_servers), &(mmc->buildreq->failed_servers));
|
||||
mmc_queue_copy(&(mmc->buildreq->failed_servers), &(failed_request->failed_servers));
|
||||
mmc->buildreq->failed_index = failed_request->failed_index;
|
||||
}
|
||||
|
||||
@@ -1191,7 +1190,7 @@ int mmc_pool_schedule_get(
|
||||
failover_handler, failover_handler_param TSRMLS_CC);
|
||||
|
||||
if (failed_request != NULL) {
|
||||
mmc_queue_copy(&(failed_request->failed_servers), &(mmc->buildreq->failed_servers));
|
||||
mmc_queue_copy(&(mmc->buildreq->failed_servers), &(failed_request->failed_servers));
|
||||
mmc->buildreq->failed_index = failed_request->failed_index;
|
||||
}
|
||||
|
||||
@@ -1525,19 +1524,15 @@ inline int mmc_prepare_key(zval *key, char *result, unsigned int *result_len) /
|
||||
return mmc_prepare_key_ex(Z_STRVAL_P(key), Z_STRLEN_P(key), result, result_len);
|
||||
} else {
|
||||
int res;
|
||||
zval *keytmp;
|
||||
ALLOC_ZVAL(keytmp);
|
||||
|
||||
*keytmp = *key;
|
||||
zval_copy_ctor(keytmp);
|
||||
INIT_PZVAL(keytmp);
|
||||
convert_to_string(keytmp);
|
||||
|
||||
res = mmc_prepare_key_ex(Z_STRVAL_P(keytmp), Z_STRLEN_P(keytmp), result, result_len);
|
||||
|
||||
zval_dtor(keytmp);
|
||||
FREE_ZVAL(keytmp);
|
||||
zval keytmp = *key;
|
||||
|
||||
zval_copy_ctor(&keytmp);
|
||||
INIT_PZVAL(&keytmp);
|
||||
convert_to_string(&keytmp);
|
||||
|
||||
res = mmc_prepare_key_ex(Z_STRVAL(keytmp), Z_STRLEN(keytmp), result, result_len);
|
||||
|
||||
zval_dtor(&keytmp);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
#define MMC_RESPONSE_EXISTS 0x02 /* same as binary protocol */
|
||||
#define MMC_RESPONSE_TOO_LARGE 0x03 /* same as binary protocol */
|
||||
#define MMC_RESPONSE_NOT_STORED 0x05 /* same as binary protocol */
|
||||
#define MMC_RESPONSE_UNKNOWN_CMD 0x81 /* same as binary protocol */
|
||||
#define MMC_RESPONSE_OUT_OF_MEMORY 0x82 /* same as binary protocol */
|
||||
|
||||
#define MMC_STANDARD_HASH 1
|
||||
@@ -128,10 +129,12 @@ typedef struct mmc_request mmc_request_t;
|
||||
typedef int (*mmc_request_reader)(mmc_t *mmc, mmc_request_t *request TSRMLS_DC);
|
||||
typedef int (*mmc_request_parser)(mmc_t *mmc, mmc_request_t *request TSRMLS_DC);
|
||||
typedef int (*mmc_request_value_handler)(
|
||||
const char *key, unsigned int key_len, void *value, unsigned int value_len,
|
||||
const char *key, unsigned int key_len, zval *value,
|
||||
unsigned int flags, unsigned long cas, void *param TSRMLS_DC);
|
||||
typedef int (*mmc_request_response_handler)(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC);
|
||||
typedef int (*mmc_request_failover_handler)(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param TSRMLS_DC);
|
||||
typedef int (*mmc_request_response_handler)(
|
||||
mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC);
|
||||
typedef int (*mmc_request_failover_handler)(
|
||||
mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param TSRMLS_DC);
|
||||
|
||||
void mmc_request_reset(mmc_request_t *);
|
||||
void mmc_request_free(mmc_request_t *);
|
||||
@@ -201,6 +204,7 @@ struct mmc {
|
||||
#define MMC_OP_PREPEND 0x35 /* not supported by binary protocol */
|
||||
|
||||
typedef mmc_request_t * (*mmc_protocol_create_request)();
|
||||
typedef void (*mmc_protocol_clone_request)(mmc_request_t *clone, mmc_request_t *request);
|
||||
typedef void (*mmc_protocol_reset_request)(mmc_request_t *request);
|
||||
typedef void (*mmc_protocol_free_request)(mmc_request_t *request);
|
||||
|
||||
@@ -213,7 +217,7 @@ typedef int (*mmc_protocol_store)(
|
||||
mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len,
|
||||
unsigned int flags, unsigned int exptime, unsigned long cas, zval *value TSRMLS_DC);
|
||||
typedef void (*mmc_protocol_delete)(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime);
|
||||
typedef void (*mmc_protocol_mutate)(mmc_request_t *request, const char *key, unsigned int key_len, long value, long defval, unsigned int exptime);
|
||||
typedef void (*mmc_protocol_mutate)(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, unsigned int exptime);
|
||||
|
||||
typedef void (*mmc_protocol_flush)(mmc_request_t *request, unsigned int exptime);
|
||||
typedef void (*mmc_protocol_stats)(mmc_request_t *request, const char *type, long slabid, long limit);
|
||||
@@ -221,6 +225,7 @@ typedef void (*mmc_protocol_version)(mmc_request_t *request);
|
||||
|
||||
typedef struct mmc_protocol {
|
||||
mmc_protocol_create_request create_request;
|
||||
mmc_protocol_clone_request clone_request;
|
||||
mmc_protocol_reset_request reset_request;
|
||||
mmc_protocol_free_request free_request;
|
||||
|
||||
|
||||
@@ -92,10 +92,10 @@ inline void mmc_queue_free(mmc_queue_t *queue) {
|
||||
memset(queue, 0, sizeof(*queue));
|
||||
}
|
||||
|
||||
inline void mmc_queue_copy(mmc_queue_t *source, mmc_queue_t *target) {
|
||||
inline void mmc_queue_copy(mmc_queue_t *target, mmc_queue_t *source) {
|
||||
if (target->alloc != source->alloc) {
|
||||
target->alloc = source->alloc;
|
||||
erealloc(target->items, sizeof(*target->items) * target->alloc);
|
||||
target->items = erealloc(target->items, sizeof(*target->items) * target->alloc);
|
||||
}
|
||||
|
||||
memcpy(target->items, source->items, sizeof(*source->items) * source->alloc);
|
||||
|
||||
@@ -72,7 +72,8 @@ PHP_FUNCTION(memcache_flush);
|
||||
|
||||
/* internal functions */
|
||||
mmc_t *mmc_find_persistent(const char *, int, unsigned short, unsigned short, int, int TSRMLS_DC);
|
||||
int mmc_value_handler_single(const char *, unsigned int, void *, unsigned int, unsigned int, unsigned long, void * TSRMLS_DC);
|
||||
int mmc_value_handler_single(const char *, unsigned int, zval *, unsigned int, unsigned long, void * TSRMLS_DC);
|
||||
int mmc_value_handler_multi(const char *, unsigned int, zval *, unsigned int, unsigned long, void * TSRMLS_DC);
|
||||
int mmc_stored_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void * TSRMLS_DC);
|
||||
|
||||
/* session handler struct */
|
||||
|
||||
@@ -10,7 +10,7 @@ include 'connect.inc';
|
||||
$var = 'test';
|
||||
$key = "test\r\n\0 - really strange key";
|
||||
|
||||
memcache_set($memcache, $key, $var, false, 10);
|
||||
memcache_set($memcache, $key, $var, false);
|
||||
$result = memcache_get($memcache, $key);
|
||||
var_dump($result);
|
||||
|
||||
@@ -106,7 +106,7 @@ var_dump($result);
|
||||
string(4) "test"
|
||||
bool(false)
|
||||
int(1)
|
||||
string(28) "test____-_really_strange_key"
|
||||
string(28) "%s"
|
||||
|
||||
Warning: memcache_set(): %s
|
||||
bool(false)
|
||||
|
||||
@@ -22,7 +22,9 @@ $memcache2->set($balanceKey2, '', false, 2);
|
||||
$result1 = $memcache->set($balanceKey1, $var1, false, 2); // hashes to $host2
|
||||
$result2 = $memcache->set($balanceKey2, $var2, false, 2); // hashes to $host1
|
||||
$result3 = $memcache->get(array($balanceKey1, $balanceKey2));
|
||||
sort($result3);
|
||||
|
||||
if (is_array($result3))
|
||||
sort($result3);
|
||||
|
||||
var_dump($result1);
|
||||
var_dump($result2);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--TEST--
|
||||
memcache->get() over UDP
|
||||
--SKIPIF--
|
||||
<?php include 'connect.inc'; if (!isset($udpPort)) print 'skip UDP is not enabled in connect.inc'; ?>
|
||||
<?php include 'connect.inc'; if (empty($udpPort)) print 'skip UDP is not enabled in connect.inc'; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--TEST--
|
||||
memcache->get() over multi-datagram UDP
|
||||
--SKIPIF--
|
||||
<?php include 'connect.inc'; if (!isset($udpPort)) print 'skip UDP is not enabled in connect.inc'; ?>
|
||||
<?php include 'connect.inc'; if (empty($udpPort)) print 'skip UDP is not enabled in connect.inc'; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
||||
@@ -7,11 +7,6 @@ ini_set('memcache.session_redundancy')
|
||||
|
||||
include 'connect.inc';
|
||||
|
||||
if (!isset($udpPort))
|
||||
$udpPort = 0;
|
||||
if (!isset($udpPort2))
|
||||
$udpPort2 = 0;
|
||||
|
||||
ini_set('memcache.session_redundancy', 10);
|
||||
ini_set('session.save_handler', 'memcache');
|
||||
ini_set('session.save_path', "tcp://$host:$port?udp_port=$udpPort, tcp://$host2:$port2?udp_port=$udpPort2");
|
||||
|
||||
@@ -10,8 +10,8 @@ include 'connect.inc';
|
||||
class testclass {
|
||||
function __wakeup() {
|
||||
global $memcache;
|
||||
$result = $memcache->get('_test_key3');
|
||||
var_dump($result);
|
||||
$this->result = $memcache->get('_test_key3');
|
||||
var_dump($this->result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,18 +25,23 @@ $a2 = $memcache->get('_test_key1');
|
||||
var_dump($a2);
|
||||
|
||||
$result = $memcache->get(array('_test_key1', '_test_key2'));
|
||||
ksort($result);
|
||||
if (is_array($result))
|
||||
ksort($result);
|
||||
var_dump($result);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
string(5) "test3"
|
||||
object(testclass)%s {
|
||||
["result"]=>
|
||||
string(5) "test3"
|
||||
}
|
||||
string(5) "test3"
|
||||
array(2) {
|
||||
["_test_key1"]=>
|
||||
object(testclass)%s {
|
||||
["result"]=>
|
||||
string(5) "test3"
|
||||
}
|
||||
["_test_key2"]=>
|
||||
array(1) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--TEST--
|
||||
memcache->append(), memcache->prepend()
|
||||
--SKIPIF--
|
||||
<?php include 'connect.inc'; if (!isset($host2)) die('skip $host2 not set'); ?>
|
||||
<?php include 'connect.inc'; if (ini_get('memcache.protocol') == 'binary') die('skip binary protocol doesn\'t support append/prepend'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
||||
@@ -20,19 +20,6 @@ $result2 = $memcache->get('test_key');
|
||||
var_dump($result1);
|
||||
var_dump($result2);
|
||||
|
||||
$result = $memcache->flush(time()+3);
|
||||
var_dump($result);
|
||||
|
||||
sleep(1);
|
||||
|
||||
$result = $memcache->get('test_key');
|
||||
var_dump($result);
|
||||
|
||||
sleep(2);
|
||||
|
||||
$result = $memcache->get('test_key');
|
||||
var_dump($result);
|
||||
|
||||
// Test partly failing flush
|
||||
$memcache = new Memcache();
|
||||
$memcache->addServer($host, $port);
|
||||
@@ -52,8 +39,5 @@ var_dump($result);
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
string(3) "abc"
|
||||
bool(true)
|
||||
string(3) "abc"
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
|
||||
59
tests/100b.phpt
Normal file
59
tests/100b.phpt
Normal file
@@ -0,0 +1,59 @@
|
||||
--TEST--
|
||||
memcache->flush() with time in future
|
||||
--SKIPIF--
|
||||
<?php include 'connect.inc'; if (ini_get('memcache.protocol') == 'binary') die('skip binary protocol doesn\'t support append/prepend'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// This test must be run last or some concurrency problems will occur
|
||||
// since the "flush_all" seems to be done async and therefore will
|
||||
// affect subsequent calls to set() done with a second or so.
|
||||
|
||||
include 'connect.inc';
|
||||
|
||||
// Test flush in future
|
||||
$memcache = new Memcache();
|
||||
$memcache->addServer($host, $port);
|
||||
|
||||
$result1 = $memcache->set('test_key', 'abc');
|
||||
$result2 = $memcache->get('test_key');
|
||||
var_dump($result1);
|
||||
var_dump($result2);
|
||||
|
||||
$result = $memcache->flush(time()+3);
|
||||
var_dump($result);
|
||||
|
||||
sleep(1);
|
||||
|
||||
$result = $memcache->get('test_key');
|
||||
var_dump($result);
|
||||
|
||||
sleep(2);
|
||||
|
||||
$result = $memcache->get('test_key');
|
||||
var_dump($result);
|
||||
|
||||
// Test partly failing flush
|
||||
$memcache = new Memcache();
|
||||
$memcache->addServer($host, $port);
|
||||
$memcache->addServer($nonExistingHost, $nonExistingPort);
|
||||
|
||||
$result = @$memcache->flush();
|
||||
var_dump($result);
|
||||
|
||||
// Test failing flush
|
||||
$memcache = new Memcache();
|
||||
$memcache->addServer($nonExistingHost, $nonExistingPort);
|
||||
|
||||
$result = @$memcache->flush();
|
||||
var_dump($result);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
string(3) "abc"
|
||||
bool(true)
|
||||
string(3) "abc"
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
@@ -22,6 +22,11 @@ $udpPort2 = 11212;
|
||||
//ini_set('memcache.hash_function', 'fnv');
|
||||
//ini_set('memcache.protocol', 'binary');
|
||||
|
||||
if (ini_get('memcache.protocol') == 'binary') {
|
||||
$udpPort = 0;
|
||||
$udpPort2 = 0;
|
||||
}
|
||||
|
||||
/* Start a server listening to a unix domain socket
|
||||
*
|
||||
* mkdir /var/run/memcached
|
||||
@@ -51,6 +56,11 @@ $strat = strtolower(ini_get('memcache.hash_strategy'));
|
||||
$func = strtolower(ini_get('memcache.hash_function'));
|
||||
list ($balanceKey1, $balanceKey2) = $balanceKeys[$strat][$func];
|
||||
|
||||
if (!isset($udpPort))
|
||||
$udpPort = 0;
|
||||
if (!isset($udpPort2))
|
||||
$udpPort2 = 0;
|
||||
|
||||
$memcache = memcache_connect($host, $port);
|
||||
|
||||
function test_connect1() {
|
||||
|
||||
Reference in New Issue
Block a user