From 49974c9359ad6b58cea15106cf6511bdb31d31a9 Mon Sep 17 00:00:00 2001 From: Mark Nunberg Date: Sun, 4 Mar 2018 18:17:16 +0200 Subject: [PATCH] Call connect(2) again for non-blocking connect This retrieves the actual error which occurred, as getsockopt is not always reliable in this regard. --- async.c | 24 ++++++++++++------------ async.h | 4 ++++ hiredis.c | 2 ++ hiredis.h | 3 +++ net.c | 35 ++++++++++++++++++++++++++++++++++- net.h | 1 + 6 files changed, 56 insertions(+), 13 deletions(-) diff --git a/async.c b/async.c index cb5b841..7309344 100644 --- a/async.c +++ b/async.c @@ -506,22 +506,22 @@ void redisProcessCallbacks(redisAsyncContext *ac) { * write event fires. When connecting was not successful, the connect callback * is called with a REDIS_ERR status and the context is free'd. */ static int __redisAsyncHandleConnect(redisAsyncContext *ac) { + int completed = 0; redisContext *c = &(ac->c); - - if (redisCheckSocketError(c) == REDIS_ERR) { - /* Try again later when connect(2) is still in progress. */ - if (errno == EINPROGRESS) - return REDIS_OK; - - if (ac->onConnect) ac->onConnect(ac,REDIS_ERR); + if (redisFinishAsyncConnect(c, &completed) == REDIS_ERR) { + /* Error! */ + redisCheckSocketError(c); + if (ac->onConnect) ac->onConnect(ac, REDIS_ERR); __redisAsyncDisconnect(ac); return REDIS_ERR; + } else if (completed == 1) { + /* connected! */ + if (ac->onConnect) ac->onConnect(ac, REDIS_OK); + c->flags |= REDIS_CONNECTED; + return REDIS_OK; + } else { + return REDIS_OK; } - - /* Mark context as connected. */ - c->flags |= REDIS_CONNECTED; - if (ac->onConnect) ac->onConnect(ac,REDIS_OK); - return REDIS_OK; } /* This function should be called when the socket is readable. diff --git a/async.h b/async.h index e69d840..740555c 100644 --- a/async.h +++ b/async.h @@ -93,6 +93,10 @@ typedef struct redisAsyncContext { /* Regular command callbacks */ redisCallbackList replies; + /* Address used for connect() */ + struct sockaddr *saddr; + size_t addrlen; + /* Subscription callbacks */ struct { redisCallbackList invalid; diff --git a/hiredis.c b/hiredis.c index 98f43c9..351dfbc 100644 --- a/hiredis.c +++ b/hiredis.c @@ -606,12 +606,14 @@ void redisFree(redisContext *c) { return; if (c->fd > 0) close(c->fd); + sdsfree(c->obuf); redisReaderFree(c->reader); free(c->tcp.host); free(c->tcp.source_addr); free(c->unix_sock.path); free(c->timeout); + free(c->saddr); free(c); } diff --git a/hiredis.h b/hiredis.h index bd53e7e..1b0d5e6 100644 --- a/hiredis.h +++ b/hiredis.h @@ -134,6 +134,9 @@ typedef struct redisContext { char *path; } unix_sock; + /* For non-blocking connect */ + struct sockadr *saddr; + size_t addrlen; } redisContext; redisContext *redisConnect(const char *ip, int port); diff --git a/net.c b/net.c index 5d50f7c..62c6ca0 100644 --- a/net.c +++ b/net.c @@ -232,8 +232,28 @@ static int redisContextWaitReady(redisContext *c, long msec) { return REDIS_ERR; } +int redisFinishAsyncConnect(redisContext *c, int *completed) { + int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen); + if (rc == 0) { + *completed = 1; + return REDIS_OK; + } + switch (errno) { + case EISCONN: + *completed = 1; + return REDIS_OK; + case EALREADY: + case EINPROGRESS: + case EWOULDBLOCK: + *completed = 0; + return REDIS_OK; + default: + return REDIS_ERR; + } +} + int redisCheckSocketError(redisContext *c) { - int err = 0; + int err = 0, errno_saved = errno; socklen_t errlen = sizeof(err); if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { @@ -241,6 +261,10 @@ int redisCheckSocketError(redisContext *c) { return REDIS_ERR; } + if (err == 0) { + err = errno_saved; + } + if (err) { errno = err; __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); @@ -373,6 +397,15 @@ addrretry: goto error; } } + + /* For repeat connection */ + if (c->saddr) { + free(c->saddr); + } + c->saddr = malloc(sizeof(*p->ai_addr)); + memcpy(c->saddr, p->ai_addr, p->ai_addrlen); + c->addrlen = p->ai_addrlen; + if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { if (errno == EHOSTUNREACH) { redisContextCloseFd(c); diff --git a/net.h b/net.h index d9dc362..c35facc 100644 --- a/net.h +++ b/net.h @@ -45,5 +45,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, const char *source_addr); int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); int redisKeepAlive(redisContext *c, int interval); +int redisFinishAsyncConnect(redisContext *c, int *completed); #endif