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