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.
@@ -1,6 +1,8 @@
1
1
  /*
2
2
  * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
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 <stdio.h> /* for size_t */
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 11
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
- #define REDIS_REPLY_STRING 1
83
- #define REDIS_REPLY_ARRAY 2
84
- #define REDIS_REPLY_INTEGER 3
85
- #define REDIS_REPLY_NIL 4
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
- #define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
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
- int len; /* Length of string */
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
- int redisSetTimeout(redisContext *c, struct timeval tv);
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) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
- * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
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
- char buf[128];
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(errno,buf+len,sizeof(buf)-len);
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, int fd) {
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
- close(fd);
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,s) == REDIS_ERR) {
96
+ if (redisSetReuseAddr(c) == REDIS_ERR) {
85
97
  return REDIS_ERR;
86
98
  }
87
99
  }
88
- return s;
100
+ return REDIS_OK;
89
101
  }
90
102
 
91
- static int redisSetBlocking(redisContext *c, int fd, int blocking) {
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
- close(fd);
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
- close(fd);
122
+ redisContextCloseFd(c);
111
123
  return REDIS_ERR;
112
124
  }
113
125
  return REDIS_OK;
114
126
  }
115
127
 
116
- static int redisSetTcpNoDelay(redisContext *c, int fd) {
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
- close(fd);
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 redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
129
- struct pollfd wfd[1];
130
- long msec;
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
- close(fd);
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
- close(fd);
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
- close(fd);
220
+ redisContextCloseFd(c);
161
221
  return REDIS_ERR;
162
222
  }
163
223
 
164
- if (redisCheckSocketError(c, fd) != REDIS_OK)
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
- close(fd);
231
+ redisContextCloseFd(c);
172
232
  return REDIS_ERR;
173
233
  }
174
234
 
175
- int redisCheckSocketError(redisContext *c, int fd) {
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 redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
208
- int s, rv;
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 ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
219
- __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
220
- return REDIS_ERR;
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
- if (redisSetBlocking(c,s,0) != REDIS_OK)
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
- close(s);
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,s,timeout) != REDIS_OK)
390
+ if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
236
391
  goto error;
237
392
  }
238
393
  }
239
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
394
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
240
395
  goto error;
241
- if (redisSetTcpNoDelay(c,s) != REDIS_OK)
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
- freeaddrinfo(servinfo);
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 redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
264
- int s;
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 ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
436
+ if (redisCreateSocket(c,AF_UNIX) < 0)
269
437
  return REDIS_ERR;
270
- if (redisSetBlocking(c,s,0) != REDIS_OK)
438
+ if (redisSetBlocking(c,0) != REDIS_OK)
271
439
  return REDIS_ERR;
272
440
 
273
- sa.sun_family = AF_LOCAL;
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(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
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,s,timeout) != REDIS_OK)
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,s,1) != REDIS_OK)
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
  }