hiredis-client 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc000cc925b26584a60d6a74360cf9b39db974e718c6be6af4f19f7e4ac77051
4
- data.tar.gz: f54b30ef2a1310200d60d76e310a1ea750001affe145c0994a59e262934e6865
3
+ metadata.gz: 767ca647924d14a2509280e51bee10968a669b9ea1e2df60f4fee4c8be4d3f0f
4
+ data.tar.gz: eebad74d4427e86f5c94ec6d83e0cb5aa55bd2826fd150190d5a0fabd4c64fea
5
5
  SHA512:
6
- metadata.gz: d386889884c0a22a1e44ed346f659aa8f95e74be3363ba891b2ac372efb3b02f18bc14e6ccbe347a6e6f3daae3d48cf376dcb10793272db96574233108157177
7
- data.tar.gz: e5264e89206c6d5d723cc1b814867c0e9420ec1f904ca6596a23b840bcc2a0ffe3ae5e2ff9e1069e841eb61d3040d5e916a325a8bb1b325a6b7508261a2df224
6
+ metadata.gz: ca70fbf8254d7101b8b63ffb223d0530b8c7fb2854cb667ff0093f62fb10be848ca9153b123bf5a5ae1988bf61b63f0afcd7abb0b552e7ae35c5f52ac1aa2e1f
7
+ data.tar.gz: 1affad896f66ddd864619b1786586f0d9f352aa741f873d6ca69cbb37b41a37020c7c9391f8431011a532f49346f5d8712bca32be2c15d5ade449cafa3c2b421
@@ -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.0
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-10 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.0
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.0
27
27
  description:
28
28
  email:
29
29
  - jean.boussier@gmail.com