Skip to content

Commit 6c1db60

Browse files
committed
Add support for nonblocking custom BIO
1 parent 393d5fb commit 6c1db60

File tree

3 files changed

+65
-1
lines changed

3 files changed

+65
-1
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ static VALUE eSSLErrorWaitReadable;
3838
static VALUE eSSLErrorWaitWritable;
3939

4040
static ID id_call, ID_callback_state, id_npn_protocols_encoded, id_each;
41-
static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
41+
static VALUE sym_exception;
42+
VALUE sym_wait_readable, sym_wait_writable;
4243

4344
static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
4445
id_i_verify_depth, id_i_verify_callback, id_i_client_ca,

ext/openssl/ossl_ssl_custom_bio.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,18 @@
1515
#include "ruby/io/buffer.h"
1616

1717
extern VALUE eSSLError;
18+
extern VALUE sym_wait_readable, sym_wait_writable;
1819
static ID id_read, id_write, id_call, id_eof_p;
1920

21+
inline void bio_set_retry_state(BIO *bio, VALUE sym) {
22+
if (sym == sym_wait_readable)
23+
BIO_set_retry_read(bio);
24+
else if (sym == sym_wait_writable)
25+
BIO_set_retry_write(bio);
26+
else
27+
rb_raise(eSSLError, "Invalid return value from custom BIO");
28+
}
29+
2030
static int
2131
ossl_ssl_custom_bio_in_read(BIO *bio, char *buf, int blen)
2232
{
@@ -37,6 +47,11 @@ ossl_ssl_custom_bio_in_read(BIO *bio, char *buf, int blen)
3747
VALUE buffer = rb_io_buffer_new(buf, blen, RB_IO_BUFFER_LOCKED);
3848
VALUE res = rb_funcall(read_proc, id_call, 2, buffer, INT2NUM(blen));
3949
rb_io_buffer_free_locked(buffer);
50+
51+
if (TYPE(res) == T_SYMBOL) {
52+
bio_set_retry_state(bio, res);
53+
return 0;
54+
}
4055
return NUM2INT(res);
4156
}
4257
default:
@@ -63,6 +78,11 @@ ossl_ssl_custom_bio_out_write(BIO *bio, const char *buf, int blen)
6378
VALUE res = rb_funcall(write_proc, id_call, 2, buffer, INT2NUM(blen));
6479
RB_GC_GUARD(buffer);
6580
rb_io_buffer_free_locked(buffer);
81+
82+
if (TYPE(res) == T_SYMBOL) {
83+
bio_set_retry_state(bio, res);
84+
return 0;
85+
}
6686
return NUM2INT(res);
6787
}
6888
default:

test/openssl/test_ssl.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,6 +2488,49 @@ def test_bio_method_custom_exception
24882488
end
24892489
}
24902490
end
2491+
2492+
def test_bio_method_custom_nonblock
2493+
omit_on_no_bio_method
2494+
2495+
start_server(ignore_listener_error: true) { |port|
2496+
begin
2497+
ops = []
2498+
ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port)
2499+
ssl.sync_close = true
2500+
2501+
io = ssl.to_io
2502+
read_proc = ->(buf, maxlen) {
2503+
ops << :read
2504+
ret = io.read_nonblock(maxlen, exception: false)
2505+
return ret if ret.is_a?(Symbol)
2506+
2507+
len = ret.bytesize
2508+
buf.set_string(ret)
2509+
len
2510+
}
2511+
write_proc = ->(buf, len) {
2512+
ops << :write
2513+
str = buf.get_string(0, len)
2514+
io.write_nonblock(str, exception: false)
2515+
}
2516+
2517+
ssl.bio_method = [read_proc, write_proc]
2518+
2519+
return_values = []
2520+
loop do
2521+
ret = ssl.connect_nonblock(exception: false)
2522+
return_values << ret
2523+
break if ret == ssl
2524+
end
2525+
2526+
assert_equal [:write, :read], ops.uniq
2527+
assert_equal [:wait_readable, ssl], return_values.uniq
2528+
ensure
2529+
ssl&.close rescue nil
2530+
end
2531+
}
2532+
end
2533+
24912534
def test_bio_method_invalid
24922535
omit_on_no_bio_method
24932536

0 commit comments

Comments
 (0)