hiredis 0.2.0 → 0.3.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.
data/Rakefile CHANGED
@@ -1,57 +1,11 @@
1
1
  require 'rake'
2
- require 'rake/gempackagetask'
3
2
  require 'rake/testtask'
4
-
5
- gem 'rake-compiler', '~> 0.7.1'
6
3
  require "rake/extensiontask"
7
4
 
8
- $:.unshift File.join(File.dirname(__FILE__), 'lib')
9
- require 'hiredis/version'
10
-
11
- GEM = 'hiredis'
12
- GEM_VERSION = Hiredis::VERSION
13
- AUTHORS = ['Pieter Noordhuis']
14
- EMAIL = "pcnoordhuis@gmail.com"
15
- HOMEPAGE = "http://github.com/pietern/hiredis-rb"
16
- SUMMARY = "Ruby extension that wraps Hiredis (blocking connection and reply parsing)"
17
-
18
- spec = Gem::Specification.new do |s|
19
- s.name = GEM
20
- s.version = GEM_VERSION
21
- s.platform = Gem::Platform::RUBY
22
- s.has_rdoc = true
23
- s.extra_rdoc_files = ["COPYING"]
24
- s.summary = SUMMARY
25
- s.description = s.summary
26
- s.authors = AUTHORS
27
- s.email = EMAIL
28
- s.homepage = HOMEPAGE
29
- s.require_path = 'lib'
30
- s.extensions = FileList["ext/**/extconf.rb"]
31
-
32
- ext_files = Dir.glob("ext/**/*.{rb,c,h}")
33
- lib_files = Dir.glob("lib/**/*.rb")
34
- hiredis_files = Dir.glob("vendor/hiredis/*.{c,h}") -
35
- Dir.glob("vendor/hiredis/example*") +
36
- Dir.glob("vendor/hiredis/COPYING") +
37
- Dir.glob("vendor/hiredis/Makefile")
38
- s.files = %w(COPYING Rakefile) + ext_files + lib_files + hiredis_files
39
-
40
- s.add_runtime_dependency "rake-compiler", "~> 0.7.1"
41
- s.add_runtime_dependency "redis", "~> 2.1.1"
42
- end
43
-
44
- desc "create a gemspec file"
45
- task :gemspec do
46
- File.open("#{GEM}.gemspec", "w") do |file|
47
- file.puts spec.to_ruby
48
- end
49
- end
50
-
51
5
  Rake::ExtensionTask.new('hiredis_ext') do |task|
52
6
  # Pass --with-foo-config args to extconf.rb
53
7
  task.config_options = ARGV[1..-1]
54
- task.lib_dir = File.join(*['lib', 'hiredis'])
8
+ task.lib_dir = File.join(*['lib', 'hiredis', 'ext'])
55
9
  end
56
10
 
57
11
  namespace :hiredis do
@@ -28,24 +28,43 @@ static void parent_context_free(redisParentContext *pc) {
28
28
  free(pc);
29
29
  }
30
30
 
31
+ static void parent_context_raise(redisParentContext *pc) {
32
+ int err;
33
+ char errstr[1024];
34
+
35
+ /* Copy error and free context */
36
+ err = pc->context->err;
37
+ snprintf(errstr,sizeof(errstr),"%s",pc->context->errstr);
38
+ parent_context_try_free(pc);
39
+
40
+ switch(err) {
41
+ case REDIS_ERR_IO:
42
+ /* Raise native Ruby I/O error */
43
+ rb_sys_fail(0);
44
+ break;
45
+ case REDIS_ERR_EOF:
46
+ /* Raise our own EOF error */
47
+ rb_raise(error_eof,"%s",errstr);
48
+ break;
49
+ default:
50
+ /* Raise something else */
51
+ rb_raise(rb_eRuntimeError,"%s",errstr);
52
+ }
53
+ }
54
+
31
55
  static VALUE connection_parent_context_alloc(VALUE klass) {
32
56
  redisParentContext *pc = malloc(sizeof(*pc));
33
57
  pc->context = NULL;
34
58
  return Data_Wrap_Struct(klass, parent_context_mark, parent_context_free, pc);
35
59
  }
36
60
 
37
- static VALUE connection_connect(VALUE self, VALUE _host, VALUE _port) {
61
+ static VALUE connection_generic_connect(VALUE self, redisContext *c) {
38
62
  redisParentContext *pc;
39
- redisContext *c;
40
- char *host = StringValuePtr(_host);
41
- int port = NUM2INT(_port);
42
63
  int err;
43
64
  char errstr[1024];
44
65
 
45
66
  Data_Get_Struct(self,redisParentContext,pc);
46
- parent_context_try_free(pc);
47
67
 
48
- c = redisConnect(host,port);
49
68
  if (c->err) {
50
69
  /* Copy error and free context */
51
70
  err = c->err;
@@ -66,6 +85,79 @@ static VALUE connection_connect(VALUE self, VALUE _host, VALUE _port) {
66
85
  return Qnil;
67
86
  }
68
87
 
88
+ static struct timeval __timeout_from_robj(VALUE usecs) {
89
+ int s = NUM2INT(usecs)/1000000;
90
+ int us = NUM2INT(usecs)-(s*1000000);
91
+ struct timeval timeout = { s, us };
92
+ return timeout;
93
+ }
94
+
95
+ static VALUE connection_connect(int argc, VALUE *argv, VALUE self) {
96
+ redisParentContext *pc;
97
+ redisContext *c;
98
+ VALUE *_host = NULL;
99
+ VALUE *_port = NULL;
100
+ VALUE *_timeout = NULL;
101
+ char *host;
102
+ int port;
103
+ struct timeval timeout;
104
+
105
+ if (argc == 2 || argc == 3) {
106
+ _host = &argv[0];
107
+ _port = &argv[1];
108
+ if (argc == 3)
109
+ _timeout = &argv[2];
110
+ } else {
111
+ rb_raise(rb_eArgError, "invalid number of arguments");
112
+ return Qnil;
113
+ }
114
+
115
+ Data_Get_Struct(self,redisParentContext,pc);
116
+ parent_context_try_free(pc);
117
+
118
+ host = StringValuePtr(*_host);
119
+ port = NUM2INT(*_port);
120
+ if (_timeout != NULL) {
121
+ timeout = __timeout_from_robj(*_timeout);
122
+ c = redisConnectWithTimeout(host,port,timeout);
123
+ } else {
124
+ c = redisConnect(host,port);
125
+ }
126
+
127
+ return connection_generic_connect(self,c);
128
+ }
129
+
130
+ static VALUE connection_connect_unix(int argc, VALUE *argv, VALUE self) {
131
+ redisParentContext *pc;
132
+ redisContext *c;
133
+ VALUE *_path = NULL;
134
+ VALUE *_timeout = NULL;
135
+ char *path;
136
+ struct timeval timeout;
137
+
138
+ if (argc == 1 || argc == 2) {
139
+ _path = &argv[0];
140
+ if (argc == 2)
141
+ _timeout = &argv[1];
142
+ } else {
143
+ rb_raise(rb_eArgError, "invalid number of arguments");
144
+ return Qnil;
145
+ }
146
+
147
+ Data_Get_Struct(self,redisParentContext,pc);
148
+ parent_context_try_free(pc);
149
+
150
+ path = StringValuePtr(*_path);
151
+ if (_timeout != NULL) {
152
+ timeout = __timeout_from_robj(*_timeout);
153
+ c = redisConnectUnixWithTimeout(path,timeout);
154
+ } else {
155
+ c = redisConnectUnix(path);
156
+ }
157
+
158
+ return connection_generic_connect(self,c);
159
+ }
160
+
69
161
  static VALUE connection_is_connected(VALUE self) {
70
162
  redisParentContext *pc;
71
163
  Data_Get_Struct(self,redisParentContext,pc);
@@ -145,33 +237,13 @@ static int __get_reply(redisParentContext *pc, VALUE *reply) {
145
237
  static VALUE connection_read(VALUE self) {
146
238
  redisParentContext *pc;
147
239
  VALUE reply;
148
- int err;
149
- char errstr[1024];
150
240
 
151
241
  Data_Get_Struct(self,redisParentContext,pc);
152
242
  if (!pc->context)
153
243
  rb_raise(rb_eRuntimeError, "not connected");
154
244
 
155
- if (__get_reply(pc,&reply) == -1) {
156
- /* Copy error and free context */
157
- err = pc->context->err;
158
- snprintf(errstr,sizeof(errstr),"%s",pc->context->errstr);
159
- parent_context_try_free(pc);
160
-
161
- switch(err) {
162
- case REDIS_ERR_IO:
163
- /* Raise native Ruby I/O error */
164
- rb_sys_fail(0);
165
- break;
166
- case REDIS_ERR_EOF:
167
- /* Raise our own EOF error */
168
- rb_raise(error_eof,"%s",errstr);
169
- break;
170
- default:
171
- /* Raise something else */
172
- rb_raise(rb_eRuntimeError,"%s",errstr);
173
- }
174
- }
245
+ if (__get_reply(pc,&reply) == -1)
246
+ parent_context_raise(pc);
175
247
 
176
248
  return reply;
177
249
  }
@@ -181,17 +253,13 @@ static VALUE connection_set_timeout(VALUE self, VALUE usecs) {
181
253
  int s = NUM2INT(usecs)/1000000;
182
254
  int us = NUM2INT(usecs)-(s*1000000);
183
255
  struct timeval timeout = { s, us };
184
- char errstr[1024];
185
256
 
186
257
  Data_Get_Struct(self,redisParentContext,pc);
187
258
  if (!pc->context)
188
259
  rb_raise(rb_eRuntimeError, "not connected");
189
260
 
190
- if (redisSetTimeout(pc->context,timeout) == REDIS_ERR) {
191
- snprintf(errstr,sizeof(errstr),"%s",pc->context->errstr);
192
- parent_context_try_free(pc);
193
- rb_raise(rb_eRuntimeError,"%s",errstr);
194
- }
261
+ if (redisSetTimeout(pc->context,timeout) == REDIS_ERR)
262
+ parent_context_raise(pc);
195
263
 
196
264
  return usecs;
197
265
  }
@@ -202,7 +270,8 @@ VALUE error_eof;
202
270
  void InitConnection(VALUE mod) {
203
271
  klass_connection = rb_define_class_under(mod, "Connection", rb_cObject);
204
272
  rb_define_alloc_func(klass_connection, connection_parent_context_alloc);
205
- rb_define_method(klass_connection, "connect", connection_connect, 2);
273
+ rb_define_method(klass_connection, "connect", connection_connect, -1);
274
+ rb_define_method(klass_connection, "connect_unix", connection_connect_unix, -1);
206
275
  rb_define_method(klass_connection, "connected?", connection_is_connected, 0);
207
276
  rb_define_method(klass_connection, "disconnect", connection_disconnect, 0);
208
277
  rb_define_method(klass_connection, "timeout=", connection_set_timeout, 1);
@@ -18,4 +18,4 @@ system("cd #{bundled_hiredis_dir} && make static") if File.directory?(bundled_hi
18
18
 
19
19
  need_header('hiredis.h')
20
20
  need_library('hiredis', 'redisReplyReaderCreate')
21
- create_makefile('hiredis/hiredis_ext')
21
+ create_makefile('hiredis/ext/hiredis_ext')
@@ -4,8 +4,10 @@
4
4
  #include "hiredis_ext.h"
5
5
 
6
6
  VALUE mod_hiredis;
7
+ VALUE mod_ext;
7
8
  void Init_hiredis_ext() {
8
9
  mod_hiredis = rb_define_module("Hiredis");
9
- InitReader(mod_hiredis);
10
- InitConnection(mod_hiredis);
10
+ mod_ext = rb_define_module_under(mod_hiredis,"Ext");
11
+ InitReader(mod_ext);
12
+ InitConnection(mod_ext);
11
13
  }
@@ -10,7 +10,6 @@ extern VALUE mod_hiredis;
10
10
  /* Defined in reader.c */
11
11
  extern redisReplyObjectFunctions redisExtReplyObjectFunctions;
12
12
  extern VALUE klass_reader;
13
- extern ID ivar_hiredis_error; /* ivar used to store error reply ("-ERR message") */
14
13
  extern void InitReader(VALUE module);
15
14
 
16
15
  /* Defined in connection.c */
@@ -6,9 +6,6 @@ static VALUE enc_klass;
6
6
  static ID enc_default_external = 0;
7
7
  static ID str_force_encoding = 0;
8
8
 
9
- /* Singleton method to test if the reply contains an error. */
10
- ID ivar_hiredis_error;
11
-
12
9
  /* Add VALUE to parent when the redisReadTask has a parent.
13
10
  * Note that the parent should always be of type T_ARRAY. */
14
11
  static void *tryParentize(const redisReadTask *task, VALUE v) {
@@ -20,10 +17,6 @@ static void *tryParentize(const redisReadTask *task, VALUE v) {
20
17
  return (void*)v;
21
18
  }
22
19
 
23
- static VALUE object_contains_error(VALUE self) {
24
- return Qtrue;
25
- }
26
-
27
20
  static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
28
21
  VALUE v, enc;
29
22
  v = rb_str_new(str,len);
@@ -36,17 +29,6 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
36
29
 
37
30
  if (task->type == REDIS_REPLY_ERROR) {
38
31
  v = rb_funcall(rb_eRuntimeError,rb_intern("new"),1,v);
39
- rb_ivar_set(v,ivar_hiredis_error,v);
40
-
41
- if (task && task->parent != NULL) {
42
- /* Also make the parent respond to this method. Redis currently
43
- * only emits nested multi bulks of depth 2, so we don't need
44
- * to cascade setting this ivar. Make sure to only set the first
45
- * error reply on the parent. */
46
- VALUE parent = (VALUE)task->parent->obj;
47
- if (!rb_ivar_defined(parent,ivar_hiredis_error))
48
- rb_ivar_set(parent,ivar_hiredis_error,v);
49
- }
50
32
  }
51
33
 
52
34
  return tryParentize(task,v);
@@ -93,7 +75,6 @@ static VALUE reader_allocate(VALUE klass) {
93
75
 
94
76
  static VALUE reader_feed(VALUE klass, VALUE str) {
95
77
  void *reader;
96
- unsigned int size;
97
78
 
98
79
  if (TYPE(str) != T_STRING)
99
80
  rb_raise(rb_eTypeError, "not a string");
@@ -122,7 +103,6 @@ void InitReader(VALUE mod) {
122
103
  rb_define_alloc_func(klass_reader, reader_allocate);
123
104
  rb_define_method(klass_reader, "feed", reader_feed, 1);
124
105
  rb_define_method(klass_reader, "gets", reader_gets, 0);
125
- ivar_hiredis_error = rb_intern("@__hiredis_error");
126
106
 
127
107
  /* If the Encoding class is present, #default_external should be used to
128
108
  * determine the encoding for new strings. The "enc_default_external"
@@ -1,10 +1,2 @@
1
- require 'hiredis/version'
2
- require 'hiredis/connection'
3
-
4
- # Make redis-rb use the Hiredis Connection class
5
- class Redis
6
- Connection = ::Hiredis::Connection
7
- end
8
-
9
- # Load redis
10
- require 'redis'
1
+ require "hiredis/version"
2
+ require "hiredis/connection"
@@ -1,17 +1,10 @@
1
- require 'hiredis/hiredis_ext'
2
-
3
1
  module Hiredis
4
- class Connection
5
- # Raise CONNRESET on EOF
6
- alias :_read :read
7
- def read
8
- reply = _read
9
- error = reply.instance_variable_get(:@__hiredis_error)
10
- raise error if error
11
-
12
- reply
13
- rescue EOFError
14
- raise Errno::ECONNRESET
15
- end
2
+ begin
3
+ require "hiredis/ext/connection"
4
+ Connection = Ext::Connection
5
+ rescue LoadError
6
+ warn "WARNING: could not load hiredis extension, using (slower) pure Ruby implementation."
7
+ require "hiredis/ruby/connection"
8
+ Connection = Ruby::Connection
16
9
  end
17
10
  end
@@ -0,0 +1,16 @@
1
+ require "hiredis/ext/hiredis_ext"
2
+ require "hiredis/version"
3
+
4
+ module Hiredis
5
+ module Ext
6
+ class Connection
7
+ # Raise CONNRESET on EOF
8
+ alias :_read :read
9
+ def read
10
+ _read
11
+ rescue EOFError
12
+ raise Errno::ECONNRESET
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,2 @@
1
+ require "hiredis/ext/hiredis_ext"
2
+ require "hiredis/version"
@@ -1 +1,10 @@
1
- require 'hiredis/hiredis_ext'
1
+ module Hiredis
2
+ begin
3
+ require "hiredis/ext/reader"
4
+ Reader = Ext::Reader
5
+ rescue LoadError
6
+ warn "WARNING: could not load hiredis extension, using (slower) pure Ruby implementation."
7
+ require "hiredis/ruby/reader"
8
+ Reader = Ruby::Reader
9
+ end
10
+ end
@@ -0,0 +1,113 @@
1
+ require "socket"
2
+ require "timeout"
3
+ require "hiredis/ruby/reader"
4
+ require "hiredis/version"
5
+
6
+ module Hiredis
7
+ module Ruby
8
+ class Connection
9
+
10
+ def initialize
11
+ @sock = nil
12
+ end
13
+
14
+ def connected?
15
+ !! @sock
16
+ end
17
+
18
+ def connect(host, port, usecs = 0)
19
+ @reader = ::Hiredis::Ruby::Reader.new
20
+
21
+ begin
22
+ begin
23
+ Timeout.timeout(usecs.to_f / 1_000_000) do
24
+ @sock = TCPSocket.new(host, port)
25
+ @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
26
+ end
27
+ rescue Timeout::Error
28
+ raise Errno::ETIMEDOUT
29
+ end
30
+ rescue SocketError => error
31
+ # Raise RuntimeError when host cannot be resolved
32
+ if error.message.start_with?("getaddrinfo:")
33
+ raise error.message
34
+ else
35
+ raise error
36
+ end
37
+ end
38
+ end
39
+
40
+ def connect_unix(path, usecs = 0)
41
+ @reader = ::Hiredis::Ruby::Reader.new
42
+
43
+ begin
44
+ Timeout.timeout(usecs.to_f / 1_000_000) do
45
+ @sock = UNIXSocket.new(path)
46
+ end
47
+ rescue Timeout::Error
48
+ raise Errno::ETIMEDOUT
49
+ end
50
+ end
51
+
52
+ def disconnect
53
+ @sock.close
54
+ rescue
55
+ ensure
56
+ @sock = nil
57
+ end
58
+
59
+ def timeout=(usecs)
60
+ raise "not connected" unless connected?
61
+
62
+ secs = Integer(usecs / 1_000_000)
63
+ usecs = Integer(usecs - (secs * 1_000_000)) # 0 - 999_999
64
+
65
+ optval = [secs, usecs].pack("l_2")
66
+
67
+ begin
68
+ @sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
69
+ @sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
70
+ rescue Errno::ENOPROTOOPT
71
+ end
72
+ end
73
+
74
+ COMMAND_DELIMITER = "\r\n".freeze
75
+
76
+ def write(args)
77
+ command = []
78
+ command << "*#{args.size}"
79
+ args.each do |arg|
80
+ arg = arg.to_s
81
+ command << "$#{string_size arg}"
82
+ command << arg
83
+ end
84
+
85
+ @sock.syswrite(command.join(COMMAND_DELIMITER) + COMMAND_DELIMITER)
86
+ end
87
+
88
+ def read
89
+ raise "not connected" unless connected?
90
+
91
+ while (reply = @reader.gets) == false
92
+ @reader.feed @sock.sysread(1024)
93
+ end
94
+
95
+ reply
96
+ rescue EOFError
97
+ raise Errno::ECONNRESET
98
+ end
99
+
100
+ protected
101
+
102
+ if "".respond_to?(:bytesize)
103
+ def string_size(string)
104
+ string.to_s.bytesize
105
+ end
106
+ else
107
+ def string_size(string)
108
+ string.to_s.size
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end