redis-client 0.2.0 → 0.4.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +1 -2
  4. data/Gemfile.lock +2 -3
  5. data/README.md +66 -3
  6. data/Rakefile +43 -23
  7. data/lib/redis_client/command_builder.rb +83 -0
  8. data/lib/redis_client/config.rb +9 -48
  9. data/lib/redis_client/connection_mixin.rb +38 -0
  10. data/lib/redis_client/decorator.rb +84 -0
  11. data/lib/redis_client/pooled.rb +38 -30
  12. data/lib/redis_client/ruby_connection/buffered_io.rb +153 -0
  13. data/lib/redis_client/{resp3.rb → ruby_connection/resp3.rb} +0 -26
  14. data/lib/redis_client/{connection.rb → ruby_connection.rb} +26 -31
  15. data/lib/redis_client/version.rb +1 -1
  16. data/lib/redis_client.rb +162 -36
  17. data/redis-client.gemspec +2 -4
  18. metadata +12 -59
  19. data/.rubocop.yml +0 -190
  20. data/ext/redis_client/hiredis/export.clang +0 -2
  21. data/ext/redis_client/hiredis/export.gcc +0 -7
  22. data/ext/redis_client/hiredis/extconf.rb +0 -62
  23. data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
  24. data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
  25. data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
  26. data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
  27. data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
  28. data/ext/redis_client/hiredis/vendor/COPYING +0 -29
  29. data/ext/redis_client/hiredis/vendor/Makefile +0 -308
  30. data/ext/redis_client/hiredis/vendor/README.md +0 -664
  31. data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
  32. data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
  33. data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
  34. data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
  35. data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
  36. data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
  37. data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
  38. data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
  39. data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
  40. data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
  41. data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
  42. data/ext/redis_client/hiredis/vendor/async.c +0 -887
  43. data/ext/redis_client/hiredis/vendor/async.h +0 -147
  44. data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
  45. data/ext/redis_client/hiredis/vendor/dict.c +0 -352
  46. data/ext/redis_client/hiredis/vendor/dict.h +0 -126
  47. data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
  48. data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
  49. data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
  50. data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
  51. data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
  52. data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
  53. data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
  54. data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
  55. data/ext/redis_client/hiredis/vendor/net.c +0 -612
  56. data/ext/redis_client/hiredis/vendor/net.h +0 -56
  57. data/ext/redis_client/hiredis/vendor/read.c +0 -739
  58. data/ext/redis_client/hiredis/vendor/read.h +0 -129
  59. data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
  60. data/ext/redis_client/hiredis/vendor/sds.h +0 -278
  61. data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
  62. data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
  63. data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
  64. data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
  65. data/ext/redis_client/hiredis/vendor/test.c +0 -1401
  66. data/ext/redis_client/hiredis/vendor/test.sh +0 -78
  67. data/ext/redis_client/hiredis/vendor/win32.h +0 -56
  68. data/lib/redis_client/buffered_io.rb +0 -151
  69. data/lib/redis_client/hiredis_connection.rb +0 -80
@@ -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
- }