hiredis 0.5.2 → 0.6.0

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.
@@ -31,27 +31,16 @@
31
31
 
32
32
  #ifndef __HIREDIS_H
33
33
  #define __HIREDIS_H
34
- #include <stdio.h> /* for size_t */
34
+ #include "read.h"
35
35
  #include <stdarg.h> /* for va_list */
36
36
  #include <sys/time.h> /* for struct timeval */
37
+ #include <stdint.h> /* uintXX_t, etc */
38
+ #include "sds.h" /* for sds */
37
39
 
38
40
  #define HIREDIS_MAJOR 0
39
- #define HIREDIS_MINOR 11
41
+ #define HIREDIS_MINOR 12
40
42
  #define HIREDIS_PATCH 0
41
43
 
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... */
54
-
55
44
  /* Connection type can be blocking or non-blocking and is set in the
56
45
  * least significant bit of the flags field in redisContext. */
57
46
  #define REDIS_BLOCK 0x1
@@ -79,14 +68,38 @@
79
68
  /* Flag that is set when monitor mode is active */
80
69
  #define REDIS_MONITORING 0x40
81
70
 
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. */
71
+ /* Flag that is set when we should set SO_REUSEADDR before calling bind() */
72
+ #define REDIS_REUSEADDR 0x80
73
+
74
+ #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
75
+
76
+ /* number of times we retry to connect in the case of EADDRNOTAVAIL and
77
+ * SO_REUSEADDR is being used. */
78
+ #define REDIS_CONNECT_RETRIES 10
79
+
80
+ /* strerror_r has two completely different prototypes and behaviors
81
+ * depending on system issues, so we need to operate on the error buffer
82
+ * differently depending on which strerror_r we're using. */
83
+ #ifndef _GNU_SOURCE
84
+ /* "regular" POSIX strerror_r that does the right thing. */
85
+ #define __redis_strerror_r(errno, buf, len) \
86
+ do { \
87
+ strerror_r((errno), (buf), (len)); \
88
+ } while (0)
89
+ #else
90
+ /* "bad" GNU strerror_r we need to clean up after. */
91
+ #define __redis_strerror_r(errno, buf, len) \
92
+ do { \
93
+ char *err_str = strerror_r((errno), (buf), (len)); \
94
+ /* If return value _isn't_ the start of the buffer we passed in, \
95
+ * then GNU strerror_r returned an internal static buffer and we \
96
+ * need to copy the result into our private buffer. */ \
97
+ if (err_str != (buf)) { \
98
+ buf[(len)] = '\0'; \
99
+ strncat((buf), err_str, ((len) - 1)); \
100
+ } \
101
+ } while (0)
102
+ #endif
90
103
 
91
104
  #ifdef __cplusplus
92
105
  extern "C" {
@@ -102,55 +115,7 @@ typedef struct redisReply {
102
115
  struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
103
116
  } redisReply;
104
117
 
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
118
  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
119
 
155
120
  /* Function to free the reply objects hiredis returns by default. */
156
121
  void freeReplyObject(void *reply);
@@ -159,6 +124,9 @@ void freeReplyObject(void *reply);
159
124
  int redisvFormatCommand(char **target, const char *format, va_list ap);
160
125
  int redisFormatCommand(char **target, const char *format, ...);
161
126
  int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
127
+ int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
128
+ void redisFreeCommand(char *cmd);
129
+ void redisFreeSdsCommand(sds cmd);
162
130
 
163
131
  /* Context for a connection to Redis */
164
132
  typedef struct redisContext {
@@ -171,13 +139,20 @@ typedef struct redisContext {
171
139
  } redisContext;
172
140
 
173
141
  redisContext *redisConnect(const char *ip, int port);
174
- redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
142
+ redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
175
143
  redisContext *redisConnectNonBlock(const char *ip, int port);
144
+ redisContext *redisConnectBindNonBlock(const char *ip, int port,
145
+ const char *source_addr);
146
+ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
147
+ const char *source_addr);
176
148
  redisContext *redisConnectUnix(const char *path);
177
- redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
149
+ redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
178
150
  redisContext *redisConnectUnixNonBlock(const char *path);
179
- int redisSetTimeout(redisContext *c, struct timeval tv);
151
+ redisContext *redisConnectFd(int fd);
152
+ int redisSetTimeout(redisContext *c, const struct timeval tv);
153
+ int redisEnableKeepAlive(redisContext *c);
180
154
  void redisFree(redisContext *c);
155
+ int redisFreeKeepFd(redisContext *c);
181
156
  int redisBufferRead(redisContext *c);
182
157
  int redisBufferWrite(redisContext *c, int *done);
183
158
 
@@ -188,6 +163,10 @@ int redisBufferWrite(redisContext *c, int *done);
188
163
  int redisGetReply(redisContext *c, void **reply);
189
164
  int redisGetReplyFromReader(redisContext *c, void **reply);
190
165
 
166
+ /* Write a formatted command to the output buffer. Use these functions in blocking mode
167
+ * to get a pipeline of commands. */
168
+ int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
169
+
191
170
  /* Write a command to the output buffer. Use these functions in blocking mode
192
171
  * to get a pipeline of commands. */
193
172
  int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
@@ -54,21 +54,28 @@
54
54
  /* Defined in hiredis.c */
55
55
  void __redisSetError(redisContext *c, int type, const char *str);
56
56
 
57
+ static void redisContextCloseFd(redisContext *c) {
58
+ if (c && c->fd >= 0) {
59
+ close(c->fd);
60
+ c->fd = -1;
61
+ }
62
+ }
63
+
57
64
  static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
58
- char buf[128];
65
+ char buf[128] = { 0 };
59
66
  size_t len = 0;
60
67
 
61
68
  if (prefix != NULL)
62
69
  len = snprintf(buf,sizeof(buf),"%s: ",prefix);
63
- strerror_r(errno,buf+len,sizeof(buf)-len);
70
+ __redis_strerror_r(errno, (char *)(buf + len), sizeof(buf) - len);
64
71
  __redisSetError(c,type,buf);
65
72
  }
66
73
 
67
- static int redisSetReuseAddr(redisContext *c, int fd) {
74
+ static int redisSetReuseAddr(redisContext *c) {
68
75
  int on = 1;
69
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
76
+ if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
70
77
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
71
- close(fd);
78
+ redisContextCloseFd(c);
72
79
  return REDIS_ERR;
73
80
  }
74
81
  return REDIS_OK;
@@ -80,23 +87,24 @@ static int redisCreateSocket(redisContext *c, int type) {
80
87
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
81
88
  return REDIS_ERR;
82
89
  }
90
+ c->fd = s;
83
91
  if (type == AF_INET) {
84
- if (redisSetReuseAddr(c,s) == REDIS_ERR) {
92
+ if (redisSetReuseAddr(c) == REDIS_ERR) {
85
93
  return REDIS_ERR;
86
94
  }
87
95
  }
88
- return s;
96
+ return REDIS_OK;
89
97
  }
90
98
 
91
- static int redisSetBlocking(redisContext *c, int fd, int blocking) {
99
+ static int redisSetBlocking(redisContext *c, int blocking) {
92
100
  int flags;
93
101
 
94
102
  /* Set the socket nonblocking.
95
103
  * Note that fcntl(2) for F_GETFL and F_SETFL can't be
96
104
  * interrupted by a signal. */
97
- if ((flags = fcntl(fd, F_GETFL)) == -1) {
105
+ if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
98
106
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
99
- close(fd);
107
+ redisContextCloseFd(c);
100
108
  return REDIS_ERR;
101
109
  }
102
110
 
@@ -105,19 +113,61 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
105
113
  else
106
114
  flags |= O_NONBLOCK;
107
115
 
108
- if (fcntl(fd, F_SETFL, flags) == -1) {
116
+ if (fcntl(c->fd, F_SETFL, flags) == -1) {
109
117
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
110
- close(fd);
118
+ redisContextCloseFd(c);
119
+ return REDIS_ERR;
120
+ }
121
+ return REDIS_OK;
122
+ }
123
+
124
+ int redisKeepAlive(redisContext *c, int interval) {
125
+ int val = 1;
126
+ int fd = c->fd;
127
+
128
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
129
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
130
+ return REDIS_ERR;
131
+ }
132
+
133
+ val = interval;
134
+
135
+ #ifdef _OSX
136
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
137
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
138
+ return REDIS_ERR;
139
+ }
140
+ #else
141
+ #if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
142
+ val = interval;
143
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
144
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
145
+ return REDIS_ERR;
146
+ }
147
+
148
+ val = interval/3;
149
+ if (val == 0) val = 1;
150
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
151
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
152
+ return REDIS_ERR;
153
+ }
154
+
155
+ val = 3;
156
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
157
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
111
158
  return REDIS_ERR;
112
159
  }
160
+ #endif
161
+ #endif
162
+
113
163
  return REDIS_OK;
114
164
  }
115
165
 
116
- static int redisSetTcpNoDelay(redisContext *c, int fd) {
166
+ static int redisSetTcpNoDelay(redisContext *c) {
117
167
  int yes = 1;
118
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
168
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
119
169
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
120
- close(fd);
170
+ redisContextCloseFd(c);
121
171
  return REDIS_ERR;
122
172
  }
123
173
  return REDIS_OK;
@@ -125,18 +175,19 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
125
175
 
126
176
  #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
127
177
 
128
- static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
178
+ static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) {
129
179
  struct pollfd wfd[1];
130
180
  long msec;
131
181
 
132
182
  msec = -1;
133
- wfd[0].fd = fd;
183
+ wfd[0].fd = c->fd;
134
184
  wfd[0].events = POLLOUT;
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
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
190
+ redisContextCloseFd(c);
140
191
  return REDIS_ERR;
141
192
  }
142
193
 
@@ -152,47 +203,45 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
152
203
 
153
204
  if ((res = poll(wfd, 1, msec)) == -1) {
154
205
  __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
155
- close(fd);
206
+ redisContextCloseFd(c);
156
207
  return REDIS_ERR;
157
208
  } else if (res == 0) {
158
209
  errno = ETIMEDOUT;
159
210
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
160
- close(fd);
211
+ redisContextCloseFd(c);
161
212
  return REDIS_ERR;
162
213
  }
163
214
 
164
- if (redisCheckSocketError(c, fd) != REDIS_OK)
215
+ if (redisCheckSocketError(c) != REDIS_OK)
165
216
  return REDIS_ERR;
166
217
 
167
218
  return REDIS_OK;
168
219
  }
169
220
 
170
221
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
171
- close(fd);
222
+ redisContextCloseFd(c);
172
223
  return REDIS_ERR;
173
224
  }
174
225
 
175
- int redisCheckSocketError(redisContext *c, int fd) {
226
+ int redisCheckSocketError(redisContext *c) {
176
227
  int err = 0;
177
228
  socklen_t errlen = sizeof(err);
178
229
 
179
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
230
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
180
231
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
181
- close(fd);
182
232
  return REDIS_ERR;
183
233
  }
184
234
 
185
235
  if (err) {
186
236
  errno = err;
187
237
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
188
- close(fd);
189
238
  return REDIS_ERR;
190
239
  }
191
240
 
192
241
  return REDIS_OK;
193
242
  }
194
243
 
195
- int redisContextSetTimeout(redisContext *c, struct timeval tv) {
244
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
196
245
  if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
197
246
  __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
198
247
  return REDIS_ERR;
@@ -204,44 +253,95 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
204
253
  return REDIS_OK;
205
254
  }
206
255
 
207
- int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
208
- int s, rv;
256
+ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
257
+ const struct timeval *timeout,
258
+ const char *source_addr) {
259
+ int s, rv, n;
209
260
  char _port[6]; /* strlen("65535"); */
210
- struct addrinfo hints, *servinfo, *p;
261
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
211
262
  int blocking = (c->flags & REDIS_BLOCK);
263
+ int reuseaddr = (c->flags & REDIS_REUSEADDR);
264
+ int reuses = 0;
212
265
 
213
266
  snprintf(_port, 6, "%d", port);
214
267
  memset(&hints,0,sizeof(hints));
215
268
  hints.ai_family = AF_INET;
216
269
  hints.ai_socktype = SOCK_STREAM;
217
270
 
271
+ /* Try with IPv6 if no IPv4 address was found. We do it in this order since
272
+ * in a Redis client you can't afford to test if you have IPv6 connectivity
273
+ * as this would add latency to every connect. Otherwise a more sensible
274
+ * route could be: Use IPv6 if both addresses are available and there is IPv6
275
+ * connectivity. */
218
276
  if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
219
- __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
220
- return REDIS_ERR;
277
+ hints.ai_family = AF_INET6;
278
+ if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
279
+ __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
280
+ return REDIS_ERR;
281
+ }
221
282
  }
222
283
  for (p = servinfo; p != NULL; p = p->ai_next) {
284
+ addrretry:
223
285
  if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
224
286
  continue;
225
287
 
226
- if (redisSetBlocking(c,s,0) != REDIS_OK)
288
+ c->fd = s;
289
+ if (redisSetBlocking(c,0) != REDIS_OK)
227
290
  goto error;
291
+ if (source_addr) {
292
+ int bound = 0;
293
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
294
+ if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) {
295
+ char buf[128];
296
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
297
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
298
+ goto error;
299
+ }
300
+
301
+ if (reuseaddr) {
302
+ n = 1;
303
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
304
+ sizeof(n)) < 0) {
305
+ goto error;
306
+ }
307
+ }
308
+
309
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
310
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
311
+ bound = 1;
312
+ break;
313
+ }
314
+ }
315
+ freeaddrinfo(bservinfo);
316
+ if (!bound) {
317
+ char buf[128];
318
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
319
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
320
+ goto error;
321
+ }
322
+ }
228
323
  if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
229
324
  if (errno == EHOSTUNREACH) {
230
- close(s);
325
+ redisContextCloseFd(c);
231
326
  continue;
232
327
  } else if (errno == EINPROGRESS && !blocking) {
233
328
  /* This is ok. */
329
+ } else if (errno == EADDRNOTAVAIL && reuseaddr) {
330
+ if (++reuses >= REDIS_CONNECT_RETRIES) {
331
+ goto error;
332
+ } else {
333
+ goto addrretry;
334
+ }
234
335
  } else {
235
- if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
336
+ if (redisContextWaitReady(c,timeout) != REDIS_OK)
236
337
  goto error;
237
338
  }
238
339
  }
239
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
340
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
240
341
  goto error;
241
- if (redisSetTcpNoDelay(c,s) != REDIS_OK)
342
+ if (redisSetTcpNoDelay(c) != REDIS_OK)
242
343
  goto error;
243
344
 
244
- c->fd = s;
245
345
  c->flags |= REDIS_CONNECTED;
246
346
  rv = REDIS_OK;
247
347
  goto end;
@@ -260,32 +360,41 @@ end:
260
360
  return rv; // Need to return REDIS_OK if alright
261
361
  }
262
362
 
263
- int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
264
- int s;
363
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port,
364
+ const struct timeval *timeout) {
365
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
366
+ }
367
+
368
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
369
+ const struct timeval *timeout,
370
+ const char *source_addr) {
371
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
372
+ }
373
+
374
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
265
375
  int blocking = (c->flags & REDIS_BLOCK);
266
376
  struct sockaddr_un sa;
267
377
 
268
- if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
378
+ if (redisCreateSocket(c,AF_LOCAL) < 0)
269
379
  return REDIS_ERR;
270
- if (redisSetBlocking(c,s,0) != REDIS_OK)
380
+ if (redisSetBlocking(c,0) != REDIS_OK)
271
381
  return REDIS_ERR;
272
382
 
273
383
  sa.sun_family = AF_LOCAL;
274
384
  strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
275
- if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
385
+ if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
276
386
  if (errno == EINPROGRESS && !blocking) {
277
387
  /* This is ok. */
278
388
  } else {
279
- if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
389
+ if (redisContextWaitReady(c,timeout) != REDIS_OK)
280
390
  return REDIS_ERR;
281
391
  }
282
392
  }
283
393
 
284
394
  /* Reset socket to be blocking after connect(2). */
285
- if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
395
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
286
396
  return REDIS_ERR;
287
397
 
288
- c->fd = s;
289
398
  c->flags |= REDIS_CONNECTED;
290
399
  return REDIS_OK;
291
400
  }