-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ssl: support IO-like object as the underlying transport #736
base: master
Are you sure you want to change the base?
Conversation
Implement a minimum BIO_METHOD required for SSL/TLS. The underlying IO-like object must implement the following methods: - #read_nonblock(len, exception: false) - #write_nonblock(str, exception: false) - #flush The IO-like object is also required to implement several other methods to function in a later commit in this series.
The result value is only used for generating an informative error message. Let's just say "unsupported" if it's not available.
The value is used to determine whether SSLSocket should skip buffering in OpenSSL::Buffering or not. Defaulting to true (no buffering) should be a safe option.
OpenSSL::SSL::SSLSocket currently requires a real IO (socket) object because it passes the file descriptor to OpenSSL. OpenSSL internally uses an I/O abstraction layer called BIO to interact with the underlying socket. BIO is pluggable; the implementation can be provided by a user application. It's possible to implement our own BIO implementation ("BIO method") that wraps any Ruby IO-like object. Let's do it. This allows establishing a TLS connection on top of another TLS connection. For the performance reason, this patch continues to use the original socket BIO if the user passes a real IO object.
BIO_set_retry_write(p->bio); | ||
return INT2FIX(0); | ||
} | ||
else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing an:
else if (ret == Qnil) {
when the socket is closed.
|
||
if (RB_TYPE_P(ret, T_STRING)) { | ||
int len = RSTRING_LENINT(ret); | ||
if (len > p->dlen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this check necessary?
BIO_clear_retry_flags(p->bio); | ||
|
||
VALUE fargs[] = { INT2NUM(p->dlen), nonblock_kwargs }; | ||
VALUE ret = rb_funcallv_public_kw(io, rb_intern("read_nonblock"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should't this ret
be somhow marked so it's not moved? (thinking of compaction here).
probably too early for optimizations, ,but this bit could benefit of a socket-local string to act as a buffer (second argument of :read_nonblock
)
p->readbytes = len; | ||
return INT2FIX(1); | ||
} | ||
else if (NIL_P(ret)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, right? (nil means socket is closed.) It should perhaps return something other than 0.
rb_io_set_nonblock(fptr); | ||
} | ||
else { | ||
// Not meant to be a comprehensive check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per the BIO impl, shouldn't this also verify "#flush"? (hypothetically, understand this wasn't as exhaustive check).
@@ -1735,6 +1805,11 @@ no_exception_p(VALUE opts) | |||
static void | |||
io_wait_writable(VALUE io) | |||
{ | |||
if (!is_real_socket(io)) { | |||
if (!RTEST(rb_funcallv(io, rb_intern("wait_writable"), 0, NULL))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be the same as the rb_io_maybe_wait_writable
call below?
@rhenium anything I can help with to move this forward? |
An implementation of #731. The test suite passes on my box, but it needs more testing especially around the error handling.
This adds support for IO-like object that is not backed by a file descriptor by defining a
BIO_METHOD
to wrap the following Ruby methods.