puma: Error in reactor loop escaped: System error: Undefined error: 0 - 0 (Puma::MiniSSL::SSLError)

I am setting up capybara to use SSL through puma. It all works in terms of connectivity, but this gets putsed out to STDOUT and I can find no way to fix it or silence it. I’m ok with either as a solution.

Steps to reproduce

  1. Setup SSL as such:
# https://gist.github.com/tadast/9932075
if Rails.env.test?
  unless File.exist?(Rails.root.join('config', 'ssl', 'localhost.test.key'))
    def generate_root_cert(root_key)
      root_ca = OpenSSL::X509::Certificate.new
      root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
      root_ca.serial = 0x0
      root_ca.subject = OpenSSL::X509::Name.parse "/C=BE/O=A1/OU=A/CN=localhost.test"
      root_ca.issuer = root_ca.subject # root CA's are "self-signed"
      root_ca.public_key = root_key.public_key
      root_ca.not_before = Time.now
      root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
      root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
      root_ca
    end

    root_key = OpenSSL::PKey::RSA.new(2048)
    file = File.new( Rails.root.join('config', 'ssl', 'localhost.test.key'), "wb")
    file.write(root_key)
    file.close

    root_cert = generate_root_cert(root_key)

    file = File.new( Rails.root.join('config','ssl', 'localhost.test.crt'), "wb")
    file.write(root_cert)
    file.close
  end
    
  ssl_bind 'localhost.test', 3000, {
    key: Rails.root.join('config','ssl', 'localhost.test.key'),
    cert: Rails.root.join('config','ssl', 'localhost.test.crt'),
    verify_mode: 'none'
  }
end
  1. Run Rspec feature specs using capybara, headless chrome, and puma
# https://robots.thoughtbot.com/headless-feature-specs-with-chrome
Capybara.register_driver :chrome do |app|
  Capybara::Selenium::Driver.new(app, browser: :chrome)
end

Capybara.register_driver :headless_chrome do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: {
      args: %w(headless disable-gpu),
      'profile.managed_default_content_settings.notifications': 2,
    }
  )
  
  profile = Selenium::WebDriver::Chrome::Profile.new
  profile["profile.default_content_settings"] = { images: '2' }

  options = Selenium::WebDriver::Chrome::Options.new(args: ['headless', '--blink-settings=imagesEnabled=false'])

  Capybara::Selenium::Driver.new app,
    browser: :chrome,
    desired_capabilities: capabilities,
    profile: profile,
    options: options
end

Capybara.javascript_driver = :headless_chrome
# If you’d like to watch the tests execute while debugging, you can change the driver to chrome.
# Capybara.javascript_driver = :chrome
# Screenshot with save_and_open_screenshot





key_file = Rails.root.join('config', 'ssl', 'localhost.test.key')
cert_file = Rails.root.join('config', 'ssl', 'localhost.test.crt')

Capybara.server = :puma, {
  Silent: true,
  Host: "ssl://#{Capybara.server_host}:#{Capybara.server_port}?key=#{key_file}&cert=#{cert_file}"
}

Expected behavior

I would expect Silent: true to actually keep things silent.

Actual behavior

I get the following puts on every run:

Error in reactor loop escaped: System error: Undefined error: 0 - 0 (Puma::MiniSSL::SSLError)
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/minissl.rb:41:in `read'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/minissl.rb:41:in `engine_read_all'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/minissl.rb:52:in `read_nonblock'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/minissl.rb:127:in `read_and_drop'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/minissl.rb:144:in `close'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/client.rb:123:in `close'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/reactor.rb:212:in `rescue in block in run_internal'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/reactor.rb:170:in `block in run_internal'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/reactor.rb:140:in `each'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/reactor.rb:140:in `run_internal'
/Users/[REDACTED]/.rvm/gems/ruby-2.5.3@[REDACTED]/gems/puma-3.12.0/lib/puma/reactor.rb:251:in `block in run_in_thread'

System configuration

Ruby version: 2.5.3 Rails version: 5.2.2 Puma version: 3.12.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 11
  • Comments: 32 (14 by maintainers)

Most upvoted comments

@hwhelchel

are you working on a PR

Not right now. This is a bit messy, as I’m not sure if the issue is specific to OpenSSL 1.1.1 or not. If it is, it might point to an issue in minissl.rb or minissl.c.

Regardless, I think expecting a socket to close when it has already generated an error is dicey. The main thing Puma should be doing is removing it from the reactor, closing it might even be something that GC could do, especially if we assume autoclose is always true. Haven’t checked.

It could be a malicious connection, which might be better to ignore.

Sorry for generalizing things.

@MSP-Greg switching to c.close rescue nil on line 266 silenced the error. Switching to c.close unless c.closed? had no effect. Looks like c.close rescue nil on line 266 is what we want.

@hwhelchel

Two other suggestions, replace: https://github.com/puma/puma/blob/5fef2b715fdb69697161b21585c118995b6ee399/lib/puma/reactor.rb#L266 with:

c.lose unless c.closed?

or

c.close rescue nil

Sorry there’s a bit more to the incantation than I thought.

rails runner "require 'puma/minissl'; require 'puma/puma_http11'; Puma::Server.class; Puma::MiniSSL.check; puts Puma::MiniSSL::OPENSSL_LIBRARY_VERSION"

Looks like this needs someone to think through Greg’s concerns a bit more and then submit a PR.