httpi 2.4.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/development.yml +48 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +10 -5
- data/README.md +20 -16
- data/Rakefile +5 -3
- data/UPDATING.md +7 -0
- data/httpi.gemspec +6 -7
- data/lib/httpi/adapter/curb.rb +8 -2
- data/lib/httpi/adapter/em_http.rb +5 -4
- data/lib/httpi/adapter/excon.rb +17 -5
- data/lib/httpi/adapter/http.rb +15 -2
- data/lib/httpi/adapter/httpclient.rb +16 -4
- data/lib/httpi/adapter/net_http.rb +42 -16
- data/lib/httpi/adapter/net_http_persistent.rb +6 -2
- data/lib/httpi/adapter/rack.rb +13 -6
- data/lib/httpi/auth/ssl.rb +68 -3
- data/lib/httpi/logger.rb +6 -1
- data/lib/httpi/request.rb +12 -5
- data/lib/httpi/response.rb +2 -0
- data/lib/httpi/version.rb +1 -1
- data/lib/httpi.rb +4 -4
- data/spec/fixtures/client_cert.pem +18 -14
- data/spec/fixtures/client_key.pem +25 -13
- data/spec/httpi/adapter/curb_spec.rb +36 -10
- data/spec/httpi/adapter/em_http_spec.rb +23 -21
- data/spec/httpi/adapter/excon_spec.rb +28 -90
- data/spec/httpi/adapter/http_spec.rb +23 -96
- data/spec/httpi/adapter/httpclient_spec.rb +46 -4
- data/spec/httpi/adapter/net_http_persistent_spec.rb +31 -81
- data/spec/httpi/adapter/net_http_spec.rb +39 -99
- data/spec/httpi/adapter/rack_spec.rb +6 -8
- data/spec/httpi/auth/ssl_spec.rb +49 -1
- data/spec/httpi/httpi_spec.rb +16 -4
- data/spec/httpi/request_spec.rb +5 -0
- data/spec/integration/curb_spec.rb +20 -0
- data/spec/integration/em_http_spec.rb +19 -2
- data/spec/integration/excon_spec.rb +174 -0
- data/spec/integration/fixtures/ca_all.pem +17 -42
- data/spec/integration/fixtures/server.cert +17 -17
- data/spec/integration/fixtures/server.key +25 -13
- data/spec/integration/http_spec.rb +156 -0
- data/spec/integration/httpclient_spec.rb +20 -0
- data/spec/integration/net_http_persistent_spec.rb +34 -2
- data/spec/integration/net_http_spec.rb +144 -1
- data/spec/integration/support/application.rb +4 -2
- data/spec/integration/support/server.rb +1 -2
- data/spec/spec_helper.rb +0 -2
- metadata +31 -29
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b488ad4ac7b6d32a313798d387be88d00976ffe2533a1413836bd7e102cfda83
|
4
|
+
data.tar.gz: f555c552fbdfa36b727c75e5bf2b3a9a19559098145ce3f969c94f250380b185
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
12
|
-
gem 'net-http-persistent', '~>
|
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](
|
5
|
+
[Documentation](https://www.rubydoc.info/gems/httpi) |
|
6
6
|
[Mailing list](https://groups.google.com/forum/#!forum/httpirb)
|
7
7
|
|
8
|
-
[](http://badge.fury.io/rb/httpi)
|
10
|
-
[](https://codeclimate.com/github/savonrb/httpi)
|
11
|
-
[](https://coveralls.io/r/savonrb/httpi)
|
12
|
-
|
8
|
+
[](https://github.com/savonrb/httpi/actions/workflows/development.yml)
|
13
9
|
|
14
10
|
## Installation
|
15
11
|
|
16
|
-
HTTPI is available through [Rubygems](
|
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
|
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
|
-
|
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.
|
17
|
-
s.license = 'MIT'
|
15
|
+
s.required_ruby_version = '>= 2.3'
|
18
16
|
|
19
|
-
s.
|
17
|
+
s.license = 'MIT'
|
20
18
|
|
21
19
|
s.add_development_dependency 'rubyntlm', '~> 0.3.2'
|
22
|
-
s.add_development_dependency 'rake', '~>
|
23
|
-
s.add_development_dependency 'rspec', '~>
|
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', '~>
|
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'
|
data/lib/httpi/adapter/curb.rb
CHANGED
@@ -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
|
-
|
76
|
-
@client.
|
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
|
-
|
74
|
-
|
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
|
|
data/lib/httpi/adapter/excon.rb
CHANGED
@@ -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
|
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
|
-
|
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[:
|
68
|
-
opts[:
|
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
|
-
|
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
|
data/lib/httpi/adapter/http.rb
CHANGED
@@ -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 =
|
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
|
-
|
31
|
-
|
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
|
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
|
-
|
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?
|
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?
|
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 =
|
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
|
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
|
data/lib/httpi/adapter/rack.rb
CHANGED
@@ -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
|
-
|
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
|
|