httpi 4.0.4 → 4.0.6
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +2 -2
- data/lib/httpi/adapter/base.rb +0 -3
- data/lib/httpi/adapter/curb.rb +15 -18
- data/lib/httpi/adapter/em_http.rb +7 -10
- data/lib/httpi/adapter/excon.rb +17 -20
- data/lib/httpi/adapter/http.rb +12 -14
- data/lib/httpi/adapter/httpclient.rb +5 -8
- data/lib/httpi/adapter/net_http.rb +30 -34
- data/lib/httpi/adapter/net_http_persistent.rb +3 -5
- data/lib/httpi/adapter/rack.rb +24 -16
- data/lib/httpi/adapter.rb +9 -12
- data/lib/httpi/auth/config.rb +0 -3
- data/lib/httpi/auth/ssl.rb +15 -18
- data/lib/httpi/cookie.rb +0 -3
- data/lib/httpi/cookie_store.rb +0 -3
- data/lib/httpi/dime.rb +13 -16
- data/lib/httpi/logger.rb +1 -3
- data/lib/httpi/query_builder.rb +5 -11
- data/lib/httpi/request.rb +11 -14
- data/lib/httpi/response.rb +2 -5
- data/lib/httpi/utils.rb +9 -11
- data/lib/httpi/version.rb +1 -1
- data/lib/httpi.rb +8 -11
- metadata +22 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d6d915a51674973b7ce1d5d435037f117c0c745223e72f992b21b4157c9abdf5
|
|
4
|
+
data.tar.gz: a4878abbbed22a7dbd1d4f02717066b0a088de74fdf935d0cc24d26ee982e837
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1709457a67aa799d46d6c7d8afb597b194f21311822d3531f91274961bee70d4a832103dbd6089569da4145d88e163642415d2d1535ae55c71c1ce29232e9115
|
|
7
|
+
data.tar.gz: e2d82c42ceb35d2967130068e82697e6966c49612367c9930c0ec48e88d59d9150e12dd4e539964526574ec81697dc6b3984bc0c087f6fb937e72aa43813d39c
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
* Add your changelog entry here
|
|
4
4
|
|
|
5
|
+
### 4.0.6 (2026-10-29)
|
|
6
|
+
|
|
7
|
+
* Republish the 4.0.5 changes as 4.0.6. The 4.0.5 gem never reached RubyGems, as its push workflow failed before the push step. The 4.0.5 tag is protected, so a new version carries the same gem code.
|
|
8
|
+
|
|
9
|
+
### 4.0.5 (2026-10-29)
|
|
10
|
+
|
|
11
|
+
* Add `logger` as a direct dependency, so the gem works on Ruby 4.0, which no longer ships `logger` (#276)
|
|
12
|
+
* Fix the Rack adapter to send Content-Type and Content-Length without the `HTTP_` prefix, as the Rack SPEC requires
|
|
13
|
+
|
|
5
14
|
### 4.0.4 (2024-07-14)
|
|
6
15
|
|
|
7
16
|
* gemspec: Exclude spec files from published gem by @ChristopherBert in https://github.com/savonrb/httpi/pull/252
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# HTTPI
|
|
2
2
|
|
|
3
|
-
A common interface for Ruby's HTTP libraries. This project is now in maintenance mode. For new projects, we recommend [faraday](https://github.com/lostisland/faraday).
|
|
3
|
+
A common interface for Ruby's HTTP libraries. This project is now in [maintenance mode](https://github.com/savonrb/httpi/issues/238): it receives bug fixes, security updates, and ecosystem-compatibility changes, but no new features. For new projects, we recommend [faraday](https://github.com/lostisland/faraday).
|
|
4
4
|
|
|
5
5
|
[Documentation](https://www.rubydoc.info/gems/httpi)
|
|
6
6
|
|
|
7
|
-
[](https://github.com/savonrb/httpi/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
data/lib/httpi/adapter/base.rb
CHANGED
|
@@ -2,13 +2,11 @@ require "httpi/adapter"
|
|
|
2
2
|
|
|
3
3
|
module HTTPI
|
|
4
4
|
module Adapter
|
|
5
|
-
|
|
6
5
|
# HTTPI::Adapter::Base
|
|
7
6
|
#
|
|
8
7
|
# Allows you to build your own adapter by implementing all public instance methods.
|
|
9
8
|
# Register your adapter by calling the base class' .register method.
|
|
10
9
|
class Base
|
|
11
|
-
|
|
12
10
|
# Registers an adapter.
|
|
13
11
|
def self.register(name, options = {})
|
|
14
12
|
deps = options.fetch(:deps, [])
|
|
@@ -28,7 +26,6 @@ module HTTPI
|
|
|
28
26
|
def request(method)
|
|
29
27
|
raise NotImplementedError, "Adapters need to implement a #request method"
|
|
30
28
|
end
|
|
31
|
-
|
|
32
29
|
end
|
|
33
30
|
end
|
|
34
31
|
end
|
data/lib/httpi/adapter/curb.rb
CHANGED
|
@@ -3,14 +3,12 @@ require "httpi/response"
|
|
|
3
3
|
|
|
4
4
|
module HTTPI
|
|
5
5
|
module Adapter
|
|
6
|
-
|
|
7
6
|
# = HTTPI::Adapter::Curb
|
|
8
7
|
#
|
|
9
8
|
# Adapter for the Curb client.
|
|
10
9
|
# http://rubygems.org/gems/curb
|
|
11
10
|
class Curb < Base
|
|
12
|
-
|
|
13
|
-
register :curb, :deps => %w(curb)
|
|
11
|
+
register :curb, deps: %w[curb]
|
|
14
12
|
|
|
15
13
|
def initialize(request)
|
|
16
14
|
@request = request
|
|
@@ -98,7 +96,7 @@ module HTTPI
|
|
|
98
96
|
# The curl man page (http://curl.haxx.se/docs/manpage.html) says that
|
|
99
97
|
# you have to specify a fake username when using Negotiate auth, and
|
|
100
98
|
# they use ':' in their example.
|
|
101
|
-
@client.username =
|
|
99
|
+
@client.username = ":"
|
|
102
100
|
end
|
|
103
101
|
|
|
104
102
|
def setup_ssl_auth
|
|
@@ -116,21 +114,21 @@ module HTTPI
|
|
|
116
114
|
@client.cert_key = ssl.cert_key_file
|
|
117
115
|
@client.cert = ssl.cert_file
|
|
118
116
|
@client.certpassword = ssl.cert_key_password
|
|
119
|
-
@client.set(:ssl_cipher_list, ssl.ciphers.join(
|
|
117
|
+
@client.set(:ssl_cipher_list, ssl.ciphers.join(":")) if ssl.ciphers
|
|
120
118
|
|
|
121
119
|
@client.ssl_verify_peer = ssl.verify_mode == :peer
|
|
122
120
|
end
|
|
123
121
|
|
|
124
122
|
@client.ssl_version = case ssl.ssl_version
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
when :TLSv1_2 then 1
|
|
124
|
+
when :TLSv1_1 then 1
|
|
125
|
+
when :TLSv1 then 1
|
|
126
|
+
when :SSLv2 then 2
|
|
127
|
+
when :SSLv23 then 2
|
|
128
|
+
when :SSLv3 then 3
|
|
131
129
|
end
|
|
132
130
|
if ssl.min_version || ssl.max_version
|
|
133
|
-
raise NotSupportedError,
|
|
131
|
+
raise NotSupportedError, "Curb adapter does not support #min_version or #max_version. Please, use #ssl_version instead."
|
|
134
132
|
end
|
|
135
133
|
end
|
|
136
134
|
|
|
@@ -145,15 +143,15 @@ module HTTPI
|
|
|
145
143
|
status, headers = nil, {}
|
|
146
144
|
return [status, headers] unless header_string
|
|
147
145
|
|
|
148
|
-
header_string.split(
|
|
149
|
-
if header =~ %r
|
|
146
|
+
header_string.split("\r\n").each do |header|
|
|
147
|
+
if header =~ %r{^HTTP/1.[01] \d\d\d (.*)}
|
|
150
148
|
status = $1
|
|
151
149
|
else
|
|
152
|
-
parts = header.split(
|
|
150
|
+
parts = header.split(":", 2)
|
|
153
151
|
unless parts.empty?
|
|
154
|
-
parts[1]
|
|
152
|
+
parts[1]&.strip!
|
|
155
153
|
if headers.has_key?(parts[0])
|
|
156
|
-
headers[parts[0]] = [headers[parts[0]]] unless headers[parts[0]].
|
|
154
|
+
headers[parts[0]] = [headers[parts[0]]] unless headers[parts[0]].is_a? Array
|
|
157
155
|
headers[parts[0]] << parts[1]
|
|
158
156
|
else
|
|
159
157
|
headers[parts[0]] = parts[1]
|
|
@@ -164,7 +162,6 @@ module HTTPI
|
|
|
164
162
|
|
|
165
163
|
[status, headers]
|
|
166
164
|
end
|
|
167
|
-
|
|
168
165
|
end
|
|
169
166
|
end
|
|
170
167
|
end
|
|
@@ -3,7 +3,6 @@ require "httpi/response"
|
|
|
3
3
|
|
|
4
4
|
module HTTPI
|
|
5
5
|
module Adapter
|
|
6
|
-
|
|
7
6
|
# An HTTPI adapter for `EventMachine::HttpRequest`. Due to limitations of
|
|
8
7
|
# the em-httprequest library, not all features are supported. In particular,
|
|
9
8
|
#
|
|
@@ -28,8 +27,7 @@ module HTTPI
|
|
|
28
27
|
#
|
|
29
28
|
# are supported by em-httprequest but not HTTPI.
|
|
30
29
|
class EmHttpRequest < Base
|
|
31
|
-
|
|
32
|
-
register :em_http, :deps => %w(em-synchrony em-synchrony/em-http em-http)
|
|
30
|
+
register :em_http, deps: %w[em-synchrony em-synchrony/em-http em-http]
|
|
33
31
|
|
|
34
32
|
def initialize(request)
|
|
35
33
|
@request = request
|
|
@@ -82,17 +80,17 @@ module HTTPI
|
|
|
82
80
|
|
|
83
81
|
def client_options
|
|
84
82
|
{
|
|
85
|
-
:
|
|
86
|
-
:
|
|
87
|
-
:
|
|
83
|
+
query: @request.url.query,
|
|
84
|
+
head: @request.headers.to_hash,
|
|
85
|
+
body: @request.body
|
|
88
86
|
}
|
|
89
87
|
end
|
|
90
88
|
|
|
91
89
|
def proxy_options
|
|
92
90
|
{
|
|
93
|
-
:
|
|
94
|
-
:
|
|
95
|
-
:
|
|
91
|
+
host: @request.proxy.host,
|
|
92
|
+
port: @request.proxy.port,
|
|
93
|
+
authorization: [@request.proxy.user, @request.proxy.password]
|
|
96
94
|
}
|
|
97
95
|
end
|
|
98
96
|
|
|
@@ -134,6 +132,5 @@ module HTTPI
|
|
|
134
132
|
result
|
|
135
133
|
end
|
|
136
134
|
end
|
|
137
|
-
|
|
138
135
|
end
|
|
139
136
|
end
|
data/lib/httpi/adapter/excon.rb
CHANGED
|
@@ -3,18 +3,16 @@ require "httpi/response"
|
|
|
3
3
|
|
|
4
4
|
module HTTPI
|
|
5
5
|
module Adapter
|
|
6
|
-
|
|
7
6
|
# = HTTPI::Adapter::Excon
|
|
8
7
|
#
|
|
9
8
|
# Adapter for the Excon client.
|
|
10
9
|
# https://github.com/geemus/excon
|
|
11
10
|
class Excon < Base
|
|
12
|
-
|
|
13
|
-
register :excon, :deps => %w(excon)
|
|
11
|
+
register :excon, deps: %w[excon]
|
|
14
12
|
|
|
15
13
|
def initialize(request)
|
|
16
14
|
@request = request
|
|
17
|
-
@client
|
|
15
|
+
@client = ::Excon::Connection.new client_opts
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
attr_reader :client
|
|
@@ -36,18 +34,18 @@ module HTTPI
|
|
|
36
34
|
private
|
|
37
35
|
|
|
38
36
|
def client_opts
|
|
39
|
-
url
|
|
40
|
-
ssl
|
|
37
|
+
url = @request.url
|
|
38
|
+
ssl = @request.auth.ssl
|
|
41
39
|
|
|
42
40
|
opts = {
|
|
43
|
-
:
|
|
44
|
-
:
|
|
45
|
-
:
|
|
46
|
-
:
|
|
47
|
-
:
|
|
48
|
-
:
|
|
49
|
-
:
|
|
50
|
-
:
|
|
41
|
+
host: url.host,
|
|
42
|
+
hostname: url.hostname,
|
|
43
|
+
path: url.path,
|
|
44
|
+
port: url.port,
|
|
45
|
+
query: url.query,
|
|
46
|
+
scheme: url.scheme,
|
|
47
|
+
headers: @request.headers,
|
|
48
|
+
body: @request.body
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
if @request.auth.digest?
|
|
@@ -58,16 +56,16 @@ module HTTPI
|
|
|
58
56
|
|
|
59
57
|
opts[:user], opts[:password] = *@request.auth.credentials if @request.auth.basic?
|
|
60
58
|
opts[:connect_timeout] = @request.open_timeout if @request.open_timeout
|
|
61
|
-
opts[:read_timeout]
|
|
62
|
-
opts[:write_timeout]
|
|
63
|
-
opts[:response_block]
|
|
64
|
-
opts[:proxy]
|
|
59
|
+
opts[:read_timeout] = @request.read_timeout if @request.read_timeout
|
|
60
|
+
opts[:write_timeout] = @request.write_timeout if @request.write_timeout
|
|
61
|
+
opts[:response_block] = @request.on_body if @request.on_body
|
|
62
|
+
opts[:proxy] = @request.proxy if @request.proxy
|
|
65
63
|
|
|
66
64
|
case ssl.verify_mode
|
|
67
65
|
when :peer
|
|
68
66
|
opts[:ssl_verify_peer] = true
|
|
69
67
|
opts[:ssl_ca_file] = ssl.ca_cert_file if ssl.ca_cert_file
|
|
70
|
-
opts[:certificate] = ssl.cert.to_pem
|
|
68
|
+
opts[:certificate] = ssl.cert.to_pem if ssl.cert
|
|
71
69
|
opts[:private_key] = ssl.cert_key.to_pem if ssl.cert_key
|
|
72
70
|
when :none
|
|
73
71
|
opts[:ssl_verify_peer] = false
|
|
@@ -88,7 +86,6 @@ module HTTPI
|
|
|
88
86
|
end
|
|
89
87
|
Response.new response.status, headers, response.body
|
|
90
88
|
end
|
|
91
|
-
|
|
92
89
|
end
|
|
93
90
|
end
|
|
94
91
|
end
|
data/lib/httpi/adapter/http.rb
CHANGED
|
@@ -3,14 +3,12 @@ require "httpi/response"
|
|
|
3
3
|
|
|
4
4
|
module HTTPI
|
|
5
5
|
module Adapter
|
|
6
|
-
|
|
7
6
|
# = HTTPI::Adapter::HTTP
|
|
8
7
|
#
|
|
9
8
|
# Adapter for the http.rb client.
|
|
10
9
|
# https://github.com/httprb/http.rb
|
|
11
10
|
class HTTP < Base
|
|
12
|
-
|
|
13
|
-
register :http, :deps => %w(http)
|
|
11
|
+
register :http, deps: %w[http]
|
|
14
12
|
|
|
15
13
|
def initialize(request)
|
|
16
14
|
if request.auth.digest?
|
|
@@ -33,7 +31,7 @@ module HTTPI
|
|
|
33
31
|
raise NotSupportedError, "http.rb does not support custom HTTP methods"
|
|
34
32
|
end
|
|
35
33
|
response = begin
|
|
36
|
-
@client.send(method, @request.url, :
|
|
34
|
+
@client.send(method, @request.url, body: @request.body)
|
|
37
35
|
rescue OpenSSL::SSL::SSLError
|
|
38
36
|
raise SSLError
|
|
39
37
|
end
|
|
@@ -49,30 +47,30 @@ module HTTPI
|
|
|
49
47
|
|
|
50
48
|
context.options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
|
51
49
|
|
|
52
|
-
if
|
|
50
|
+
if !@request.auth.ssl.ca_cert_file.nil?
|
|
53
51
|
context.ca_file = @request.auth.ssl.ca_cert_file
|
|
54
52
|
else
|
|
55
53
|
context.cert_store = OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
|
|
56
54
|
end
|
|
57
55
|
|
|
58
|
-
context.cert
|
|
59
|
-
context.key
|
|
60
|
-
context.ssl_version = @request.auth.ssl.ssl_version if
|
|
61
|
-
context.min_version = @request.auth.ssl.min_version if
|
|
62
|
-
context.max_version = @request.auth.ssl.max_version if
|
|
56
|
+
context.cert = @request.auth.ssl.cert
|
|
57
|
+
context.key = @request.auth.ssl.cert_key
|
|
58
|
+
context.ssl_version = @request.auth.ssl.ssl_version if !@request.auth.ssl.ssl_version.nil?
|
|
59
|
+
context.min_version = @request.auth.ssl.min_version if !@request.auth.ssl.min_version.nil?
|
|
60
|
+
context.max_version = @request.auth.ssl.max_version if !@request.auth.ssl.max_version.nil?
|
|
63
61
|
context.verify_mode = @request.auth.ssl.openssl_verify_mode
|
|
64
|
-
context.ciphers
|
|
62
|
+
context.ciphers = @request.auth.ssl.ciphers if @request.auth.ssl.ciphers
|
|
65
63
|
|
|
66
|
-
client = ::HTTP::Client.new(:
|
|
64
|
+
client = ::HTTP::Client.new(ssl_context: context)
|
|
67
65
|
else
|
|
68
66
|
client = ::HTTP
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
if @request.auth.basic?
|
|
72
|
-
client = client.basic_auth(:
|
|
70
|
+
client = client.basic_auth(user: @request.auth.credentials[0], pass: @request.auth.credentials[1])
|
|
73
71
|
end
|
|
74
72
|
|
|
75
|
-
if
|
|
73
|
+
if !@request.proxy.nil?
|
|
76
74
|
client = client.via(@request.proxy.host, @request.proxy.port, @request.proxy.user, @request.proxy.password)
|
|
77
75
|
end
|
|
78
76
|
|
|
@@ -3,14 +3,12 @@ require "httpi/response"
|
|
|
3
3
|
|
|
4
4
|
module HTTPI
|
|
5
5
|
module Adapter
|
|
6
|
-
|
|
7
6
|
# = HTTPI::Adapter::HTTPClient
|
|
8
7
|
#
|
|
9
8
|
# Adapter for the HTTPClient client.
|
|
10
9
|
# http://rubygems.org/gems/httpclient
|
|
11
10
|
class HTTPClient < Base
|
|
12
|
-
|
|
13
|
-
register :httpclient, :deps => %w(httpclient)
|
|
11
|
+
register :httpclient, deps: %w[httpclient]
|
|
14
12
|
|
|
15
13
|
def initialize(request)
|
|
16
14
|
@request = request
|
|
@@ -80,22 +78,21 @@ module HTTPI
|
|
|
80
78
|
|
|
81
79
|
@client.ssl_config.ssl_version = ssl.ssl_version.to_s if ssl.ssl_version
|
|
82
80
|
if ssl.min_version || ssl.max_version
|
|
83
|
-
raise NotSupportedError,
|
|
81
|
+
raise NotSupportedError, "Httpclient adapter does not support #min_version or #max_version. Please, use #ssl_version instead"
|
|
84
82
|
end
|
|
85
83
|
end
|
|
86
84
|
|
|
87
85
|
def respond_with(response)
|
|
88
86
|
headers = {}
|
|
89
87
|
response.header.all.each do |(header, value)|
|
|
90
|
-
if headers.key?(header)
|
|
91
|
-
|
|
88
|
+
headers[header] = if headers.key?(header)
|
|
89
|
+
Array(headers[header]) << value
|
|
92
90
|
else
|
|
93
|
-
|
|
91
|
+
value
|
|
94
92
|
end
|
|
95
93
|
end
|
|
96
94
|
Response.new response.code, headers, response.content
|
|
97
95
|
end
|
|
98
|
-
|
|
99
96
|
end
|
|
100
97
|
end
|
|
101
98
|
end
|
|
@@ -2,19 +2,17 @@ require "uri"
|
|
|
2
2
|
|
|
3
3
|
require "httpi/adapter/base"
|
|
4
4
|
require "httpi/response"
|
|
5
|
-
require
|
|
6
|
-
require
|
|
5
|
+
require "kconv"
|
|
6
|
+
require "socket"
|
|
7
7
|
|
|
8
8
|
module HTTPI
|
|
9
9
|
module Adapter
|
|
10
|
-
|
|
11
10
|
# = HTTPI::Adapter::NetHTTP
|
|
12
11
|
#
|
|
13
12
|
# Adapter for the Net::HTTP client.
|
|
14
13
|
# http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/
|
|
15
14
|
class NetHTTP < Base
|
|
16
|
-
|
|
17
|
-
register :net_http, :deps => %w(net/https)
|
|
15
|
+
register :net_http, deps: %w[net/https]
|
|
18
16
|
def initialize(request)
|
|
19
17
|
check_net_ntlm_version! if request.auth.ntlm?
|
|
20
18
|
@request = request
|
|
@@ -28,14 +26,14 @@ module HTTPI
|
|
|
28
26
|
def request(method)
|
|
29
27
|
# Determine if Net::HTTP supports the method using reflection
|
|
30
28
|
unless Net::HTTP.const_defined?(:"#{method.to_s.capitalize}") &&
|
|
31
|
-
Net::HTTP.const_get(:"#{method.to_s.capitalize}").
|
|
29
|
+
Net::HTTP.const_get(:"#{method.to_s.capitalize}").instance_of?(Class)
|
|
32
30
|
|
|
33
|
-
raise NotSupportedError, "Net::HTTP does not support "\
|
|
31
|
+
raise NotSupportedError, "Net::HTTP does not support " \
|
|
34
32
|
"#{method.to_s.upcase}"
|
|
35
33
|
end
|
|
36
34
|
do_request(method) do |http, http_request|
|
|
37
35
|
http_request.body = @request.body
|
|
38
|
-
if @request.on_body
|
|
36
|
+
if @request.on_body
|
|
39
37
|
perform(http, http_request) do |res|
|
|
40
38
|
res.read_body do |seg|
|
|
41
39
|
@request.on_body.call(seg)
|
|
@@ -53,19 +51,18 @@ module HTTPI
|
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
private
|
|
54
|
+
|
|
56
55
|
def ntlm_version
|
|
57
56
|
Net::NTLM::VERSION::STRING
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def check_net_ntlm_version!
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
raise ArgumentError, 'Invalid version of rubyntlm. Please use v0.3.2+.'
|
|
66
|
-
end
|
|
67
|
-
rescue LoadError
|
|
60
|
+
require "net/ntlm"
|
|
61
|
+
require "net/ntlm/version" unless Net::NTLM.const_defined?(:VERSION, false)
|
|
62
|
+
unless ntlm_version >= "0.3.2"
|
|
63
|
+
raise ArgumentError, "Invalid version of rubyntlm. Please use v0.3.2+."
|
|
68
64
|
end
|
|
65
|
+
rescue LoadError
|
|
69
66
|
end
|
|
70
67
|
|
|
71
68
|
def perform(http, http_request, &block)
|
|
@@ -74,10 +71,10 @@ module HTTPI
|
|
|
74
71
|
|
|
75
72
|
def create_client
|
|
76
73
|
proxy_url = @request.proxy || URI("")
|
|
77
|
-
if URI(proxy_url).scheme ==
|
|
78
|
-
|
|
74
|
+
proxy = if URI(proxy_url).scheme == "socks"
|
|
75
|
+
Net::HTTP.SOCKSProxy(proxy_url.host, proxy_url.port)
|
|
79
76
|
else
|
|
80
|
-
|
|
77
|
+
Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
|
|
81
78
|
end
|
|
82
79
|
proxy.new(@request.url.host, @request.url.port)
|
|
83
80
|
end
|
|
@@ -98,24 +95,24 @@ module HTTPI
|
|
|
98
95
|
|
|
99
96
|
def negotiate_ntlm_auth(http, &requester)
|
|
100
97
|
unless Net.const_defined?(:NTLM)
|
|
101
|
-
raise NotSupportedError,
|
|
98
|
+
raise NotSupportedError, "Net::NTLM is not available. Install via gem install rubyntlm."
|
|
102
99
|
end
|
|
103
100
|
|
|
104
101
|
# first figure out if we should use NTLM or Negotiate
|
|
105
102
|
nego_auth_response = respond_with(requester.call(http, request_client(:head)))
|
|
106
|
-
if nego_auth_response.headers[
|
|
107
|
-
auth_method =
|
|
108
|
-
elsif nego_auth_response.headers[
|
|
109
|
-
auth_method =
|
|
103
|
+
if nego_auth_response.headers["www-authenticate"]&.include?("Negotiate")
|
|
104
|
+
auth_method = "Negotiate"
|
|
105
|
+
elsif nego_auth_response.headers["www-authenticate"]&.include?("NTLM")
|
|
106
|
+
auth_method = "NTLM"
|
|
110
107
|
else
|
|
111
|
-
auth_method =
|
|
112
|
-
HTTPI.logger.debug
|
|
108
|
+
auth_method = "NTLM"
|
|
109
|
+
HTTPI.logger.debug "Server does not support NTLM/Negotiate. Trying NTLM anyway"
|
|
113
110
|
end
|
|
114
111
|
|
|
115
112
|
# initiate a request is to authenticate (exchange secret and auth) using the method determined above...
|
|
116
113
|
ntlm_message_type1 = Net::NTLM::Message::Type1.new
|
|
117
|
-
%w
|
|
118
|
-
ntlm_message_type1.send("#{a}=",
|
|
114
|
+
%w[workstation domain].each do |a|
|
|
115
|
+
ntlm_message_type1.send("#{a}=", "")
|
|
119
116
|
ntlm_message_type1.enable(a.to_sym)
|
|
120
117
|
end
|
|
121
118
|
|
|
@@ -134,14 +131,14 @@ module HTTPI
|
|
|
134
131
|
message_builder[:password] = @request.auth.ntlm[1]
|
|
135
132
|
|
|
136
133
|
# we need to provide a domain in the packet if an only if it was provided by the user in the auth request
|
|
137
|
-
if @request.auth.ntlm[2]
|
|
138
|
-
|
|
134
|
+
message_builder[:domain] = if @request.auth.ntlm[2]
|
|
135
|
+
@request.auth.ntlm[2].upcase
|
|
139
136
|
else
|
|
140
|
-
|
|
137
|
+
""
|
|
141
138
|
end
|
|
142
139
|
|
|
143
|
-
ntlm_response = ntlm_message.response(message_builder
|
|
144
|
-
|
|
140
|
+
ntlm_response = ntlm_message.response(message_builder,
|
|
141
|
+
{ntlmv2: true})
|
|
145
142
|
# Finally add header of Authorization
|
|
146
143
|
@request.headers["Authorization"] = "#{auth_method} #{ntlm_response.encode64}"
|
|
147
144
|
end
|
|
@@ -212,10 +209,9 @@ module HTTPI
|
|
|
212
209
|
headers.each do |key, value|
|
|
213
210
|
headers[key] = value[0] if value.size <= 1
|
|
214
211
|
end
|
|
215
|
-
body = (response.body.
|
|
212
|
+
body = (response.body.is_a?(Net::ReadAdapter) ? "" : response.body)
|
|
216
213
|
Response.new response.code, headers, body
|
|
217
214
|
end
|
|
218
|
-
|
|
219
215
|
end
|
|
220
216
|
end
|
|
221
217
|
end
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
module HTTPI
|
|
2
2
|
module Adapter
|
|
3
|
-
|
|
4
3
|
# = HTTPI::Adapter::NetHTTPPersistent
|
|
5
4
|
#
|
|
6
5
|
# Adapter for the Net::HTTP::Persistent client.
|
|
7
6
|
# http://docs.seattlerb.org/net-http-persistent/Net/HTTP/Persistent.html
|
|
8
7
|
class NetHTTPPersistent < NetHTTP
|
|
9
|
-
|
|
10
|
-
register :net_http_persistent, :deps => %w(net/http/persistent)
|
|
8
|
+
register :net_http_persistent, deps: %w[net/http/persistent]
|
|
11
9
|
|
|
12
10
|
private
|
|
13
11
|
|
|
14
12
|
def create_client
|
|
15
|
-
if Gem::Version.new(Net::HTTP::Persistent::VERSION) >= Gem::Version.new(
|
|
13
|
+
if Gem::Version.new(Net::HTTP::Persistent::VERSION) >= Gem::Version.new("3.0.0")
|
|
16
14
|
Net::HTTP::Persistent.new name: thread_key
|
|
17
15
|
else
|
|
18
16
|
Net::HTTP::Persistent.new thread_key
|
|
@@ -40,7 +38,7 @@ module HTTPI
|
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def thread_key
|
|
43
|
-
@request.url.host.split(/\W/).reject{|p|p == ""}.join(
|
|
41
|
+
@request.url.host.split(/\W/).reject { |p| p == "" }.join("-")
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
44
|
end
|
data/lib/httpi/adapter/rack.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "base64"
|
|
2
|
+
require "httpi/adapter/base"
|
|
3
|
+
require "httpi/response"
|
|
4
4
|
|
|
5
5
|
module HTTPI
|
|
6
6
|
module Adapter
|
|
7
|
-
|
|
8
7
|
# = HTTPI::Adapter::Rack
|
|
9
8
|
#
|
|
10
9
|
# Adapter for Rack::MockRequest.
|
|
@@ -16,7 +15,7 @@ module HTTPI
|
|
|
16
15
|
# HTTPI::Adapter::Rack.mount 'application', RackApplication
|
|
17
16
|
# HTTPI.get("http://application/path", :rack)
|
|
18
17
|
class Rack < Base
|
|
19
|
-
register :rack, :
|
|
18
|
+
register :rack, deps: %w[rack/mock]
|
|
20
19
|
|
|
21
20
|
attr_reader :client
|
|
22
21
|
|
|
@@ -29,27 +28,26 @@ module HTTPI
|
|
|
29
28
|
# Attaches Rack endpoint at specified host.
|
|
30
29
|
# Endpoint will be acessible at {http://host/ http://host/} url.
|
|
31
30
|
def self.mount(host, application)
|
|
32
|
-
|
|
31
|
+
mounted_apps[host] = application
|
|
33
32
|
end
|
|
34
33
|
|
|
35
34
|
# Removes Rack endpoint.
|
|
36
35
|
def self.unmount(host)
|
|
37
|
-
|
|
36
|
+
mounted_apps.delete(host)
|
|
38
37
|
end
|
|
39
38
|
|
|
40
39
|
def initialize(request)
|
|
41
40
|
@app = self.class.mounted_apps[request.url.host]
|
|
42
41
|
|
|
43
|
-
|
|
44
42
|
if @app.nil?
|
|
45
|
-
message
|
|
43
|
+
message = "Application '#{request.url.host}' not mounted: "
|
|
46
44
|
message += "use `HTTPI::Adapter::Rack.mount('#{request.url.host}', RackApplicationClass)`"
|
|
47
45
|
|
|
48
46
|
raise message
|
|
49
47
|
end
|
|
50
48
|
|
|
51
49
|
@request = request
|
|
52
|
-
@client
|
|
50
|
+
@client = ::Rack::MockRequest.new(@app)
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
# Executes arbitrary HTTP requests.
|
|
@@ -68,9 +66,9 @@ module HTTPI
|
|
|
68
66
|
|
|
69
67
|
if @request.auth.http?
|
|
70
68
|
if @request.auth.basic?
|
|
71
|
-
basic_auth = @request.auth.basic.join(
|
|
72
|
-
encoded = Base64.encode64(basic_auth).gsub('\n',
|
|
73
|
-
@request.headers[
|
|
69
|
+
basic_auth = @request.auth.basic.join(":")
|
|
70
|
+
encoded = Base64.encode64(basic_auth).gsub('\n', "")
|
|
71
|
+
@request.headers["Authorization"] = "Basic #{encoded}"
|
|
74
72
|
else
|
|
75
73
|
raise NotSupportedError, "Rack adapter does not support HTTP #{@request.auth.type} auth"
|
|
76
74
|
end
|
|
@@ -86,14 +84,24 @@ module HTTPI
|
|
|
86
84
|
|
|
87
85
|
env = {}
|
|
88
86
|
@request.headers.each do |header, value|
|
|
89
|
-
|
|
87
|
+
# The headers CONTENT_TYPE and CONTENT_LENGTH should not have "HTTP_"
|
|
88
|
+
# added as a prefix. For all other headers, add the prefix "HTTP_"
|
|
89
|
+
# https://github.com/rack/rack/blob/main/SPEC.rdoc#label-HTTP_+Headers
|
|
90
|
+
normalized = header.tr("-", "_").upcase
|
|
91
|
+
key = if normalized == "CONTENT_TYPE" || normalized == "CONTENT_LENGTH"
|
|
92
|
+
normalized
|
|
93
|
+
else
|
|
94
|
+
"HTTP_#{normalized}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
env[key] = value
|
|
90
98
|
end
|
|
91
99
|
|
|
92
100
|
response = @client.request(method.to_s.upcase, @request.url.to_s,
|
|
93
|
-
|
|
101
|
+
{fatal: true, input: @request.body.to_s}.merge(env))
|
|
94
102
|
|
|
95
103
|
Response.new(response.status, response.headers, response.body)
|
|
96
104
|
end
|
|
97
105
|
end
|
|
98
106
|
end
|
|
99
|
-
end
|
|
107
|
+
end
|