From 455c1d63e87c243c7cc2af2bc9211d9954138778 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 13 Jan 2024 22:22:36 +1300 Subject: [PATCH 1/5] Add support for IO#timeout. --- ext/openssl/extconf.rb | 1 + ext/openssl/ossl_ssl.c | 15 +++++++++++++-- lib/openssl/ssl.rb | 10 ++++++++++ test/openssl/test_ssl.rb | 11 +++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 56f4a1c3a..dadf66cc0 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -49,6 +49,7 @@ have_func("rb_io_descriptor") have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1 +have_var("rb_eIOTimeoutError", "ruby/io.h") Logging::message "=== Checking for system dependent stuff... ===\n" have_library("nsl", "t_open") diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 236d455ff..3735a57f8 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1725,11 +1725,20 @@ no_exception_p(VALUE opts) #define RUBY_IO_TIMEOUT_DEFAULT Qnil #endif +#ifdef HAVE_RB_EIOTIMEOUTERROR +#define IO_TIMEOUT_ERROR rb_eIOTimeoutError +#else +#define IO_TIMEOUT_ERROR rb_eIOError +#endif + + static void io_wait_writable(VALUE io) { #ifdef HAVE_RB_IO_MAYBE_WAIT - rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT); + if (!rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) { + rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become writable!"); + } #else rb_io_t *fptr; GetOpenFile(io, fptr); @@ -1741,7 +1750,9 @@ static void io_wait_readable(VALUE io) { #ifdef HAVE_RB_IO_MAYBE_WAIT - rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT); + if (!rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) { + rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become readable!"); + } #else rb_io_t *fptr; GetOpenFile(io, fptr); diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb index ccc945f2f..4278eb1b4 100644 --- a/lib/openssl/ssl.rb +++ b/lib/openssl/ssl.rb @@ -299,6 +299,16 @@ def wait_readable(*args) def wait_writable(*args) to_io.wait_writable(*args) end + + if IO.method_defined?(:timeout) + def timeout + to_io.timeout + end + + def timeout=(value) + to_io.timeout=(value) + end + end end def verify_certificate_identity(cert, hostname) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 07dc9a343..14b3dc3bd 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -193,6 +193,17 @@ def test_sysread_and_syswrite } end + def test_read_with_timeout + omit "does not support timeout" unless IO.method_defined?(:timeout) + + start_server do |port| + server_connect(port) do |ssl| + ssl.timeout = 0.001 + assert_raise(IO::TimeoutError) {ssl.read(1)} + end + end + end + def test_getbyte start_server { |port| server_connect(port) { |ssl| From a9eec79b373c8905646fe1f408c2032b027af440 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 13 Jan 2024 23:05:26 +1300 Subject: [PATCH 2/5] Prefer checking for `rb_io_timeout`. --- ext/openssl/extconf.rb | 2 +- ext/openssl/ossl_ssl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index dadf66cc0..4119c72c4 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -49,7 +49,7 @@ have_func("rb_io_descriptor") have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1 -have_var("rb_eIOTimeoutError", "ruby/io.h") +have_func("rb_io_timeout", "ruby/io.h") Logging::message "=== Checking for system dependent stuff... ===\n" have_library("nsl", "t_open") diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 3735a57f8..9f374b65f 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1725,7 +1725,7 @@ no_exception_p(VALUE opts) #define RUBY_IO_TIMEOUT_DEFAULT Qnil #endif -#ifdef HAVE_RB_EIOTIMEOUTERROR +#ifdef HAVE_RB_IO_TIMEOUT #define IO_TIMEOUT_ERROR rb_eIOTimeoutError #else #define IO_TIMEOUT_ERROR rb_eIOError From 135e2b25d2cd4d2beca538f8af070719da1c7f5b Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 13 Jan 2024 23:15:20 +1300 Subject: [PATCH 3/5] Ensure connection is closed for windows? --- test/openssl/test_ssl.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 14b3dc3bd..4c69c8a19 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -200,6 +200,8 @@ def test_read_with_timeout server_connect(port) do |ssl| ssl.timeout = 0.001 assert_raise(IO::TimeoutError) {ssl.read(1)} + ensure + ssl.close end end end From f29a31f925dd26512a0c9f088212ab62b18f322f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 14 Jan 2024 00:09:01 +1300 Subject: [PATCH 4/5] Try to do a bit more for windows? --- test/openssl/test_ssl.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 4c69c8a19..ce88ee56b 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -198,10 +198,15 @@ def test_read_with_timeout start_server do |port| server_connect(port) do |ssl| - ssl.timeout = 0.001 + str = +("x" * 100 + "\n") + ssl.syswrite(str) + assert_equal(str, ssl.sysread(str.bytesize)) + + ssl.timeout = 0.01 assert_raise(IO::TimeoutError) {ssl.read(1)} - ensure - ssl.close + + ssl.syswrite(str) + assert_equal(str, ssl.sysread(str.bytesize)) end end end From 898f3bcac75275cc5fc03331871aa7b8784b0d2f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 14 Jan 2024 10:49:47 +1300 Subject: [PATCH 5/5] Increase timeout. --- test/openssl/test_ssl.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index ce88ee56b..dcb7757ad 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -202,7 +202,7 @@ def test_read_with_timeout ssl.syswrite(str) assert_equal(str, ssl.sysread(str.bytesize)) - ssl.timeout = 0.01 + ssl.timeout = 1 assert_raise(IO::TimeoutError) {ssl.read(1)} ssl.syswrite(str)