durable_rules 0.33.13 → 0.34.01

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 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
  }