hiredis 0.5.2 → 0.6.0

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