httpi 2.4.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![
|
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](
|
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
|
|