openssl-nonblock is a gem which enables non-blocking support in Ruby’s
OpenSSL classes by monkeypatching into the heart of some of its C code.
It’s vile, it’s nasty, but it works!
Thanks to a patchfrom Young H., the latest release now uses exceptions
which aim to bridge compatibility with the non-blocking SSL support in
the
forthcoming Ruby 1.9.2 release. When a read or write is needed,
openssl-nonblock now raises IO::WaitReadable or IO::WaitWritable,
depending
on the IO event that needs waiting upon.
This release also effectively disables itself on Ruby 1.9.2, deferring
to
the native implementation. This means you can require openssl-nonblock
as a
dependency on Ruby 1.8.6+, where it will bridge the gap, but on Ruby
1.9.2
it will effectively shut itself off and let you use the core Ruby
implementation directly.
I have not probed the semantic gap between the gem and the core
implementation in Ruby 1.9.2, so this release should be considered
experimental. You can expect a forthcoming 0.3.0 release which will
ensure,
as best as possible, that the openssl-nonblock gem implements an API
identical to what will be found in Ruby 1.9.2.
And perhaps an FYI as to why I am CCing ruby-core:
I am not sure how closely my implementation matches what’s in Ruby
1.9.2.
My apologies for not further investigating this myself. I would like to
match what’s in Ruby 1.9.2. thoroughly and completely, so any comments
as to
how well I’ve matched the Ruby 1.9.2 API are most certainly welcome.
I implement the following methods as part of OpenSSL::SSL::SSLSocket:
connect_nonblock: connect to an SSL server
accept_nonblock: accept an SSL connection
read_nonblock(length): read up to the given length, but don’t block
write_nonblock(data): write as much of the given data as possible
without blocking
I raise the following exceptions:
IO::WaitReadable: this means OpenSSL needs to read more data to
complete the
given request. You should wait until the underlying IO object becomes
readable again before retrying the request.
IO::WaitWritable similar to above, except OpenSSL is trying to
write
data to
the IO object, and the IO object is not presently writable. You
should
wait
until the object becomes writable again before retrying the request.
Does this sound like I am properly matching what is available in Ruby
1.9.2?
given request. You should wait until the underlying IO object becomes
readable again before retrying the request.
• IO::WaitWritable similar to above, except OpenSSL is trying to
write data to
the IO object, and the IO object is not presently writable. You
should wait
until the object becomes writable again before retrying the request.
Does this sound like I am properly matching what is available in
Ruby 1.9.2?
I compared the behavior of openssl-nonblock to Ruby 1.9.2 (but not
exhaustively), and the main issue I saw was the use of a different
underlying exception object. Ruby 1.9.2 raises an SSLError while
openssl-nonblock raises its own ReadAgain/WriteAgain object extended
with IO::WaitReadable/IO::WaitWritable. Now, if all one cares about
is being notified that the operation would block (that is, if one is
just rescuing WaitReadable/WaitWritable), then openssl-nonblock and
Ruby 1.9.2 are indistinguishable. However, it’s possible for someone
to rescue SSLError directly, and then the difference shows up.
I tried creating a patch to make openssl-nonblock use SSLError just
like Ruby 1.9.2, but that seemed like it was going to be more work
than I expected (partly because somethings are defined static inside
the OpenSSL extension).
I’m not sure how important this difference is in practice, because it
mainly affects a narrow use case–running a program written for Ruby
1.9.2 or later on an older Ruby (that is, backporting features).
That’s a different use case than supporting nonblocking OpenSSL I/O on
older Ruby versions in a forward compatible manner (obviously, for
portability, such code should not rescue ReadAgain/WriteAgain, only
WaitReadable/WaitWritable).
I am not sure how closely my implementation matches what’s in Ruby 1.9.2.
My apologies for not further investigating this myself. I would like to
match what’s in Ruby 1.9.2. thoroughly and completely, so any comments as to
how well I’ve matched the Ruby 1.9.2 API are most certainly welcome.
Use the tests comes with Ruby 1.9.2.
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.