hiredis 0.5.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Rakefile +8 -6
- data/ext/hiredis_ext/connection.c +4 -3
- data/ext/hiredis_ext/extconf.rb +25 -10
- data/ext/hiredis_ext/reader.c +12 -6
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +119 -54
- data/vendor/hiredis/async.c +121 -27
- data/vendor/hiredis/async.h +5 -0
- data/vendor/hiredis/dict.c +2 -2
- data/vendor/hiredis/fmacros.h +5 -9
- data/vendor/hiredis/hiredis.c +275 -554
- data/vendor/hiredis/hiredis.h +64 -75
- data/vendor/hiredis/net.c +242 -56
- data/vendor/hiredis/net.h +12 -10
- data/vendor/hiredis/read.c +598 -0
- data/vendor/hiredis/read.h +111 -0
- data/vendor/hiredis/sds.c +847 -180
- data/vendor/hiredis/sds.h +203 -18
- data/vendor/hiredis/sdsalloc.h +42 -0
- data/vendor/hiredis/test.c +286 -19
- data/vendor/hiredis/win32.h +42 -0
- metadata +46 -28
- data/lib/hiredis/errors.rb +0 -5
data/vendor/hiredis/hiredis.h
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
/*
|
2
2
|
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
3
|
-
* Copyright (c) 2010-
|
3
|
+
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
4
|
+
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
5
|
+
* Jan-Erik Rediger <janerik at fnordig dot com>
|
4
6
|
*
|
5
7
|
* All rights reserved.
|
6
8
|
*
|
@@ -31,26 +33,16 @@
|
|
31
33
|
|
32
34
|
#ifndef __HIREDIS_H
|
33
35
|
#define __HIREDIS_H
|
34
|
-
#include
|
36
|
+
#include "read.h"
|
35
37
|
#include <stdarg.h> /* for va_list */
|
36
38
|
#include <sys/time.h> /* for struct timeval */
|
39
|
+
#include <stdint.h> /* uintXX_t, etc */
|
40
|
+
#include "sds.h" /* for sds */
|
37
41
|
|
38
42
|
#define HIREDIS_MAJOR 0
|
39
|
-
#define HIREDIS_MINOR
|
43
|
+
#define HIREDIS_MINOR 14
|
40
44
|
#define HIREDIS_PATCH 0
|
41
|
-
|
42
|
-
#define REDIS_ERR -1
|
43
|
-
#define REDIS_OK 0
|
44
|
-
|
45
|
-
/* When an error occurs, the err flag in a context is set to hold the type of
|
46
|
-
* error that occured. REDIS_ERR_IO means there was an I/O error and you
|
47
|
-
* should use the "errno" variable to find out what is wrong.
|
48
|
-
* For other values, the "errstr" field will hold a description. */
|
49
|
-
#define REDIS_ERR_IO 1 /* Error in read or write */
|
50
|
-
#define REDIS_ERR_EOF 3 /* End of file */
|
51
|
-
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
52
|
-
#define REDIS_ERR_OOM 5 /* Out of memory */
|
53
|
-
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
45
|
+
#define HIREDIS_SONAME 0.14
|
54
46
|
|
55
47
|
/* Connection type can be blocking or non-blocking and is set in the
|
56
48
|
* least significant bit of the flags field in redisContext. */
|
@@ -79,14 +71,14 @@
|
|
79
71
|
/* Flag that is set when monitor mode is active */
|
80
72
|
#define REDIS_MONITORING 0x40
|
81
73
|
|
82
|
-
|
83
|
-
#define
|
84
|
-
|
85
|
-
#define
|
86
|
-
#define REDIS_REPLY_STATUS 5
|
87
|
-
#define REDIS_REPLY_ERROR 6
|
74
|
+
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
|
75
|
+
#define REDIS_REUSEADDR 0x80
|
76
|
+
|
77
|
+
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
88
78
|
|
89
|
-
|
79
|
+
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
80
|
+
* SO_REUSEADDR is being used. */
|
81
|
+
#define REDIS_CONNECT_RETRIES 10
|
90
82
|
|
91
83
|
#ifdef __cplusplus
|
92
84
|
extern "C" {
|
@@ -96,61 +88,13 @@ extern "C" {
|
|
96
88
|
typedef struct redisReply {
|
97
89
|
int type; /* REDIS_REPLY_* */
|
98
90
|
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
|
99
|
-
|
91
|
+
size_t len; /* Length of string */
|
100
92
|
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
|
101
93
|
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
102
94
|
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
103
95
|
} redisReply;
|
104
96
|
|
105
|
-
typedef struct redisReadTask {
|
106
|
-
int type;
|
107
|
-
int elements; /* number of elements in multibulk container */
|
108
|
-
int idx; /* index in parent (array) object */
|
109
|
-
void *obj; /* holds user-generated value for a read task */
|
110
|
-
struct redisReadTask *parent; /* parent task */
|
111
|
-
void *privdata; /* user-settable arbitrary field */
|
112
|
-
} redisReadTask;
|
113
|
-
|
114
|
-
typedef struct redisReplyObjectFunctions {
|
115
|
-
void *(*createString)(const redisReadTask*, char*, size_t);
|
116
|
-
void *(*createArray)(const redisReadTask*, int);
|
117
|
-
void *(*createInteger)(const redisReadTask*, long long);
|
118
|
-
void *(*createNil)(const redisReadTask*);
|
119
|
-
void (*freeObject)(void*);
|
120
|
-
} redisReplyObjectFunctions;
|
121
|
-
|
122
|
-
/* State for the protocol parser */
|
123
|
-
typedef struct redisReader {
|
124
|
-
int err; /* Error flags, 0 when there is no error */
|
125
|
-
char errstr[128]; /* String representation of error when applicable */
|
126
|
-
|
127
|
-
char *buf; /* Read buffer */
|
128
|
-
size_t pos; /* Buffer cursor */
|
129
|
-
size_t len; /* Buffer length */
|
130
|
-
size_t maxbuf; /* Max length of unused buffer */
|
131
|
-
|
132
|
-
redisReadTask rstack[9];
|
133
|
-
int ridx; /* Index of current read task */
|
134
|
-
void *reply; /* Temporary reply pointer */
|
135
|
-
|
136
|
-
redisReplyObjectFunctions *fn;
|
137
|
-
void *privdata;
|
138
|
-
} redisReader;
|
139
|
-
|
140
|
-
/* Public API for the protocol parser. */
|
141
97
|
redisReader *redisReaderCreate(void);
|
142
|
-
void redisReaderFree(redisReader *r);
|
143
|
-
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
144
|
-
int redisReaderGetReply(redisReader *r, void **reply);
|
145
|
-
|
146
|
-
/* Backwards compatibility, can be removed on big version bump. */
|
147
|
-
#define redisReplyReaderCreate redisReaderCreate
|
148
|
-
#define redisReplyReaderFree redisReaderFree
|
149
|
-
#define redisReplyReaderFeed redisReaderFeed
|
150
|
-
#define redisReplyReaderGetReply redisReaderGetReply
|
151
|
-
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
152
|
-
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
153
|
-
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
154
98
|
|
155
99
|
/* Function to free the reply objects hiredis returns by default. */
|
156
100
|
void freeReplyObject(void *reply);
|
@@ -159,6 +103,14 @@ void freeReplyObject(void *reply);
|
|
159
103
|
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
160
104
|
int redisFormatCommand(char **target, const char *format, ...);
|
161
105
|
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
106
|
+
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
|
107
|
+
void redisFreeCommand(char *cmd);
|
108
|
+
void redisFreeSdsCommand(sds cmd);
|
109
|
+
|
110
|
+
enum redisConnectionType {
|
111
|
+
REDIS_CONN_TCP,
|
112
|
+
REDIS_CONN_UNIX
|
113
|
+
};
|
162
114
|
|
163
115
|
/* Context for a connection to Redis */
|
164
116
|
typedef struct redisContext {
|
@@ -168,16 +120,49 @@ typedef struct redisContext {
|
|
168
120
|
int flags;
|
169
121
|
char *obuf; /* Write buffer */
|
170
122
|
redisReader *reader; /* Protocol reader */
|
123
|
+
|
124
|
+
enum redisConnectionType connection_type;
|
125
|
+
struct timeval *timeout;
|
126
|
+
|
127
|
+
struct {
|
128
|
+
char *host;
|
129
|
+
char *source_addr;
|
130
|
+
int port;
|
131
|
+
} tcp;
|
132
|
+
|
133
|
+
struct {
|
134
|
+
char *path;
|
135
|
+
} unix_sock;
|
136
|
+
|
171
137
|
} redisContext;
|
172
138
|
|
173
139
|
redisContext *redisConnect(const char *ip, int port);
|
174
|
-
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
|
140
|
+
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
175
141
|
redisContext *redisConnectNonBlock(const char *ip, int port);
|
142
|
+
redisContext *redisConnectBindNonBlock(const char *ip, int port,
|
143
|
+
const char *source_addr);
|
144
|
+
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
145
|
+
const char *source_addr);
|
176
146
|
redisContext *redisConnectUnix(const char *path);
|
177
|
-
redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
|
147
|
+
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
178
148
|
redisContext *redisConnectUnixNonBlock(const char *path);
|
179
|
-
|
149
|
+
redisContext *redisConnectFd(int fd);
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Reconnect the given context using the saved information.
|
153
|
+
*
|
154
|
+
* This re-uses the exact same connect options as in the initial connection.
|
155
|
+
* host, ip (or path), timeout and bind address are reused,
|
156
|
+
* flags are used unmodified from the existing context.
|
157
|
+
*
|
158
|
+
* Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
|
159
|
+
*/
|
160
|
+
int redisReconnect(redisContext *c);
|
161
|
+
|
162
|
+
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
163
|
+
int redisEnableKeepAlive(redisContext *c);
|
180
164
|
void redisFree(redisContext *c);
|
165
|
+
int redisFreeKeepFd(redisContext *c);
|
181
166
|
int redisBufferRead(redisContext *c);
|
182
167
|
int redisBufferWrite(redisContext *c, int *done);
|
183
168
|
|
@@ -188,6 +173,10 @@ int redisBufferWrite(redisContext *c, int *done);
|
|
188
173
|
int redisGetReply(redisContext *c, void **reply);
|
189
174
|
int redisGetReplyFromReader(redisContext *c, void **reply);
|
190
175
|
|
176
|
+
/* Write a formatted command to the output buffer. Use these functions in blocking mode
|
177
|
+
* to get a pipeline of commands. */
|
178
|
+
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
|
179
|
+
|
191
180
|
/* Write a command to the output buffer. Use these functions in blocking mode
|
192
181
|
* to get a pipeline of commands. */
|
193
182
|
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
|
data/vendor/hiredis/net.c
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
2
2
|
*
|
3
|
-
* Copyright (c)
|
4
|
-
* Copyright (c) 2010-
|
3
|
+
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
4
|
+
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
5
|
+
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
6
|
+
* Jan-Erik Rediger <janerik at fnordig dot com>
|
5
7
|
*
|
6
8
|
* All rights reserved.
|
7
9
|
*
|
@@ -47,6 +49,7 @@
|
|
47
49
|
#include <stdio.h>
|
48
50
|
#include <poll.h>
|
49
51
|
#include <limits.h>
|
52
|
+
#include <stdlib.h>
|
50
53
|
|
51
54
|
#include "net.h"
|
52
55
|
#include "sds.h"
|
@@ -54,21 +57,29 @@
|
|
54
57
|
/* Defined in hiredis.c */
|
55
58
|
void __redisSetError(redisContext *c, int type, const char *str);
|
56
59
|
|
60
|
+
static void redisContextCloseFd(redisContext *c) {
|
61
|
+
if (c && c->fd >= 0) {
|
62
|
+
close(c->fd);
|
63
|
+
c->fd = -1;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
57
67
|
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
58
|
-
|
68
|
+
int errorno = errno; /* snprintf() may change errno */
|
69
|
+
char buf[128] = { 0 };
|
59
70
|
size_t len = 0;
|
60
71
|
|
61
72
|
if (prefix != NULL)
|
62
73
|
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
63
|
-
strerror_r(
|
74
|
+
strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
|
64
75
|
__redisSetError(c,type,buf);
|
65
76
|
}
|
66
77
|
|
67
|
-
static int redisSetReuseAddr(redisContext *c
|
78
|
+
static int redisSetReuseAddr(redisContext *c) {
|
68
79
|
int on = 1;
|
69
|
-
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
80
|
+
if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
70
81
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
71
|
-
|
82
|
+
redisContextCloseFd(c);
|
72
83
|
return REDIS_ERR;
|
73
84
|
}
|
74
85
|
return REDIS_OK;
|
@@ -80,23 +91,24 @@ static int redisCreateSocket(redisContext *c, int type) {
|
|
80
91
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
81
92
|
return REDIS_ERR;
|
82
93
|
}
|
94
|
+
c->fd = s;
|
83
95
|
if (type == AF_INET) {
|
84
|
-
if (redisSetReuseAddr(c
|
96
|
+
if (redisSetReuseAddr(c) == REDIS_ERR) {
|
85
97
|
return REDIS_ERR;
|
86
98
|
}
|
87
99
|
}
|
88
|
-
return
|
100
|
+
return REDIS_OK;
|
89
101
|
}
|
90
102
|
|
91
|
-
static int redisSetBlocking(redisContext *c, int
|
103
|
+
static int redisSetBlocking(redisContext *c, int blocking) {
|
92
104
|
int flags;
|
93
105
|
|
94
106
|
/* Set the socket nonblocking.
|
95
107
|
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
96
108
|
* interrupted by a signal. */
|
97
|
-
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
109
|
+
if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
|
98
110
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
99
|
-
|
111
|
+
redisContextCloseFd(c);
|
100
112
|
return REDIS_ERR;
|
101
113
|
}
|
102
114
|
|
@@ -105,19 +117,60 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
|
|
105
117
|
else
|
106
118
|
flags |= O_NONBLOCK;
|
107
119
|
|
108
|
-
if (fcntl(fd, F_SETFL, flags) == -1) {
|
120
|
+
if (fcntl(c->fd, F_SETFL, flags) == -1) {
|
109
121
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
110
|
-
|
122
|
+
redisContextCloseFd(c);
|
111
123
|
return REDIS_ERR;
|
112
124
|
}
|
113
125
|
return REDIS_OK;
|
114
126
|
}
|
115
127
|
|
116
|
-
|
128
|
+
int redisKeepAlive(redisContext *c, int interval) {
|
129
|
+
int val = 1;
|
130
|
+
int fd = c->fd;
|
131
|
+
|
132
|
+
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
133
|
+
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
134
|
+
return REDIS_ERR;
|
135
|
+
}
|
136
|
+
|
137
|
+
val = interval;
|
138
|
+
|
139
|
+
#if defined(__APPLE__) && defined(__MACH__)
|
140
|
+
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
141
|
+
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
142
|
+
return REDIS_ERR;
|
143
|
+
}
|
144
|
+
#else
|
145
|
+
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
|
146
|
+
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
147
|
+
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
148
|
+
return REDIS_ERR;
|
149
|
+
}
|
150
|
+
|
151
|
+
val = interval/3;
|
152
|
+
if (val == 0) val = 1;
|
153
|
+
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
154
|
+
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
155
|
+
return REDIS_ERR;
|
156
|
+
}
|
157
|
+
|
158
|
+
val = 3;
|
159
|
+
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
160
|
+
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
161
|
+
return REDIS_ERR;
|
162
|
+
}
|
163
|
+
#endif
|
164
|
+
#endif
|
165
|
+
|
166
|
+
return REDIS_OK;
|
167
|
+
}
|
168
|
+
|
169
|
+
static int redisSetTcpNoDelay(redisContext *c) {
|
117
170
|
int yes = 1;
|
118
|
-
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
171
|
+
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
119
172
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
120
|
-
|
173
|
+
redisContextCloseFd(c);
|
121
174
|
return REDIS_ERR;
|
122
175
|
}
|
123
176
|
return REDIS_OK;
|
@@ -125,18 +178,15 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
|
|
125
178
|
|
126
179
|
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
127
180
|
|
128
|
-
static int
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
msec = -1;
|
133
|
-
wfd[0].fd = fd;
|
134
|
-
wfd[0].events = POLLOUT;
|
181
|
+
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
182
|
+
{
|
183
|
+
const struct timeval *timeout = c->timeout;
|
184
|
+
long msec = -1;
|
135
185
|
|
136
186
|
/* Only use timeout when not NULL. */
|
137
187
|
if (timeout != NULL) {
|
138
188
|
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
139
|
-
|
189
|
+
*result = msec;
|
140
190
|
return REDIS_ERR;
|
141
191
|
}
|
142
192
|
|
@@ -147,52 +197,60 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
|
|
147
197
|
}
|
148
198
|
}
|
149
199
|
|
200
|
+
*result = msec;
|
201
|
+
return REDIS_OK;
|
202
|
+
}
|
203
|
+
|
204
|
+
static int redisContextWaitReady(redisContext *c, long msec) {
|
205
|
+
struct pollfd wfd[1];
|
206
|
+
|
207
|
+
wfd[0].fd = c->fd;
|
208
|
+
wfd[0].events = POLLOUT;
|
209
|
+
|
150
210
|
if (errno == EINPROGRESS) {
|
151
211
|
int res;
|
152
212
|
|
153
213
|
if ((res = poll(wfd, 1, msec)) == -1) {
|
154
214
|
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
155
|
-
|
215
|
+
redisContextCloseFd(c);
|
156
216
|
return REDIS_ERR;
|
157
217
|
} else if (res == 0) {
|
158
218
|
errno = ETIMEDOUT;
|
159
219
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
160
|
-
|
220
|
+
redisContextCloseFd(c);
|
161
221
|
return REDIS_ERR;
|
162
222
|
}
|
163
223
|
|
164
|
-
if (redisCheckSocketError(c
|
224
|
+
if (redisCheckSocketError(c) != REDIS_OK)
|
165
225
|
return REDIS_ERR;
|
166
226
|
|
167
227
|
return REDIS_OK;
|
168
228
|
}
|
169
229
|
|
170
230
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
171
|
-
|
231
|
+
redisContextCloseFd(c);
|
172
232
|
return REDIS_ERR;
|
173
233
|
}
|
174
234
|
|
175
|
-
int redisCheckSocketError(redisContext *c
|
235
|
+
int redisCheckSocketError(redisContext *c) {
|
176
236
|
int err = 0;
|
177
237
|
socklen_t errlen = sizeof(err);
|
178
238
|
|
179
|
-
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
239
|
+
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
180
240
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
181
|
-
close(fd);
|
182
241
|
return REDIS_ERR;
|
183
242
|
}
|
184
243
|
|
185
244
|
if (err) {
|
186
245
|
errno = err;
|
187
246
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
188
|
-
close(fd);
|
189
247
|
return REDIS_ERR;
|
190
248
|
}
|
191
249
|
|
192
250
|
return REDIS_OK;
|
193
251
|
}
|
194
252
|
|
195
|
-
int redisContextSetTimeout(redisContext *c, struct timeval tv) {
|
253
|
+
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
196
254
|
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
|
197
255
|
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
198
256
|
return REDIS_ERR;
|
@@ -204,44 +262,140 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
|
|
204
262
|
return REDIS_OK;
|
205
263
|
}
|
206
264
|
|
207
|
-
int
|
208
|
-
|
265
|
+
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
266
|
+
const struct timeval *timeout,
|
267
|
+
const char *source_addr) {
|
268
|
+
int s, rv, n;
|
209
269
|
char _port[6]; /* strlen("65535"); */
|
210
|
-
struct addrinfo hints, *servinfo, *p;
|
270
|
+
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
211
271
|
int blocking = (c->flags & REDIS_BLOCK);
|
272
|
+
int reuseaddr = (c->flags & REDIS_REUSEADDR);
|
273
|
+
int reuses = 0;
|
274
|
+
long timeout_msec = -1;
|
275
|
+
|
276
|
+
servinfo = NULL;
|
277
|
+
c->connection_type = REDIS_CONN_TCP;
|
278
|
+
c->tcp.port = port;
|
279
|
+
|
280
|
+
/* We need to take possession of the passed parameters
|
281
|
+
* to make them reusable for a reconnect.
|
282
|
+
* We also carefully check we don't free data we already own,
|
283
|
+
* as in the case of the reconnect method.
|
284
|
+
*
|
285
|
+
* This is a bit ugly, but atleast it works and doesn't leak memory.
|
286
|
+
**/
|
287
|
+
if (c->tcp.host != addr) {
|
288
|
+
free(c->tcp.host);
|
289
|
+
|
290
|
+
c->tcp.host = strdup(addr);
|
291
|
+
}
|
292
|
+
|
293
|
+
if (timeout) {
|
294
|
+
if (c->timeout != timeout) {
|
295
|
+
if (c->timeout == NULL)
|
296
|
+
c->timeout = malloc(sizeof(struct timeval));
|
297
|
+
|
298
|
+
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
299
|
+
}
|
300
|
+
} else {
|
301
|
+
free(c->timeout);
|
302
|
+
c->timeout = NULL;
|
303
|
+
}
|
304
|
+
|
305
|
+
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
306
|
+
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
307
|
+
goto error;
|
308
|
+
}
|
309
|
+
|
310
|
+
if (source_addr == NULL) {
|
311
|
+
free(c->tcp.source_addr);
|
312
|
+
c->tcp.source_addr = NULL;
|
313
|
+
} else if (c->tcp.source_addr != source_addr) {
|
314
|
+
free(c->tcp.source_addr);
|
315
|
+
c->tcp.source_addr = strdup(source_addr);
|
316
|
+
}
|
212
317
|
|
213
318
|
snprintf(_port, 6, "%d", port);
|
214
319
|
memset(&hints,0,sizeof(hints));
|
215
320
|
hints.ai_family = AF_INET;
|
216
321
|
hints.ai_socktype = SOCK_STREAM;
|
217
322
|
|
218
|
-
if
|
219
|
-
|
220
|
-
|
323
|
+
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
324
|
+
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
325
|
+
* as this would add latency to every connect. Otherwise a more sensible
|
326
|
+
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
327
|
+
* connectivity. */
|
328
|
+
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
|
329
|
+
hints.ai_family = AF_INET6;
|
330
|
+
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
331
|
+
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
332
|
+
return REDIS_ERR;
|
333
|
+
}
|
221
334
|
}
|
222
335
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
336
|
+
addrretry:
|
223
337
|
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
224
338
|
continue;
|
225
339
|
|
226
|
-
|
340
|
+
c->fd = s;
|
341
|
+
if (redisSetBlocking(c,0) != REDIS_OK)
|
227
342
|
goto error;
|
343
|
+
if (c->tcp.source_addr) {
|
344
|
+
int bound = 0;
|
345
|
+
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
|
346
|
+
if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
|
347
|
+
char buf[128];
|
348
|
+
snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
|
349
|
+
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
350
|
+
goto error;
|
351
|
+
}
|
352
|
+
|
353
|
+
if (reuseaddr) {
|
354
|
+
n = 1;
|
355
|
+
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
|
356
|
+
sizeof(n)) < 0) {
|
357
|
+
freeaddrinfo(bservinfo);
|
358
|
+
goto error;
|
359
|
+
}
|
360
|
+
}
|
361
|
+
|
362
|
+
for (b = bservinfo; b != NULL; b = b->ai_next) {
|
363
|
+
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
|
364
|
+
bound = 1;
|
365
|
+
break;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
freeaddrinfo(bservinfo);
|
369
|
+
if (!bound) {
|
370
|
+
char buf[128];
|
371
|
+
snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
|
372
|
+
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
373
|
+
goto error;
|
374
|
+
}
|
375
|
+
}
|
228
376
|
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
229
377
|
if (errno == EHOSTUNREACH) {
|
230
|
-
|
378
|
+
redisContextCloseFd(c);
|
231
379
|
continue;
|
232
380
|
} else if (errno == EINPROGRESS && !blocking) {
|
233
381
|
/* This is ok. */
|
382
|
+
} else if (errno == EADDRNOTAVAIL && reuseaddr) {
|
383
|
+
if (++reuses >= REDIS_CONNECT_RETRIES) {
|
384
|
+
goto error;
|
385
|
+
} else {
|
386
|
+
redisContextCloseFd(c);
|
387
|
+
goto addrretry;
|
388
|
+
}
|
234
389
|
} else {
|
235
|
-
if (redisContextWaitReady(c,
|
390
|
+
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
236
391
|
goto error;
|
237
392
|
}
|
238
393
|
}
|
239
|
-
if (blocking && redisSetBlocking(c,
|
394
|
+
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
240
395
|
goto error;
|
241
|
-
if (redisSetTcpNoDelay(c
|
396
|
+
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
242
397
|
goto error;
|
243
398
|
|
244
|
-
c->fd = s;
|
245
399
|
c->flags |= REDIS_CONNECTED;
|
246
400
|
rv = REDIS_OK;
|
247
401
|
goto end;
|
@@ -256,36 +410,68 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t
|
|
256
410
|
error:
|
257
411
|
rv = REDIS_ERR;
|
258
412
|
end:
|
259
|
-
|
413
|
+
if(servinfo) {
|
414
|
+
freeaddrinfo(servinfo);
|
415
|
+
}
|
416
|
+
|
260
417
|
return rv; // Need to return REDIS_OK if alright
|
261
418
|
}
|
262
419
|
|
263
|
-
int
|
264
|
-
|
420
|
+
int redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
421
|
+
const struct timeval *timeout) {
|
422
|
+
return _redisContextConnectTcp(c, addr, port, timeout, NULL);
|
423
|
+
}
|
424
|
+
|
425
|
+
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
426
|
+
const struct timeval *timeout,
|
427
|
+
const char *source_addr) {
|
428
|
+
return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
|
429
|
+
}
|
430
|
+
|
431
|
+
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
265
432
|
int blocking = (c->flags & REDIS_BLOCK);
|
266
433
|
struct sockaddr_un sa;
|
434
|
+
long timeout_msec = -1;
|
267
435
|
|
268
|
-
if (
|
436
|
+
if (redisCreateSocket(c,AF_UNIX) < 0)
|
269
437
|
return REDIS_ERR;
|
270
|
-
if (redisSetBlocking(c,
|
438
|
+
if (redisSetBlocking(c,0) != REDIS_OK)
|
271
439
|
return REDIS_ERR;
|
272
440
|
|
273
|
-
|
441
|
+
c->connection_type = REDIS_CONN_UNIX;
|
442
|
+
if (c->unix_sock.path != path)
|
443
|
+
c->unix_sock.path = strdup(path);
|
444
|
+
|
445
|
+
if (timeout) {
|
446
|
+
if (c->timeout != timeout) {
|
447
|
+
if (c->timeout == NULL)
|
448
|
+
c->timeout = malloc(sizeof(struct timeval));
|
449
|
+
|
450
|
+
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
451
|
+
}
|
452
|
+
} else {
|
453
|
+
free(c->timeout);
|
454
|
+
c->timeout = NULL;
|
455
|
+
}
|
456
|
+
|
457
|
+
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
458
|
+
return REDIS_ERR;
|
459
|
+
|
460
|
+
sa.sun_family = AF_UNIX;
|
274
461
|
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
275
|
-
if (connect(
|
462
|
+
if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
276
463
|
if (errno == EINPROGRESS && !blocking) {
|
277
464
|
/* This is ok. */
|
278
465
|
} else {
|
279
|
-
if (redisContextWaitReady(c,
|
466
|
+
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
280
467
|
return REDIS_ERR;
|
281
468
|
}
|
282
469
|
}
|
283
470
|
|
284
471
|
/* Reset socket to be blocking after connect(2). */
|
285
|
-
if (blocking && redisSetBlocking(c,
|
472
|
+
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
286
473
|
return REDIS_ERR;
|
287
474
|
|
288
|
-
c->fd = s;
|
289
475
|
c->flags |= REDIS_CONNECTED;
|
290
476
|
return REDIS_OK;
|
291
477
|
}
|