2010-11-02 16:09:26 +00:00
|
|
|
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
2010-05-18 15:11:09 +00:00
|
|
|
*
|
2015-04-16 19:28:10 +00:00
|
|
|
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
|
|
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
|
|
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
|
|
|
* Jan-Erik Rediger <janerik at fnordig dot com>
|
2010-05-18 15:11:09 +00:00
|
|
|
*
|
2010-12-16 21:08:39 +00:00
|
|
|
* All rights reserved.
|
2010-12-29 14:52:07 +00:00
|
|
|
*
|
2010-05-18 15:11:09 +00:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Redis nor the names of its contributors may be used
|
|
|
|
* to endorse or promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fmacros.h"
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
2012-02-21 23:20:29 +00:00
|
|
|
#include <limits.h>
|
2015-04-16 17:28:12 +00:00
|
|
|
#include <stdlib.h>
|
2010-05-18 15:11:09 +00:00
|
|
|
|
2010-12-16 21:59:07 +00:00
|
|
|
#include "net.h"
|
2010-11-02 16:09:26 +00:00
|
|
|
#include "sds.h"
|
2019-03-31 16:17:19 +00:00
|
|
|
#include "sockcompat.h"
|
|
|
|
#include "win32.h"
|
2010-05-18 15:11:09 +00:00
|
|
|
|
2011-04-21 13:56:22 +00:00
|
|
|
/* Defined in hiredis.c */
|
|
|
|
void __redisSetError(redisContext *c, int type, const char *str);
|
|
|
|
|
2019-03-31 16:03:11 +00:00
|
|
|
void redisNetClose(redisContext *c) {
|
2019-03-31 16:05:32 +00:00
|
|
|
if (c && c->fd != REDIS_INVALID_FD) {
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
close(c->fd);
|
2019-03-31 16:05:32 +00:00
|
|
|
c->fd = REDIS_INVALID_FD;
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 21:38:16 +00:00
|
|
|
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
|
|
|
ssize_t nread = recv(c->fd, buf, bufcap, 0);
|
2019-03-31 16:03:11 +00:00
|
|
|
if (nread == -1) {
|
2019-03-31 16:10:34 +00:00
|
|
|
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
2019-03-31 16:03:11 +00:00
|
|
|
/* Try again later */
|
|
|
|
return 0;
|
2019-08-12 01:55:08 +00:00
|
|
|
} else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {
|
|
|
|
/* especially in windows */
|
|
|
|
__redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
|
|
|
|
return -1;
|
2019-03-31 16:03:11 +00:00
|
|
|
} else {
|
|
|
|
__redisSetError(c, REDIS_ERR_IO, NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (nread == 0) {
|
|
|
|
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 21:38:16 +00:00
|
|
|
ssize_t redisNetWrite(redisContext *c) {
|
|
|
|
ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);
|
2019-03-31 16:03:11 +00:00
|
|
|
if (nwritten < 0) {
|
2019-03-31 16:10:34 +00:00
|
|
|
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
2019-03-31 16:03:11 +00:00
|
|
|
/* Try again later */
|
|
|
|
} else {
|
|
|
|
__redisSetError(c, REDIS_ERR_IO, NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nwritten;
|
|
|
|
}
|
|
|
|
|
2011-04-21 13:56:22 +00:00
|
|
|
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
2016-12-30 15:13:02 +00:00
|
|
|
int errorno = errno; /* snprintf() may change errno */
|
2013-05-07 14:26:05 +00:00
|
|
|
char buf[128] = { 0 };
|
2011-04-21 13:56:22 +00:00
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
if (prefix != NULL)
|
|
|
|
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
2018-04-17 20:04:37 +00:00
|
|
|
strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetError(c,type,buf);
|
|
|
|
}
|
2010-05-18 15:11:09 +00:00
|
|
|
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
static int redisSetReuseAddr(redisContext *c) {
|
2011-06-17 16:41:28 +00:00
|
|
|
int on = 1;
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
2011-06-17 16:41:28 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2011-06-17 16:41:28 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
2010-11-03 10:08:24 +00:00
|
|
|
static int redisCreateSocket(redisContext *c, int type) {
|
2019-03-31 16:05:32 +00:00
|
|
|
redisFD s;
|
|
|
|
if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
2010-11-03 10:08:24 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
c->fd = s;
|
2010-11-03 10:08:24 +00:00
|
|
|
if (type == AF_INET) {
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (redisSetReuseAddr(c) == REDIS_ERR) {
|
2010-11-03 10:08:24 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
}
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
return REDIS_OK;
|
2010-11-03 10:08:24 +00:00
|
|
|
}
|
|
|
|
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
static int redisSetBlocking(redisContext *c, int blocking) {
|
2019-03-31 16:17:19 +00:00
|
|
|
#ifndef _WIN32
|
2010-05-18 15:11:09 +00:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
/* Set the socket nonblocking.
|
|
|
|
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
|
|
|
* interrupted by a signal. */
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2010-11-02 16:09:26 +00:00
|
|
|
return REDIS_ERR;
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
2011-02-04 14:26:28 +00:00
|
|
|
|
|
|
|
if (blocking)
|
|
|
|
flags &= ~O_NONBLOCK;
|
|
|
|
else
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (fcntl(c->fd, F_SETFL, flags) == -1) {
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2010-11-02 16:09:26 +00:00
|
|
|
return REDIS_ERR;
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
2019-03-31 16:17:19 +00:00
|
|
|
#else
|
|
|
|
u_long mode = blocking ? 0 : 1;
|
|
|
|
if (ioctl(c->fd, FIONBIO, &mode) == -1) {
|
|
|
|
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)");
|
|
|
|
redisNetClose(c);
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
2010-11-02 16:09:26 +00:00
|
|
|
return REDIS_OK;
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
|
|
|
|
2013-04-29 16:11:57 +00:00
|
|
|
int redisKeepAlive(redisContext *c, int interval) {
|
2013-04-19 03:26:43 +00:00
|
|
|
int val = 1;
|
2019-03-31 16:05:32 +00:00
|
|
|
redisFD fd = c->fd;
|
2013-04-29 16:11:57 +00:00
|
|
|
|
2013-04-19 03:26:43 +00:00
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
2013-05-01 16:23:06 +00:00
|
|
|
val = interval;
|
2014-04-09 14:31:37 +00:00
|
|
|
|
2018-04-13 19:40:34 +00:00
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
2013-05-01 16:23:06 +00:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
#else
|
2014-07-14 21:33:46 +00:00
|
|
|
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
|
2013-04-19 03:26:43 +00:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = interval/3;
|
|
|
|
if (val == 0) val = 1;
|
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = 3;
|
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
2014-04-09 14:31:37 +00:00
|
|
|
#endif
|
2013-05-01 16:23:06 +00:00
|
|
|
#endif
|
2013-04-19 03:26:43 +00:00
|
|
|
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
2020-06-22 20:20:30 +00:00
|
|
|
int redisSetTcpNoDelay(redisContext *c) {
|
2010-05-18 15:11:09 +00:00
|
|
|
int yes = 1;
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2011-01-07 12:04:42 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
2012-02-21 23:20:29 +00:00
|
|
|
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
|
|
|
|
2015-11-18 08:36:29 +00:00
|
|
|
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
|
|
|
{
|
|
|
|
const struct timeval *timeout = c->timeout;
|
|
|
|
long msec = -1;
|
2011-02-04 14:26:28 +00:00
|
|
|
|
|
|
|
/* Only use timeout when not NULL. */
|
|
|
|
if (timeout != NULL) {
|
2012-02-21 23:20:29 +00:00
|
|
|
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
2015-11-18 08:36:29 +00:00
|
|
|
*result = msec;
|
2012-02-21 23:20:29 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
|
|
|
|
|
|
|
|
if (msec < 0 || msec > INT_MAX) {
|
|
|
|
msec = INT_MAX;
|
|
|
|
}
|
2011-02-04 14:26:28 +00:00
|
|
|
}
|
|
|
|
|
2015-11-18 08:36:29 +00:00
|
|
|
*result = msec;
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int redisContextWaitReady(redisContext *c, long msec) {
|
|
|
|
struct pollfd wfd[1];
|
|
|
|
|
|
|
|
wfd[0].fd = c->fd;
|
|
|
|
wfd[0].events = POLLOUT;
|
|
|
|
|
2011-02-04 14:26:28 +00:00
|
|
|
if (errno == EINPROGRESS) {
|
2012-02-21 23:20:29 +00:00
|
|
|
int res;
|
2011-02-04 14:26:28 +00:00
|
|
|
|
2012-02-21 23:20:29 +00:00
|
|
|
if ((res = poll(wfd, 1, msec)) == -1) {
|
|
|
|
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2011-02-04 14:26:28 +00:00
|
|
|
return REDIS_ERR;
|
2012-02-21 23:20:29 +00:00
|
|
|
} else if (res == 0) {
|
2011-02-04 14:26:28 +00:00
|
|
|
errno = ETIMEDOUT;
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2011-02-04 14:26:28 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
2018-03-05 10:17:49 +00:00
|
|
|
if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
|
|
|
|
redisCheckSocketError(c);
|
2011-07-10 15:51:37 +00:00
|
|
|
return REDIS_ERR;
|
2018-03-05 10:17:49 +00:00
|
|
|
}
|
2011-07-10 15:51:37 +00:00
|
|
|
|
2011-02-04 14:26:28 +00:00
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2011-02-04 14:26:28 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
2018-03-05 10:17:49 +00:00
|
|
|
int redisCheckConnectDone(redisContext *c, int *completed) {
|
2018-03-04 16:17:16 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
int redisCheckSocketError(redisContext *c) {
|
2018-03-04 16:17:16 +00:00
|
|
|
int err = 0, errno_saved = errno;
|
2011-06-27 21:42:18 +00:00
|
|
|
socklen_t errlen = sizeof(err);
|
|
|
|
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
2011-06-27 21:42:18 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
2018-03-04 16:17:16 +00:00
|
|
|
if (err == 0) {
|
|
|
|
err = errno_saved;
|
|
|
|
}
|
|
|
|
|
2011-06-27 21:42:18 +00:00
|
|
|
if (err) {
|
|
|
|
errno = err;
|
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
2012-05-30 15:33:48 +00:00
|
|
|
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
2019-08-12 01:54:12 +00:00
|
|
|
const void *to_ptr = &tv;
|
|
|
|
size_t to_sz = sizeof(tv);
|
2020-04-03 05:41:34 +00:00
|
|
|
|
2019-08-12 01:54:12 +00:00
|
|
|
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
2011-01-07 12:04:42 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
2019-08-12 01:54:12 +00:00
|
|
|
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
|
2011-04-21 13:56:22 +00:00
|
|
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
|
2010-11-02 16:09:26 +00:00
|
|
|
return REDIS_ERR;
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
2010-11-02 16:09:26 +00:00
|
|
|
return REDIS_OK;
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
|
|
|
|
2020-05-22 16:27:49 +00:00
|
|
|
static int _redisContextUpdateTimeout(redisContext *c, const struct timeval *timeout) {
|
|
|
|
/* Same timeval struct, short circuit */
|
|
|
|
if (c->timeout == timeout)
|
|
|
|
return REDIS_OK;
|
|
|
|
|
|
|
|
/* Allocate context timeval if we need to */
|
|
|
|
if (c->timeout == NULL) {
|
|
|
|
c->timeout = hi_malloc(sizeof(*c->timeout));
|
|
|
|
if (c->timeout == NULL)
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(c->timeout, timeout, sizeof(*c->timeout));
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
|
2014-04-08 17:15:36 +00:00
|
|
|
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
|
|
|
const struct timeval *timeout,
|
2014-04-10 05:30:04 +00:00
|
|
|
const char *source_addr) {
|
2019-03-31 16:05:32 +00:00
|
|
|
redisFD s;
|
|
|
|
int rv, n;
|
2011-06-17 18:26:46 +00:00
|
|
|
char _port[6]; /* strlen("65535"); */
|
2014-04-08 17:15:36 +00:00
|
|
|
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
2010-11-02 16:09:26 +00:00
|
|
|
int blocking = (c->flags & REDIS_BLOCK);
|
2014-08-14 20:24:59 +00:00
|
|
|
int reuseaddr = (c->flags & REDIS_REUSEADDR);
|
|
|
|
int reuses = 0;
|
2015-11-18 08:36:29 +00:00
|
|
|
long timeout_msec = -1;
|
2010-05-18 15:11:09 +00:00
|
|
|
|
2015-11-18 08:36:29 +00:00
|
|
|
servinfo = NULL;
|
2015-04-16 17:28:12 +00:00
|
|
|
c->connection_type = REDIS_CONN_TCP;
|
|
|
|
c->tcp.port = port;
|
|
|
|
|
|
|
|
/* We need to take possession of the passed parameters
|
|
|
|
* to make them reusable for a reconnect.
|
|
|
|
* We also carefully check we don't free data we already own,
|
|
|
|
* as in the case of the reconnect method.
|
|
|
|
*
|
|
|
|
* This is a bit ugly, but atleast it works and doesn't leak memory.
|
|
|
|
**/
|
|
|
|
if (c->tcp.host != addr) {
|
2020-05-22 16:27:49 +00:00
|
|
|
hi_free(c->tcp.host);
|
2015-04-16 17:28:12 +00:00
|
|
|
|
2020-01-28 20:13:05 +00:00
|
|
|
c->tcp.host = hi_strdup(addr);
|
2020-05-22 16:27:49 +00:00
|
|
|
if (c->tcp.host == NULL)
|
|
|
|
goto oom;
|
2015-04-16 17:28:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (timeout) {
|
2020-05-22 16:27:49 +00:00
|
|
|
if (_redisContextUpdateTimeout(c, timeout) == REDIS_ERR)
|
|
|
|
goto oom;
|
2015-04-16 17:28:12 +00:00
|
|
|
} else {
|
2020-05-22 16:27:49 +00:00
|
|
|
hi_free(c->timeout);
|
2015-04-16 17:28:12 +00:00
|
|
|
c->timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2015-11-18 08:36:29 +00:00
|
|
|
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
|
|
|
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2015-04-16 17:28:12 +00:00
|
|
|
if (source_addr == NULL) {
|
2020-05-22 16:27:49 +00:00
|
|
|
hi_free(c->tcp.source_addr);
|
2015-04-16 17:28:12 +00:00
|
|
|
c->tcp.source_addr = NULL;
|
|
|
|
} else if (c->tcp.source_addr != source_addr) {
|
2020-05-22 16:27:49 +00:00
|
|
|
hi_free(c->tcp.source_addr);
|
2020-01-28 20:13:05 +00:00
|
|
|
c->tcp.source_addr = hi_strdup(source_addr);
|
2015-04-16 17:28:12 +00:00
|
|
|
}
|
|
|
|
|
2011-06-17 18:26:46 +00:00
|
|
|
snprintf(_port, 6, "%d", port);
|
|
|
|
memset(&hints,0,sizeof(hints));
|
|
|
|
hints.ai_family = AF_INET;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
2010-05-18 15:11:09 +00:00
|
|
|
|
2013-07-11 09:39:03 +00:00
|
|
|
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
|
|
|
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
|
|
|
* as this would add latency to every connect. Otherwise a more sensible
|
|
|
|
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
|
|
|
* connectivity. */
|
2015-04-16 17:28:12 +00:00
|
|
|
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
|
2013-07-11 09:39:03 +00:00
|
|
|
hints.ai_family = AF_INET6;
|
|
|
|
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
2011-06-17 18:26:46 +00:00
|
|
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
2014-08-14 20:24:59 +00:00
|
|
|
addrretry:
|
2019-03-31 16:05:32 +00:00
|
|
|
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)
|
2011-06-17 18:26:46 +00:00
|
|
|
continue;
|
|
|
|
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
c->fd = s;
|
|
|
|
if (redisSetBlocking(c,0) != REDIS_OK)
|
2011-06-17 18:26:46 +00:00
|
|
|
goto error;
|
2015-04-16 17:28:12 +00:00
|
|
|
if (c->tcp.source_addr) {
|
2014-04-08 17:15:36 +00:00
|
|
|
int bound = 0;
|
|
|
|
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
|
2015-04-16 17:28:12 +00:00
|
|
|
if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
|
2014-04-08 17:15:36 +00:00
|
|
|
char buf[128];
|
|
|
|
snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
|
|
|
goto error;
|
|
|
|
}
|
2014-08-14 20:24:59 +00:00
|
|
|
|
|
|
|
if (reuseaddr) {
|
|
|
|
n = 1;
|
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
|
|
|
|
sizeof(n)) < 0) {
|
2017-05-15 16:14:12 +00:00
|
|
|
freeaddrinfo(bservinfo);
|
2014-08-14 20:24:59 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-08 17:15:36 +00:00
|
|
|
for (b = bservinfo; b != NULL; b = b->ai_next) {
|
|
|
|
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
|
|
|
|
bound = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-09-18 18:51:27 +00:00
|
|
|
freeaddrinfo(bservinfo);
|
2014-04-08 17:15:36 +00:00
|
|
|
if (!bound) {
|
|
|
|
char buf[128];
|
|
|
|
snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2018-03-04 16:17:16 +00:00
|
|
|
|
|
|
|
/* For repeat connection */
|
2020-05-22 16:27:49 +00:00
|
|
|
hi_free(c->saddr);
|
2020-01-28 20:13:05 +00:00
|
|
|
c->saddr = hi_malloc(p->ai_addrlen);
|
2020-05-22 16:27:49 +00:00
|
|
|
if (c->saddr == NULL)
|
|
|
|
goto oom;
|
|
|
|
|
2018-03-04 16:17:16 +00:00
|
|
|
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
|
|
|
|
c->addrlen = p->ai_addrlen;
|
|
|
|
|
2011-06-17 18:26:46 +00:00
|
|
|
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
2011-06-18 13:08:25 +00:00
|
|
|
if (errno == EHOSTUNREACH) {
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2011-06-18 13:08:25 +00:00
|
|
|
continue;
|
2018-03-05 10:17:49 +00:00
|
|
|
} else if (errno == EINPROGRESS) {
|
|
|
|
if (blocking) {
|
|
|
|
goto wait_for_ready;
|
|
|
|
}
|
|
|
|
/* This is ok.
|
|
|
|
* Note that even when it's in blocking mode, we unset blocking
|
|
|
|
* for `connect()`
|
|
|
|
*/
|
2014-08-14 20:24:59 +00:00
|
|
|
} else if (errno == EADDRNOTAVAIL && reuseaddr) {
|
|
|
|
if (++reuses >= REDIS_CONNECT_RETRIES) {
|
|
|
|
goto error;
|
|
|
|
} else {
|
2019-03-31 16:03:11 +00:00
|
|
|
redisNetClose(c);
|
2014-08-14 20:24:59 +00:00
|
|
|
goto addrretry;
|
|
|
|
}
|
2011-06-17 18:26:46 +00:00
|
|
|
} else {
|
2018-03-05 10:17:49 +00:00
|
|
|
wait_for_ready:
|
2015-11-18 08:36:29 +00:00
|
|
|
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
2011-06-17 18:26:46 +00:00
|
|
|
goto error;
|
2020-06-22 20:20:30 +00:00
|
|
|
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
|
|
|
goto error;
|
2011-06-17 18:26:46 +00:00
|
|
|
}
|
2010-11-03 10:08:24 +00:00
|
|
|
}
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
2011-06-17 18:26:46 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
c->flags |= REDIS_CONNECTED;
|
|
|
|
rv = REDIS_OK;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (p == NULL) {
|
|
|
|
char buf[128];
|
|
|
|
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
|
|
|
|
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
|
|
|
goto error;
|
2010-11-03 10:08:24 +00:00
|
|
|
}
|
2010-05-18 15:11:09 +00:00
|
|
|
|
2020-05-22 16:27:49 +00:00
|
|
|
oom:
|
|
|
|
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
2011-06-17 18:26:46 +00:00
|
|
|
error:
|
|
|
|
rv = REDIS_ERR;
|
|
|
|
end:
|
2018-04-17 18:47:51 +00:00
|
|
|
if(servinfo) {
|
|
|
|
freeaddrinfo(servinfo);
|
|
|
|
}
|
|
|
|
|
2011-06-17 18:26:46 +00:00
|
|
|
return rv; // Need to return REDIS_OK if alright
|
2010-05-18 15:11:09 +00:00
|
|
|
}
|
2010-11-03 10:12:26 +00:00
|
|
|
|
2014-04-08 17:15:36 +00:00
|
|
|
int redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
|
|
|
const struct timeval *timeout) {
|
|
|
|
return _redisContextConnectTcp(c, addr, port, timeout, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
2014-04-10 05:30:04 +00:00
|
|
|
const struct timeval *timeout,
|
|
|
|
const char *source_addr) {
|
2014-04-08 17:15:36 +00:00
|
|
|
return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
|
|
|
|
}
|
|
|
|
|
2012-05-30 15:33:48 +00:00
|
|
|
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
2019-03-31 16:17:19 +00:00
|
|
|
#ifndef _WIN32
|
2010-11-03 10:12:26 +00:00
|
|
|
int blocking = (c->flags & REDIS_BLOCK);
|
2018-12-05 12:47:05 +00:00
|
|
|
struct sockaddr_un *sa;
|
2015-11-18 08:36:29 +00:00
|
|
|
long timeout_msec = -1;
|
2010-11-03 10:12:26 +00:00
|
|
|
|
2018-04-12 20:22:51 +00:00
|
|
|
if (redisCreateSocket(c,AF_UNIX) < 0)
|
2010-11-03 10:12:26 +00:00
|
|
|
return REDIS_ERR;
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (redisSetBlocking(c,0) != REDIS_OK)
|
2010-11-03 10:12:26 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
|
2015-04-16 17:28:12 +00:00
|
|
|
c->connection_type = REDIS_CONN_UNIX;
|
2020-05-22 16:27:49 +00:00
|
|
|
if (c->unix_sock.path != path) {
|
|
|
|
hi_free(c->unix_sock.path);
|
|
|
|
|
2020-01-28 20:13:05 +00:00
|
|
|
c->unix_sock.path = hi_strdup(path);
|
2020-05-22 16:27:49 +00:00
|
|
|
if (c->unix_sock.path == NULL)
|
|
|
|
goto oom;
|
|
|
|
}
|
2015-04-16 17:28:12 +00:00
|
|
|
|
|
|
|
if (timeout) {
|
2020-05-22 16:27:49 +00:00
|
|
|
if (_redisContextUpdateTimeout(c, timeout) == REDIS_ERR)
|
|
|
|
goto oom;
|
2015-04-16 17:28:12 +00:00
|
|
|
} else {
|
2020-05-22 16:27:49 +00:00
|
|
|
hi_free(c->timeout);
|
2015-04-16 17:28:12 +00:00
|
|
|
c->timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2015-11-18 08:36:29 +00:00
|
|
|
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
|
|
|
return REDIS_ERR;
|
|
|
|
|
2020-05-22 16:27:49 +00:00
|
|
|
/* Don't leak sockaddr if we're reconnecting */
|
|
|
|
if (c->saddr) hi_free(c->saddr);
|
|
|
|
|
2020-01-28 20:13:05 +00:00
|
|
|
sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));
|
2020-05-22 16:27:49 +00:00
|
|
|
if (sa == NULL)
|
|
|
|
goto oom;
|
|
|
|
|
2019-01-28 13:54:42 +00:00
|
|
|
c->addrlen = sizeof(struct sockaddr_un);
|
2018-12-05 12:47:05 +00:00
|
|
|
sa->sun_family = AF_UNIX;
|
2018-12-13 16:24:40 +00:00
|
|
|
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
|
2018-12-05 12:47:05 +00:00
|
|
|
if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {
|
2010-11-03 10:12:26 +00:00
|
|
|
if (errno == EINPROGRESS && !blocking) {
|
|
|
|
/* This is ok. */
|
|
|
|
} else {
|
2015-11-18 08:36:29 +00:00
|
|
|
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
2011-02-04 14:26:28 +00:00
|
|
|
return REDIS_ERR;
|
2010-11-03 10:12:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-04 14:26:28 +00:00
|
|
|
/* Reset socket to be blocking after connect(2). */
|
Remove possiblity of multiple close on same fd
With all the async connects and disconnects and error handling
going on in hiredis, we need to centralize how we close our fd
and set it so it doesn't get re-closed. Prior to this commit,
sometimes we'd close(fd), run an async error handler, then
call close(fd) again.
To stop multiple closes, we now set fd to -1 after we free it,
but that requires not passing fd as an independent argument to
functions.
This commit moves all fd usage to c->fd. Since the context
has a fd field and all functions receive the context, it makes
more sense to use the fd inside of c instead of passing along fd
as an independent argument.
Also, by only using c->fd, we can set c->fd after we close it to
signify we shouldn't re-close the same fd again.
This does change one semi-public interface function redisCheckSocketError()
to only take (context) instead of (context, fd). A search on github
returned zero occasions of people using redisCheckSocketError()
outside of net.{c,h} in hiredis itself.
Commit inspired by the bug report at:
https://groups.google.com/forum/#!topic/redis-db/mQm46XkIPOY
Thanks go out to Thijs for trying high-frequency reconnects on
a host that isn't there.
Closes #230
2014-04-07 15:57:26 +00:00
|
|
|
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
2011-02-04 14:26:28 +00:00
|
|
|
return REDIS_ERR;
|
|
|
|
|
2010-12-01 11:54:47 +00:00
|
|
|
c->flags |= REDIS_CONNECTED;
|
2010-11-03 10:12:26 +00:00
|
|
|
return REDIS_OK;
|
2019-03-31 16:17:19 +00:00
|
|
|
#else
|
|
|
|
/* We currently do not support Unix sockets for Windows. */
|
|
|
|
/* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */
|
|
|
|
errno = EPROTONOSUPPORT;
|
|
|
|
return REDIS_ERR;
|
|
|
|
#endif /* _WIN32 */
|
2020-05-22 16:27:49 +00:00
|
|
|
oom:
|
|
|
|
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
|
|
|
return REDIS_ERR;
|
2010-11-03 10:12:26 +00:00
|
|
|
}
|