hiredis-futureproof 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +28 -0
  3. data/Rakefile +53 -0
  4. data/ext/hiredis_ext/connection.c +611 -0
  5. data/ext/hiredis_ext/extconf.rb +48 -0
  6. data/ext/hiredis_ext/hiredis_ext.c +15 -0
  7. data/ext/hiredis_ext/hiredis_ext.h +44 -0
  8. data/ext/hiredis_ext/reader.c +124 -0
  9. data/lib/hiredis/connection.rb +10 -0
  10. data/lib/hiredis/ext/connection.rb +29 -0
  11. data/lib/hiredis/ext/reader.rb +2 -0
  12. data/lib/hiredis/reader.rb +10 -0
  13. data/lib/hiredis/ruby/connection.rb +316 -0
  14. data/lib/hiredis/ruby/reader.rb +183 -0
  15. data/lib/hiredis/version.rb +3 -0
  16. data/lib/hiredis.rb +2 -0
  17. data/vendor/hiredis/COPYING +29 -0
  18. data/vendor/hiredis/Makefile +308 -0
  19. data/vendor/hiredis/alloc.c +86 -0
  20. data/vendor/hiredis/alloc.h +91 -0
  21. data/vendor/hiredis/async.c +892 -0
  22. data/vendor/hiredis/async.h +147 -0
  23. data/vendor/hiredis/async_private.h +75 -0
  24. data/vendor/hiredis/dict.c +352 -0
  25. data/vendor/hiredis/dict.h +126 -0
  26. data/vendor/hiredis/fmacros.h +12 -0
  27. data/vendor/hiredis/hiredis.c +1173 -0
  28. data/vendor/hiredis/hiredis.h +336 -0
  29. data/vendor/hiredis/hiredis_ssl.h +127 -0
  30. data/vendor/hiredis/net.c +612 -0
  31. data/vendor/hiredis/net.h +56 -0
  32. data/vendor/hiredis/read.c +739 -0
  33. data/vendor/hiredis/read.h +129 -0
  34. data/vendor/hiredis/sds.c +1289 -0
  35. data/vendor/hiredis/sds.h +278 -0
  36. data/vendor/hiredis/sdsalloc.h +44 -0
  37. data/vendor/hiredis/sockcompat.c +248 -0
  38. data/vendor/hiredis/sockcompat.h +92 -0
  39. data/vendor/hiredis/ssl.c +526 -0
  40. data/vendor/hiredis/test.c +1387 -0
  41. data/vendor/hiredis/win32.h +56 -0
  42. metadata +128 -0
@@ -0,0 +1,612 @@
1
+ /* Extracted from anet.c to work properly with Hiredis error reporting.
2
+ *
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>
7
+ *
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without
11
+ * modification, are permitted provided that the following conditions are met:
12
+ *
13
+ * * Redistributions of source code must retain the above copyright notice,
14
+ * this list of conditions and the following disclaimer.
15
+ * * Redistributions in binary form must reproduce the above copyright
16
+ * notice, this list of conditions and the following disclaimer in the
17
+ * documentation and/or other materials provided with the distribution.
18
+ * * Neither the name of Redis nor the names of its contributors may be used
19
+ * to endorse or promote products derived from this software without
20
+ * specific prior written permission.
21
+ *
22
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ * POSSIBILITY OF SUCH DAMAGE.
33
+ */
34
+
35
+ #include "fmacros.h"
36
+ #include <sys/types.h>
37
+ #include <fcntl.h>
38
+ #include <string.h>
39
+ #include <errno.h>
40
+ #include <stdarg.h>
41
+ #include <stdio.h>
42
+ #include <limits.h>
43
+ #include <stdlib.h>
44
+
45
+ #include "net.h"
46
+ #include "sds.h"
47
+ #include "sockcompat.h"
48
+ #include "win32.h"
49
+
50
+ /* Defined in hiredis.c */
51
+ void __redisSetError(redisContext *c, int type, const char *str);
52
+
53
+ void redisNetClose(redisContext *c) {
54
+ if (c && c->fd != REDIS_INVALID_FD) {
55
+ close(c->fd);
56
+ c->fd = REDIS_INVALID_FD;
57
+ }
58
+ }
59
+
60
+ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
61
+ ssize_t nread = recv(c->fd, buf, bufcap, 0);
62
+ if (nread == -1) {
63
+ if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
64
+ /* Try again later */
65
+ return 0;
66
+ } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {
67
+ /* especially in windows */
68
+ __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
69
+ return -1;
70
+ } else {
71
+ __redisSetError(c, REDIS_ERR_IO, NULL);
72
+ return -1;
73
+ }
74
+ } else if (nread == 0) {
75
+ __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
76
+ return -1;
77
+ } else {
78
+ return nread;
79
+ }
80
+ }
81
+
82
+ ssize_t redisNetWrite(redisContext *c) {
83
+ ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);
84
+ if (nwritten < 0) {
85
+ if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
86
+ /* Try again later */
87
+ } else {
88
+ __redisSetError(c, REDIS_ERR_IO, NULL);
89
+ return -1;
90
+ }
91
+ }
92
+ return nwritten;
93
+ }
94
+
95
+ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
96
+ int errorno = errno; /* snprintf() may change errno */
97
+ char buf[128] = { 0 };
98
+ size_t len = 0;
99
+
100
+ if (prefix != NULL)
101
+ len = snprintf(buf,sizeof(buf),"%s: ",prefix);
102
+ strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
103
+ __redisSetError(c,type,buf);
104
+ }
105
+
106
+ static int redisSetReuseAddr(redisContext *c) {
107
+ int on = 1;
108
+ if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
109
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
110
+ redisNetClose(c);
111
+ return REDIS_ERR;
112
+ }
113
+ return REDIS_OK;
114
+ }
115
+
116
+ static int redisCreateSocket(redisContext *c, int type) {
117
+ redisFD s;
118
+ if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {
119
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
120
+ return REDIS_ERR;
121
+ }
122
+ c->fd = s;
123
+ if (type == AF_INET) {
124
+ if (redisSetReuseAddr(c) == REDIS_ERR) {
125
+ return REDIS_ERR;
126
+ }
127
+ }
128
+ return REDIS_OK;
129
+ }
130
+
131
+ static int redisSetBlocking(redisContext *c, int blocking) {
132
+ #ifndef _WIN32
133
+ int flags;
134
+
135
+ /* Set the socket nonblocking.
136
+ * Note that fcntl(2) for F_GETFL and F_SETFL can't be
137
+ * interrupted by a signal. */
138
+ if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
139
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
140
+ redisNetClose(c);
141
+ return REDIS_ERR;
142
+ }
143
+
144
+ if (blocking)
145
+ flags &= ~O_NONBLOCK;
146
+ else
147
+ flags |= O_NONBLOCK;
148
+
149
+ if (fcntl(c->fd, F_SETFL, flags) == -1) {
150
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
151
+ redisNetClose(c);
152
+ return REDIS_ERR;
153
+ }
154
+ #else
155
+ u_long mode = blocking ? 0 : 1;
156
+ if (ioctl(c->fd, FIONBIO, &mode) == -1) {
157
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)");
158
+ redisNetClose(c);
159
+ return REDIS_ERR;
160
+ }
161
+ #endif /* _WIN32 */
162
+ return REDIS_OK;
163
+ }
164
+
165
+ int redisKeepAlive(redisContext *c, int interval) {
166
+ int val = 1;
167
+ redisFD fd = c->fd;
168
+
169
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
170
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
171
+ return REDIS_ERR;
172
+ }
173
+
174
+ val = interval;
175
+
176
+ #if defined(__APPLE__) && defined(__MACH__)
177
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
178
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
179
+ return REDIS_ERR;
180
+ }
181
+ #else
182
+ #if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
183
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
184
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
185
+ return REDIS_ERR;
186
+ }
187
+
188
+ val = interval/3;
189
+ if (val == 0) val = 1;
190
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
191
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
192
+ return REDIS_ERR;
193
+ }
194
+
195
+ val = 3;
196
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
197
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
198
+ return REDIS_ERR;
199
+ }
200
+ #endif
201
+ #endif
202
+
203
+ return REDIS_OK;
204
+ }
205
+
206
+ int redisSetTcpNoDelay(redisContext *c) {
207
+ int yes = 1;
208
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
209
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
210
+ redisNetClose(c);
211
+ return REDIS_ERR;
212
+ }
213
+ return REDIS_OK;
214
+ }
215
+
216
+ #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
217
+
218
+ static int redisContextTimeoutMsec(redisContext *c, long *result)
219
+ {
220
+ const struct timeval *timeout = c->connect_timeout;
221
+ long msec = -1;
222
+
223
+ /* Only use timeout when not NULL. */
224
+ if (timeout != NULL) {
225
+ if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
226
+ *result = msec;
227
+ return REDIS_ERR;
228
+ }
229
+
230
+ msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
231
+
232
+ if (msec < 0 || msec > INT_MAX) {
233
+ msec = INT_MAX;
234
+ }
235
+ }
236
+
237
+ *result = msec;
238
+ return REDIS_OK;
239
+ }
240
+
241
+ static int redisContextWaitReady(redisContext *c, long msec) {
242
+ struct pollfd wfd[1];
243
+
244
+ wfd[0].fd = c->fd;
245
+ wfd[0].events = POLLOUT;
246
+
247
+ if (errno == EINPROGRESS) {
248
+ int res;
249
+
250
+ if ((res = poll(wfd, 1, msec)) == -1) {
251
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
252
+ redisNetClose(c);
253
+ return REDIS_ERR;
254
+ } else if (res == 0) {
255
+ errno = ETIMEDOUT;
256
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
257
+ redisNetClose(c);
258
+ return REDIS_ERR;
259
+ }
260
+
261
+ if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
262
+ redisCheckSocketError(c);
263
+ return REDIS_ERR;
264
+ }
265
+
266
+ return REDIS_OK;
267
+ }
268
+
269
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
270
+ redisNetClose(c);
271
+ return REDIS_ERR;
272
+ }
273
+
274
+ int redisCheckConnectDone(redisContext *c, int *completed) {
275
+ int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);
276
+ if (rc == 0) {
277
+ *completed = 1;
278
+ return REDIS_OK;
279
+ }
280
+ switch (errno) {
281
+ case EISCONN:
282
+ *completed = 1;
283
+ return REDIS_OK;
284
+ case EALREADY:
285
+ case EINPROGRESS:
286
+ case EWOULDBLOCK:
287
+ *completed = 0;
288
+ return REDIS_OK;
289
+ default:
290
+ return REDIS_ERR;
291
+ }
292
+ }
293
+
294
+ int redisCheckSocketError(redisContext *c) {
295
+ int err = 0, errno_saved = errno;
296
+ socklen_t errlen = sizeof(err);
297
+
298
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
299
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
300
+ return REDIS_ERR;
301
+ }
302
+
303
+ if (err == 0) {
304
+ err = errno_saved;
305
+ }
306
+
307
+ if (err) {
308
+ errno = err;
309
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
310
+ return REDIS_ERR;
311
+ }
312
+
313
+ return REDIS_OK;
314
+ }
315
+
316
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
317
+ const void *to_ptr = &tv;
318
+ size_t to_sz = sizeof(tv);
319
+
320
+ if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
321
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
322
+ return REDIS_ERR;
323
+ }
324
+ if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
325
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
326
+ return REDIS_ERR;
327
+ }
328
+ return REDIS_OK;
329
+ }
330
+
331
+ int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {
332
+ /* Same timeval struct, short circuit */
333
+ if (c->connect_timeout == timeout)
334
+ return REDIS_OK;
335
+
336
+ /* Allocate context timeval if we need to */
337
+ if (c->connect_timeout == NULL) {
338
+ c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
339
+ if (c->connect_timeout == NULL)
340
+ return REDIS_ERR;
341
+ }
342
+
343
+ memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
344
+ return REDIS_OK;
345
+ }
346
+
347
+ int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {
348
+ /* Same timeval struct, short circuit */
349
+ if (c->command_timeout == timeout)
350
+ return REDIS_OK;
351
+
352
+ /* Allocate context timeval if we need to */
353
+ if (c->command_timeout == NULL) {
354
+ c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
355
+ if (c->command_timeout == NULL)
356
+ return REDIS_ERR;
357
+ }
358
+
359
+ memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
360
+ return REDIS_OK;
361
+ }
362
+
363
+ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
364
+ const struct timeval *timeout,
365
+ const char *source_addr) {
366
+ redisFD s;
367
+ int rv, n;
368
+ char _port[6]; /* strlen("65535"); */
369
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
370
+ int blocking = (c->flags & REDIS_BLOCK);
371
+ int reuseaddr = (c->flags & REDIS_REUSEADDR);
372
+ int reuses = 0;
373
+ long timeout_msec = -1;
374
+
375
+ servinfo = NULL;
376
+ c->connection_type = REDIS_CONN_TCP;
377
+ c->tcp.port = port;
378
+
379
+ /* We need to take possession of the passed parameters
380
+ * to make them reusable for a reconnect.
381
+ * We also carefully check we don't free data we already own,
382
+ * as in the case of the reconnect method.
383
+ *
384
+ * This is a bit ugly, but atleast it works and doesn't leak memory.
385
+ **/
386
+ if (c->tcp.host != addr) {
387
+ hi_free(c->tcp.host);
388
+
389
+ c->tcp.host = hi_strdup(addr);
390
+ if (c->tcp.host == NULL)
391
+ goto oom;
392
+ }
393
+
394
+ if (timeout) {
395
+ if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
396
+ goto oom;
397
+ } else {
398
+ hi_free(c->connect_timeout);
399
+ c->connect_timeout = NULL;
400
+ }
401
+
402
+ if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
403
+ __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
404
+ goto error;
405
+ }
406
+
407
+ if (source_addr == NULL) {
408
+ hi_free(c->tcp.source_addr);
409
+ c->tcp.source_addr = NULL;
410
+ } else if (c->tcp.source_addr != source_addr) {
411
+ hi_free(c->tcp.source_addr);
412
+ c->tcp.source_addr = hi_strdup(source_addr);
413
+ }
414
+
415
+ snprintf(_port, 6, "%d", port);
416
+ memset(&hints,0,sizeof(hints));
417
+ hints.ai_family = AF_INET;
418
+ hints.ai_socktype = SOCK_STREAM;
419
+
420
+ /* Try with IPv6 if no IPv4 address was found. We do it in this order since
421
+ * in a Redis client you can't afford to test if you have IPv6 connectivity
422
+ * as this would add latency to every connect. Otherwise a more sensible
423
+ * route could be: Use IPv6 if both addresses are available and there is IPv6
424
+ * connectivity. */
425
+ if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
426
+ hints.ai_family = AF_INET6;
427
+ if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
428
+ __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
429
+ return REDIS_ERR;
430
+ }
431
+ }
432
+ for (p = servinfo; p != NULL; p = p->ai_next) {
433
+ addrretry:
434
+ if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)
435
+ continue;
436
+
437
+ c->fd = s;
438
+ if (redisSetBlocking(c,0) != REDIS_OK)
439
+ goto error;
440
+ if (c->tcp.source_addr) {
441
+ int bound = 0;
442
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
443
+ if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
444
+ char buf[128];
445
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
446
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
447
+ goto error;
448
+ }
449
+
450
+ if (reuseaddr) {
451
+ n = 1;
452
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
453
+ sizeof(n)) < 0) {
454
+ freeaddrinfo(bservinfo);
455
+ goto error;
456
+ }
457
+ }
458
+
459
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
460
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
461
+ bound = 1;
462
+ break;
463
+ }
464
+ }
465
+ freeaddrinfo(bservinfo);
466
+ if (!bound) {
467
+ char buf[128];
468
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
469
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
470
+ goto error;
471
+ }
472
+ }
473
+
474
+ /* For repeat connection */
475
+ hi_free(c->saddr);
476
+ c->saddr = hi_malloc(p->ai_addrlen);
477
+ if (c->saddr == NULL)
478
+ goto oom;
479
+
480
+ memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
481
+ c->addrlen = p->ai_addrlen;
482
+
483
+ if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
484
+ if (errno == EHOSTUNREACH) {
485
+ redisNetClose(c);
486
+ continue;
487
+ } else if (errno == EINPROGRESS) {
488
+ if (blocking) {
489
+ goto wait_for_ready;
490
+ }
491
+ /* This is ok.
492
+ * Note that even when it's in blocking mode, we unset blocking
493
+ * for `connect()`
494
+ */
495
+ } else if (errno == EADDRNOTAVAIL && reuseaddr) {
496
+ if (++reuses >= REDIS_CONNECT_RETRIES) {
497
+ goto error;
498
+ } else {
499
+ redisNetClose(c);
500
+ goto addrretry;
501
+ }
502
+ } else {
503
+ wait_for_ready:
504
+ if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
505
+ goto error;
506
+ if (redisSetTcpNoDelay(c) != REDIS_OK)
507
+ goto error;
508
+ }
509
+ }
510
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
511
+ goto error;
512
+
513
+ c->flags |= REDIS_CONNECTED;
514
+ rv = REDIS_OK;
515
+ goto end;
516
+ }
517
+ if (p == NULL) {
518
+ char buf[128];
519
+ snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
520
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
521
+ goto error;
522
+ }
523
+
524
+ oom:
525
+ __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
526
+ error:
527
+ rv = REDIS_ERR;
528
+ end:
529
+ if(servinfo) {
530
+ freeaddrinfo(servinfo);
531
+ }
532
+
533
+ return rv; // Need to return REDIS_OK if alright
534
+ }
535
+
536
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port,
537
+ const struct timeval *timeout) {
538
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
539
+ }
540
+
541
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
542
+ const struct timeval *timeout,
543
+ const char *source_addr) {
544
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
545
+ }
546
+
547
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
548
+ #ifndef _WIN32
549
+ int blocking = (c->flags & REDIS_BLOCK);
550
+ struct sockaddr_un *sa;
551
+ long timeout_msec = -1;
552
+
553
+ if (redisCreateSocket(c,AF_UNIX) < 0)
554
+ return REDIS_ERR;
555
+ if (redisSetBlocking(c,0) != REDIS_OK)
556
+ return REDIS_ERR;
557
+
558
+ c->connection_type = REDIS_CONN_UNIX;
559
+ if (c->unix_sock.path != path) {
560
+ hi_free(c->unix_sock.path);
561
+
562
+ c->unix_sock.path = hi_strdup(path);
563
+ if (c->unix_sock.path == NULL)
564
+ goto oom;
565
+ }
566
+
567
+ if (timeout) {
568
+ if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
569
+ goto oom;
570
+ } else {
571
+ hi_free(c->connect_timeout);
572
+ c->connect_timeout = NULL;
573
+ }
574
+
575
+ if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
576
+ return REDIS_ERR;
577
+
578
+ /* Don't leak sockaddr if we're reconnecting */
579
+ if (c->saddr) hi_free(c->saddr);
580
+
581
+ sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));
582
+ if (sa == NULL)
583
+ goto oom;
584
+
585
+ c->addrlen = sizeof(struct sockaddr_un);
586
+ sa->sun_family = AF_UNIX;
587
+ strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
588
+ if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {
589
+ if (errno == EINPROGRESS && !blocking) {
590
+ /* This is ok. */
591
+ } else {
592
+ if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
593
+ return REDIS_ERR;
594
+ }
595
+ }
596
+
597
+ /* Reset socket to be blocking after connect(2). */
598
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
599
+ return REDIS_ERR;
600
+
601
+ c->flags |= REDIS_CONNECTED;
602
+ return REDIS_OK;
603
+ #else
604
+ /* We currently do not support Unix sockets for Windows. */
605
+ /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */
606
+ errno = EPROTONOSUPPORT;
607
+ return REDIS_ERR;
608
+ #endif /* _WIN32 */
609
+ oom:
610
+ __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
611
+ return REDIS_ERR;
612
+ }
@@ -0,0 +1,56 @@
1
+ /* Extracted from anet.c to work properly with Hiredis error reporting.
2
+ *
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>
7
+ *
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without
11
+ * modification, are permitted provided that the following conditions are met:
12
+ *
13
+ * * Redistributions of source code must retain the above copyright notice,
14
+ * this list of conditions and the following disclaimer.
15
+ * * Redistributions in binary form must reproduce the above copyright
16
+ * notice, this list of conditions and the following disclaimer in the
17
+ * documentation and/or other materials provided with the distribution.
18
+ * * Neither the name of Redis nor the names of its contributors may be used
19
+ * to endorse or promote products derived from this software without
20
+ * specific prior written permission.
21
+ *
22
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ * POSSIBILITY OF SUCH DAMAGE.
33
+ */
34
+
35
+ #ifndef __NET_H
36
+ #define __NET_H
37
+
38
+ #include "hiredis.h"
39
+
40
+ void redisNetClose(redisContext *c);
41
+ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap);
42
+ ssize_t redisNetWrite(redisContext *c);
43
+
44
+ int redisCheckSocketError(redisContext *c);
45
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv);
46
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
47
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
48
+ const struct timeval *timeout,
49
+ const char *source_addr);
50
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
51
+ int redisKeepAlive(redisContext *c, int interval);
52
+ int redisCheckConnectDone(redisContext *c, int *completed);
53
+
54
+ int redisSetTcpNoDelay(redisContext *c);
55
+
56
+ #endif