durable_rules 0.33.13 → 0.34.01

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 10
40
- #define HIREDIS_PATCH 1
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... */
43
+ #define HIREDIS_MINOR 13
44
+ #define HIREDIS_PATCH 3
45
+ #define HIREDIS_SONAME 0.13
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,38 @@
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
88
-
89
- #define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
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 */
78
+
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
82
+
83
+ /* strerror_r has two completely different prototypes and behaviors
84
+ * depending on system issues, so we need to operate on the error buffer
85
+ * differently depending on which strerror_r we're using. */
86
+ #ifndef _GNU_SOURCE_PLUS
87
+ /* "regular" POSIX strerror_r that does the right thing. */
88
+ #define __redis_strerror_r(errno, buf, len) \
89
+ do { \
90
+ strerror_r((errno), (buf), (len)); \
91
+ } while (0)
92
+ #else
93
+ /* "bad" GNU strerror_r we need to clean up after. */
94
+ #define __redis_strerror_r(errno, buf, len) \
95
+ do { \
96
+ char *err_str = strerror_r((errno), (buf), (len)); \
97
+ /* If return value _isn't_ the start of the buffer we passed in, \
98
+ * then GNU strerror_r returned an internal static buffer and we \
99
+ * need to copy the result into our private buffer. */ \
100
+ if (err_str != (buf)) { \
101
+ buf[(len)] = '\0'; \
102
+ strncat((buf), err_str, ((len) - 1)); \
103
+ } \
104
+ } while (0)
105
+ #endif
90
106
 
91
107
  #ifdef __cplusplus
92
108
  extern "C" {
@@ -102,55 +118,7 @@ typedef struct redisReply {
102
118
  struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
103
119
  } redisReply;
104
120
 
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
121
  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
122
 
155
123
  /* Function to free the reply objects hiredis returns by default. */
156
124
  void freeReplyObject(void *reply);
@@ -159,6 +127,14 @@ void freeReplyObject(void *reply);
159
127
  int redisvFormatCommand(char **target, const char *format, va_list ap);
160
128
  int redisFormatCommand(char **target, const char *format, ...);
161
129
  int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
130
+ int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
131
+ void redisFreeCommand(char *cmd);
132
+ void redisFreeSdsCommand(sds cmd);
133
+
134
+ enum redisConnectionType {
135
+ REDIS_CONN_TCP,
136
+ REDIS_CONN_UNIX,
137
+ };
162
138
 
163
139
  /* Context for a connection to Redis */
164
140
  typedef struct redisContext {
@@ -168,16 +144,49 @@ typedef struct redisContext {
168
144
  int flags;
169
145
  char *obuf; /* Write buffer */
170
146
  redisReader *reader; /* Protocol reader */
147
+
148
+ enum redisConnectionType connection_type;
149
+ struct timeval *timeout;
150
+
151
+ struct {
152
+ char *host;
153
+ char *source_addr;
154
+ int port;
155
+ } tcp;
156
+
157
+ struct {
158
+ char *path;
159
+ } unix_sock;
160
+
171
161
  } redisContext;
172
162
 
173
163
  redisContext *redisConnect(const char *ip, int port);
174
- redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
164
+ redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
175
165
  redisContext *redisConnectNonBlock(const char *ip, int port);
166
+ redisContext *redisConnectBindNonBlock(const char *ip, int port,
167
+ const char *source_addr);
168
+ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
169
+ const char *source_addr);
176
170
  redisContext *redisConnectUnix(const char *path);
177
- redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
171
+ redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
178
172
  redisContext *redisConnectUnixNonBlock(const char *path);
179
- int redisSetTimeout(redisContext *c, struct timeval tv);
173
+ redisContext *redisConnectFd(int fd);
174
+
175
+ /**
176
+ * Reconnect the given context using the saved information.
177
+ *
178
+ * This re-uses the exact same connect options as in the initial connection.
179
+ * host, ip (or path), timeout and bind address are reused,
180
+ * flags are used unmodified from the existing context.
181
+ *
182
+ * Returns REDIS_OK on successfull connect or REDIS_ERR otherwise.
183
+ */
184
+ int redisReconnect(redisContext *c);
185
+
186
+ int redisSetTimeout(redisContext *c, const struct timeval tv);
187
+ int redisEnableKeepAlive(redisContext *c);
180
188
  void redisFree(redisContext *c);
189
+ int redisFreeKeepFd(redisContext *c);
181
190
  int redisBufferRead(redisContext *c);
182
191
  int redisBufferWrite(redisContext *c, int *done);
183
192
 
@@ -188,6 +197,10 @@ int redisBufferWrite(redisContext *c, int *done);
188
197
  int redisGetReply(redisContext *c, void **reply);
189
198
  int redisGetReplyFromReader(redisContext *c, void **reply);
190
199
 
200
+ /* Write a formatted command to the output buffer. Use these functions in blocking mode
201
+ * to get a pipeline of commands. */
202
+ int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
203
+
191
204
  /* Write a command to the output buffer. Use these functions in blocking mode
192
205
  * to get a pipeline of commands. */
193
206
  int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
data/deps/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,28 @@
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
+ char buf[128] = { 0 };
59
69
  size_t len = 0;
60
70
 
61
71
  if (prefix != NULL)
62
72
  len = snprintf(buf,sizeof(buf),"%s: ",prefix);
63
- strerror_r(errno,buf+len,sizeof(buf)-len);
73
+ //__redis_strerror_r(errno, (char *)(buf + len), sizeof(buf) - len);
64
74
  __redisSetError(c,type,buf);
65
75
  }
66
76
 
67
- static int redisSetReuseAddr(redisContext *c, int fd) {
77
+ static int redisSetReuseAddr(redisContext *c) {
68
78
  int on = 1;
69
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
79
+ if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
70
80
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
71
- close(fd);
81
+ redisContextCloseFd(c);
72
82
  return REDIS_ERR;
73
83
  }
74
84
  return REDIS_OK;
@@ -80,23 +90,24 @@ static int redisCreateSocket(redisContext *c, int type) {
80
90
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
81
91
  return REDIS_ERR;
82
92
  }
93
+ c->fd = s;
83
94
  if (type == AF_INET) {
84
- if (redisSetReuseAddr(c,s) == REDIS_ERR) {
95
+ if (redisSetReuseAddr(c) == REDIS_ERR) {
85
96
  return REDIS_ERR;
86
97
  }
87
98
  }
88
- return s;
99
+ return REDIS_OK;
89
100
  }
90
101
 
91
- static int redisSetBlocking(redisContext *c, int fd, int blocking) {
102
+ static int redisSetBlocking(redisContext *c, int blocking) {
92
103
  int flags;
93
104
 
94
105
  /* Set the socket nonblocking.
95
106
  * Note that fcntl(2) for F_GETFL and F_SETFL can't be
96
107
  * interrupted by a signal. */
97
- if ((flags = fcntl(fd, F_GETFL)) == -1) {
108
+ if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
98
109
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
99
- close(fd);
110
+ redisContextCloseFd(c);
100
111
  return REDIS_ERR;
101
112
  }
102
113
 
@@ -105,19 +116,61 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
105
116
  else
106
117
  flags |= O_NONBLOCK;
107
118
 
108
- if (fcntl(fd, F_SETFL, flags) == -1) {
119
+ if (fcntl(c->fd, F_SETFL, flags) == -1) {
109
120
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
110
- close(fd);
121
+ redisContextCloseFd(c);
111
122
  return REDIS_ERR;
112
123
  }
113
124
  return REDIS_OK;
114
125
  }
115
126
 
116
- static int redisSetTcpNoDelay(redisContext *c, int fd) {
127
+ int redisKeepAlive(redisContext *c, int interval) {
128
+ int val = 1;
129
+ int fd = c->fd;
130
+
131
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
132
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
133
+ return REDIS_ERR;
134
+ }
135
+
136
+ val = interval;
137
+
138
+ #ifdef _OSX
139
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
140
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
141
+ return REDIS_ERR;
142
+ }
143
+ #else
144
+ #if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
145
+ val = interval;
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,19 @@ 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) {
181
+ static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) {
129
182
  struct pollfd wfd[1];
130
183
  long msec;
131
184
 
132
185
  msec = -1;
133
- wfd[0].fd = fd;
186
+ wfd[0].fd = c->fd;
134
187
  wfd[0].events = POLLOUT;
135
188
 
136
189
  /* Only use timeout when not NULL. */
137
190
  if (timeout != NULL) {
138
191
  if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
139
- close(fd);
192
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
193
+ redisContextCloseFd(c);
140
194
  return REDIS_ERR;
141
195
  }
142
196
 
@@ -152,47 +206,45 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
152
206
 
153
207
  if ((res = poll(wfd, 1, msec)) == -1) {
154
208
  __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
155
- close(fd);
209
+ redisContextCloseFd(c);
156
210
  return REDIS_ERR;
157
211
  } else if (res == 0) {
158
212
  errno = ETIMEDOUT;
159
213
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
160
- close(fd);
214
+ redisContextCloseFd(c);
161
215
  return REDIS_ERR;
162
216
  }
163
217
 
164
- if (redisCheckSocketError(c, fd) != REDIS_OK)
218
+ if (redisCheckSocketError(c) != REDIS_OK)
165
219
  return REDIS_ERR;
166
220
 
167
221
  return REDIS_OK;
168
222
  }
169
223
 
170
224
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
171
- close(fd);
225
+ redisContextCloseFd(c);
172
226
  return REDIS_ERR;
173
227
  }
174
228
 
175
- int redisCheckSocketError(redisContext *c, int fd) {
229
+ int redisCheckSocketError(redisContext *c) {
176
230
  int err = 0;
177
231
  socklen_t errlen = sizeof(err);
178
232
 
179
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
233
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
180
234
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
181
- close(fd);
182
235
  return REDIS_ERR;
183
236
  }
184
237
 
185
238
  if (err) {
186
239
  errno = err;
187
240
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
188
- close(fd);
189
241
  return REDIS_ERR;
190
242
  }
191
243
 
192
244
  return REDIS_OK;
193
245
  }
194
246
 
195
- int redisContextSetTimeout(redisContext *c, struct timeval tv) {
247
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
196
248
  if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
197
249
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
198
250
  return REDIS_ERR;
@@ -204,11 +256,53 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
204
256
  return REDIS_OK;
205
257
  }
206
258
 
207
- int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
208
- int s, rv;
259
+ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
260
+ const struct timeval *timeout,
261
+ const char *source_addr) {
262
+ int s, rv, n;
209
263
  char _port[6]; /* strlen("65535"); */
210
- struct addrinfo hints, *servinfo, *p;
264
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
211
265
  int blocking = (c->flags & REDIS_BLOCK);
266
+ int reuseaddr = (c->flags & REDIS_REUSEADDR);
267
+ int reuses = 0;
268
+
269
+ c->connection_type = REDIS_CONN_TCP;
270
+ c->tcp.port = port;
271
+
272
+ /* We need to take possession of the passed parameters
273
+ * to make them reusable for a reconnect.
274
+ * We also carefully check we don't free data we already own,
275
+ * as in the case of the reconnect method.
276
+ *
277
+ * This is a bit ugly, but atleast it works and doesn't leak memory.
278
+ **/
279
+ if (c->tcp.host != addr) {
280
+ if (c->tcp.host)
281
+ free(c->tcp.host);
282
+
283
+ c->tcp.host = strdup(addr);
284
+ }
285
+
286
+ if (timeout) {
287
+ if (c->timeout != timeout) {
288
+ if (c->timeout == NULL)
289
+ c->timeout = malloc(sizeof(struct timeval));
290
+
291
+ memcpy(c->timeout, timeout, sizeof(struct timeval));
292
+ }
293
+ } else {
294
+ if (c->timeout)
295
+ free(c->timeout);
296
+ c->timeout = NULL;
297
+ }
298
+
299
+ if (source_addr == NULL) {
300
+ free(c->tcp.source_addr);
301
+ c->tcp.source_addr = NULL;
302
+ } else if (c->tcp.source_addr != source_addr) {
303
+ free(c->tcp.source_addr);
304
+ c->tcp.source_addr = strdup(source_addr);
305
+ }
212
306
 
213
307
  snprintf(_port, 6, "%d", port);
214
308
  memset(&hints,0,sizeof(hints));
@@ -220,7 +314,7 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t
220
314
  * as this would add latency to every connect. Otherwise a more sensible
221
315
  * route could be: Use IPv6 if both addresses are available and there is IPv6
222
316
  * connectivity. */
223
- if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
317
+ if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
224
318
  hints.ai_family = AF_INET6;
225
319
  if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
226
320
  __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
@@ -228,28 +322,67 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t
228
322
  }
229
323
  }
230
324
  for (p = servinfo; p != NULL; p = p->ai_next) {
325
+ addrretry:
231
326
  if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
232
327
  continue;
233
328
 
234
- if (redisSetBlocking(c,s,0) != REDIS_OK)
329
+ c->fd = s;
330
+ if (redisSetBlocking(c,0) != REDIS_OK)
235
331
  goto error;
332
+ if (c->tcp.source_addr) {
333
+ int bound = 0;
334
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
335
+ if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
336
+ char buf[128];
337
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
338
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
339
+ goto error;
340
+ }
341
+
342
+ if (reuseaddr) {
343
+ n = 1;
344
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
345
+ sizeof(n)) < 0) {
346
+ goto error;
347
+ }
348
+ }
349
+
350
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
351
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
352
+ bound = 1;
353
+ break;
354
+ }
355
+ }
356
+ freeaddrinfo(bservinfo);
357
+ if (!bound) {
358
+ char buf[128];
359
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
360
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
361
+ goto error;
362
+ }
363
+ }
236
364
  if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
237
365
  if (errno == EHOSTUNREACH) {
238
- close(s);
366
+ redisContextCloseFd(c);
239
367
  continue;
240
368
  } else if (errno == EINPROGRESS && !blocking) {
241
369
  /* This is ok. */
370
+ } else if (errno == EADDRNOTAVAIL && reuseaddr) {
371
+ if (++reuses >= REDIS_CONNECT_RETRIES) {
372
+ goto error;
373
+ } else {
374
+ goto addrretry;
375
+ }
242
376
  } else {
243
- if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
377
+ if (redisContextWaitReady(c,c->timeout) != REDIS_OK)
244
378
  goto error;
245
379
  }
246
380
  }
247
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
381
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
248
382
  goto error;
249
- if (redisSetTcpNoDelay(c,s) != REDIS_OK)
383
+ if (redisSetTcpNoDelay(c) != REDIS_OK)
250
384
  goto error;
251
385
 
252
- c->fd = s;
253
386
  c->flags |= REDIS_CONNECTED;
254
387
  rv = REDIS_OK;
255
388
  goto end;
@@ -268,32 +401,58 @@ end:
268
401
  return rv; // Need to return REDIS_OK if alright
269
402
  }
270
403
 
271
- int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
272
- int s;
404
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port,
405
+ const struct timeval *timeout) {
406
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
407
+ }
408
+
409
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
410
+ const struct timeval *timeout,
411
+ const char *source_addr) {
412
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
413
+ }
414
+
415
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
273
416
  int blocking = (c->flags & REDIS_BLOCK);
274
417
  struct sockaddr_un sa;
275
418
 
276
- if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
419
+ if (redisCreateSocket(c,AF_LOCAL) < 0)
277
420
  return REDIS_ERR;
278
- if (redisSetBlocking(c,s,0) != REDIS_OK)
421
+ if (redisSetBlocking(c,0) != REDIS_OK)
279
422
  return REDIS_ERR;
280
423
 
424
+ c->connection_type = REDIS_CONN_UNIX;
425
+ if (c->unix_sock.path != path)
426
+ c->unix_sock.path = strdup(path);
427
+
428
+ if (timeout) {
429
+ if (c->timeout != timeout) {
430
+ if (c->timeout == NULL)
431
+ c->timeout = malloc(sizeof(struct timeval));
432
+
433
+ memcpy(c->timeout, timeout, sizeof(struct timeval));
434
+ }
435
+ } else {
436
+ if (c->timeout)
437
+ free(c->timeout);
438
+ c->timeout = NULL;
439
+ }
440
+
281
441
  sa.sun_family = AF_LOCAL;
282
442
  strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
283
- if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
443
+ if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
284
444
  if (errno == EINPROGRESS && !blocking) {
285
445
  /* This is ok. */
286
446
  } else {
287
- if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
447
+ if (redisContextWaitReady(c,c->timeout) != REDIS_OK)
288
448
  return REDIS_ERR;
289
449
  }
290
450
  }
291
451
 
292
452
  /* Reset socket to be blocking after connect(2). */
293
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
453
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
294
454
  return REDIS_ERR;
295
455
 
296
- c->fd = s;
297
456
  c->flags |= REDIS_CONNECTED;
298
457
  return REDIS_OK;
299
458
  }