diff options
author | Mark Nunberg <mnunberg@haskalah.org> | 2018-03-04 18:17:16 +0200 |
---|---|---|
committer | Mark Nunberg <mnunberg@haskalah.org> | 2018-09-25 20:21:37 -0400 |
commit | 49974c9359ad6b58cea15106cf6511bdb31d31a9 (patch) | |
tree | 5cfbe5664581f331404fde52c5b169d5af2397dc | |
parent | 685030652cd98c5414ce554ff5b356dfe8437870 (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.c | 24 | ||||
-rw-r--r-- | async.h | 4 | ||||
-rw-r--r-- | hiredis.c | 2 | ||||
-rw-r--r-- | hiredis.h | 3 | ||||
-rw-r--r-- | net.c | 35 | ||||
-rw-r--r-- | net.h | 1 |
6 files changed, 56 insertions, 13 deletions
@@ -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. @@ -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; @@ -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); } @@ -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); @@ -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); @@ -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 |