summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Nunberg <mnunberg@haskalah.org>2018-03-04 18:17:16 +0200
committerMark Nunberg <mnunberg@haskalah.org>2018-09-25 20:21:37 -0400
commit49974c9359ad6b58cea15106cf6511bdb31d31a9 (patch)
tree5cfbe5664581f331404fde52c5b169d5af2397dc
parent685030652cd98c5414ce554ff5b356dfe8437870 (diff)
Call connect(2) again for non-blocking connect
This retrieves the actual error which occurred, as getsockopt is not always reliable in this regard.
-rw-r--r--async.c24
-rw-r--r--async.h4
-rw-r--r--hiredis.c2
-rw-r--r--hiredis.h3
-rw-r--r--net.c35
-rw-r--r--net.h1
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