httpi 2.4.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/development.yml +48 -0
  3. data/CHANGELOG.md +45 -0
  4. data/Gemfile +10 -5
  5. data/README.md +20 -16
  6. data/Rakefile +5 -3
  7. data/UPDATING.md +7 -0
  8. data/httpi.gemspec +6 -7
  9. data/lib/httpi/adapter/curb.rb +8 -2
  10. data/lib/httpi/adapter/em_http.rb +5 -4
  11. data/lib/httpi/adapter/excon.rb +17 -5
  12. data/lib/httpi/adapter/http.rb +15 -2
  13. data/lib/httpi/adapter/httpclient.rb +16 -4
  14. data/lib/httpi/adapter/net_http.rb +42 -16
  15. data/lib/httpi/adapter/net_http_persistent.rb +6 -2
  16. data/lib/httpi/adapter/rack.rb +13 -6
  17. data/lib/httpi/auth/ssl.rb +68 -3
  18. data/lib/httpi/logger.rb +6 -1
  19. data/lib/httpi/request.rb +12 -5
  20. data/lib/httpi/response.rb +2 -0
  21. data/lib/httpi/version.rb +1 -1
  22. data/lib/httpi.rb +4 -4
  23. data/spec/fixtures/client_cert.pem +18 -14
  24. data/spec/fixtures/client_key.pem +25 -13
  25. data/spec/httpi/adapter/curb_spec.rb +36 -10
  26. data/spec/httpi/adapter/em_http_spec.rb +23 -21
  27. data/spec/httpi/adapter/excon_spec.rb +28 -90
  28. data/spec/httpi/adapter/http_spec.rb +23 -96
  29. data/spec/httpi/adapter/httpclient_spec.rb +46 -4
  30. data/spec/httpi/adapter/net_http_persistent_spec.rb +31 -81
  31. data/spec/httpi/adapter/net_http_spec.rb +39 -99
  32. data/spec/httpi/adapter/rack_spec.rb +6 -8
  33. data/spec/httpi/auth/ssl_spec.rb +49 -1
  34. data/spec/httpi/httpi_spec.rb +16 -4
  35. data/spec/httpi/request_spec.rb +5 -0
  36. data/spec/integration/curb_spec.rb +20 -0
  37. data/spec/integration/em_http_spec.rb +19 -2
  38. data/spec/integration/excon_spec.rb +174 -0
  39. data/spec/integration/fixtures/ca_all.pem +17 -42
  40. data/spec/integration/fixtures/server.cert +17 -17
  41. data/spec/integration/fixtures/server.key +25 -13
  42. data/spec/integration/http_spec.rb +156 -0
  43. data/spec/integration/httpclient_spec.rb +20 -0
  44. data/spec/integration/net_http_persistent_spec.rb +34 -2
  45. data/spec/integration/net_http_spec.rb +144 -1
  46. data/spec/integration/support/application.rb +4 -2
  47. data/spec/integration/support/server.rb +1 -2
  48. data/spec/spec_helper.rb +0 -2
  49. metadata +31 -29
  50. data/.travis.yml +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e6ca1f335780f0839e9e5316c9ecf1a6d3753bea
4
- data.tar.gz: f5e52a1dde48d6c783c2bc66b691df4cb4e1f4cd
2
+ SHA256:
3
+ metadata.gz: b488ad4ac7b6d32a313798d387be88d00976ffe2533a1413836bd7e102cfda83
4
+ data.tar.gz: f555c552fbdfa36b727c75e5bf2b3a9a19559098145ce3f969c94f250380b185
5
5
  SHA512:
6
- metadata.gz: f7e7e5d0cf5c2a2f59ad4b8bf331c4917cfcef503c0e905b6613b08ad03a8a23e20727668b51a3e27762167905b5a3c1a526a9fb987cd1d528dd6429090ee98c
7
- data.tar.gz: ffe4093b24fc493cda6c8b7e266a5abcdc97785759e59024a6f02ef984f15b9c6707e70dfbd76e7f29bd91ed5077af219bd5c593d99bc675f720024e467193c6
6
+ metadata.gz: fccd5e6e95668a6b49e440eee050a2e8f7e456434e6a973bd2cc8d54c97854c1961fc5e941456ff6e3a4356a03bb6b8a7aad023fd7b8d74f0d73a2e1a18fd682
7
+ data.tar.gz: 825d197e0ccb00ba983aba28a40cb6c0f1789bc6ae9cb3ba82363548330fd66609c5c4e904285038d67de6c95850f1ccaca3901f7698b7317657e754f1035f7d
@@ -0,0 +1,48 @@
1
+ name: Development
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ name: ${{matrix.ruby}} on ${{matrix.os}}
8
+ runs-on: ${{matrix.os}}-latest
9
+ continue-on-error: ${{matrix.experimental}}
10
+
11
+ strategy:
12
+ matrix:
13
+ os:
14
+ - ubuntu
15
+
16
+ ruby:
17
+ - "2.6"
18
+ - "2.7"
19
+ - "3.0"
20
+
21
+ experimental: [false]
22
+ env: [""]
23
+
24
+ include:
25
+ - os: ubuntu
26
+ ruby: truffleruby
27
+ experimental: true
28
+ - os: ubuntu
29
+ ruby: jruby
30
+ experimental: true
31
+ - os: ubuntu
32
+ ruby: head
33
+ experimental: true
34
+
35
+ steps:
36
+ - uses: actions/checkout@v2
37
+
38
+ - name: Install dependencies
39
+ run: sudo apt-get install libcurl4-openssl-dev
40
+
41
+ - uses: ruby/setup-ruby@v1
42
+ with:
43
+ ruby-version: ${{matrix.ruby}}
44
+ bundler-cache: true
45
+
46
+ - name: Run tests
47
+ timeout-minutes: 5
48
+ run: ${{matrix.env}} bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,3 +1,48 @@
1
+ ### 3.0.0 (2021-10-19)
2
+
3
+ * Improvement: [#225](https://github.com/savonrb/httpi/pull/225) Make rack and socksify dependencies optional.
4
+
5
+ ### 2.5.0 (2021-10-05)
6
+
7
+ * Feature: [#214](https://github.com/savonrb/httpi/pull/214) Add SSL ciphers configuration
8
+ * Improvement: [#227](https://github.com/savonrb/httpi/pull/227) Use GitHub Actions as CI. Require at least Ruby v2.3.
9
+
10
+ ### 2.4.5
11
+
12
+ * Improvement: [#209](https://github.com/savonrb/httpi/pull/209) Drop Travis CI support for Ruby < 2.3.0 and jruby.
13
+ * Feature: [#208](https://github.com/savonrb/httpi/pull/208) Add SSL min/max_version configuration for supporting adapters
14
+ * Improvement: [#206](https://github.com/savonrb/httpi/pull/206) Support for net-http-persistent v3
15
+ * Improvement: [#204](https://github.com/savonrb/httpi/pull/204) Avoid excon warning
16
+
17
+ ### 2.4.4
18
+
19
+ * Improvement: [#197](https://github.com/savonrb/httpi/pull/197) Add support for new write timeout option to all adapters
20
+ * Fix: [#196](https://github.com/savonrb/httpi/pull/196) Fix httpi adapters support for read/open timeout
21
+ * Improvement: [Remove references to broken site](https://github.com/savonrb/httpi/commit/345e5e2b1a4376a7be769f67088a431895de09ad)
22
+ * Fix: [#190](https://github.com/savonrb/httpi/pull/190) Don't convert port to string on Excon adapter
23
+
24
+ ### 2.4.3
25
+
26
+ * Fix: [#171](https://github.com/savonrb/httpi/pull/171) bug with rubyntlm v0.6.0
27
+ * Fix: [#181](https://github.com/savonrb/httpi/pull/181) excon and http adapters
28
+ * Feature: [#183](https://github.com/savonrb/httpi/pull/183) Logging ssl information of a request
29
+ * Fix: [#187](https://github.com/savonrb/httpi/pull/187) only require ntlm if ntlm auth was requested
30
+
31
+
32
+ ### 2.4.2
33
+
34
+ * Feature: [#165](https://github.com/savonrb/httpi/pull/165) Extended net_http adapter ssl options with cert_store and ca_path
35
+ * Improvement: [#166](https://github.com/savonrb/httpi/pull/166) fix some warnings
36
+ * Feature: [#167](https://github.com/savonrb/httpi/pull/167) adds missing support for cert password
37
+ * Feature: [#163](https://github.com/savonrb/httpi/pull/163) Support HTTP Basic auth in Rack adapter
38
+ * Fix: [#162](https://github.com/savonrb/httpi/pull/162) fixing excon adapter to pass client_cert and key as strings instead of objects
39
+ * Fix: [#160](https://github.com/savonrb/httpi/pull/160) httpclient adapter now accepts NTLM auth
40
+ * Improvement: [#158](https://github.com/savonrb/httpi/pull/158) Limit the maximum of redirects to prevent infinite redirects
41
+ * Improvement: [#153](https://github.com/savonrb/httpi/pull/153) Dynamically determine methods Net::HTTP supports
42
+ * Improvement: [#155](https://github.com/savonrb/httpi/pull/155) Enable socks proxy server specification
43
+ * Improvement: [#151](https://github.com/savonrb/httpi/pull/151) Fix excon verify mode
44
+ * Fix: [#149](https://github.com/savonrb/httpi/pull/149) Ensure that header exists before accessing it
45
+
1
46
  ### 2.4.1
2
47
 
3
48
  * Fix: [#147](https://github.com/savonrb/httpi/pull/147) Fix call Curb client "SSL peer certificate or SSH remote key was not OK" bug
data/Gemfile CHANGED
@@ -3,15 +3,20 @@ gemspec
3
3
 
4
4
  gem 'jruby-openssl', :platforms => :jruby
5
5
 
6
+ gem 'public_suffix', '~> 4.0'
7
+
6
8
  # http clients
7
9
  gem 'httpclient', '~> 2.3', :require => false
8
- gem 'curb', '~> 0.8', :require => false, :platforms => :ruby
9
- gem 'em-http-request', :require => false, :platforms => [:ruby, :jruby]
10
+ gem 'curb', '~> 0.8', :require => false, :platforms => [:ruby]
11
+ gem 'em-http-request', :require => false, :platforms => [:ruby]
10
12
  gem 'em-synchrony', :require => false, :platforms => [:ruby, :jruby]
11
- gem 'excon', '~> 0.21.0', :require => false, :platforms => [:ruby, :jruby]
12
- gem 'net-http-persistent', '~> 2.8', :require => false
13
+ gem 'excon', '~> 0.21', :require => false, :platforms => [:ruby, :jruby]
14
+ gem 'net-http-persistent', '~> 4.0', :require => false
13
15
  gem 'http', :require => false
14
16
 
17
+ # adapter extensions
18
+ gem 'rack'
19
+ gem 'socksify'
20
+
15
21
  # coverage
16
22
  gem 'simplecov', :require => false
17
- gem 'coveralls', :require => false
data/README.md CHANGED
@@ -2,33 +2,23 @@
2
2
 
3
3
  A common interface for Ruby's HTTP libraries.
4
4
 
5
- [Documentation](http://httpirb.com) | [RDoc](http://rubydoc.info/gems/httpi) |
5
+ [Documentation](https://www.rubydoc.info/gems/httpi) |
6
6
  [Mailing list](https://groups.google.com/forum/#!forum/httpirb)
7
7
 
8
- [![Build Status](https://secure.travis-ci.org/savonrb/httpi.png?branch=master)](http://travis-ci.org/savonrb/httpi)
9
- [![Gem Version](https://badge.fury.io/rb/httpi.png)](http://badge.fury.io/rb/httpi)
10
- [![Code Climate](https://codeclimate.com/github/savonrb/httpi.png)](https://codeclimate.com/github/savonrb/httpi)
11
- [![Coverage Status](https://coveralls.io/repos/savonrb/httpi/badge.png?branch=master)](https://coveralls.io/r/savonrb/httpi)
12
-
8
+ [![Development](https://github.com/savonrb/httpi/actions/workflows/development.yml/badge.svg)](https://github.com/savonrb/httpi/actions/workflows/development.yml)
13
9
 
14
10
  ## Installation
15
11
 
16
- HTTPI is available through [Rubygems](http://rubygems.org/gems/httpi) and can be installed via:
12
+ HTTPI is available through [Rubygems](https://rubygems.org/gems/httpi) and can be installed via:
17
13
 
18
- ```
19
- $ gem install httpi
20
- ```
14
+ $ gem install httpi
21
15
 
22
16
  or add it to your Gemfile like this:
23
17
 
24
- ```
25
- gem 'httpi', '~> 2.1.0'
26
- ```
27
-
18
+ gem 'httpi', '~> 3.0.0'
28
19
 
29
20
  ## Usage example
30
21
 
31
-
32
22
  ``` ruby
33
23
  require "httpi"
34
24
 
@@ -49,7 +39,21 @@ HTTPI.adapter = :httpclient
49
39
  HTTPI.request(:custom, request)
50
40
  ```
51
41
 
42
+ ### Rack Mock Adapter
43
+
44
+ To use the Rack mock adapter, please add the `rack` gem to your gemfile.
45
+
46
+ ### SOCKS Proxy Support
47
+
48
+ To use the the SOCKS proxy support, please add the `socksify` gem to your gemfile, and add the following code:
49
+
50
+ ``` ruby
51
+ require 'socksify'
52
+ require 'socksify/http'
53
+ ```
54
+
55
+ to your project.
52
56
 
53
57
  ## Documentation
54
58
 
55
- Continue reading at [httpirb.com](http://httpirb.com)
59
+ Continue reading at https://www.rubydoc.info/gems/httpi
data/Rakefile CHANGED
@@ -10,7 +10,9 @@ RSpec::Core::RakeTask.new "spec_integration" do |t|
10
10
  t.pattern = "spec/integration/*_spec.rb"
11
11
  end
12
12
 
13
- task :default => :spec
14
-
15
13
  desc "Run RSpec code and integration examples"
16
- task :ci => [:spec, :spec_integration]
14
+ RSpec::Core::RakeTask.new "ci" do |t|
15
+ t.pattern = "spec/{httpi,integration}/**/*_spec.rb"
16
+ end
17
+
18
+ task :default => :spec
data/UPDATING.md ADDED
@@ -0,0 +1,7 @@
1
+ # Update guide
2
+
3
+ ## From 2.x to 3.x
4
+
5
+ BREAKING CHANGE: the [#255](https://github.com/savonrb/httpi/pull/225) made the gem socksify and rack gems optional dependencies.
6
+
7
+ In order to restore the old behavior, see the README section "SOCKS Proxy Support" and "Rack Mock Adapter".
data/httpi.gemspec CHANGED
@@ -11,18 +11,17 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "http://github.com/savonrb/#{s.name}"
12
12
  s.summary = "Common interface for Ruby's HTTP libraries"
13
13
  s.description = s.summary
14
- s.required_ruby_version = '>= 1.9.2'
15
14
 
16
- s.rubyforge_project = s.name
17
- s.license = 'MIT'
15
+ s.required_ruby_version = '>= 2.3'
18
16
 
19
- s.add_dependency 'rack'
17
+ s.license = 'MIT'
20
18
 
21
19
  s.add_development_dependency 'rubyntlm', '~> 0.3.2'
22
- s.add_development_dependency 'rake', '~> 10.0'
23
- s.add_development_dependency 'rspec', '~> 2.14'
20
+ s.add_development_dependency 'rake', '~> 13.0'
21
+ s.add_development_dependency 'rspec', '~> 3.5'
24
22
  s.add_development_dependency 'mocha', '~> 0.13'
25
- s.add_development_dependency 'puma', '~> 2.3.2'
23
+ s.add_development_dependency 'puma', '~> 5.0'
24
+ s.add_development_dependency 'webmock'
26
25
 
27
26
  s.files = `git ls-files`.split("\n")
28
27
  s.require_path = 'lib'
@@ -72,8 +72,9 @@ module HTTPI
72
72
  def basic_setup
73
73
  @client.url = @request.url.to_s
74
74
  @client.proxy_url = @request.proxy.to_s if @request.proxy
75
- @client.timeout = @request.read_timeout if @request.read_timeout
76
- @client.connect_timeout = @request.open_timeout if @request.open_timeout
75
+ read_or_write_timeout = @request.read_timeout || @request.write_timeout
76
+ @client.timeout_ms = read_or_write_timeout * 1000 if read_or_write_timeout
77
+ @client.connect_timeout_ms = @request.open_timeout * 1000 if @request.open_timeout
77
78
  @client.headers = @request.headers.to_hash
78
79
  @client.verbose = false
79
80
  # cURL workaround
@@ -114,6 +115,8 @@ module HTTPI
114
115
  # Send client-side certificate regardless of state of SSL verify mode
115
116
  @client.cert_key = ssl.cert_key_file
116
117
  @client.cert = ssl.cert_file
118
+ @client.certpassword = ssl.cert_key_password
119
+ @client.set(:ssl_cipher_list, ssl.ciphers.join(':')) if ssl.ciphers
117
120
 
118
121
  @client.ssl_verify_peer = ssl.verify_mode == :peer
119
122
  end
@@ -126,6 +129,9 @@ module HTTPI
126
129
  when :SSLv23 then 2
127
130
  when :SSLv3 then 3
128
131
  end
132
+ if ssl.min_version || ssl.max_version
133
+ raise NotSupportedError, 'Curb adapter does not support #min_version or #max_version. Please, use #ssl_version instead.'
134
+ end
129
135
  end
130
136
 
131
137
  def respond_with(client)
@@ -69,10 +69,11 @@ module HTTPI
69
69
  end
70
70
 
71
71
  def connection_options
72
- options = {
73
- :connect_timeout => @request.open_timeout,
74
- :inactivity_timeout => @request.read_timeout
75
- }
72
+ options = {}
73
+
74
+ read_or_write_timeout = @request.read_timeout || @request.write_timeout
75
+ options[:inactivity_timeout] = read_or_write_timeout if read_or_write_timeout
76
+ options[:connect_timeout] = @request.open_timeout if @request.open_timeout
76
77
 
77
78
  options[:proxy] = proxy_options if @request.proxy
78
79
 
@@ -41,8 +41,9 @@ module HTTPI
41
41
 
42
42
  opts = {
43
43
  :host => url.host,
44
+ :hostname => url.hostname,
44
45
  :path => url.path,
45
- :port => url.port.to_s,
46
+ :port => url.port,
46
47
  :query => url.query,
47
48
  :scheme => url.scheme,
48
49
  :headers => @request.headers,
@@ -58,23 +59,34 @@ module HTTPI
58
59
  opts[:user], opts[:password] = *@request.auth.credentials if @request.auth.basic?
59
60
  opts[:connect_timeout] = @request.open_timeout if @request.open_timeout
60
61
  opts[:read_timeout] = @request.read_timeout if @request.read_timeout
62
+ opts[:write_timeout] = @request.write_timeout if @request.write_timeout
61
63
  opts[:response_block] = @request.on_body if @request.on_body
62
64
  opts[:proxy] = @request.proxy if @request.proxy
63
65
 
64
- if ssl.verify_mode == :peer
66
+ case ssl.verify_mode
67
+ when :peer
65
68
  opts[:ssl_verify_peer] = true
66
69
  opts[:ssl_ca_file] = ssl.ca_cert_file if ssl.ca_cert_file
67
- opts[:client_cert] = ssl.cert if ssl.cert
68
- opts[:client_key] = ssl.cert_key if ssl.cert_key
70
+ opts[:certificate] = ssl.cert.to_pem if ssl.cert
71
+ opts[:private_key] = ssl.cert_key.to_pem if ssl.cert_key
72
+ when :none
73
+ opts[:ssl_verify_peer] = false
69
74
  end
70
75
 
76
+ opts[:ciphers] = ssl.ciphers if ssl.ciphers
71
77
  opts[:ssl_version] = ssl.ssl_version if ssl.ssl_version
78
+ opts[:ssl_min_version] = ssl.min_version if ssl.min_version
79
+ opts[:ssl_max_version] = ssl.max_version if ssl.max_version
72
80
 
73
81
  opts
74
82
  end
75
83
 
76
84
  def respond_with(response)
77
- Response.new response.status, response.headers, response.body
85
+ headers = response.headers.dup
86
+ if (cookies = response.data[:cookies]) && !cookies.empty?
87
+ headers["Set-Cookie"] = cookies
88
+ end
89
+ Response.new response.status, headers, response.body
78
90
  end
79
91
 
80
92
  end
@@ -32,9 +32,13 @@ module HTTPI
32
32
  unless ::HTTP::Request::METHODS.include? method
33
33
  raise NotSupportedError, "http.rb does not support custom HTTP methods"
34
34
  end
35
- response = @client.send(method, @request.url, :body => @request.body)
35
+ response = begin
36
+ @client.send(method, @request.url, :body => @request.body)
37
+ rescue OpenSSL::SSL::SSLError
38
+ raise SSLError
39
+ end
36
40
 
37
- Response.new(response.code, response.headers, response.body.to_s)
41
+ Response.new(response.code, response.headers.to_h, response.body.to_s)
38
42
  end
39
43
 
40
44
  private
@@ -54,7 +58,10 @@ module HTTPI
54
58
  context.cert = @request.auth.ssl.cert
55
59
  context.key = @request.auth.ssl.cert_key
56
60
  context.ssl_version = @request.auth.ssl.ssl_version if @request.auth.ssl.ssl_version != nil
61
+ context.min_version = @request.auth.ssl.min_version if @request.auth.ssl.min_version != nil
62
+ context.max_version = @request.auth.ssl.max_version if @request.auth.ssl.max_version != nil
57
63
  context.verify_mode = @request.auth.ssl.openssl_verify_mode
64
+ context.ciphers = @request.auth.ssl.ciphers if @request.auth.ssl.ciphers
58
65
 
59
66
  client = ::HTTP::Client.new(:ssl_context => context)
60
67
  else
@@ -69,6 +76,12 @@ module HTTPI
69
76
  client = client.via(@request.proxy.host, @request.proxy.port, @request.proxy.user, @request.proxy.password)
70
77
  end
71
78
 
79
+ timeouts = {}
80
+ timeouts[:connect] = @request.open_timeout if @request.open_timeout
81
+ timeouts[:read] = @request.read_timeout if @request.read_timeout
82
+ timeouts[:write] = @request.write_timeout if @request.write_timeout
83
+ client = client.timeout(timeouts) if timeouts.any?
84
+
72
85
  client.headers(@request.headers)
73
86
  end
74
87
  end
@@ -36,11 +36,8 @@ module HTTPI
36
36
  def setup_client
37
37
  basic_setup
38
38
 
39
- if @request.auth.ntlm?
40
- raise NotSupportedError, "HTTPClient adapter does not support NTLM authentication"
41
- end
42
-
43
39
  setup_auth if @request.auth.http?
40
+ setup_ntlm_auth if @request.auth.ntlm?
44
41
  setup_ssl_auth if @request.auth.ssl? || @request.ssl?
45
42
  end
46
43
 
@@ -48,12 +45,23 @@ module HTTPI
48
45
  @client.proxy = @request.proxy if @request.proxy
49
46
  @client.connect_timeout = @request.open_timeout if @request.open_timeout
50
47
  @client.receive_timeout = @request.read_timeout if @request.read_timeout
48
+ @client.send_timeout = @request.write_timeout if @request.write_timeout
51
49
  end
52
50
 
53
51
  def setup_auth
54
52
  @client.set_auth @request.url, *@request.auth.credentials
55
53
  end
56
54
 
55
+ def setup_ntlm_auth
56
+ username, password, domain = @request.auth.credentials
57
+
58
+ unless domain.nil?
59
+ username = "#{domain.upcase}\\#{username}"
60
+ end
61
+
62
+ @client.set_auth @request.url, username, password
63
+ end
64
+
57
65
  def setup_ssl_auth
58
66
  ssl = @request.auth.ssl
59
67
 
@@ -65,11 +73,15 @@ module HTTPI
65
73
  # Send client-side certificate regardless of state of SSL verify mode
66
74
  @client.ssl_config.client_cert = ssl.cert
67
75
  @client.ssl_config.client_key = ssl.cert_key
76
+ @client.ssl_config.ciphers = ssl.ciphers if ssl.ciphers
68
77
 
69
78
  @client.ssl_config.verify_mode = ssl.openssl_verify_mode
70
79
  end
71
80
 
72
81
  @client.ssl_config.ssl_version = ssl.ssl_version.to_s if ssl.ssl_version
82
+ if ssl.min_version || ssl.max_version
83
+ raise NotSupportedError, 'Httpclient adapter does not support #min_version or #max_version. Please, use #ssl_version instead'
84
+ end
73
85
  end
74
86
 
75
87
  def respond_with(response)
@@ -16,8 +16,7 @@ module HTTPI
16
16
 
17
17
  register :net_http, :deps => %w(net/https)
18
18
  def initialize(request)
19
- check_net_ntlm_version!
20
-
19
+ check_net_ntlm_version! if request.auth.ntlm?
21
20
  @request = request
22
21
  @client = create_client
23
22
  end
@@ -27,8 +26,12 @@ module HTTPI
27
26
  # Executes arbitrary HTTP requests.
28
27
  # @see HTTPI.request
29
28
  def request(method)
30
- unless REQUEST_METHODS.include? method
31
- raise NotSupportedError, "Net::HTTP does not support custom HTTP methods"
29
+ # Determine if Net::HTTP supports the method using reflection
30
+ unless Net::HTTP.const_defined?(:"#{method.to_s.capitalize}") &&
31
+ Net::HTTP.const_get(:"#{method.to_s.capitalize}").class == Class
32
+
33
+ raise NotSupportedError, "Net::HTTP does not support "\
34
+ "#{method.to_s.upcase}"
32
35
  end
33
36
  do_request(method) do |http, http_request|
34
37
  http_request.body = @request.body
@@ -50,11 +53,15 @@ module HTTPI
50
53
  end
51
54
 
52
55
  private
56
+ def ntlm_version
57
+ Net::NTLM::VERSION::STRING
58
+ end
59
+
53
60
  def check_net_ntlm_version!
54
61
  begin
55
62
  require 'net/ntlm'
56
- require 'net/ntlm/version' unless Net::NTLM.const_defined?(:VERSION)
57
- unless Net::NTLM::VERSION::STRING >= '0.3.2'
63
+ require 'net/ntlm/version' unless Net::NTLM.const_defined?(:VERSION, false)
64
+ unless ntlm_version >= '0.3.2'
58
65
  raise ArgumentError, 'Invalid version of rubyntlm. Please use v0.3.2+.'
59
66
  end
60
67
  rescue LoadError
@@ -67,7 +74,11 @@ module HTTPI
67
74
 
68
75
  def create_client
69
76
  proxy_url = @request.proxy || URI("")
70
- proxy = Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
77
+ if URI(proxy_url).scheme == 'socks'
78
+ proxy =Net::HTTP.SOCKSProxy(proxy_url.host, proxy_url.port)
79
+ else
80
+ proxy = Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
81
+ end
71
82
  proxy.new(@request.url.host, @request.url.port)
72
83
  end
73
84
 
@@ -92,9 +103,9 @@ module HTTPI
92
103
 
93
104
  # first figure out if we should use NTLM or Negotiate
94
105
  nego_auth_response = respond_with(requester.call(http, request_client(:head)))
95
- if nego_auth_response.headers['www-authenticate'].include? 'Negotiate'
106
+ if nego_auth_response.headers['www-authenticate'] && nego_auth_response.headers['www-authenticate'].include?('Negotiate')
96
107
  auth_method = 'Negotiate'
97
- elsif nego_auth_response.headers['www-authenticate'].include? 'NTLM'
108
+ elsif nego_auth_response.headers['www-authenticate'] && nego_auth_response.headers['www-authenticate'].include?('NTLM')
98
109
  auth_method = 'NTLM'
99
110
  else
100
111
  auth_method = 'NTLM'
@@ -142,6 +153,13 @@ module HTTPI
142
153
  @client.use_ssl = @request.ssl?
143
154
  @client.open_timeout = @request.open_timeout if @request.open_timeout
144
155
  @client.read_timeout = @request.read_timeout if @request.read_timeout
156
+ if @request.write_timeout
157
+ if @client.respond_to?(:write_timeout=) # Expected to appear in Ruby 2.6
158
+ @client.write_timeout = @request.write_timeout
159
+ else
160
+ raise NotSupportedError, "Net::HTTP supports write_timeout starting from Ruby 2.6"
161
+ end
162
+ end
145
163
  end
146
164
 
147
165
  def setup_ssl_auth
@@ -150,26 +168,34 @@ module HTTPI
150
168
  if @request.auth.ssl?
151
169
  unless ssl.verify_mode == :none
152
170
  @client.ca_file = ssl.ca_cert_file if ssl.ca_cert_file
171
+ @client.ca_path = ssl.ca_cert_path if ssl.ca_cert_path
172
+ @client.cert_store = ssl_cert_store(ssl)
153
173
  end
154
174
 
155
175
  # Send client-side certificate regardless of state of SSL verify mode
156
176
  @client.key = ssl.cert_key
157
177
  @client.cert = ssl.cert
178
+ @client.ciphers = ssl.ciphers if ssl.ciphers
158
179
 
159
180
  @client.verify_mode = ssl.openssl_verify_mode
160
181
  end
161
182
 
162
183
  @client.ssl_version = ssl.ssl_version if ssl.ssl_version
184
+ @client.min_version = ssl.min_version if ssl.min_version
185
+ @client.max_version = ssl.max_version if ssl.max_version
186
+ end
187
+
188
+ def ssl_cert_store(ssl)
189
+ return ssl.cert_store if ssl.cert_store
190
+
191
+ # Use the default cert store by default, i.e. system ca certs
192
+ cert_store = OpenSSL::X509::Store.new
193
+ cert_store.set_default_paths
194
+ cert_store
163
195
  end
164
196
 
165
197
  def request_client(type)
166
- request_class = case type
167
- when :get then Net::HTTP::Get
168
- when :post then Net::HTTP::Post
169
- when :head then Net::HTTP::Head
170
- when :put then Net::HTTP::Put
171
- when :delete then Net::HTTP::Delete
172
- end
198
+ request_class = Net::HTTP.const_get(:"#{type.to_s.capitalize}")
173
199
 
174
200
  request_client = request_class.new @request.url.request_uri, @request.headers
175
201
  request_client.basic_auth(*@request.auth.credentials) if @request.auth.basic?
@@ -12,7 +12,11 @@ module HTTPI
12
12
  private
13
13
 
14
14
  def create_client
15
- Net::HTTP::Persistent.new thread_key
15
+ if Gem::Version.new(Net::HTTP::Persistent::VERSION) >= Gem::Version.new('3.0.0')
16
+ Net::HTTP::Persistent.new name: thread_key
17
+ else
18
+ Net::HTTP::Persistent.new thread_key
19
+ end
16
20
  end
17
21
 
18
22
  def perform(http, http_request, &on_body)
@@ -32,12 +36,12 @@ module HTTPI
32
36
 
33
37
  @client.open_timeout = @request.open_timeout if @request.open_timeout
34
38
  @client.read_timeout = @request.read_timeout if @request.read_timeout
39
+ raise NotSupportedError, "Net::HTTP::Persistent does not support write_timeout" if @request.write_timeout
35
40
  end
36
41
 
37
42
  def thread_key
38
43
  @request.url.host.split(/\W/).reject{|p|p == ""}.join('-')
39
44
  end
40
-
41
45
  end
42
46
  end
43
47
  end
@@ -1,3 +1,4 @@
1
+ require 'base64'
1
2
  require 'httpi/adapter/base'
2
3
  require 'httpi/response'
3
4
 
@@ -61,17 +62,18 @@ module HTTPI
61
62
  raise NotSupportedError, "Rack adapter does not support custom HTTP methods"
62
63
  end
63
64
 
64
- env = {}
65
- @request.headers.each do |header, value|
66
- env["HTTP_#{header.gsub('-', '_').upcase}"] = value
67
- end
68
-
69
65
  if @request.proxy
70
66
  raise NotSupportedError, "Rack adapter does not support proxying"
71
67
  end
72
68
 
73
69
  if @request.auth.http?
74
- raise NotSupportedError, "Rack adapter does not support HTTP auth"
70
+ if @request.auth.basic?
71
+ basic_auth = @request.auth.basic.join(':')
72
+ encoded = Base64.encode64(basic_auth).gsub('\n', '')
73
+ @request.headers['Authorization'] = "Basic #{encoded}"
74
+ else
75
+ raise NotSupportedError, "Rack adapter does not support HTTP #{@request.auth.type} auth"
76
+ end
75
77
  end
76
78
 
77
79
  if @request.auth.ssl?
@@ -82,6 +84,11 @@ module HTTPI
82
84
  raise NotSupportedError, "Rack adapter does not support response streaming"
83
85
  end
84
86
 
87
+ env = {}
88
+ @request.headers.each do |header, value|
89
+ env["HTTP_#{header.gsub('-', '_').upcase}"] = value
90
+ end
91
+
85
92
  response = @client.request(method.to_s.upcase, @request.url.to_s,
86
93
  { :fatal => true, :input => @request.body.to_s }.merge(env))
87
94