hiredis 0.5.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }