Skip to content

Conversation

olleolleolle
Copy link
Member

@olleolleolle olleolleolle commented Oct 14, 2016

This PR attempts to support Net::HTTP::Persistent 3.x or 2.x with its existing Faraday Adapter.

Changes introduced

  • For Net::HTTP::Persistent 3.x, there were changed APIs which needed changes in the existing Faraday Adapter
    • a constructor had gotten keyword arguments instead of positional ones
    • a cleaning-up method after SSL connections had been renamed
  • Introduce the use of ruby RUBY_VERSION Ruby version hinting for Bundler in the Gemfile.
    • The Gemfile is used during development and during test runs in CI.
    • with this, it was possible to avoid pinning to existing versions, and have the latest versions available supported by the currently running platform

TODO

  • use parameters instead of Net::HTTP::Persistent::VERSION to find out which version is running

Out of scope for this PR:

  • tests for all 2.x/3.x of Net::HTTP::Persistent on all Ruby platforms. Example solution: Thoughtbot's Appraisal gem with sets of generated Gemfiles

@olleolleolle olleolleolle changed the title Attempt to support Net::HTTP::Persistent v3.0.0 Adapter support for Net::HTTP::Persistent v3.0.0 Oct 14, 2016
mislav
mislav previously requested changes Oct 14, 2016
Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

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

Hey, thanks for trying to solve this!

Gemfile Outdated
@@ -13,7 +13,11 @@ group :test do
gem 'httpclient', '>= 2.2'
gem 'mime-types', '~> 1.25', :platforms => [:jruby, :ruby_18]
gem 'minitest', '>= 5.0.5'
gem 'net-http-persistent', '~> 2.9.4'
if RUBY_VERSION >= '2.1'
Copy link
Contributor

Choose a reason for hiding this comment

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

We can't use conditionals like these in a Gemfile. However, you can use the :platform to scope only to certain Rubies.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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


[!] There was an error parsing `Gemfile`: You cannot specify the same gem twice with different version requirements.
You specified: net-http-persistent (>= 0) and net-http-persistent (~> 2.9.4). Bundler cannot continue.

 #  from /Users/olle/opensource/faraday/Gemfile:20
 #  -------------------------------------------
 #    install_if -> { RUBY_VERSION < '2.1' } do
 >      gem 'net-http-persistent', '~> 2.9.4'
 #    end
 #  -------------------------------------------

Copy link
Member

Choose a reason for hiding this comment

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

@mislav This looks like the only working solution and is what it was done already in the past for other gems at the bottom of the Gemfile. Given the Gemfile is only used for tests, I think that is acceptable.

Copy link
Member Author

Choose a reason for hiding this comment

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

@iMacTia Would you want a squash-and-rebase? Any other changes?

Copy link
Member

Choose a reason for hiding this comment

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

No, I personally think that even though it's not the most elegant solution, this is the only one we can use in this specific case. So I'm saying that for me it's ok as it is.

@@ -17,8 +17,11 @@ def with_net_http_connection(env)
define_method(:password) { proxy[:password] }
end if proxy[:user]
end

yield Net::HTTP::Persistent.new 'Faraday', proxy_uri
if Net::HTTP::Persistent::VERSION >= '3'
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of checking a version string like this, could we simply check the arity of the constructor?

Net::HTTP::Persistent.method(:new).arity

We could detect how many arguments does the constructor expect this way, and switch between two different invocation styles without being dependent on the version.

Copy link
Member Author

Choose a reason for hiding this comment

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

Our problem here is that they both (3.x and 2.x) return -1 for that arity - they both default on each of the given arguments.

Inspired by this comment, I changed the test invocation to avoid testing for the version.

Copy link

Choose a reason for hiding this comment

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

🙇🏻

I switched this to open the possibility of adding arbitrary configuration keyword argument to the constructor. Keeping the few positional arguments seemed silly.

Copy link

Choose a reason for hiding this comment

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

This will break if there is ever a net-http-persistent 10 release, but otherwise is fine.

@@ -10,8 +10,10 @@ def setup
if defined?(Net::HTTP::Persistent)
# work around problems with mixed SSL certificates
# https://github.com/drbrain/net-http-persistent/issues/45
http = Net::HTTP::Persistent.new('Faraday')
http.ssl_cleanup(4)
if Net::HTTP::Persistent::VERSION < '3'
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto my comment about version string checking.

if Net::HTTP::Persistent::instance_methods.include?(:ssl_cleanup)
http = Net::HTTP::Persistent.new('Faraday')
http.ssl_cleanup(4)
end
Copy link
Member

Choose a reason for hiding this comment

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

Was this fixed in version 3.0 or simply the method was removed/renamed?
The issue appears to be still open and we're now skipping the ssl_cleanup...

Copy link
Member Author

@olleolleolle olleolleolle Oct 21, 2016

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

(Investigating.)

Is this the current cleanup? drbrain/net-http-persistent@dad5d68

I begin to think that this is the current way of "cleaning up" after finishing a connection.

Here's the ssl_cleanup implementation's comment (in v2.9.4):

Finishes all connections on the given +thread+ that were created before

the given +generation+ in the threads +generation_key+ list.

See #shutdown for a bunch of scary warning about misusing this method.

Copy link
Member

Choose a reason for hiding this comment

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

right, looks like you have to use this reuse_ssl_sessions. Did you spot also what the default value is?

Copy link
Member Author

Choose a reason for hiding this comment

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

This 2011 note from History.txt said:

For HTTPS connections, SSL sessions are now reused avoiding the extra
round trips and computations of extra SSL handshakes. If you have
problems with SSL session reuse it can be disabled by
Net::HTTP::Persistent#reuse_ssl_sessions

Copy link
Member Author

@olleolleolle olleolleolle Oct 21, 2016

Choose a reason for hiding this comment

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

Answer: By default, SSL connections are reused: https://github.com/drbrain/net-http-persistent/blob/master/lib/net/http/persistent.rb#L409 Set to false to not do that.

Copy link
Member

Choose a reason for hiding this comment

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

Ok so in order for tests to work properly, we need to set this to false at the beginning.
I'm wondering if this option exists on v2.9 as well given it was there in 2011 😮

Copy link

Choose a reason for hiding this comment

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

The comment immediately above this references issues with SSL client certificates, I think the correct thing to do is to call reconnect_ssl to address that.

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'll try adding a case for when that method is available and then use it.

But it is a test setup, and I'll have to check if the 4 in this cleanup isn't an exact ssl generation number to reset to (synthetically keeping it there to be able to count and compare ssl generation changing when different operations are tested).

Thanks for the hints, all!

@olleolleolle
Copy link
Member Author

@drbrain Sorry I took forever, but I made a conditional else case with the v3.0.0 method you named. The tests still pass. Did I make it the way you mentioned?

@olleolleolle
Copy link
Member Author

olleolleolle commented Nov 22, 2016

@iMacTia The last change to the Gemfile is one I was really aching to get in: using the ruby version marker to hint to Bundler's Rubygem-finding that we only need matching gems for our version.

See Bundler's docs on Gemfile for (spare) details: http://bundler.io/v1.13/man/gemfile.5.html#RUBY

I hope it can be used as it is; support for it arrived in Bundler 1.2 - otherwise we need to have a before_install: step in .travis.yml where we install latest Bundler.

@iMacTia
Copy link
Member

iMacTia commented Nov 22, 2016

Maybe I'm missing something, but looking at this I don't find a reason to specify something like ruby RUBY_VERSION in the Gemfile.
I understand using it to fi it to a specific version, but using RUBY_VERSION doesn't really make sense to me. Am I missing something?

@@ -10,8 +10,13 @@ def setup
if defined?(Net::HTTP::Persistent)
# work around problems with mixed SSL certificates
# https://github.com/drbrain/net-http-persistent/issues/45
http = Net::HTTP::Persistent.new('Faraday')
http.ssl_cleanup(4)
if Net::HTTP::Persistent::instance_methods.include?(:ssl_cleanup)
Copy link

Choose a reason for hiding this comment

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

#reconnect_ssl exists back to net-http-persistent v2.4.1, so you probably don't need this if

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

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

(Well, I needed the if in order to new with the 2 arities of constructor.)

@olleolleolle
Copy link
Member Author

@iMacTia I understand using it to fi it to a specific version, but using RUBY_VERSION doesn't really make sense to me. Am I missing something?

Oh, the very thing is that we're not a deployed application where dependencies are to be locked down to 1 and only 1 gem selection, but a library which needs 1 selection per Ruby version we support.

Warning: This is more of a current measure, Bundler's future has better support for knowing this on its own.

So by leveraging Bundler's "Ruby version hinting", we can try to minimize ifs in the Gemfile.

Bundler outsources the decision about sub-dependencies' version needs to the gemspecs involved, by taking the required_ruby_version gemspec data into account.

(You know, I'll try improving the Bundler website with an FAQ item about just this.)

@joevandyk
Copy link

any update on this?

@iMacTia
Copy link
Member

iMacTia commented Mar 3, 2017

There are some important changes to the Gemfile, so I would like @mislav to review this PR as well before merging it in.

@aaronmcadam
Copy link

Is the issue about how to support both 2.x and 3.x of net-http-persistent?

@olleolleolle
Copy link
Member Author

@aaronmcadam Yes!

@aaronmcadam
Copy link

Hey @olleolleolle I've not used it myself, but I've seen https://github.com/thoughtbot/appraisal being used for supporting multiple gem versions.

@olleolleolle
Copy link
Member Author

@aaronmcadam I will extend the Description of this PR, in order to make it clear what the changes are and why they're needed.

@JulienItard
Copy link

Hi @olleolleolle ! Any idea when this will be available ?

Thanks 👍

@iancward
Copy link

@iMacTia @mislav any update on this? I just ran into this issue as well.

@olleolleolle
Copy link
Member Author

@iancward To meet earlier requested change: Formulate a parameters-based version picking, instead of using the VERSION constant.

@mtwentyman
Copy link

If it is helpful, I've been using a version of the patch provided by @olleolleolle for a few projects (by way of making a new adapter available) and it has worked superbly.

@iMacTia
Copy link
Member

iMacTia commented Jul 19, 2017

Tests are passing and changes from @mislav have been implemented so it should now be mergeable.
I'll need to go through it one more time though and then I'll decide if including it into 0.12.2 (soon to be released) or 0.13.0 as it's mid-way between the feature and the fix type of PR 😅.
I know everyone is voting for 0.12.2 so no need to point that out 😄

@iMacTia
Copy link
Member

iMacTia commented Jul 28, 2017

Tests are green and the Gemfile is cleaner now!
Moreover, Travis is testing both v2 and v3 of net_http_persistent as v2 will be installed for ruby < 2.1, v3 otherwise.
This was long and suffered, but thanks to @olleolleolle perseverance we can finally merge this in!
🎉 🎉 🎉 🎉 🎉

@iMacTia iMacTia merged commit f8892b1 into lostisland:master Jul 28, 2017
@olleolleolle
Copy link
Member Author

Celebrate!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants