From 0d742d367e5a57a3d068b05b7f1f2342ec0fbacc Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Fri, 30 Oct 2026 15:48:21 +0900 Subject: [PATCH 1/4] ci: add JRuby test job --- .github/workflows/test.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7ec4ac9f..723bcd0d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,3 +63,25 @@ jobs: cache-version: 2 - name: Run tests run: bundle exec rake --trace RUBYOPT="${{ matrix.ruby-opt }}" + + jruby: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Java + uses: actions/setup-java@v6 + with: + distribution: temurin + java-version: '21' + - name: Set up JRuby + uses: ruby/setup-ruby@v1 + with: + ruby-version: jruby-10.1.2.0 + bundler-cache: true + cache-version: 2 + - name: Run tests + run: bundle exec rake --trace RUBYOPT="-rbundler/setup" + - name: Run JRuby-specific tests + run: bundle exec ruby -Ilib test/jruby_ssl_socket/test_pemutils.rb + - name: Run JRuby socket timeout tests + run: bundle exec ruby -Ilib test/jruby_ssl_socket/test_socket_timeout.rb From 0dff6ae28100f0ab3fa0f4b38fc07dbd6b8bfdd6 Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Fri, 30 Oct 2026 15:58:44 +0900 Subject: [PATCH 2/4] fix: restore JRuby SSL test compatibility --- lib/httpclient/jruby_ssl_socket.rb | 2 ++ test/helper.rb | 32 ++++++++++++++++++++ test/jruby_ssl_socket/test_socket_timeout.rb | 18 +++++++++++ test/test_ssl.rb | 12 ++++++-- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/jruby_ssl_socket/test_socket_timeout.rb diff --git a/lib/httpclient/jruby_ssl_socket.rb b/lib/httpclient/jruby_ssl_socket.rb index 4a30db66..ae2a8b0d 100644 --- a/lib/httpclient/jruby_ssl_socket.rb +++ b/lib/httpclient/jruby_ssl_socket.rb @@ -128,6 +128,8 @@ def fill @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size] end size + rescue java.net.SocketTimeoutException => e + raise HTTPClient::ReceiveTimeoutError.new(e.getMessage) rescue java.io.IOException => e raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}") end diff --git a/test/helper.rb b/test/helper.rb index 5ecbb85a..e58c2eb7 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -9,6 +9,38 @@ require 'cgi' require 'webrick/httputils' +if RUBY_ENGINE == 'jruby' + require 'delegate' + + module JRubyWEBrickSSLReadBuffer + class ReadableIO < SimpleDelegator + def initialize(socket) + @ssl_socket = socket + super(socket.to_io) + end + + def wait_readable(timeout = nil) + # JRuby may buffer request bytes while completing the TLS handshake. + @ssl_socket.pending > 0 || super + end + end + + module HTTPServerPatch + def run(sock) + if sock.respond_to?(:pending) + readable_io = ReadableIO.new(sock) + sock.define_singleton_method(:to_io) { readable_io } + end + super + ensure + sock.singleton_class.remove_method(:to_io) if readable_io + end + end + end + + WEBrick::HTTPServer.prepend(JRubyWEBrickSSLReadBuffer::HTTPServerPatch) +end + module Helper Port = 17171 diff --git a/test/jruby_ssl_socket/test_socket_timeout.rb b/test/jruby_ssl_socket/test_socket_timeout.rb new file mode 100644 index 00000000..8b854431 --- /dev/null +++ b/test/jruby_ssl_socket/test_socket_timeout.rb @@ -0,0 +1,18 @@ +require File.expand_path('../helper', File.dirname(__FILE__)) + + +class JRubySocketTimeoutTest < Test::Unit::TestCase + def test_read_timeout_is_reported_as_receive_timeout + socket = HTTPClient::JavaSocketWrap.allocate + input = Object.new + input.define_singleton_method(:read) do |_buffer| + raise Java::JavaNet::SocketTimeoutException.new('Read timed out') + end + socket.instance_variable_set(:@instr, input) + socket.instance_variable_set(:@buf, Java::byte[HTTPClient::JavaSocketWrap::BUF_SIZE].new) + + assert_raise(HTTPClient::ReceiveTimeoutError) do + socket.send(:fill) + end + end +end diff --git a/test/test_ssl.rb b/test/test_ssl.rb index 35cd23d2..44f0400e 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -386,7 +386,15 @@ def test_no_sslv3 end end - if OPENSSL_VERSION < 3 + if RUBY_ENGINE == 'jruby' + def test_disallow_tlsv1 + @client.ssl_config.ssl_version = :TLSv1 + @client.ssl_config.verify_mode = nil + assert_raise(OpenSSL::SSL::SSLError) do + @client.get(@url) + end + end + elsif OPENSSL_VERSION < 3 def test_allow_tlsv1 teardown_server setup_server_with_ssl_version(:TLSv1) @@ -408,7 +416,7 @@ def test_disallow_tlsv1 end def test_use_higher_TLS - omit('TODO: it does not pass with Java 7 or old openssl') + omit('TODO: it does not pass with Java 7 or old openssl') unless RUBY_ENGINE == 'jruby' teardown_server setup_server_with_ssl_version('TLSv1_2') assert_nothing_raised do From da6b6e47ceee5eaca65bda2db8dec92bc53ad94d Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Fri, 30 Oct 2026 16:04:11 +0900 Subject: [PATCH 3/4] test: use an ECDHE cipher for JRuby SSL checks --- test/test_ssl.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_ssl.rb b/test/test_ssl.rb index 44f0400e..be7dff39 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -332,7 +332,7 @@ def test_ciphers assert_match(/No appropriate protocol/, ssle.message) end # - cfg.ciphers = %w(TLS_RSA_WITH_AES_128_CBC_SHA) + cfg.ciphers = %w(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) assert_equal("hello", @client.get_content(@url)) # cfg.ciphers = HTTPClient::SSLConfig::CIPHERS_DEFAULT From 43fb167cf0b81f32cbe92ba8dc1c0f3b4cee64ab Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Fri, 30 Oct 2026 16:30:25 +0900 Subject: [PATCH 4/4] test: explain JRuby cipher compatibility --- test/test_ssl.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_ssl.rb b/test/test_ssl.rb index be7dff39..c8372744 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -332,6 +332,9 @@ def test_ciphers assert_match(/No appropriate protocol/, ssle.message) end # + # CI's Temurin 21 disables static-RSA suites such as + # TLS_RSA_WITH_AES_128_CBC_SHA in jdk.tls.disabledAlgorithms. Use an enabled + # ECDHE suite to verify explicitly configured ciphers can be negotiated. cfg.ciphers = %w(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) assert_equal("hello", @client.get_content(@url)) #