diff --git a/hiredis.c b/hiredis.c index 5e8b606..57619a4 100644 --- a/hiredis.c +++ b/hiredis.c @@ -829,6 +829,9 @@ redisContext *redisConnectWithOptions(const redisOptions *options) { c->flags |= REDIS_PREFER_IPV6; } + /* Set any configured socket callback function */ + c->socket_cb = options->socket_cb; + /* Set any user supplied RESP3 PUSH handler or use freeReplyObject * as a default unless specifically flagged that we don't want one. */ if (options->push_cb != NULL) diff --git a/hiredis.h b/hiredis.h index 0a17495..5b64e56 100644 --- a/hiredis.h +++ b/hiredis.h @@ -216,6 +216,10 @@ typedef struct { redisFD fd; } endpoint; + /* Socket callback function executed after socket creation but before + * we connect. This can be used to set OS specific socket options. */ + int (*socket_cb)(redisFD fd, void *privdata, char **errstr); + /* Optional user defined data/destructor */ void *privdata; void (*free_privdata)(void *); @@ -239,6 +243,10 @@ typedef struct { (opts)->endpoint.unix_socket = path; \ } while(0) +#define REDIS_OPTIONS_SET_SOCKET_CB(opts, cb) do { \ + (opts)->socket_cb = cb; \ + } while(0) + #define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) do { \ (opts)->privdata = data; \ (opts)->free_privdata = dtor; \ @@ -288,6 +296,11 @@ typedef struct redisContext { struct sockaddr *saddr; size_t addrlen; + /* A callback to be invoked after successful socket creation but before we + * connect. This can be usefull for setting OS specific socket flags. + * The callback may optionally set errstr in the event of an error. */ + int (*socket_cb)(redisFD fd, void *privdata, char **errstr); + /* Optional data and corresponding destructor users can use to provide * context to a given redisContext. Not used by hiredis. */ void *privdata; diff --git a/net.c b/net.c index 0f36da2..66796ff 100644 --- a/net.c +++ b/net.c @@ -402,6 +402,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, int rv, n; char _port[6]; /* strlen("65535"); */ struct addrinfo hints, *servinfo, *bservinfo, *p, *b; + char *errstr; int blocking = (c->flags & REDIS_BLOCK); int reuseaddr = (c->flags & REDIS_REUSEADDR); int reuses = 0; @@ -477,6 +478,13 @@ addrretry: if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD) continue; + /* Invoke any user-provided socket callback */ + errstr = NULL; + if (c->socket_cb && c->socket_cb(s, c->privdata, &errstr) != REDIS_OK) { + __redisSetError(c, REDIS_ERR_OTHER, errstr ? errstr : "Failure in user-defined socket callback"); + goto error; + } + c->fd = s; if (redisSetBlocking(c,0) != REDIS_OK) goto error;