Skip to content
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

Allow Puma to compile when built without SSL, load SSL files on demand #2305

Merged
merged 5 commits into from Sep 14, 2020

Conversation

MSP-Greg
Copy link
Member

@MSP-Greg MSP-Greg commented Jul 6, 2020

Description

Currently, Puma will not function if built without SSL support. Also, SSL related files are all loaded regardless, so make them load-on-demand.

Changes

  1. Allows compiling without OpenSSL & runs 'no ssl' CI on a few jobs
  2. SSL test (now Puma::HAS_SSL or Puma.ssl?) relies on classes that only exist in the compiled extension if properly compiled with OpenSSL.
  3. If compiled with 'no ssl', don't load ssl related files in Puma. Also, don't load Ruby MRI OpenSSL. JRuby currently requires it for some reason. (?)

Commits (need to be squashed for bisect)

  1. 'Adjust code for compiling without SSL (MRI & JRuby), add ssl detection' - Removes MiniSSL.check, replace with Puma::HAS_SSL or Puma.ssl?

  2. 'Adjust test files for 'no ssl' compile'

  3. 'Actions - add 'no ssl' workflow, puma-no-ssl.yml' - three jobs: Ubuntu-20.04 with 2.7 & JRuby, and Windows 2.7

  4. 'Update History.md'

  5. 'README.md - add 'SSL Connection Support' section'

Your checklist for this pull request

  • I have reviewed the guidelines for contributing to this repository.
  • I have added an entry to History.md if this PR fixes a bug or adds a feature. If it doesn't need an entry to HISTORY.md, I have added [changelog skip] the pull request title.
  • I have added appropriate tests if this PR fixes a bug or adds a feature.
  • My pull request is 100 lines added/removed or less so that it can be easily reviewed.
  • If this PR doesn't need tests (docs change), I added [ci skip] to the title of the PR.
  • If this closes any issues, I have added "Closes #issue" to the PR description or my commit messages.
  • I have updated the documentation accordingly.
  • All new and existing tests passed, including Rubocop.

lib/puma/binder.rb Outdated Show resolved Hide resolved
@MSP-Greg MSP-Greg force-pushed the no-ssl branch 2 times, most recently from f364079 to 68cc2df Compare July 7, 2020 01:19
@sj26
Copy link
Contributor

sj26 commented Jul 7, 2020

Thanks for the quick work on this! Sorry it took me a while to test it, complications. But I just gave it a go and it works great. 👍

lib/puma/binder.rb Outdated Show resolved Hide resolved
@@ -308,5 +307,5 @@ def close
@socket.close unless @socket.closed? # closed? call is for Windows
end
end
end
end if IS_JRUBY || (Puma::MiniSSL.check.nil? rescue nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a better check, silently skipping tests if e.g. there is a typo, seems bad.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That check is to not load minissl.rb if MRI Puma wasn't compiled with SSL support. Similar checks for some of the tests also. The silent skips will only happen when Puma isn't compiled with SSL support. I'm working with it in my fork (adding a 'none SSL' job) and locally. JFYI, working with JRuby head locally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can leave a comment (or add a new method to Puma detect) to explain.

Copy link
Member Author

@MSP-Greg MSP-Greg Sep 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Let me think about that (MRI/JRuby/TruffleRuby). Puma detect is good, but it overlaps with Puma::MiniSSL.check. Maybe lose Puma::MiniSSL.check and have a method in detect that returns a boolean rather than the damn error in Puma::MiniSSL.check. Someone using it with UNIXSockets may chose to compile without OpenSSL... Sorry, rant off.

@nateberkopec nateberkopec added bug maintenance waiting-for-changes Waiting on changes from the requestor labels Jul 16, 2020
@nateberkopec nateberkopec added waiting-for-review Waiting on review from anyone and removed waiting-for-changes Waiting on changes from the requestor labels Sep 5, 2020
@nateberkopec
Copy link
Member

Hmm, how do we want to do CI for this? Just trigger it with an environment variable?

@nateberkopec nateberkopec added waiting-for-changes Waiting on changes from the requestor and removed waiting-for-review Waiting on review from anyone labels Sep 6, 2020
@MSP-Greg
Copy link
Member Author

MSP-Greg commented Sep 6, 2020

Hmm, how do we want to do CI for this? Just trigger it with an environment variable?

I was looking at that this week, and didn't finish. extconf.rb works with MRI, but I'm not sure about other platforms?

@nateberkopec
Copy link
Member

Well MRI is the only platform we're worried about supporting w/o SSL, though, right?

@MSP-Greg
Copy link
Member Author

Well MRI is the only platform we're worried about supporting w/o SSL, though, right?

No, I need to check it with JRuby, I'll also check against TruffleRuby, but I think it should behave similar to MRI... (?)

@MSP-Greg
Copy link
Member Author

MSP-Greg commented Sep 12, 2020

I believe I've got JRuby compiling without OpenSSL (simple changes).

At present, runtime SSL detection is kind of odd (putting that nicely). I hate using rescue to do so.

I think a better way is checking what classes exist in puma_http11.

Question - Since puma_http11 is always used, maybe move the require to Puma, and add the runtime OpenSSL detection in puma/detect.rb so we have a constant/method to check?

A goal is to not need to require Ruby's OpenSSL if Puma is compiled without OpenSSL...

@nateberkopec
Copy link
Member

Question - Since puma_http11 is always used, maybe move the require to Puma, and add the runtime OpenSSL detection in puma/detect.rb so we have a constant/method to check?

A goal is to not need to require Ruby's OpenSSL if Puma is compiled without OpenSSL...

Makes sense!

@MSP-Greg
Copy link
Member Author

MSP-Greg commented Sep 12, 2020

Two items:

  1. There are three classes (I think) that rescue MiniSSL or OpenSSL errors (Client, Reactor, & Server). If one compiles without SSL, these constants are not defined. I'm thinking of setting the rescue errors to LocalJumpError, which is defined, and will never be raised?

  2. I'll separate lib and test changes, but for this PR to bisect, it needs to be squashed.

@MSP-Greg
Copy link
Member Author

MSP-Greg commented Sep 12, 2020

This is done, updated notes, etc.

JRuby & TruffleRuby still intermittently lock up in testing. The last pair of jobs in my fork both passed, using JRuby for the 'no ssl' workflow (not jruby-head).

@MSP-Greg
Copy link
Member Author

I just pushed an update. Before, the code to determine whether Puma was built with ssl in detect.rb was:

HAS_SSL = const_defined?(:MiniSSL, false)

Updated code is:

# at present, MiniSSL::Engine is only defined in extension code, not in minissl.rb
HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)

Puma::MiniSSL is defined in minissl.rb (using module MiniSSL). So if minissl.rb was loaded first, the detection would fail. Currently, Puma::MiniSSL::Engine is only defined in 'extension' code, so it's not affected by loading minissl.rb...

lib/puma/binder.rb Show resolved Hide resolved
require 'puma/configuration'

module Puma

if HAS_SSL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of moving these from the top of the file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move it above and use Puma::HAS_SSL. I put it here for the namespace.

This was an issue I was about to post a message about.

Currently, if Puma successfully compiles with OpenSSL, it loads all the files. Should we make so that it only loads the files if one binds to an ssl socket?

That is trickier...

lib/puma/client.rb Outdated Show resolved Hide resolved
lib/puma/client.rb Outdated Show resolved Hide resolved
README.md Outdated
@@ -13,11 +13,19 @@ Puma is a **simple, fast, multi-threaded, and highly concurrent HTTP 1.1 server

## Built For Speed & Concurrency

Puma processes requests using a C-optimized Ragel extension (inherited from Mongrel) that provides fast, accurate HTTP 1.1 protocol parsing in a portable way. Puma then serves the request using a thread pool. Each request is served in a separate thread, so truly concurrent Ruby implementations (JRuby, Rubinius) will use all available CPU cores.
Puma processes requests using a C-optimized Ragel extension (inherited from
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you change anything in here other than 80-char line breaks?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a sentence to Quick Start, which maybe should be expanded on?

"Puma expects to find OpenSSL development files when installed/compiled. If you want to compile it without ssl support, set ENV['DISABLE_SSL']."

I can pull this and do a separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed to format changes and just added an ssl section.

@@ -1,6 +1,7 @@
## 5.0.0

* Features
* Allow compiling without OpenSSL and dynamically load files needed for SSL, add 'no ssl' CI (#2305)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like for JRuby, for this to work, you need to have DISABLE_SSL set, but for MRI, you don't. Is that true?

Copy link
Member Author

@MSP-Greg MSP-Greg Sep 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re MRI, I know how to uninstall 'openssl dev', and it works locally.

I don't know how to uninstall Java's equivalent of 'openssl dev'. For MRI systems, it's a separate install, I don't think that's the case with Java?

EDIT:

Sorry I wasn't clear.

for JRuby, for this to work, you need to have DISABLE_SSL set

Correct.

for MRI, you don't

If you don't have 'openssl dev' installed, there is no need for ENV['DISABLE_SSL']. If it is installed, you need to set the ENV variable in shell to disable compiling with OpenSSL.

@MSP-Greg
Copy link
Member Author

Interesting. The last push (which passed CI here) here also ran in my fork. The only failure was on
ubuntu-18.04 truffleruby-head, see:

https://github.com/MSP-Greg/puma/runs/1109525323?check_suite_focus=true#step:7:599

Note that the test suite passed, but it just barely timed out, so it shows as a failure. First time I've seen that, but maybe extend the test step's timeout to 12? Hate to do that...

@MSP-Greg
Copy link
Member Author

I noticed that test_puma_server_ssl.rb would not run as a single test file:

rake test TESTOPTS=--verbose TEST=test/test_puma_server_ssl.rb

Fixed.

@MSP-Greg
Copy link
Member Author

Removed Actions TruffleRuby RuboCop commit

If the system does not have OpenSSL development files installed, Puma will
install/compile, but it will not allow ssl connections.

If the system has OpenSSL development files installed, but you don't want Puma
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is a little unclear. If you don't bind Puma to SSL, why should you need to use DISABLE_SSL?

As written, this makes it sound like anyone using Puma w/o SSL must set DISABLE_SSL.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't bind Puma to SSL, why should you need to use DISABLE_SSL?

You don't have to, but 'If the system has OpenSSL development files', it will compile the SSL functions into puma_http11.so/jar and will load the OpenSSL libraries/dlls when using MRI. It also loads Ruby OpenSSL on MRI.
I believe using DISABLE_SSL stops all of that.

this makes it sound like anyone using Puma w/o SSL must set DISABLE_SSL.

Didn't mean to imply that. Maybe a rephrasing is in order. I meant to make it clear that it's optional. But, I didn't say anything about benefits...

BTW, thanks for reviewing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I just ended up removing it. I think it's too confusing. Good feature to have but not one that needs explaining right in README.md.

Thanks so much for your work on the test suite over the last 2 weeks Greg, it's so much better now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug maintenance waiting-for-changes Waiting on changes from the requestor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants