Provide option-struct initialization

This reduces the boilerplate of all the redisConnectXXX functions, and
allows us to provide more connection options in the future.
This commit is contained in:
Mark Nunberg 2018-12-05 20:28:13 -05:00
parent d329cc952a
commit e4a78006e7
2 changed files with 117 additions and 79 deletions

151
hiredis.c
View File

@ -584,14 +584,21 @@ redisReader *redisReaderCreate(void) {
return redisReaderCreateWithFunctions(&defaultFunctions);
}
static redisContext *redisContextInit(void) {
static redisContext *redisContextInit(const redisOptions *options) {
redisContext *c;
c = calloc(1,sizeof(redisContext));
c = calloc(1, sizeof(*c));
if (c == NULL)
return NULL;
c->err = 0;
c->errstr[0] = '\0';
c->obuf = sdsempty();
c->flags = 0;
c->tcp.host = NULL;
c->tcp.source_addr = NULL;
c->unix_sock.path = NULL;
c->timeout = NULL;
c->reader = redisReaderCreate();
if (c->obuf == NULL || c->reader == NULL) {
@ -655,112 +662,100 @@ int redisReconnect(redisContext *c) {
return REDIS_ERR;
}
redisContext *redisConnectWithOptions(const redisOptions *options) {
redisContext *c = redisContextInit(options);
if (c == NULL) {
return NULL;
}
if (!(options->options & REDIS_OPT_NONBLOCK)) {
c->flags |= REDIS_BLOCK;
}
if (options->options & REDIS_OPT_REUSEADDR) {
c->flags |= REDIS_REUSEADDR;
}
if (options->type == REDIS_CONN_TCP) {
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
options->endpoint.tcp.port, options->timeout,
options->endpoint.tcp.source_addr);
} else if (options->type == REDIS_CONN_UNIX) {
redisContextConnectUnix(c, options->endpoint.unix_socket,
options->timeout);
} else if (options->type == REDIS_CONN_USERFD) {
c->fd = options->endpoint.fd;
c->flags |= REDIS_CONNECTED;
} else {
// Unknown type - FIXME - FREE
return NULL;
}
return c;
}
/* Connect to a Redis instance. On error the field error in the returned
* context will be set to the return value of the error function.
* When no set of reply functions is given, the default set will be used. */
redisContext *redisConnect(const char *ip, int port) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags |= REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,NULL);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
return redisConnectWithOptions(&options);
}
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags |= REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,&tv);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.timeout = &tv;
return redisConnectWithOptions(&options);
}
redisContext *redisConnectNonBlock(const char *ip, int port) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags &= ~REDIS_BLOCK;
redisContextConnectTcp(c,ip,port,NULL);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.options |= REDIS_OPT_NONBLOCK;
return redisConnectWithOptions(&options);
}
redisContext *redisConnectBindNonBlock(const char *ip, int port,
const char *source_addr) {
redisContext *c = redisContextInit();
if (c == NULL)
return NULL;
c->flags &= ~REDIS_BLOCK;
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.endpoint.tcp.source_addr = source_addr;
options.options |= REDIS_OPT_NONBLOCK;
return redisConnectWithOptions(&options);
}
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
const char *source_addr) {
redisContext *c = redisContextInit();
if (c == NULL)
return NULL;
c->flags &= ~REDIS_BLOCK;
c->flags |= REDIS_REUSEADDR;
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.endpoint.tcp.source_addr = source_addr;
options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;
return redisConnectWithOptions(&options);
}
redisContext *redisConnectUnix(const char *path) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags |= REDIS_BLOCK;
redisContextConnectUnix(c,path,NULL);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_UNIX(&options, path);
return redisConnectWithOptions(&options);
}
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags |= REDIS_BLOCK;
redisContextConnectUnix(c,path,&tv);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_UNIX(&options, path);
options.timeout = &tv;
return redisConnectWithOptions(&options);
}
redisContext *redisConnectUnixNonBlock(const char *path) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->flags &= ~REDIS_BLOCK;
redisContextConnectUnix(c,path,NULL);
return c;
redisOptions options = {0};
REDIS_OPTIONS_SET_UNIX(&options, path);
options.options |= REDIS_OPT_NONBLOCK;
return redisConnectWithOptions(&options);
}
redisContext *redisConnectFd(int fd) {
redisContext *c;
c = redisContextInit();
if (c == NULL)
return NULL;
c->fd = fd;
c->flags |= REDIS_BLOCK | REDIS_CONNECTED;
return c;
redisOptions options = {REDIS_CONN_USERFD};
options.endpoint.fd = fd;
return redisConnectWithOptions(&options);
}
int redisSecureConnection(redisContext *c, const char *caPath,

View File

@ -112,11 +112,53 @@ void redisFreeSdsCommand(sds cmd);
enum redisConnectionType {
REDIS_CONN_TCP,
REDIS_CONN_UNIX
REDIS_CONN_UNIX,
REDIS_CONN_USERFD
};
struct redisSsl;
#define REDIS_OPT_NONBLOCK 0x01
#define REDIS_OPT_REUSEADDR 0x02
typedef struct {
/*
* the type of connection to use. This also indicates which
* `endpoint` member field to use
*/
int type;
/* bit field of REDIS_OPT_xxx */
int options;
/* timeout value. if NULL, no timeout is used */
const struct timeval *timeout;
union {
/** use this field for tcp/ip connections */
struct {
const char *source_addr;
const char *ip;
int port;
} tcp;
/** use this field for unix domain sockets */
const char *unix_socket;
/**
* use this field to have hiredis operate an already-open
* file descriptor */
int fd;
} endpoint;
} redisOptions;
/**
* Helper macros to initialize options to their specified fields.
*/
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \
(opts)->type = REDIS_CONN_TCP; \
(opts)->endpoint.tcp.ip = ip_; \
(opts)->endpoint.tcp.port = port;
#define REDIS_OPTIONS_SET_UNIX(opts, path) \
(opts)->type = REDIS_CONN_UNIX; \
(opts)->endpoint.unix_socket = path;
/* Context for a connection to Redis */
typedef struct redisContext {
int err; /* Error flags, 0 when there is no error */
@ -147,6 +189,7 @@ typedef struct redisContext {
} redisContext;
redisContext *redisConnectWithOptions(const redisOptions *options);
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);