hiredis-client 0.13.0 → 0.14.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc000cc925b26584a60d6a74360cf9b39db974e718c6be6af4f19f7e4ac77051
4
- data.tar.gz: f54b30ef2a1310200d60d76e310a1ea750001affe145c0994a59e262934e6865
3
+ metadata.gz: 3506adf6e5b2e5da54f4fc75e762a0fd4f23b9bd9ea225a468b2ae96360d0273
4
+ data.tar.gz: 484b29d8fb080fad9076e71f1db9a1703b9b2a3a5ae173b6a5073d5abb2bfbe6
5
5
  SHA512:
6
- metadata.gz: d386889884c0a22a1e44ed346f659aa8f95e74be3363ba891b2ac372efb3b02f18bc14e6ccbe347a6e6f3daae3d48cf376dcb10793272db96574233108157177
7
- data.tar.gz: e5264e89206c6d5d723cc1b814867c0e9420ec1f904ca6596a23b840bcc2a0ffe3ae5e2ff9e1069e841eb61d3040d5e916a325a8bb1b325a6b7508261a2df224
6
+ metadata.gz: 3e238fe1e94a808f6c1966c8c2ce1136fa173594cfa824156199c3509b3b61c8b9f97a75336458edf7c50dcddc7a7d08833358a9f34bf64f141587fb1f19fa74
7
+ data.tar.gz: a6ddda3ab9d41f591cd82c1319fd43fc701cbb0c515768e0d940bc095fc69c69334bbf932c860ab37944a7f73e141a5e6b25fc4a2de454fb726f4dd199fadf48
@@ -3,6 +3,10 @@
3
3
  require "mkmf"
4
4
 
5
5
  class HiredisConnectionExtconf
6
+ def initialize(debug)
7
+ @debug = debug
8
+ end
9
+
6
10
  def configure
7
11
  if RUBY_ENGINE == "ruby" && !RUBY_PLATFORM.match?(/mswin/)
8
12
  configure_extension
@@ -18,12 +22,16 @@ class HiredisConnectionExtconf
18
22
  have_func("rb_hash_new_capa", "ruby.h")
19
23
 
20
24
  $CFLAGS = concat_flags($CFLAGS, "-I#{hiredis_dir}", "-std=c99", "-fvisibility=hidden")
21
- $CFLAGS = if ENV["EXT_PEDANTIC"]
22
- concat_flags($CFLAGS, "-Werror", "-g")
25
+ $CFLAGS = if @debug
26
+ concat_flags($CFLAGS, "-Werror", "-g", RbConfig::CONFIG["debugflags"])
23
27
  else
24
28
  concat_flags($CFLAGS, "-O3")
25
29
  end
26
30
 
31
+ if @debug
32
+ $CPPFLAGS = concat_flags($CPPFLAGS, "-DRUBY_DEBUG=1")
33
+ end
34
+
27
35
  append_cflags("-Wno-declaration-after-statement") # Older compilers
28
36
  append_cflags("-Wno-compound-token-split-by-macro") # Older rubies on macos
29
37
  end
@@ -33,7 +41,7 @@ class HiredisConnectionExtconf
33
41
  "USE_SSL" => 1,
34
42
  "CFLAGS" => concat_flags(ENV["CFLAGS"], "-fvisibility=hidden"),
35
43
  }
36
- env["OPTIMIZATION"] = "-g" if ENV["EXT_PEDANTIC"]
44
+ env["OPTIMIZATION"] = "-g" if @debug
37
45
 
38
46
  env = configure_openssl(env)
39
47
 
@@ -97,4 +105,4 @@ class HiredisConnectionExtconf
97
105
  end
98
106
  end
99
107
 
100
- HiredisConnectionExtconf.new.configure
108
+ HiredisConnectionExtconf.new(ENV["EXT_PEDANTIC"]).configure
@@ -37,8 +37,13 @@
37
37
  #include <sys/socket.h>
38
38
  #include <stdbool.h>
39
39
  #include "hiredis.h"
40
+ #include "net.h"
40
41
  #include "hiredis_ssl.h"
41
42
 
43
+ #if !defined(RUBY_ASSERT)
44
+ # define RUBY_ASSERT(condition) ((void)0)
45
+ #endif
46
+
42
47
  #if !defined(HAVE_RB_HASH_NEW_CAPA)
43
48
  static inline VALUE rb_hash_new_capa(long capa)
44
49
  {
@@ -46,6 +51,16 @@ static inline VALUE rb_hash_new_capa(long capa)
46
51
  }
47
52
  #endif
48
53
 
54
+ static void redis_set_error(redisContext *c, int type, const char *str) {
55
+ size_t len;
56
+
57
+ c->err = type;
58
+ len = strlen(str);
59
+ len = len < (sizeof(c->errstr) - 1) ? len : (sizeof(c->errstr) - 1);
60
+ memcpy(c->errstr, str, len);
61
+ c->errstr[len] = '\0';
62
+ }
63
+
49
64
  static VALUE rb_eRedisClientCommandError, rb_eRedisClientConnectionError, rb_eRedisClientCannotConnectError, rb_eRedisClientProtocolError;
50
65
  static VALUE rb_eRedisClientReadTimeoutError, rb_eRedisClientWriteTimeoutError;
51
66
  static VALUE Redis_Qfalse;
@@ -60,11 +75,11 @@ typedef struct {
60
75
  #define SSL_CONTEXT(from, name) \
61
76
  hiredis_ssl_context_t *name = NULL; \
62
77
  TypedData_Get_Struct(from, hiredis_ssl_context_t, &hiredis_ssl_context_data_type, name); \
63
- if(name == NULL) { \
78
+ if (name == NULL) { \
64
79
  rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \
65
80
  }
66
81
 
67
- void hiredis_ssl_context_free(void *ptr) {
82
+ static void hiredis_ssl_context_free(void *ptr) {
68
83
  hiredis_ssl_context_t *ssl_context = (hiredis_ssl_context_t *)ptr;
69
84
  if (ssl_context->context) {
70
85
  redisFreeSSLContext(ssl_context->context);
@@ -84,9 +99,6 @@ static const rb_data_type_t hiredis_ssl_context_data_type = {
84
99
  .dmark = NULL,
85
100
  .dfree = hiredis_ssl_context_free,
86
101
  .dsize = hiredis_ssl_context_memsize,
87
- #ifdef HAS_GC_COMPACT
88
- .dcompact = NULL
89
- #endif
90
102
  },
91
103
  .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
92
104
  };
@@ -120,10 +132,18 @@ static VALUE hiredis_ssl_context_init(VALUE self, VALUE ca_file, VALUE ca_path,
120
132
  return Qnil;
121
133
  }
122
134
 
135
+ typedef struct {
136
+ VALUE stack;
137
+ int *task_index;
138
+ } hiredis_reader_state_t;
139
+
123
140
  static void *reply_append(const redisReadTask *task, VALUE value) {
124
- if (task && task->parent) {
125
- VALUE parent = (VALUE)task->parent->obj;
141
+ hiredis_reader_state_t *state = (hiredis_reader_state_t *)task->privdata;
142
+ int task_index = *state->task_index;
126
143
 
144
+ if (task->parent) {
145
+ RUBY_ASSERT(task_index > 0);
146
+ VALUE parent = rb_ary_entry(state->stack, task_index - 1);
127
147
  switch (task->parent->type) {
128
148
  case REDIS_REPLY_ARRAY:
129
149
  case REDIS_REPLY_SET:
@@ -132,11 +152,10 @@ static void *reply_append(const redisReadTask *task, VALUE value) {
132
152
  break;
133
153
  case REDIS_REPLY_MAP:
134
154
  if (task->idx % 2) {
135
- VALUE key = (VALUE)task->parent->privdata;
136
- task->parent->privdata = NULL;
155
+ VALUE key = rb_ary_pop(state->stack);
137
156
  rb_hash_aset(parent, key, value);
138
157
  } else {
139
- task->parent->privdata = (void*)value;
158
+ rb_ary_push(state->stack, value);
140
159
  }
141
160
  break;
142
161
  default:
@@ -144,6 +163,7 @@ static void *reply_append(const redisReadTask *task, VALUE value) {
144
163
  break;
145
164
  }
146
165
  }
166
+ rb_ary_store(state->stack, task_index, value);
147
167
  return (void*)value;
148
168
  }
149
169
 
@@ -219,13 +239,78 @@ static redisReplyObjectFunctions reply_functions = {
219
239
  reply_free,
220
240
  };
221
241
 
242
+ typedef struct {
243
+ redisContext *context;
244
+ int return_value;
245
+ } hiredis_buffer_read_args_t;
246
+
247
+ static void *hiredis_buffer_read_safe(void *_args) {
248
+ hiredis_buffer_read_args_t *args = _args;
249
+ args->return_value = redisBufferRead(args->context);
250
+ return NULL;
251
+ }
252
+ static int hiredis_buffer_read_nogvl(redisContext *context) {
253
+ hiredis_buffer_read_args_t args = {
254
+ .context = context,
255
+ };
256
+ rb_thread_call_without_gvl(hiredis_buffer_read_safe, &args, RUBY_UBF_IO, 0);
257
+ return args.return_value;
258
+ }
259
+
260
+ typedef struct {
261
+ redisContext *context;
262
+ int *done;
263
+ int return_value;
264
+ } hiredis_buffer_write_args_t;
265
+
266
+ static void *hiredis_buffer_write_safe(void *_args) {
267
+ hiredis_buffer_write_args_t *args = _args;
268
+ args->return_value = redisBufferWrite(args->context, args->done);
269
+ return NULL;
270
+ }
271
+ static int hiredis_buffer_write_nogvl(redisContext *context, int *done) {
272
+ hiredis_buffer_write_args_t args = {
273
+ .context = context,
274
+ .done = done,
275
+ };
276
+ rb_thread_call_without_gvl(hiredis_buffer_write_safe, &args, RUBY_UBF_IO, 0);
277
+ return args.return_value;
278
+ }
279
+
222
280
  #define CONNECTION(from, name) \
223
281
  hiredis_connection_t *name = NULL; \
224
282
  TypedData_Get_Struct(from, hiredis_connection_t, &hiredis_connection_data_type, name); \
225
- if(name == NULL) { \
283
+ if (name == NULL) { \
226
284
  rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \
227
285
  }
228
286
 
287
+ typedef struct {
288
+ redisContext *context;
289
+ int return_value;
290
+ } hiredis_reconnect_args_t;
291
+
292
+ static void *hiredis_reconnect_safe(void *_args) {
293
+ hiredis_reconnect_args_t *args = _args;
294
+ args->return_value = redisReconnect(args->context);
295
+ return NULL;
296
+ }
297
+
298
+ static int hiredis_reconnect_nogvl(redisContext *context) {
299
+ hiredis_reconnect_args_t args = {
300
+ .context = context,
301
+ .return_value = 0
302
+ };
303
+ rb_thread_call_without_gvl(hiredis_reconnect_safe, &args, RUBY_UBF_IO, 0);
304
+ return args.return_value;
305
+ }
306
+
307
+ static void *hiredis_connect_with_options_safe(void *options) {
308
+ return (void *)redisConnectWithOptions(options);
309
+ }
310
+
311
+ static redisContext *hiredis_connect_with_options_nogvl(redisOptions *options) {
312
+ return (redisContext *)rb_thread_call_without_gvl(hiredis_connect_with_options_safe, options, RUBY_UBF_IO, 0);
313
+ }
229
314
 
230
315
  typedef struct {
231
316
  redisContext *context;
@@ -234,42 +319,19 @@ typedef struct {
234
319
  struct timeval write_timeout;
235
320
  } hiredis_connection_t;
236
321
 
237
- void hiredis_connection_mark_task(redisReadTask *task) {
238
- while (task) {
239
- if (task->obj) { rb_gc_mark((VALUE)task->obj); }
240
- if (task->privdata) { rb_gc_mark((VALUE)task->privdata); }
241
- task = task->parent;
242
- }
243
- }
244
-
245
- void hiredis_connection_mark(void *ptr) {
246
- hiredis_connection_t *connection = ptr;
247
- if (connection->context) {
248
- redisReader *reader = connection->context->reader;
249
- for (int index = 0; index < reader->tasks; index++) {
250
- hiredis_connection_mark_task(reader->task[index]);
251
- }
252
- }
253
- }
254
- void hiredis_connection_free(void *ptr) {
322
+ static void hiredis_connection_free(void *ptr) {
255
323
  hiredis_connection_t *connection = ptr;
256
324
  if (connection) {
257
325
  if (connection->context) {
258
- redisFree(connection->context);
326
+ // redisFree may calls close() if we're still connected, but
327
+ // it shoudln't be considered a "blocking" call given we don't
328
+ // use any socket option that may make it a blocking operation.
329
+ redisFree(connection->context);
259
330
  }
260
331
  xfree(connection);
261
332
  }
262
333
  }
263
334
 
264
- static size_t hiredis_connection_task_memsize(redisReadTask *task) {
265
- size_t size = 0;
266
- while (task) {
267
- size += sizeof(redisReadTask);
268
- task = task->parent;
269
- }
270
- return size;
271
- }
272
-
273
335
  static size_t hiredis_connection_memsize(const void *ptr) {
274
336
  hiredis_connection_t *connection = (hiredis_connection_t *)ptr;
275
337
 
@@ -281,10 +343,7 @@ static size_t hiredis_connection_memsize(const void *ptr) {
281
343
  redisReader *reader = connection->context->reader;
282
344
  size += sizeof(redisReader);
283
345
  size += reader->maxbuf;
284
-
285
- for (int index = 0; index < reader->tasks; index++) {
286
- size += hiredis_connection_task_memsize(reader->task[index]);
287
- }
346
+ size += reader->tasks * sizeof(redisReadTask);
288
347
  }
289
348
  }
290
349
 
@@ -294,14 +353,11 @@ static size_t hiredis_connection_memsize(const void *ptr) {
294
353
  static const rb_data_type_t hiredis_connection_data_type = {
295
354
  .wrap_struct_name = "redis-client:hiredis_connection",
296
355
  .function = {
297
- .dmark = hiredis_connection_mark,
356
+ .dmark = NULL,
298
357
  .dfree = hiredis_connection_free,
299
358
  .dsize = hiredis_connection_memsize,
300
- #ifdef HAS_GC_COMPACT
301
- .dcompact = NULL
302
- #endif
303
359
  },
304
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
360
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
305
361
  };
306
362
 
307
363
  static VALUE hiredis_alloc(VALUE klass) {
@@ -309,7 +365,7 @@ static VALUE hiredis_alloc(VALUE klass) {
309
365
  return TypedData_Make_Struct(klass, hiredis_connection_t, &hiredis_connection_data_type, connection);
310
366
  }
311
367
 
312
- void redis_set_io_error(redisContext *context, int err) {
368
+ static void redis_set_io_error(redisContext *context, int err) {
313
369
  if (err) {
314
370
  errno = err;
315
371
  }
@@ -325,10 +381,10 @@ static inline void redis_raise_error_and_disconnect(redisContext *context, VALUE
325
381
  if (context->err) {
326
382
  strncpy(errstr, context->errstr, 128);
327
383
  }
328
- redisFree(context);
384
+ redisNetClose(context);
329
385
 
330
386
  if (!err) {
331
- rb_raise(timeout_error, "Unknown Error");
387
+ rb_raise(timeout_error, "Unknown Error (redis_raise_error_and_disconnect)");
332
388
  }
333
389
 
334
390
  // OpenSSL bug: The SSL_ERROR_SYSCALL with errno value of 0 indicates unexpected EOF from the peer.
@@ -437,6 +493,12 @@ static int hiredis_wait_writable(int fd, const struct timeval *timeout, int *iss
437
493
  }
438
494
 
439
495
  static VALUE hiredis_connect_finish(hiredis_connection_t *connection, redisContext *context) {
496
+ if (connection->context && connection->context != context) {
497
+ redisFree(context);
498
+ rb_raise(rb_eRuntimeError, "HiredisConnection is already connected, must be a bug");
499
+ }
500
+ connection->context = context;
501
+
440
502
  if (context->err) {
441
503
  redis_raise_error_and_disconnect(context, rb_eRedisClientCannotConnectError);
442
504
  }
@@ -470,34 +532,10 @@ static VALUE hiredis_connect_finish(hiredis_connection_t *connection, redisConte
470
532
 
471
533
  context->reader->fn = &reply_functions;
472
534
  redisSetPushCallback(context, NULL);
473
- connection->context = context;
474
535
  return Qtrue;
475
536
  }
476
537
 
477
- static VALUE hiredis_connect_tcp(VALUE self, VALUE host, VALUE port) {
478
- CONNECTION(self, connection);
479
- if (connection->context) {
480
- redisFree(connection->context);
481
- connection->context = NULL;
482
- }
483
- redisContext *context = redisConnectNonBlock(StringValuePtr(host), NUM2INT(port));
484
- if (context) {
485
- redisEnableKeepAlive(context);
486
- }
487
- return hiredis_connect_finish(connection, context);
488
- }
489
-
490
- static VALUE hiredis_connect_unix(VALUE self, VALUE path) {
491
- CONNECTION(self, connection);
492
- if (connection->context) {
493
- redisFree(connection->context);
494
- connection->context = NULL;
495
- }
496
- return hiredis_connect_finish(connection, redisConnectUnixNonBlock(StringValuePtr(path)));
497
- }
498
-
499
- static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
500
- CONNECTION(self, connection);
538
+ static void hiredis_init_ssl(hiredis_connection_t *connection, VALUE ssl_param) {
501
539
  SSL_CONTEXT(ssl_param, ssl_context)
502
540
 
503
541
  if (redisInitiateSSLWithContext(connection->context, ssl_context->context) != REDIS_OK) {
@@ -505,7 +543,7 @@ static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
505
543
  }
506
544
 
507
545
  redisSSL *redis_ssl = redisGetSSLSocket(connection->context);
508
-
546
+
509
547
  if (redis_ssl->wantRead) {
510
548
  int readable = 0;
511
549
  if (hiredis_wait_readable(connection->context->fd, &connection->connect_timeout, &readable) < 0) {
@@ -513,21 +551,81 @@ static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
513
551
  }
514
552
  if (!readable) {
515
553
  errno = EAGAIN;
516
- hiredis_raise_error_and_disconnect(connection, rb_eRedisClientCannotConnectError);
554
+ redis_set_error(connection->context, REDIS_ERR_IO, "SSL Connection Timeout");
555
+ hiredis_raise_error_and_disconnect(connection, rb_eRedisClientReadTimeoutError);
517
556
  }
518
557
 
519
558
  if (redisInitiateSSLContinue(connection->context) != REDIS_OK) {
520
559
  hiredis_raise_error_and_disconnect(connection, rb_eRedisClientCannotConnectError);
521
560
  };
522
561
  }
562
+ }
523
563
 
524
- return Qtrue;
564
+ static VALUE hiredis_connect(VALUE self, VALUE path, VALUE host, VALUE port, VALUE ssl_param) {
565
+ CONNECTION(self, connection);
566
+ if (connection->context) {
567
+ rb_raise(rb_eRuntimeError, "HiredisConnection is already connected, must be a bug");
568
+ }
569
+
570
+ redisOptions options = {
571
+ .connect_timeout = &connection->connect_timeout,
572
+ .options = REDIS_OPT_NONBLOCK
573
+ };
574
+ if (RTEST(path)) {
575
+ REDIS_OPTIONS_SET_UNIX(&options, StringValuePtr(path));
576
+ } else {
577
+ REDIS_OPTIONS_SET_TCP(&options, StringValuePtr(host), NUM2INT(port));
578
+ }
579
+
580
+ redisContext *context = hiredis_connect_with_options_nogvl(&options);
581
+ if (context && !RTEST(path)) {
582
+ redisEnableKeepAlive(context);
583
+ }
584
+
585
+ VALUE success = hiredis_connect_finish(connection, context);
586
+
587
+ if (RTEST(success) && !NIL_P(ssl_param)) {
588
+ hiredis_init_ssl(connection, ssl_param);
589
+ }
590
+
591
+ return success;
592
+ }
593
+
594
+ static VALUE hiredis_reconnect(VALUE self, VALUE unix, VALUE ssl_param) {
595
+ CONNECTION(self, connection);
596
+ if (!connection->context) {
597
+ return Qfalse;
598
+ }
599
+
600
+ hiredis_reconnect_nogvl(connection->context);
601
+
602
+ VALUE success = hiredis_connect_finish(connection, connection->context);
603
+
604
+ if (RTEST(success)) {
605
+ if (!RTEST(unix)) {
606
+ redisEnableKeepAlive(connection->context);
607
+ }
608
+
609
+ if (RTEST(ssl_param)) {
610
+ hiredis_init_ssl(connection, ssl_param);
611
+ }
612
+ }
613
+
614
+ return success;
525
615
  }
526
616
 
527
617
  static VALUE hiredis_connected_p(VALUE self) {
528
618
  CONNECTION(self, connection);
529
619
 
530
- return connection->context ? Qtrue : Qfalse;
620
+ if (!connection->context) {
621
+ return Qfalse;
622
+ }
623
+
624
+ if (connection->context->fd == REDIS_INVALID_FD) {
625
+ return Qfalse;
626
+ }
627
+
628
+ return Qtrue;
531
629
  }
532
630
 
533
631
  static VALUE hiredis_write(VALUE self, VALUE command) {
@@ -554,44 +652,6 @@ static VALUE hiredis_write(VALUE self, VALUE command) {
554
652
  return Qnil;
555
653
  }
556
654
 
557
- typedef struct {
558
- redisContext *context;
559
- int return_value;
560
- } hiredis_buffer_read_args_t;
561
-
562
- void *hiredis_buffer_read_safe(void *_args) {
563
- hiredis_buffer_read_args_t *args = _args;
564
- args->return_value = redisBufferRead(args->context);
565
- return NULL;
566
- }
567
- int hiredis_buffer_read_nogvl(redisContext *context) {
568
- hiredis_buffer_read_args_t args = {
569
- .context = context,
570
- };
571
- rb_thread_call_without_gvl(hiredis_buffer_read_safe, &args, RUBY_UBF_IO, 0);
572
- return args.return_value;
573
- }
574
-
575
- typedef struct {
576
- redisContext *context;
577
- int *done;
578
- int return_value;
579
- } hiredis_buffer_write_args_t;
580
-
581
- void *hiredis_buffer_write_safe(void *_args) {
582
- hiredis_buffer_write_args_t *args = _args;
583
- args->return_value = redisBufferWrite(args->context, args->done);
584
- return NULL;
585
- }
586
- int hiredis_buffer_write_nogvl(redisContext *context, int *done) {
587
- hiredis_buffer_write_args_t args = {
588
- .context = context,
589
- .done = done,
590
- };
591
- rb_thread_call_without_gvl(hiredis_buffer_write_safe, &args, RUBY_UBF_IO, 0);
592
- return args.return_value;
593
- }
594
-
595
655
  static VALUE hiredis_flush(VALUE self) {
596
656
  CONNECTION(self, connection);
597
657
  ENSURE_CONNECTED(connection);
@@ -628,6 +688,16 @@ static int hiredis_read_internal(hiredis_connection_t *connection, VALUE *reply)
628
688
  void *redis_reply = NULL;
629
689
  int wdone = 0;
630
690
 
691
+ // This struct being on the stack, the GC won't move nor collect that `stack` RArray.
692
+ // We use that to avoid having to have a `mark` function with write barriers.
693
+ // Not that it would be too hard, but if we mark the response objects, we'll likely end up
694
+ // promoting them to the old generation which isn't desirable.
695
+ hiredis_reader_state_t reader_state = {
696
+ .stack = rb_ary_new(),
697
+ .task_index = &connection->context->reader->ridx,
698
+ };
699
+ connection->context->reader->privdata = &reader_state;
700
+
631
701
  /* Try to read pending replies */
632
702
  if (redisGetReplyFromReader(connection->context, &redis_reply) == REDIS_ERR) {
633
703
  return HIREDIS_FATAL_CONNECTION_ERROR; // Protocol error
@@ -722,18 +792,15 @@ static VALUE hiredis_read(VALUE self) {
722
792
  static VALUE hiredis_close(VALUE self) {
723
793
  CONNECTION(self, connection);
724
794
  if (connection->context) {
725
- redisFree(connection->context);
726
- connection->context = NULL;
795
+ redisNetClose(connection->context);
727
796
  }
728
797
  return Qnil;
729
798
  }
730
799
 
731
800
  RUBY_FUNC_EXPORTED void Init_hiredis_connection(void) {
732
- #ifdef RUBY_ASSERT
733
- // Qfalse == NULL, so we can't return Qfalse in `reply_create_bool()`
734
- RUBY_ASSERT((void *)Qfalse == NULL);
735
- RUBY_ASSERT((void *)Qnil != NULL);
736
- #endif
801
+ // Qfalse == NULL, so we can't return Qfalse in `reply_create_bool()`
802
+ RUBY_ASSERT((void *)Qfalse == NULL);
803
+ RUBY_ASSERT((void *)Qnil != NULL);
737
804
 
738
805
  redisInitOpenSSL();
739
806
 
@@ -768,15 +835,14 @@ RUBY_FUNC_EXPORTED void Init_hiredis_connection(void) {
768
835
  rb_define_private_method(rb_cHiredisConnection, "read_timeout_us=", hiredis_set_read_timeout, 1);
769
836
  rb_define_private_method(rb_cHiredisConnection, "write_timeout_us=", hiredis_set_write_timeout, 1);
770
837
 
771
- rb_define_private_method(rb_cHiredisConnection, "connect_tcp", hiredis_connect_tcp, 2);
772
- rb_define_private_method(rb_cHiredisConnection, "connect_unix", hiredis_connect_unix, 1);
773
- rb_define_private_method(rb_cHiredisConnection, "init_ssl", hiredis_init_ssl, 1);
838
+ rb_define_private_method(rb_cHiredisConnection, "_connect", hiredis_connect, 4);
839
+ rb_define_private_method(rb_cHiredisConnection, "_reconnect", hiredis_reconnect, 2);
774
840
  rb_define_method(rb_cHiredisConnection, "connected?", hiredis_connected_p, 0);
775
841
 
776
842
  rb_define_private_method(rb_cHiredisConnection, "_write", hiredis_write, 1);
777
843
  rb_define_private_method(rb_cHiredisConnection, "_read", hiredis_read, 0);
778
844
  rb_define_private_method(rb_cHiredisConnection, "flush", hiredis_flush, 0);
779
- rb_define_method(rb_cHiredisConnection, "close", hiredis_close, 0);
845
+ rb_define_private_method(rb_cHiredisConnection, "_close", hiredis_close, 0);
780
846
 
781
847
  VALUE rb_cHiredisSSLContext = rb_define_class_under(rb_cHiredisConnection, "SSLContext", rb_cObject);
782
848
  rb_define_alloc_func(rb_cHiredisSSLContext, hiredis_ssl_context_alloc);
@@ -30,27 +30,32 @@ class RedisClient
30
30
 
31
31
  def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
32
32
  super()
33
+ @config = config
33
34
  self.connect_timeout = connect_timeout
34
35
  self.read_timeout = read_timeout
35
36
  self.write_timeout = write_timeout
37
+ connect
38
+ end
36
39
 
37
- if config.path
38
- begin
39
- connect_unix(config.path)
40
- rescue SystemCallError => error
41
- raise CannotConnectError, error.message, error.backtrace
42
- end
43
- else
44
- begin
45
- connect_tcp(config.host, config.port)
46
- rescue SystemCallError => error
47
- error_code = error.class.name.split("::").last
48
- raise CannotConnectError, "Failed to connect to #{config.host}:#{config.port} (#{error_code})"
49
- end
40
+ def close
41
+ _close
42
+ super
43
+ end
44
+
45
+ def reconnect
46
+ reconnected = begin
47
+ _reconnect(@config.path, @config.ssl_context)
48
+ rescue SystemCallError => error
49
+ host = @config.path || "#{@config.host}:#{@config.port}"
50
+ error_code = error.class.name.split("::").last
51
+ raise CannotConnectError, "Failed to reconnect to #{host} (#{error_code})"
50
52
  end
51
53
 
52
- if config.ssl
53
- init_ssl(config.ssl_context)
54
+ if reconnected
55
+ true
56
+ else
57
+ # Reusing the hiredis connection didn't work let's create a fresh one
58
+ super
54
59
  end
55
60
  end
56
61
 
@@ -100,5 +105,15 @@ class RedisClient
100
105
  rescue SystemCallError, IOError => error
101
106
  raise ConnectionError, error.message
102
107
  end
108
+
109
+ private
110
+
111
+ def connect
112
+ _connect(@config.path, @config.host, @config.port, @config.ssl_context)
113
+ rescue SystemCallError => error
114
+ host = @config.path || "#{@config.host}:#{@config.port}"
115
+ error_code = error.class.name.split("::").last
116
+ raise CannotConnectError, "Failed to connect to #{host} (#{error_code})"
117
+ end
103
118
  end
104
119
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiredis-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-28 00:00:00.000000000 Z
11
+ date: 2023-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.13.0
19
+ version: 0.14.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.13.0
26
+ version: 0.14.1
27
27
  description:
28
28
  email:
29
29
  - jean.boussier@gmail.com