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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -3
- data/README.md +66 -3
- data/Rakefile +43 -23
- data/lib/redis_client/command_builder.rb +83 -0
- data/lib/redis_client/config.rb +9 -48
- data/lib/redis_client/connection_mixin.rb +38 -0
- data/lib/redis_client/decorator.rb +84 -0
- data/lib/redis_client/pooled.rb +38 -30
- data/lib/redis_client/ruby_connection/buffered_io.rb +153 -0
- data/lib/redis_client/{resp3.rb → ruby_connection/resp3.rb} +0 -26
- data/lib/redis_client/{connection.rb → ruby_connection.rb} +26 -31
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +162 -36
- data/redis-client.gemspec +2 -4
- metadata +12 -59
- data/.rubocop.yml +0 -190
- data/ext/redis_client/hiredis/export.clang +0 -2
- data/ext/redis_client/hiredis/export.gcc +0 -7
- data/ext/redis_client/hiredis/extconf.rb +0 -62
- data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
- data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
- data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
- data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
- data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
- data/ext/redis_client/hiredis/vendor/COPYING +0 -29
- data/ext/redis_client/hiredis/vendor/Makefile +0 -308
- data/ext/redis_client/hiredis/vendor/README.md +0 -664
- data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
- data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
- data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
- data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
- data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
- data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
- data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
- data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
- data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
- data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
- data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
- data/ext/redis_client/hiredis/vendor/async.c +0 -887
- data/ext/redis_client/hiredis/vendor/async.h +0 -147
- data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
- data/ext/redis_client/hiredis/vendor/dict.c +0 -352
- data/ext/redis_client/hiredis/vendor/dict.h +0 -126
- data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
- data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
- data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
- data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
- data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
- data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
- data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
- data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
- data/ext/redis_client/hiredis/vendor/net.c +0 -612
- data/ext/redis_client/hiredis/vendor/net.h +0 -56
- data/ext/redis_client/hiredis/vendor/read.c +0 -739
- data/ext/redis_client/hiredis/vendor/read.h +0 -129
- data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
- data/ext/redis_client/hiredis/vendor/sds.h +0 -278
- data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
- data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
- data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
- data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
- data/ext/redis_client/hiredis/vendor/test.c +0 -1401
- data/ext/redis_client/hiredis/vendor/test.sh +0 -78
- data/ext/redis_client/hiredis/vendor/win32.h +0 -56
- data/lib/redis_client/buffered_io.rb +0 -151
- 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
|
-
}
|