redis-client 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +1 -2
  4. data/Gemfile.lock +1 -2
  5. data/README.md +5 -9
  6. data/Rakefile +41 -22
  7. data/lib/redis_client/version.rb +1 -1
  8. data/lib/redis_client.rb +1 -6
  9. data/redis-client.gemspec +2 -4
  10. metadata +6 -56
  11. data/.rubocop.yml +0 -191
  12. data/ext/redis_client/hiredis/export.clang +0 -2
  13. data/ext/redis_client/hiredis/export.gcc +0 -7
  14. data/ext/redis_client/hiredis/extconf.rb +0 -69
  15. data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
  16. data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
  17. data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
  18. data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
  19. data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
  20. data/ext/redis_client/hiredis/vendor/COPYING +0 -29
  21. data/ext/redis_client/hiredis/vendor/Makefile +0 -308
  22. data/ext/redis_client/hiredis/vendor/README.md +0 -664
  23. data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
  24. data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
  25. data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
  26. data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
  27. data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
  28. data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
  29. data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
  30. data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
  31. data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
  32. data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
  33. data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
  34. data/ext/redis_client/hiredis/vendor/async.c +0 -887
  35. data/ext/redis_client/hiredis/vendor/async.h +0 -147
  36. data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
  37. data/ext/redis_client/hiredis/vendor/dict.c +0 -352
  38. data/ext/redis_client/hiredis/vendor/dict.h +0 -126
  39. data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
  40. data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
  41. data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
  42. data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
  43. data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
  44. data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
  45. data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
  46. data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
  47. data/ext/redis_client/hiredis/vendor/net.c +0 -612
  48. data/ext/redis_client/hiredis/vendor/net.h +0 -56
  49. data/ext/redis_client/hiredis/vendor/read.c +0 -739
  50. data/ext/redis_client/hiredis/vendor/read.h +0 -129
  51. data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
  52. data/ext/redis_client/hiredis/vendor/sds.h +0 -278
  53. data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
  54. data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
  55. data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
  56. data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
  57. data/ext/redis_client/hiredis/vendor/test.c +0 -1401
  58. data/ext/redis_client/hiredis/vendor/test.sh +0 -78
  59. data/ext/redis_client/hiredis/vendor/win32.h +0 -56
  60. data/lib/redis_client/hiredis_connection.rb +0 -93
@@ -1,708 +0,0 @@
1
- // Extensive parts of this code is taken from `hiredis-rb`, so we're keeping the
2
- // initial copyright:
3
- //
4
- // Copyright (c) 2010-2012, Pieter Noordhuis
5
- //
6
- // All rights reserved.
7
- //
8
- // Redistribution and use in source and binary forms, with or without
9
- // modification, are permitted provided that the following conditions are met:
10
- //
11
- // * Redistributions of source code must retain the above copyright notice, this
12
- // list of conditions and the following disclaimer.
13
- //
14
- // * Redistributions in binary form must reproduce the above copyright notice,
15
- // this list of conditions and the following disclaimer in the documentation
16
- // and/or other materials provided with the distribution.
17
- //
18
- // * Neither the name of Redis nor the names of its contributors may be used to
19
- // endorse or promote products derived from this software without specific prior
20
- // written permission.
21
- //
22
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23
- // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24
- // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
- // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26
- // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27
- // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28
- // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29
- // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
- // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
-
33
- #include "ruby.h"
34
- #include "ruby/encoding.h"
35
- #include <errno.h>
36
- #include <sys/socket.h>
37
- #include <stdbool.h>
38
- #include "hiredis.h"
39
- #include "hiredis_ssl.h"
40
-
41
- #if !defined(HAVE_RB_HASH_NEW_CAPA)
42
- static inline VALUE rb_hash_new_capa(long capa)
43
- {
44
- return rb_hash_new();
45
- }
46
- #endif
47
-
48
- static VALUE rb_cSet, rb_eRedisClientCommandError, rb_eRedisClientConnectionError;
49
- static VALUE rb_eRedisClientConnectTimeoutError, rb_eRedisClientReadTimeoutError, rb_eRedisClientWriteTimeoutError;
50
- static ID id_parse, id_add, id_new;
51
-
52
- typedef struct {
53
- redisSSLContext *context;
54
- } hiredis_ssl_context_t;
55
-
56
- #define ENSURE_CONNECTED(connection) if (!connection->context) rb_raise(rb_eRuntimeError, "[BUG] not connected");
57
-
58
- #define SSL_CONTEXT(from, name) \
59
- hiredis_ssl_context_t *name = NULL; \
60
- TypedData_Get_Struct(from, hiredis_ssl_context_t, &hiredis_ssl_context_data_type, name); \
61
- if(name == NULL) { \
62
- rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \
63
- }
64
-
65
- void hiredis_ssl_context_mark(void *ptr) { }
66
-
67
- void hiredis_ssl_context_free(void *ptr) {
68
- hiredis_ssl_context_t *ssl_context = (hiredis_ssl_context_t *)ptr;
69
- if (ssl_context->context) {
70
- redisFreeSSLContext(ssl_context->context);
71
- ssl_context = NULL;
72
- }
73
- }
74
-
75
- static size_t hiredis_ssl_context_memsize(const void *ptr) {
76
- size_t size = sizeof(hiredis_ssl_context_t);
77
- // Note: I couldn't find a way to measure the SSLContext size.
78
- return size;
79
- }
80
-
81
- static const rb_data_type_t hiredis_ssl_context_data_type = {
82
- .wrap_struct_name = "redis-client:hiredis_ssl_context",
83
- .function = {
84
- .dmark = hiredis_ssl_context_mark,
85
- .dfree = hiredis_ssl_context_free,
86
- .dsize = hiredis_ssl_context_memsize,
87
- #ifdef HAS_GC_COMPACT
88
- .dcompact = NULL
89
- #endif
90
- },
91
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
92
- };
93
-
94
- static VALUE hiredis_ssl_context_alloc(VALUE klass) {
95
- hiredis_ssl_context_t *ssl_context;
96
- return TypedData_Make_Struct(klass, hiredis_ssl_context_t, &hiredis_ssl_context_data_type, ssl_context);
97
- }
98
-
99
- static VALUE hiredis_ssl_context_init(VALUE self, VALUE ca_file, VALUE ca_path, VALUE cert, VALUE key, VALUE hostname) {
100
- redisSSLContextError ssl_error = 0;
101
- SSL_CONTEXT(self, ssl_context);
102
-
103
- ssl_context->context = redisCreateSSLContext(
104
- RTEST(ca_file) ? StringValueCStr(ca_file) : NULL,
105
- RTEST(ca_path) ? StringValueCStr(ca_path) : NULL,
106
- RTEST(cert) ? StringValueCStr(cert) : NULL,
107
- RTEST(key) ? StringValueCStr(key) : NULL,
108
- RTEST(hostname) ? StringValueCStr(hostname) : NULL,
109
- &ssl_error
110
- );
111
-
112
- if (ssl_error) {
113
- return rb_str_new_cstr(redisSSLContextGetError(ssl_error));
114
- }
115
-
116
- if (!ssl_context->context) {
117
- return rb_str_new_cstr("Unknown error while creating SSLContext");
118
- }
119
-
120
- return Qnil;
121
- }
122
-
123
- static void *reply_append(const redisReadTask *task, VALUE value) {
124
- if (task && task->parent) {
125
- VALUE parent = (VALUE)task->parent->obj;
126
-
127
- switch (task->parent->type) {
128
- case REDIS_REPLY_ARRAY:
129
- case REDIS_REPLY_PUSH:
130
- rb_ary_store(parent, task->idx, value);
131
- break;
132
- case REDIS_REPLY_MAP:
133
- if (task->idx % 2) {
134
- VALUE key = (VALUE)task->parent->privdata;
135
- task->parent->privdata = NULL;
136
- rb_hash_aset(parent, key, value);
137
- } else {
138
- task->parent->privdata = (void*)value;
139
- }
140
- break;
141
- case REDIS_REPLY_SET:
142
- rb_funcall(parent, id_add, 1, value);
143
- break;
144
- default:
145
- rb_bug("[hiredis] Unexpected task parent type %d", task->parent->type);
146
- break;
147
- }
148
- }
149
- return (void*)value;
150
- }
151
-
152
- static void *reply_create_string(const redisReadTask *task, char *cstr, size_t len) {
153
- VALUE string = rb_external_str_new(cstr, len);
154
- if (rb_enc_str_coderange(string) == ENC_CODERANGE_BROKEN) {
155
- rb_enc_associate(string, rb_ascii8bit_encoding());
156
- }
157
-
158
- if (task->type == REDIS_REPLY_STATUS) {
159
- rb_str_freeze(string);
160
- }
161
-
162
- if (task->type == REDIS_REPLY_ERROR) {
163
- string = rb_funcall(rb_eRedisClientCommandError, id_parse, 1, string);
164
- }
165
-
166
- return reply_append(task, string);
167
- }
168
- static void *reply_create_array(const redisReadTask *task, size_t elements) {
169
- VALUE value = Qnil;
170
- switch (task->type) {
171
- case REDIS_REPLY_PUSH:
172
- case REDIS_REPLY_ARRAY:
173
- value = rb_ary_new_capa(elements);
174
- break;
175
- case REDIS_REPLY_MAP:
176
- value = rb_hash_new_capa(elements / 2);
177
- break;
178
- case REDIS_REPLY_SET:
179
- value = rb_funcallv(rb_cSet, id_new, 0, NULL);
180
- break;
181
- default:
182
- rb_bug("[hiredis] Unexpected create array type %d", task->parent->type);
183
- break;
184
- }
185
-
186
- return reply_append(task, value);
187
- }
188
- static void *reply_create_integer(const redisReadTask *task, long long value) {
189
- return reply_append(task, LL2NUM(value));
190
- }
191
- static void *reply_create_double(const redisReadTask *task, double value, char *str, size_t len) {
192
- return reply_append(task, DBL2NUM(value));
193
- }
194
- static void *reply_create_nil(const redisReadTask *task) {
195
- return reply_append(task, Qnil);
196
- }
197
- static void *reply_create_bool(const redisReadTask *task, int bval) {
198
- reply_append(task, bval ? Qtrue : Qfalse);
199
- // Qfalse == NULL, so we can't return Qfalse as it would be interpreted as out of memory error.
200
- // So we return Qnil instead.
201
- return (void*)(bval ? Qtrue : Qnil);
202
- }
203
-
204
- static void reply_free(void *ptr) {
205
- // we let GC handle it.
206
- }
207
-
208
- /* Default set of functions to build the reply. Keep in mind that such a
209
- * function returning NULL is interpreted as OOM. */
210
- static redisReplyObjectFunctions reply_functions = {
211
- reply_create_string,
212
- reply_create_array,
213
- reply_create_integer,
214
- reply_create_double,
215
- reply_create_nil,
216
- reply_create_bool,
217
- reply_free,
218
- };
219
-
220
- #define CONNECTION(from, name) \
221
- hiredis_connection_t *name = NULL; \
222
- TypedData_Get_Struct(from, hiredis_connection_t, &hiredis_connection_data_type, name); \
223
- if(name == NULL) { \
224
- rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \
225
- }
226
-
227
-
228
- typedef struct {
229
- redisContext *context;
230
- struct timeval connect_timeout;
231
- struct timeval read_timeout;
232
- struct timeval write_timeout;
233
- } hiredis_connection_t;
234
-
235
- void hiredis_connection_mark_task(redisReadTask *task) {
236
- while (task) {
237
- if (task->obj) { rb_gc_mark((VALUE)task->obj); }
238
- if (task->privdata) { rb_gc_mark((VALUE)task->privdata); }
239
- task = task->parent;
240
- }
241
- }
242
-
243
- void hiredis_connection_mark(void *ptr) {
244
- hiredis_connection_t *connection = ptr;
245
- if (connection->context) {
246
- redisReader *reader = connection->context->reader;
247
- for (int index = 0; index < reader->tasks; index++) {
248
- hiredis_connection_mark_task(reader->task[index]);
249
- }
250
- }
251
- }
252
- void hiredis_connection_free(void *ptr) {
253
- hiredis_connection_t *connection = ptr;
254
- if (connection) {
255
- if (connection->context) {
256
- redisFree(connection->context);
257
- }
258
- xfree(connection);
259
- }
260
- }
261
-
262
- static size_t hiredis_connection_task_memsize(redisReadTask *task) {
263
- size_t size = 0;
264
- while (task) {
265
- size += sizeof(redisReadTask);
266
- task = task->parent;
267
- }
268
- return size;
269
- }
270
-
271
- static size_t hiredis_connection_memsize(const void *ptr) {
272
- hiredis_connection_t *connection = (hiredis_connection_t *)ptr;
273
-
274
- size_t size = sizeof(hiredis_connection_t);
275
-
276
- if (connection->context) {
277
- size += sizeof(redisContext);
278
- if (connection->context->reader) {
279
- redisReader *reader = connection->context->reader;
280
- size += sizeof(redisReader);
281
- size += reader->maxbuf;
282
-
283
- for (int index = 0; index < reader->tasks; index++) {
284
- size += hiredis_connection_task_memsize(reader->task[index]);
285
- }
286
- }
287
- }
288
-
289
- return size;
290
- }
291
-
292
- static const rb_data_type_t hiredis_connection_data_type = {
293
- .wrap_struct_name = "redis-client:hiredis_connection",
294
- .function = {
295
- .dmark = hiredis_connection_mark,
296
- .dfree = hiredis_connection_free,
297
- .dsize = hiredis_connection_memsize,
298
- #ifdef HAS_GC_COMPACT
299
- .dcompact = NULL
300
- #endif
301
- },
302
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
303
- };
304
-
305
- static VALUE hiredis_alloc(VALUE klass) {
306
- hiredis_connection_t *connection;
307
- return TypedData_Make_Struct(klass, hiredis_connection_t, &hiredis_connection_data_type, connection);
308
- }
309
-
310
- static inline void redis_raise_error_and_disconnect(redisContext *context, VALUE timeout_error) {
311
- if (!context) return;
312
-
313
- int err = context->err;
314
- char errstr[128];
315
- MEMCPY(context->errstr, errstr, char, 128);
316
- redisFree(context);
317
-
318
- // OpenSSL bug: The SSL_ERROR_SYSCALL with errno value of 0 indicates unexpected EOF from the peer.
319
- if (errno == EAGAIN || (err == REDIS_ERR_IO && errno == 0)) {
320
- errno = 0;
321
- rb_raise(timeout_error, "Resource temporarily unavailable");
322
- }
323
-
324
- switch(err) {
325
- case REDIS_ERR_IO:
326
- rb_sys_fail(0);
327
- break;
328
- default:
329
- /* Raise something else */
330
- rb_raise(rb_eRedisClientConnectionError, "%s", errstr);
331
- }
332
- }
333
-
334
- static inline void hiredis_raise_error_and_disconnect(hiredis_connection_t *connection, VALUE timeout_error) {
335
- redisContext *context = connection->context;
336
- if (!context) return;
337
- connection->context = NULL;
338
- redis_raise_error_and_disconnect(context, timeout_error);
339
- }
340
-
341
- static VALUE hiredis_set_connect_timeout(VALUE self, VALUE timeout_us) {
342
- CONNECTION(self, connection);
343
- connection->connect_timeout.tv_sec = NUM2INT(timeout_us) / 1000000;
344
- connection->connect_timeout.tv_usec = NUM2INT(timeout_us) % 1000000;
345
- return timeout_us;
346
- }
347
-
348
- static VALUE hiredis_set_read_timeout(VALUE self, VALUE timeout_us) {
349
- CONNECTION(self, connection);
350
- connection->read_timeout.tv_sec = NUM2INT(timeout_us) / 1000000;
351
- connection->read_timeout.tv_usec = NUM2INT(timeout_us) % 1000000;
352
- return timeout_us;
353
- }
354
-
355
- static VALUE hiredis_set_write_timeout(VALUE self, VALUE timeout_us) {
356
- CONNECTION(self, connection);
357
- connection->write_timeout.tv_sec = NUM2INT(timeout_us) / 1000000;
358
- connection->write_timeout.tv_usec = NUM2INT(timeout_us) % 1000000;
359
- return timeout_us;
360
- }
361
-
362
- static int hiredis_wait_readable(int fd, const struct timeval *timeout, int *isset) {
363
- struct timeval to;
364
- struct timeval *toptr = NULL;
365
-
366
- rb_fdset_t fds;
367
-
368
- /* Be cautious: a call to rb_fd_init to initialize the rb_fdset_t structure
369
- * must be paired with a call to rb_fd_term to free it. */
370
- rb_fd_init(&fds);
371
- rb_fd_set(fd, &fds);
372
-
373
- /* rb_thread_{fd_,}select modifies the passed timeval, so we pass a copy */
374
- if (timeout != NULL && (timeout->tv_sec || timeout->tv_usec)) {
375
- memcpy(&to, timeout, sizeof(to));
376
- toptr = &to;
377
- }
378
-
379
- if (rb_thread_fd_select(fd + 1, &fds, NULL, NULL, toptr) < 0) {
380
- rb_fd_term(&fds);
381
- return -1;
382
- }
383
-
384
- if (rb_fd_isset(fd, &fds) && isset) {
385
- *isset = 1;
386
- }
387
-
388
- rb_fd_term(&fds);
389
- return 0;
390
- }
391
-
392
- static int hiredis_wait_writable(int fd, const struct timeval *timeout, int *isset) {
393
- struct timeval to;
394
- struct timeval *toptr = NULL;
395
-
396
- /* Be cautious: a call to rb_fd_init to initialize the rb_fdset_t structure
397
- * must be paired with a call to rb_fd_term to free it. */
398
- rb_fdset_t fds;
399
- rb_fd_init(&fds);
400
- rb_fd_set(fd, &fds);
401
-
402
- /* rb_thread_{fd_,}select modifies the passed timeval, so we pass a copy */
403
- if (timeout != NULL && (timeout->tv_sec || timeout->tv_usec)) {
404
- memcpy(&to, timeout, sizeof(to));
405
- toptr = &to;
406
- }
407
-
408
- if (rb_thread_fd_select(fd + 1, NULL, &fds, NULL, toptr) < 0) {
409
- rb_fd_term(&fds);
410
- return -1;
411
- }
412
-
413
- if (rb_fd_isset(fd, &fds) && isset) {
414
- *isset = 1;
415
- }
416
-
417
- rb_fd_term(&fds);
418
- return 0;
419
- }
420
-
421
- static VALUE hiredis_connect_finish(hiredis_connection_t *connection, redisContext *context) {
422
- if (context->err) {
423
- redis_raise_error_and_disconnect(context, rb_eRedisClientConnectTimeoutError);
424
- }
425
-
426
- int writable = 0;
427
- int optval = 0;
428
- socklen_t optlen = sizeof(optval);
429
-
430
- /* Wait for socket to become writable */
431
- if (hiredis_wait_writable(context->fd, &connection->connect_timeout, &writable) < 0) {
432
- redis_raise_error_and_disconnect(context, rb_eRedisClientConnectTimeoutError);
433
- }
434
-
435
- if (!writable) {
436
- errno = ETIMEDOUT;
437
- redis_raise_error_and_disconnect(context, rb_eRedisClientConnectTimeoutError);
438
- }
439
-
440
- /* Check for socket error */
441
- if (getsockopt(context->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
442
- context->err = REDIS_ERR_IO;
443
- redis_raise_error_and_disconnect(context, rb_eRedisClientConnectTimeoutError);
444
- }
445
-
446
- if (optval) {
447
- errno = optval;
448
- redis_raise_error_and_disconnect(context, rb_eRedisClientConnectTimeoutError);
449
- }
450
-
451
- context->reader->fn = &reply_functions;
452
- redisSetPushCallback(context, NULL);
453
- connection->context = context;
454
- return Qtrue;
455
- }
456
-
457
- static VALUE hiredis_connect_tcp(VALUE self, VALUE host, VALUE port) {
458
- CONNECTION(self, connection);
459
- if (connection->context) {
460
- redisFree(connection->context);
461
- connection->context = NULL;
462
- }
463
- return hiredis_connect_finish(connection, redisConnectNonBlock(StringValuePtr(host), NUM2INT(port)));
464
- }
465
-
466
- static VALUE hiredis_connect_unix(VALUE self, VALUE path) {
467
- CONNECTION(self, connection);
468
- if (connection->context) {
469
- redisFree(connection->context);
470
- connection->context = NULL;
471
- }
472
- return hiredis_connect_finish(connection, redisConnectUnixNonBlock(StringValuePtr(path)));
473
- }
474
-
475
- static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
476
- CONNECTION(self, connection);
477
- SSL_CONTEXT(ssl_param, ssl_context)
478
-
479
- if (redisInitiateSSLWithContext(connection->context, ssl_context->context) != REDIS_OK) {
480
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientConnectTimeoutError);
481
- }
482
-
483
- redisSSL *redis_ssl = redisGetSSLSocket(connection->context);
484
-
485
- if (redis_ssl->wantRead) {
486
- int readable = 0;
487
- if (hiredis_wait_readable(connection->context->fd, &connection->connect_timeout, &readable) < 0) {
488
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientConnectTimeoutError);
489
- }
490
- if (!readable) {
491
- errno = EAGAIN;
492
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientConnectTimeoutError);
493
- }
494
-
495
- if (redisInitiateSSLContinue(connection->context) != REDIS_OK) {
496
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientConnectTimeoutError);
497
- };
498
- }
499
-
500
- return Qtrue;
501
- }
502
-
503
- static VALUE hiredis_connected_p(VALUE self) {
504
- CONNECTION(self, connection);
505
-
506
- return connection->context ? Qtrue : Qfalse;
507
- }
508
-
509
- static VALUE hiredis_write(VALUE self, VALUE command) {
510
- Check_Type(command, T_ARRAY);
511
-
512
- CONNECTION(self, connection);
513
- ENSURE_CONNECTED(connection);
514
-
515
- int size = (int)RARRAY_LEN(command);
516
- VALUE _argv_handle;
517
- char **argv = RB_ALLOCV_N(char *, _argv_handle, size);
518
-
519
- VALUE _argv_len_handle;
520
- size_t *argv_len = RB_ALLOCV_N(size_t, _argv_len_handle, size);
521
-
522
- for (int index = 0; index < size; index++) {
523
- VALUE arg = rb_ary_entry(command, index);
524
- Check_Type(arg, T_STRING);
525
- argv[index] = RSTRING_PTR(arg);
526
- argv_len[index] = RSTRING_LEN(arg);
527
- }
528
-
529
- redisAppendCommandArgv(connection->context, size, (const char **)argv, argv_len);
530
- return Qnil;
531
- }
532
-
533
- static VALUE hiredis_flush(VALUE self) {
534
- CONNECTION(self, connection);
535
- ENSURE_CONNECTED(connection);
536
-
537
- int wdone = 0;
538
- while (!wdone) {
539
- errno = 0;
540
- if (redisBufferWrite(connection->context, &wdone) == REDIS_ERR) {
541
- if (errno == EAGAIN) {
542
- int writable = 0;
543
-
544
- if (hiredis_wait_writable(connection->context->fd, &connection->write_timeout, &writable) < 0) {
545
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientWriteTimeoutError);
546
- }
547
-
548
- if (!writable) {
549
- errno = EAGAIN;
550
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientWriteTimeoutError);
551
- }
552
- } else {
553
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientWriteTimeoutError);
554
- }
555
- }
556
- }
557
-
558
- return Qtrue;
559
- }
560
-
561
- static int hiredis_read_internal(hiredis_connection_t *connection, VALUE *reply) {
562
- void *redis_reply = NULL;
563
- int wdone = 0;
564
-
565
- /* Try to read pending replies */
566
- if (redisGetReplyFromReader(connection->context, &redis_reply) == REDIS_ERR) {
567
- /* Protocol error */
568
- return -1;
569
- }
570
-
571
- if (redis_reply == NULL) {
572
- /* Write until the write buffer is drained */
573
- while (!wdone) {
574
- errno = 0;
575
-
576
- if (redisBufferWrite(connection->context, &wdone) == REDIS_ERR) {
577
- /* Socket error */
578
- return -1;
579
- }
580
-
581
- if (errno == EAGAIN) {
582
- int writable = 0;
583
-
584
- if (hiredis_wait_writable(connection->context->fd, &connection->write_timeout, &writable) < 0) {
585
- return -2;
586
- }
587
-
588
- if (!writable) {
589
- errno = EAGAIN;
590
- return -2;
591
- }
592
- }
593
- }
594
-
595
- /* Read until there is a full reply */
596
- while (redis_reply == NULL) {
597
- errno = 0;
598
-
599
- if (redisBufferRead(connection->context) == REDIS_ERR) {
600
- /* Socket error */
601
- return -1;
602
- }
603
-
604
- if (errno == EAGAIN) {
605
- int readable = 0;
606
-
607
- if (hiredis_wait_readable(connection->context->fd, &connection->read_timeout, &readable) < 0) {
608
- return -2;
609
- }
610
-
611
- if (!readable) {
612
- errno = EAGAIN;
613
- return -2;
614
- }
615
-
616
- /* Retry */
617
- continue;
618
- }
619
-
620
- if (redisGetReplyFromReader(connection->context, &redis_reply) == REDIS_ERR) {
621
- /* Protocol error */
622
- return -1;
623
- }
624
- }
625
- }
626
-
627
- /* Set reply object */
628
- if (reply != NULL) {
629
- *reply = (VALUE)redis_reply;
630
- }
631
-
632
- return 0;
633
- }
634
-
635
- static VALUE hiredis_read(VALUE self) {
636
- CONNECTION(self, connection);
637
- ENSURE_CONNECTED(connection);
638
-
639
- VALUE reply = Qnil;
640
- if (hiredis_read_internal(connection, &reply)) {
641
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientReadTimeoutError);
642
- }
643
- return reply;
644
- }
645
-
646
- static VALUE hiredis_close(VALUE self) {
647
- CONNECTION(self, connection);
648
- if (connection->context) {
649
- redisFree(connection->context);
650
- connection->context = NULL;
651
- }
652
- return Qnil;
653
- }
654
-
655
- void Init_hiredis_connection(void) {
656
- #ifdef RUBY_ASSERT
657
- // Qfalse == NULL, so we can't return Qfalse in `reply_create_bool()`
658
- RUBY_ASSERT((void *)Qfalse == NULL);
659
- RUBY_ASSERT((void *)Qnil != NULL);
660
- #endif
661
-
662
- redisInitOpenSSL();
663
-
664
- id_parse = rb_intern("parse");
665
- id_add = rb_intern("add");
666
- id_new = rb_intern("new");
667
-
668
- rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
669
- rb_global_variable(&rb_cSet);
670
-
671
- VALUE rb_cRedisClient = rb_const_get(rb_cObject, rb_intern("RedisClient"));
672
-
673
- rb_eRedisClientCommandError = rb_const_get(rb_cRedisClient, rb_intern("CommandError"));
674
- rb_global_variable(&rb_eRedisClientCommandError);
675
-
676
- rb_eRedisClientConnectionError = rb_const_get(rb_cRedisClient, rb_intern("ConnectionError"));
677
- rb_global_variable(&rb_eRedisClientConnectionError);
678
-
679
- rb_eRedisClientConnectTimeoutError = rb_const_get(rb_cRedisClient, rb_intern("ConnectTimeoutError"));
680
- rb_global_variable(&rb_eRedisClientConnectTimeoutError);
681
-
682
- rb_eRedisClientReadTimeoutError = rb_const_get(rb_cRedisClient, rb_intern("ReadTimeoutError"));
683
- rb_global_variable(&rb_eRedisClientReadTimeoutError);
684
-
685
- rb_eRedisClientWriteTimeoutError = rb_const_get(rb_cRedisClient, rb_intern("WriteTimeoutError"));
686
- rb_global_variable(&rb_eRedisClientWriteTimeoutError);
687
-
688
- VALUE rb_cHiredisConnection = rb_define_class_under(rb_cRedisClient, "HiredisConnection", rb_cObject);
689
- rb_define_alloc_func(rb_cHiredisConnection, hiredis_alloc);
690
-
691
- rb_define_private_method(rb_cHiredisConnection, "connect_timeout_us=", hiredis_set_connect_timeout, 1);
692
- rb_define_private_method(rb_cHiredisConnection, "read_timeout_us=", hiredis_set_read_timeout, 1);
693
- rb_define_private_method(rb_cHiredisConnection, "write_timeout_us=", hiredis_set_write_timeout, 1);
694
-
695
- rb_define_private_method(rb_cHiredisConnection, "connect_tcp", hiredis_connect_tcp, 2);
696
- rb_define_private_method(rb_cHiredisConnection, "connect_unix", hiredis_connect_unix, 1);
697
- rb_define_private_method(rb_cHiredisConnection, "init_ssl", hiredis_init_ssl, 1);
698
- rb_define_method(rb_cHiredisConnection, "connected?", hiredis_connected_p, 0);
699
-
700
- rb_define_private_method(rb_cHiredisConnection, "_write", hiredis_write, 1);
701
- rb_define_private_method(rb_cHiredisConnection, "_read", hiredis_read, 0);
702
- rb_define_private_method(rb_cHiredisConnection, "flush", hiredis_flush, 0);
703
- rb_define_method(rb_cHiredisConnection, "close", hiredis_close, 0);
704
-
705
- VALUE rb_cHiredisSSLContext = rb_define_class_under(rb_cHiredisConnection, "SSLContext", rb_cObject);
706
- rb_define_alloc_func(rb_cHiredisSSLContext, hiredis_ssl_context_alloc);
707
- rb_define_private_method(rb_cHiredisSSLContext, "init", hiredis_ssl_context_init, 5);
708
- }