rev 0.1.4 → 0.2.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.
@@ -0,0 +1,255 @@
1
+ /*
2
+ * Copyright (C) 2008 Tony Arcieri
3
+ * Includes portions from the 'OpenSSL for Ruby' project
4
+ * Copyright (C) 2000-2002 GOTOU Yuuzou <gotoyuzo@notwork.org>
5
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
6
+ * Copyright (C) 2001-2007 Technorama Ltd. <oss-ruby@technorama.net>
7
+ * You may redistribute this under the terms of the Ruby license.
8
+ * See LICENSE for details
9
+ */
10
+
11
+ #ifdef HAVE_OPENSSL_SSL_H
12
+
13
+ #include "ruby.h"
14
+ #include "rubyio.h"
15
+
16
+ #include <openssl/ssl.h>
17
+
18
+ static VALUE mOSSL = Qnil;
19
+ static VALUE eOSSLError = Qnil;
20
+
21
+ static VALUE mSSL = Qnil;
22
+ static VALUE cSSLSocket = Qnil;
23
+ static VALUE eSSLError = Qnil;
24
+
25
+ static VALUE mRev = Qnil;
26
+ static VALUE mRev_SSL = Qnil;
27
+ static VALUE cRev_SSL_IO = Qnil;
28
+
29
+ static VALUE eRev_SSL_IO_ReadAgain = Qnil;
30
+ static VALUE eRev_SSL_IO_WriteAgain = Qnil;
31
+
32
+ static VALUE Rev_SSL_IO_connect_nonblock(VALUE self);
33
+ static VALUE Rev_SSL_IO_accept_nonblock(VALUE self);
34
+ static VALUE Rev_SSL_IO_ssl_setup(VALUE self);
35
+ static VALUE Rev_SSL_IO_ssl_setup_check(VALUE dummy, VALUE error_info);
36
+ static VALUE Rev_SSL_IO_start_ssl(VALUE self, int (*func)(), const char *funcname);
37
+
38
+ static VALUE Rev_SSL_IO_read_nonblock(int argc, VALUE *argv, VALUE self);
39
+ static VALUE Rev_SSL_IO_write_nonblock(VALUE self, VALUE str);
40
+
41
+ void Init_rev_ssl()
42
+ {
43
+ rb_require("openssl");
44
+
45
+ mOSSL = rb_define_module("OpenSSL");
46
+ eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
47
+
48
+ mSSL = rb_define_module_under(mOSSL, "SSL");
49
+ cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject);
50
+ eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError);
51
+
52
+ mRev = rb_define_module("Rev");
53
+ mRev_SSL = rb_define_module_under(mRev, "SSL");
54
+ cRev_SSL_IO = rb_define_class_under(mRev_SSL, "IO", cSSLSocket);
55
+
56
+ eRev_SSL_IO_ReadAgain = rb_define_class_under(cRev_SSL_IO, "ReadAgain", rb_eStandardError);
57
+ eRev_SSL_IO_WriteAgain = rb_define_class_under(cRev_SSL_IO, "WriteAgain", rb_eStandardError);
58
+
59
+ rb_define_method(cRev_SSL_IO, "connect_nonblock", Rev_SSL_IO_connect_nonblock, 0);
60
+ rb_define_method(cRev_SSL_IO, "accept_nonblock", Rev_SSL_IO_accept_nonblock, 0);
61
+
62
+ rb_define_method(cRev_SSL_IO, "read_nonblock", Rev_SSL_IO_read_nonblock, -1);
63
+ rb_define_method(cRev_SSL_IO, "write_nonblock", Rev_SSL_IO_write_nonblock, 1);
64
+ }
65
+
66
+ static VALUE
67
+ Rev_SSL_IO_ssl_setup(VALUE self)
68
+ {
69
+ /*
70
+ * DANGER WILL ROBINSON! CRAZY HACKS AHEAD!
71
+ *
72
+ * Before we connect or accept we need to call the ossl_ssl_setup() function
73
+ * in ossl_ssl.c. For whatever reason this isn't called in
74
+ * SSLSocket#initialize but is instead called directly from #connect and
75
+ * #accept.
76
+ *
77
+ * To make things even more awesome, it's a static function, so we can't
78
+ * call it directly. However, we can call it indirectly...
79
+ *
80
+ * There's one other method within ossl_ssl.c which calls ossl_ssl_setup(),
81
+ * and that's #session=. I'm not sure why this calls it, but its author
82
+ * left this comment to help us figure out:
83
+ *
84
+ * "why is ossl_ssl_setup delayed?"
85
+ *
86
+ * Why indeed, guy... why indeed. Well, his function calls ossl_ssl_setup(),
87
+ * then typechecks its arguments, which means if we pass a bogus one it will
88
+ * happily setup SSL for us, then raise an exception. So we can catch
89
+ * that exception and be on our merry way.
90
+ *
91
+ * I don't even know what this method is supposed to do. It appears related
92
+ * to OpenSSL::SSL::Session, which is linked into the OpenSSL library but
93
+ * never initialized, probably because it's buggy. Nevertheless, the
94
+ * #session= method is still available to use for this hack. Awesome!
95
+ */
96
+ rb_funcall(self, rb_intern("session="), 1, Qnil);
97
+ }
98
+
99
+ /* Ensure the error raised by calling #session= with a dummy argument is
100
+ * the one we were expecting */
101
+ static VALUE
102
+ Rev_SSL_IO_ssl_setup_check(VALUE dummy, VALUE err)
103
+ {
104
+ if(!rb_obj_is_kind_of(err, rb_eTypeError))
105
+ rb_raise(rb_eRuntimeError, "Rev::SSL not supported in this Ruby version, sorry");
106
+
107
+ return Qnil;
108
+ }
109
+
110
+ /*
111
+ * call-seq:
112
+ * ssl.connect => self
113
+ */
114
+ static VALUE
115
+ Rev_SSL_IO_connect_nonblock(VALUE self)
116
+ {
117
+ rb_rescue(Rev_SSL_IO_ssl_setup, self, Rev_SSL_IO_ssl_setup_check, Qnil);
118
+ return Rev_SSL_IO_start_ssl(self, SSL_connect, "SSL_connect");
119
+ }
120
+
121
+ /*
122
+ * call-seq:
123
+ * ssl.accept => self
124
+ */
125
+ static VALUE
126
+ Rev_SSL_IO_accept_nonblock(VALUE self)
127
+ {
128
+ rb_rescue(Rev_SSL_IO_ssl_setup, self, 0, 0);
129
+ return Rev_SSL_IO_start_ssl(self, SSL_accept, "SSL_accept");
130
+ }
131
+
132
+ static VALUE
133
+ Rev_SSL_IO_start_ssl(VALUE self, int (*func)(), const char *funcname)
134
+ {
135
+ SSL *ssl;
136
+ int ret, ret2;
137
+
138
+ Data_Get_Struct(self, SSL, ssl);
139
+ if(!ssl)
140
+ rb_raise(rb_eRuntimeError, "SSL never initialized");
141
+
142
+ if((ret = func(ssl)) <= 0) {
143
+ switch((ret2 = SSL_get_error(ssl, ret))) {
144
+ case SSL_ERROR_WANT_WRITE:
145
+ rb_raise(eRev_SSL_IO_WriteAgain, "write again");
146
+ case SSL_ERROR_WANT_READ:
147
+ rb_raise(eRev_SSL_IO_ReadAgain, "read again");
148
+ case SSL_ERROR_SYSCALL:
149
+ if (errno) rb_sys_fail(funcname);
150
+ rb_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s",
151
+ funcname, ret2, errno, SSL_state_string_long(ssl)
152
+ );
153
+ default:
154
+ rb_raise(eSSLError, "%s returned=%d errno=%d state=%s",
155
+ funcname, ret2, errno, SSL_state_string_long(ssl)
156
+ );
157
+ }
158
+ }
159
+
160
+ return self;
161
+ }
162
+
163
+ /*
164
+ * call-seq:
165
+ * ssl.read_nonblock(length) => string
166
+ * ssl.read_nonblock(length, buffer) => buffer
167
+ *
168
+ * === Parameters
169
+ * * +length+ is a positive integer.
170
+ * * +buffer+ is a string used to store the result.
171
+ */
172
+ static VALUE
173
+ Rev_SSL_IO_read_nonblock(int argc, VALUE *argv, VALUE self)
174
+ {
175
+ SSL *ssl;
176
+ int ilen, nread = 0;
177
+ VALUE len, str;
178
+
179
+ rb_scan_args(argc, argv, "11", &len, &str);
180
+ ilen = NUM2INT(len);
181
+
182
+ if(NIL_P(str))
183
+ str = rb_str_new(0, ilen);
184
+ else {
185
+ StringValue(str);
186
+ rb_str_modify(str);
187
+ rb_str_resize(str, ilen);
188
+ }
189
+
190
+ if(ilen == 0) return str;
191
+
192
+ Data_Get_Struct(self, SSL, ssl);
193
+
194
+ if (ssl) {
195
+ nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LEN(str));
196
+ switch(SSL_get_error(ssl, nread)){
197
+ case SSL_ERROR_NONE:
198
+ goto end;
199
+ case SSL_ERROR_ZERO_RETURN:
200
+ rb_eof_error();
201
+ case SSL_ERROR_WANT_WRITE:
202
+ rb_raise(eRev_SSL_IO_WriteAgain, "write again");
203
+ case SSL_ERROR_WANT_READ:
204
+ rb_raise(eRev_SSL_IO_ReadAgain, "read again");
205
+ case SSL_ERROR_SYSCALL:
206
+ if(ERR_peek_error() == 0 && nread == 0) rb_eof_error();
207
+ rb_sys_fail(0);
208
+ default:
209
+ rb_raise(eSSLError, "SSL_read:");
210
+ }
211
+ } else
212
+ rb_raise(rb_eRuntimeError, "SSL session is not started yet.");
213
+
214
+ end:
215
+ rb_str_set_len(str, nread);
216
+ OBJ_TAINT(str);
217
+
218
+ return str;
219
+ }
220
+
221
+ /*
222
+ * call-seq:
223
+ * ssl.write_nonblock(string) => integer
224
+ */
225
+ static VALUE
226
+ Rev_SSL_IO_write_nonblock(VALUE self, VALUE str)
227
+ {
228
+ SSL *ssl;
229
+ int nwrite = 0;
230
+
231
+ StringValue(str);
232
+ Data_Get_Struct(self, SSL, ssl);
233
+
234
+ if (ssl) {
235
+ nwrite = SSL_write(ssl, RSTRING_PTR(str), RSTRING_LEN(str));
236
+ switch(SSL_get_error(ssl, nwrite)){
237
+ case SSL_ERROR_NONE:
238
+ goto end;
239
+ case SSL_ERROR_WANT_WRITE:
240
+ rb_raise(eRev_SSL_IO_WriteAgain, "write again");
241
+ case SSL_ERROR_WANT_READ:
242
+ rb_raise(eRev_SSL_IO_ReadAgain, "read again");
243
+ case SSL_ERROR_SYSCALL:
244
+ if (errno) rb_sys_fail(0);
245
+ default:
246
+ rb_raise(eSSLError, "SSL_write:");
247
+ }
248
+ } else
249
+ rb_raise(rb_eRuntimeError, "SSL session is not started yet.");
250
+
251
+ end:
252
+ return INT2NUM(nwrite);
253
+ }
254
+
255
+ #endif
@@ -12,13 +12,11 @@
12
12
  #include "rev.h"
13
13
  #include "rev_watcher.h"
14
14
 
15
- /* Module and object handles */
16
15
  static VALUE mRev = Qnil;
17
16
  static VALUE cRev_Watcher = Qnil;
18
17
  static VALUE cRev_TimerWatcher = Qnil;
19
18
  static VALUE cRev_Loop = Qnil;
20
19
 
21
- /* Method implementations */
22
20
  static VALUE Rev_TimerWatcher_initialize(int argc, VALUE *argv, VALUE self);
23
21
  static VALUE Rev_TimerWatcher_attach(VALUE self, VALUE loop);
24
22
  static VALUE Rev_TimerWatcher_detach(VALUE self);
@@ -27,7 +25,6 @@ static VALUE Rev_TimerWatcher_disable(VALUE self);
27
25
  static VALUE Rev_TimerWatcher_reset(VALUE self);
28
26
  static VALUE Rev_TimerWatcher_on_timer(VALUE self);
29
27
 
30
- /* Callbacks */
31
28
  static void Rev_TimerWatcher_libev_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
32
29
  static void Rev_TimerWatcher_dispatch_callback(VALUE self, int revents);
33
30
 
@@ -79,8 +76,8 @@ static VALUE Rev_TimerWatcher_initialize(int argc, VALUE *argv, VALUE self)
79
76
  ev_timer_init(
80
77
  &watcher_data->event_types.ev_timer,
81
78
  Rev_TimerWatcher_libev_callback,
82
- RFLOAT_VALUE(interval),
83
- repeating == Qtrue ? RFLOAT_VALUE(interval) : 0
79
+ NUM2DBL(interval),
80
+ repeating == Qtrue ? NUM2DBL(interval) : 0
84
81
  );
85
82
  watcher_data->event_types.ev_timer.data = (void *)self;
86
83
 
@@ -112,7 +109,7 @@ static VALUE Rev_TimerWatcher_attach(VALUE self, VALUE loop)
112
109
  watcher_data->loop = loop;
113
110
 
114
111
  /* Calibrate timeout to account for potential drift */
115
- interval = RFLOAT_VALUE(rb_iv_get(self, "@interval"));
112
+ interval = NUM2DBL(rb_iv_get(self, "@interval"));
116
113
  timeout = interval + ev_time() - ev_now(loop_data->ev_loop);
117
114
 
118
115
  ev_timer_set(
@@ -0,0 +1,108 @@
1
+ /*
2
+ * Copyright (C) 2007 Tony Arcieri
3
+ * You may redistribute this under the terms of the Ruby license.
4
+ * See LICENSE for details
5
+ */
6
+
7
+ #include "ruby.h"
8
+
9
+ #include <sys/resource.h>
10
+
11
+ #ifdef HAVE_SYS_SYSCTL_H
12
+ #include <sys/param.h>
13
+ #include <sys/sysctl.h>
14
+ #endif
15
+
16
+ static VALUE mRev = Qnil;
17
+ static VALUE cRev_Utils = Qnil;
18
+
19
+ static VALUE Rev_Utils_ncpus(VALUE self);
20
+ static VALUE Rev_Utils_maxfds(VALUE self);
21
+ static VALUE Rev_Utils_setmaxfds(VALUE self, VALUE max);
22
+
23
+ /*
24
+ * Assorted utility routines
25
+ */
26
+ void Init_rev_utils()
27
+ {
28
+ mRev = rb_define_module("Rev");
29
+ cRev_Utils = rb_define_module_under(mRev, "Utils");
30
+
31
+ rb_define_singleton_method(cRev_Utils, "ncpus", Rev_Utils_ncpus, 0);
32
+ rb_define_singleton_method(cRev_Utils, "maxfds", Rev_Utils_maxfds, 0);
33
+ rb_define_singleton_method(cRev_Utils, "maxfds=", Rev_Utils_setmaxfds, 1);
34
+ }
35
+
36
+ /**
37
+ * call-seq:
38
+ * Rev::Utils.ncpus -> Integer
39
+ *
40
+ * Return the number of CPUs in the present system
41
+ */
42
+ static VALUE Rev_Utils_ncpus(VALUE self)
43
+ {
44
+ int ncpus = 0;
45
+
46
+ #ifdef HAVE_LINUX_PROCFS
47
+ #define HAVE_REV_UTILS_NCPUS
48
+ char buf[512];
49
+ FILE *cpuinfo;
50
+
51
+ if(!(cpuinfo = fopen("/proc/cpuinfo", "r")))
52
+ rb_sys_fail("fopen");
53
+
54
+ while(fgets(buf, 512, cpuinfo)) {
55
+ if(!strncmp(buf, "processor", 9))
56
+ ncpus++;
57
+ }
58
+ #endif
59
+
60
+ #ifdef HAVE_SYSCTLBYNAME
61
+ #define HAVE_REV_UTILS_NCPUS
62
+ size_t size = sizeof(int);
63
+
64
+ if(sysctlbyname("hw.ncpu", &ncpus, &size, NULL, 0))
65
+ return INT2NUM(1);
66
+ #endif
67
+
68
+ #ifndef HAVE_REV_UTILS_NCPUS
69
+ rb_raise(rb_eRuntimeError, "operation not supported");
70
+ #endif
71
+
72
+ return INT2NUM(ncpus);
73
+ }
74
+
75
+ /**
76
+ * call-seq:
77
+ * Rev::Utils.maxfds -> Integer
78
+ *
79
+ * Return the maximum number of files descriptors available to the process
80
+ */
81
+ static VALUE Rev_Utils_maxfds(VALUE self)
82
+ {
83
+ struct rlimit rlim;
84
+
85
+ if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
86
+ rb_sys_fail("getrlimit");
87
+
88
+ return INT2NUM(rlim.rlim_cur);
89
+ }
90
+
91
+ /**
92
+ * call-seq:
93
+ * Rev::Utils.maxfds=(count) -> Integer
94
+ *
95
+ * Set the number of file descriptors available to the process. May require
96
+ * superuser privileges.
97
+ */
98
+ static VALUE Rev_Utils_setmaxfds(VALUE self, VALUE max)
99
+ {
100
+ struct rlimit rlim;
101
+
102
+ rlim.rlim_cur = NUM2INT(max);
103
+
104
+ if(setrlimit(RLIMIT_NOFILE, &rlim) < 0)
105
+ rb_sys_fail("setrlimit");
106
+
107
+ return max;
108
+ }
@@ -11,16 +11,13 @@
11
11
 
12
12
  #include "rev.h"
13
13
 
14
- /* Module and object handles */
15
14
  static VALUE mRev = Qnil;
16
15
  static VALUE cRev_Watcher = Qnil;
17
16
 
18
- /* Data allocators and deallocators */
19
17
  static VALUE Rev_Watcher_allocate(VALUE klass);
20
18
  static void Rev_Watcher_mark(struct Rev_Watcher *watcher);
21
19
  static void Rev_Watcher_free(struct Rev_Watcher *watcher);
22
20
 
23
- /* Method implementations */
24
21
  static VALUE Rev_Watcher_initialize(VALUE self);
25
22
  static VALUE Rev_Watcher_attach(VALUE self, VALUE loop);
26
23
  static VALUE Rev_Watcher_detach(VALUE self);
data/lib/rev.rb CHANGED
@@ -9,6 +9,7 @@ require File.dirname(__FILE__) + '/rev/loop'
9
9
  require File.dirname(__FILE__) + '/rev/watcher'
10
10
  require File.dirname(__FILE__) + '/rev/io_watcher'
11
11
  require File.dirname(__FILE__) + '/rev/timer_watcher'
12
+ require File.dirname(__FILE__) + '/rev/async_watcher'
12
13
  require File.dirname(__FILE__) + '/rev/listener'
13
14
  require File.dirname(__FILE__) + '/rev/io'
14
15
  require File.dirname(__FILE__) + '/rev/dns_resolver'
@@ -17,6 +18,6 @@ require File.dirname(__FILE__) + '/rev/server'
17
18
  require File.dirname(__FILE__) + '/rev/http_client'
18
19
 
19
20
  module Rev
20
- Rev::VERSION = '0.1.4' unless defined? Rev::VERSION
21
+ Rev::VERSION = '0.2.0' unless defined? Rev::VERSION
21
22
  def self.version() VERSION end
22
23
  end
@@ -0,0 +1,38 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ module Rev
8
+ # The AsyncWatcher lets you signal another thread to wake up. Its
9
+ # intended use is notifying another thread of events.
10
+ class AsyncWatcher < IOWatcher
11
+ def initialize
12
+ @reader, @writer = ::IO.pipe
13
+ super(@reader)
14
+ end
15
+
16
+ # Signal the async watcher. This call is thread safe.
17
+ def signal
18
+ # Write a byte to the pipe. What we write is meaningless, it
19
+ # merely signals an event has occurred for each byte written.
20
+ @writer.write "\0"
21
+ end
22
+
23
+ # Called whenever a signal is received
24
+ def on_signal; end
25
+ event_callback :on_signal
26
+
27
+ #########
28
+ protected
29
+ #########
30
+
31
+ def on_readable
32
+ # Read a byte from the pipe. This clears readability, unless
33
+ # another signal is pending
34
+ @reader.read 1
35
+ on_signal
36
+ end
37
+ end
38
+ end