dict-redis.c revision 7db7fbea5d8a07463b625f93d69166d56018dadf
/* Copyright (c) 2008-2012 Dovecot authors, see the included COPYING redis */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "istream.h"
#include "ostream.h"
#include "connection.h"
#include "dict-private.h"
#define REDIS_DEFAULT_PORT 6379
#define DICT_USERNAME_SEPARATOR '/'
enum redis_input_state {
/* expecting $-1 / $<size> followed by GET reply */
/* expecting +QUEUED */
/* expecting +OK reply for DISCARD */
/* expecting *<nreplies> */
/* expecting EXEC reply */
};
struct redis_connection {
struct connection conn;
struct redis_dict *dict;
unsigned int bytes_left;
bool value_not_found;
bool value_received;
};
struct redis_dict_reply {
unsigned int reply_count;
void *context;
};
struct redis_dict {
char *username, *key_prefix;
unsigned int port;
unsigned int timeout_msecs;
struct redis_connection conn;
bool connected;
bool transaction_open;
};
struct redis_dict_transaction_context {
struct dict_transaction_context ctx;
unsigned int cmd_count;
bool failed;
};
static struct connection_list *redis_connections;
static void
{
}
{
}
{
const struct redis_dict_reply *reply;
}
}
{
do {
}
{
const unsigned char *data;
const char *line;
if (conn->bytes_left == 0) {
/* read the size first */
return;
return;
}
i_error("redis: Unexpected input (wanted $size): %s",
line);
return;
}
}
if (conn->bytes_left == 0) {
/* reply fully read - drop trailing CRLF */
}
}
{
struct redis_dict_reply *reply;
const enum redis_input_state *states;
enum redis_input_state state;
unsigned int count, num_replies;
const char *line;
if (count == 0) {
i_error("redis: Unexpected input (expected nothing): %s",
line);
return -1;
}
if (state == REDIS_INPUT_STATE_GET) {
return 1;
}
return 0;
switch (state) {
case REDIS_INPUT_STATE_GET:
i_unreached();
case REDIS_INPUT_STATE_MULTI:
if (line[0] != '+')
break;
return 1;
case REDIS_INPUT_STATE_EXEC:
break;
i_error("redis: EXEC expected %u replies, not %u",
return -1;
}
return 1;
break;
/* success, just ignore the actual reply */
if (--reply->reply_count == 0) {
/* if we're running in a dict-ioloop, we're handling a
synchronous commit and need to stop now */
}
return 1;
}
return -1;
}
{
int ret;
case 0:
return;
case -1:
return;
default:
break;
}
break;
}
if (ret < 0)
}
{
if (!success) {
i_error("redis: connect(%s, %u) failed: %m",
} else {
}
}
static const struct connection_settings redis_conn_set = {
};
static const struct connection_vfuncs redis_conn_vfuncs = {
};
static const char *redis_escape_username(const char *username)
{
const char *p;
for (p = username; *p != '\0'; p++) {
switch (*p) {
case DICT_USERNAME_SEPARATOR:
break;
case '\\':
break;
default:
str_append_c(str, *p);
}
}
}
static struct dict *
const char *username,
const char *base_dir ATTR_UNUSED)
{
struct redis_dict *dict;
const char *const *args;
if (redis_connections == NULL) {
}
i_unreached();
else {
/* escape the username */
}
} else {
}
}
}
{
}
}
{
i_error("redis: Lookup timed out in %u.%03u secs",
}
static const char *
{
} else {
i_unreached();
}
return key;
}
static int
{
const char *cmd;
i_error("redis: Couldn't connect to %s:%u",
} else {
/* wait for connection */
}
}
timeout_remove(&to);
}
/* we failed in some way. make sure we disconnect since the
connection state isn't known anymore */
return -1;
}
return 0;
return 1;
}
{
int ret;
if (pool->datastack_pool)
else T_BEGIN {
} T_END;
return ret;
}
static struct dict_transaction_context *
{
struct redis_dict_transaction_context *ctx;
i_error("redis: Couldn't connect to %s:%u",
/* wait for connection */
}
}
static int
void *context)
{
struct redis_dict_transaction_context *ctx =
(struct redis_dict_transaction_context *)_ctx;
struct redis_dict_reply *reply;
unsigned int i;
int ret = 1;
/* make sure we're disconnected */
ret = -1;
"*1\r\n$4\r\nEXEC\r\n");
if (async)
return 1;
}
return ret;
}
{
struct redis_dict_transaction_context *ctx =
(struct redis_dict_transaction_context *)_ctx;
struct redis_dict_reply *reply;
/* make sure we're disconnected */
"*1\r\n$7\r\nDISCARD\r\n");
}
}
{
return -1;
return 0;
"*1\r\n$5\r\nMULTI\r\n") < 0) {
return -1;
}
return 0;
}
{
struct redis_dict_transaction_context *ctx =
(struct redis_dict_transaction_context *)_ctx;
const char *cmd;
if (redis_check_transaction(ctx) < 0)
return;
}
const char *key)
{
struct redis_dict_transaction_context *ctx =
(struct redis_dict_transaction_context *)_ctx;
const char *cmd;
if (redis_check_transaction(ctx) < 0)
return;
}
{
struct redis_dict_transaction_context *ctx =
(struct redis_dict_transaction_context *)_ctx;
const char *cmd;
if (redis_check_transaction(ctx) < 0)
return;
}
{
struct redis_dict_transaction_context *ctx =
(struct redis_dict_transaction_context *)_ctx;
if (redis_check_transaction(ctx) < 0)
return;
}
struct dict dict_driver_redis = {
.name = "redis",
{
NULL,
NULL,
NULL,
NULL,
}
};