httpi 4.0.1 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +1 -1
- data/CHANGELOG.md +8 -0
- data/httpi.gemspec +2 -2
- data/lib/httpi/utils.rb +53 -196
- data/lib/httpi/version.rb +1 -1
- data/spec/httpi/adapter/httpclient_spec.rb +1 -2
- data/spec/httpi/response_spec.rb +4 -0
- data/spec/integration/curb_spec.rb +2 -1
- data/spec/integration/httpclient_spec.rb +2 -1
- data/spec/integration/net_http_spec.rb +3 -6
- data/spec/integration/support/application.rb +2 -11
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e611d4cea80f9c190772591306f7f355159f41076db623b2663eef371780e0f
|
4
|
+
data.tar.gz: 7c2c1eb582c93bc498c301f85e38d83c7917ca40f65724f646f360d93c727dd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a00241efcfcae64a4ade2fb2b3f07dce9da9524591f6dc90d76ac72caa57062246e904732835afb20794d2d307aced826f18c1e5eb8c968e93c898bdc1f017d7
|
7
|
+
data.tar.gz: 32806a0e4bd9174ea7184b231740623a1dbdef4eb7c9463c86140c6ec48d043793027453d8f40f303321cb855ff70025d823f8a477c1c0449e262fd6e4812bac
|
@@ -3,7 +3,7 @@
|
|
3
3
|
{
|
4
4
|
"name": "Ruby",
|
5
5
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
6
|
-
"image": "mcr.microsoft.com/devcontainers/ruby:1-3.
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/ruby:1-3.3-bullseye"
|
7
7
|
|
8
8
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
9
9
|
// "features": {},
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
* Add your changelog entry here
|
4
4
|
|
5
|
+
### 4.0.3 (2024-07-06)
|
6
|
+
|
7
|
+
* Allow use with rack 3.1
|
8
|
+
|
9
|
+
### 4.0.2 (2024-06-13)
|
10
|
+
|
11
|
+
* Don't change http header casing by @pcai in https://github.com/savonrb/httpi/pull/249
|
12
|
+
|
5
13
|
### 4.0.1 (2024-02-16)
|
6
14
|
|
7
15
|
HTTPI is officially in maintenance mode. Our emphasis will now be on bugs, security fixes, and compatibility with the wider ecosystem. See [this issue](https://github.com/savonrb/httpi/issues/238) for details.
|
data/httpi.gemspec
CHANGED
@@ -21,12 +21,12 @@ Gem::Specification.new do |s|
|
|
21
21
|
|
22
22
|
s.license = 'MIT'
|
23
23
|
|
24
|
-
s.add_dependency 'rack', '>= 2.0', '<
|
24
|
+
s.add_dependency 'rack', '>= 2.0', '< 4'
|
25
25
|
s.add_dependency 'nkf'
|
26
26
|
s.add_dependency 'base64'
|
27
27
|
s.add_dependency 'mutex_m'
|
28
28
|
|
29
|
-
s.add_development_dependency 'rubyntlm', '~> 0.
|
29
|
+
s.add_development_dependency 'rubyntlm', '~> 0.6.4'
|
30
30
|
s.add_development_dependency 'rake', '~> 13.0'
|
31
31
|
s.add_development_dependency 'rspec', '~> 3.5'
|
32
32
|
s.add_development_dependency 'mocha', '~> 0.13'
|
data/lib/httpi/utils.rb
CHANGED
@@ -3,236 +3,93 @@
|
|
3
3
|
# library for it.
|
4
4
|
module HTTPI
|
5
5
|
module Utils
|
6
|
-
#
|
6
|
+
# A case-insensitive Hash that preserves the original case of a
|
7
|
+
# header when set.
|
8
|
+
#
|
7
9
|
class Headers < Hash
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Accept-Patch
|
12
|
-
Accept-Ranges
|
13
|
-
Access-Control-Allow-Credentials
|
14
|
-
Access-Control-Allow-Headers
|
15
|
-
Access-Control-Allow-Methods
|
16
|
-
Access-Control-Allow-Origin
|
17
|
-
Access-Control-Expose-Headers
|
18
|
-
Access-Control-Max-Age
|
19
|
-
Age
|
20
|
-
Allow
|
21
|
-
Alt-Svc
|
22
|
-
Cache-Control
|
23
|
-
Connection
|
24
|
-
Content-Disposition
|
25
|
-
Content-Encoding
|
26
|
-
Content-Language
|
27
|
-
Content-Length
|
28
|
-
Content-Location
|
29
|
-
Content-MD5
|
30
|
-
Content-Range
|
31
|
-
Content-Security-Policy
|
32
|
-
Content-Security-Policy-Report-Only
|
33
|
-
Content-Type
|
34
|
-
Date
|
35
|
-
Delta-Base
|
36
|
-
ETag
|
37
|
-
Expect-CT
|
38
|
-
Expires
|
39
|
-
Feature-Policy
|
40
|
-
IM
|
41
|
-
Last-Modified
|
42
|
-
Link
|
43
|
-
Location
|
44
|
-
NEL
|
45
|
-
P3P
|
46
|
-
Permissions-Policy
|
47
|
-
Pragma
|
48
|
-
Preference-Applied
|
49
|
-
Proxy-Authenticate
|
50
|
-
Public-Key-Pins
|
51
|
-
Referrer-Policy
|
52
|
-
Refresh
|
53
|
-
Report-To
|
54
|
-
Retry-After
|
55
|
-
Server
|
56
|
-
Set-Cookie
|
57
|
-
Status
|
58
|
-
Strict-Transport-Security
|
59
|
-
Timing-Allow-Origin
|
60
|
-
Tk
|
61
|
-
Trailer
|
62
|
-
Transfer-Encoding
|
63
|
-
Upgrade
|
64
|
-
Vary
|
65
|
-
Via
|
66
|
-
WWW-Authenticate
|
67
|
-
Warning
|
68
|
-
X-Cascade
|
69
|
-
X-Content-Duration
|
70
|
-
X-Content-Security-Policy
|
71
|
-
X-Content-Type-Options
|
72
|
-
X-Correlation-ID
|
73
|
-
X-Correlation-Id
|
74
|
-
X-Download-Options
|
75
|
-
X-Frame-Options
|
76
|
-
X-Permitted-Cross-Domain-Policies
|
77
|
-
X-Powered-By
|
78
|
-
X-Redirect-By
|
79
|
-
X-Request-ID
|
80
|
-
X-Request-Id
|
81
|
-
X-Runtime
|
82
|
-
X-UA-Compatible
|
83
|
-
X-WebKit-CS
|
84
|
-
X-XSS-Protection
|
85
|
-
).each do |str|
|
86
|
-
downcased = str.downcase.freeze
|
87
|
-
KNOWN_HEADERS[str] = KNOWN_HEADERS[downcased] = downcased
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.[](*items)
|
91
|
-
if items.length % 2 != 0
|
92
|
-
if items.length == 1 && items.first.is_a?(Hash)
|
93
|
-
new.merge!(items.first)
|
94
|
-
else
|
95
|
-
raise ArgumentError, "odd number of arguments for Utils::Headers"
|
96
|
-
end
|
10
|
+
def self.[](headers)
|
11
|
+
if headers.is_a?(Headers) && !headers.frozen?
|
12
|
+
return headers
|
97
13
|
else
|
98
|
-
|
99
|
-
loop do
|
100
|
-
break if items.length == 0
|
101
|
-
key = items.shift
|
102
|
-
value = items.shift
|
103
|
-
hash[key] = value
|
104
|
-
end
|
105
|
-
hash
|
14
|
+
return self.new(headers)
|
106
15
|
end
|
107
16
|
end
|
108
17
|
|
109
|
-
def
|
110
|
-
super(
|
111
|
-
|
112
|
-
|
113
|
-
def []=(key, value)
|
114
|
-
super(KNOWN_HEADERS[key] || key.downcase.freeze, value)
|
115
|
-
end
|
116
|
-
alias store []=
|
117
|
-
|
118
|
-
def assoc(key)
|
119
|
-
super(downcase_key(key))
|
120
|
-
end
|
121
|
-
|
122
|
-
def compare_by_identity
|
123
|
-
raise TypeError, "Utils::Headers cannot compare by identity, use regular Hash"
|
124
|
-
end
|
125
|
-
|
126
|
-
def delete(key)
|
127
|
-
super(downcase_key(key))
|
128
|
-
end
|
129
|
-
|
130
|
-
def dig(key, *a)
|
131
|
-
super(downcase_key(key), *a)
|
18
|
+
def initialize(hash = {})
|
19
|
+
super()
|
20
|
+
@names = {}
|
21
|
+
hash.each { |k, v| self[k] = v }
|
132
22
|
end
|
133
23
|
|
134
|
-
|
135
|
-
|
24
|
+
# on dup/clone, we need to duplicate @names hash
|
25
|
+
def initialize_copy(other)
|
136
26
|
super
|
27
|
+
@names = other.names.dup
|
137
28
|
end
|
138
29
|
|
139
|
-
|
140
|
-
|
30
|
+
# on clear, we need to clear @names hash
|
31
|
+
def clear
|
32
|
+
super
|
33
|
+
@names.clear
|
141
34
|
end
|
142
35
|
|
143
|
-
def
|
144
|
-
super
|
36
|
+
def each
|
37
|
+
super do |k, v|
|
38
|
+
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
39
|
+
end
|
145
40
|
end
|
146
|
-
alias include? has_key?
|
147
|
-
alias key? has_key?
|
148
|
-
alias member? has_key?
|
149
41
|
|
150
|
-
def
|
151
|
-
hash =
|
152
|
-
each{|
|
42
|
+
def to_hash
|
43
|
+
hash = {}
|
44
|
+
each { |k, v| hash[k] = v }
|
153
45
|
hash
|
154
46
|
end
|
155
47
|
|
156
|
-
def
|
157
|
-
|
48
|
+
def [](k)
|
49
|
+
super(k) || super(@names[k.downcase])
|
158
50
|
end
|
159
51
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
52
|
+
def []=(k, v)
|
53
|
+
canonical = k.downcase.freeze
|
54
|
+
delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
|
55
|
+
@names[canonical] = k
|
56
|
+
super k, v
|
164
57
|
end
|
165
58
|
|
166
|
-
def
|
167
|
-
|
168
|
-
|
59
|
+
def delete(k)
|
60
|
+
canonical = k.downcase
|
61
|
+
result = super @names.delete(canonical)
|
62
|
+
result
|
169
63
|
end
|
170
64
|
|
171
|
-
def
|
172
|
-
|
173
|
-
hash.select!(&block)
|
174
|
-
hash
|
65
|
+
def include?(k)
|
66
|
+
super || @names.include?(k.downcase)
|
175
67
|
end
|
176
68
|
|
177
|
-
|
178
|
-
|
179
|
-
|
69
|
+
alias_method :has_key?, :include?
|
70
|
+
alias_method :member?, :include?
|
71
|
+
alias_method :key?, :include?
|
180
72
|
|
181
|
-
def
|
182
|
-
|
183
|
-
end
|
184
|
-
|
185
|
-
def update(hash, &block)
|
186
|
-
hash.each do |key, value|
|
187
|
-
self[key] = if block_given? && include?(key)
|
188
|
-
block.call(key, self[key], value)
|
189
|
-
else
|
190
|
-
value
|
191
|
-
end
|
192
|
-
end
|
73
|
+
def merge!(other)
|
74
|
+
other.each { |k, v| self[k] = v }
|
193
75
|
self
|
194
76
|
end
|
195
|
-
alias merge! update
|
196
77
|
|
197
|
-
def
|
198
|
-
|
78
|
+
def merge(other)
|
79
|
+
hash = dup
|
80
|
+
hash.merge! other
|
199
81
|
end
|
200
82
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
h = self.class.new
|
206
|
-
a.each{|k| h[k] = self[k] if has_key?(k)}
|
207
|
-
h
|
208
|
-
end
|
209
|
-
|
210
|
-
def transform_keys(&block)
|
211
|
-
dup.transform_keys!(&block)
|
212
|
-
end
|
213
|
-
|
214
|
-
def transform_keys!
|
215
|
-
hash = self.class.new
|
216
|
-
each do |k, v|
|
217
|
-
hash[yield k] = v
|
218
|
-
end
|
219
|
-
replace(hash)
|
220
|
-
end
|
83
|
+
def replace(other)
|
84
|
+
clear
|
85
|
+
other.each { |k, v| self[k] = v }
|
86
|
+
self
|
221
87
|
end
|
222
88
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
def except(*a)
|
227
|
-
super(*a.map!{|key| downcase_key(key)})
|
89
|
+
protected
|
90
|
+
def names
|
91
|
+
@names
|
228
92
|
end
|
229
|
-
end
|
230
|
-
|
231
|
-
private
|
232
|
-
|
233
|
-
def downcase_key(key)
|
234
|
-
key.is_a?(String) ? KNOWN_HEADERS[key] || key.downcase : key
|
235
|
-
end
|
236
93
|
end
|
237
94
|
end
|
238
95
|
end
|
data/lib/httpi/version.rb
CHANGED
@@ -215,8 +215,7 @@ describe HTTPI::Adapter::HTTPClient do
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
218
|
-
|
219
|
-
xit "supports NTLM authentication" do
|
218
|
+
it "supports NTLM authentication" do
|
220
219
|
request = HTTPI::Request.new(@server.url + "ntlm-auth")
|
221
220
|
|
222
221
|
request.auth.ntlm("tester", "vReqSoafRe5O")
|
data/spec/httpi/response_spec.rb
CHANGED
@@ -118,6 +118,10 @@ describe HTTPI::Response do
|
|
118
118
|
it "returns the HTTP response headers" do
|
119
119
|
expect(response.headers).to eq HTTPI::Utils::Headers.new.merge({ "Content-Type" => "application/dime" })
|
120
120
|
end
|
121
|
+
|
122
|
+
it "preserves casing" do
|
123
|
+
expect(response.headers.keys).to eq ["Content-Type"]
|
124
|
+
end
|
121
125
|
end
|
122
126
|
|
123
127
|
describe "#body" do
|
@@ -82,7 +82,8 @@ describe HTTPI::Adapter::Curb do
|
|
82
82
|
expect(response.body).to eq("basic-auth")
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
# Rack::Auth::Digest is removed in Rack 3.1
|
86
|
+
xit "supports digest authentication" do
|
86
87
|
request = HTTPI::Request.new(@server.url + "digest-auth")
|
87
88
|
request.auth.digest("admin", "secret")
|
88
89
|
|
@@ -79,7 +79,8 @@ describe HTTPI::Adapter::HTTPClient do
|
|
79
79
|
expect(response.body).to eq("basic-auth")
|
80
80
|
end
|
81
81
|
|
82
|
-
|
82
|
+
# Rack::Auth::Digest is removed in Rack 3.1
|
83
|
+
xit "supports digest authentication" do
|
83
84
|
request = HTTPI::Request.new(@server.url + "digest-auth")
|
84
85
|
request.auth.digest("admin", "secret")
|
85
86
|
|
@@ -138,8 +138,7 @@ describe HTTPI::Adapter::NetHTTP do
|
|
138
138
|
to raise_error(HTTPI::NotSupportedError, /does not support HTTP digest authentication/)
|
139
139
|
end
|
140
140
|
|
141
|
-
|
142
|
-
xit "supports ntlm authentication" do
|
141
|
+
it "supports ntlm authentication" do
|
143
142
|
request = HTTPI::Request.new(@server.url + "ntlm-auth")
|
144
143
|
request.auth.ntlm("tester", "vReqSoafRe5O")
|
145
144
|
|
@@ -167,8 +166,7 @@ describe HTTPI::Adapter::NetHTTP do
|
|
167
166
|
HTTPI::Adapter::NetHTTP.any_instance.unstub(:check_net_ntlm_version!)
|
168
167
|
end
|
169
168
|
|
170
|
-
|
171
|
-
xit "does check ntlm when ntlm authentication is requested" do
|
169
|
+
it "does check ntlm when ntlm authentication is requested" do
|
172
170
|
request = HTTPI::Request.new(@server.url + "ntlm-auth")
|
173
171
|
request.auth.ntlm("tester", "vReqSoafRe5O")
|
174
172
|
|
@@ -185,8 +183,7 @@ describe HTTPI::Adapter::NetHTTP do
|
|
185
183
|
HTTPI::Adapter::NetHTTP.any_instance.unstub(:ntlm_version)
|
186
184
|
end
|
187
185
|
|
188
|
-
|
189
|
-
xit "does not crash when authenticate header is missing (on second request)" do
|
186
|
+
it "does not crash when authenticate header is missing (on second request)" do
|
190
187
|
request = HTTPI::Request.new(@server.url + "ntlm-auth")
|
191
188
|
request.auth.ntlm("tester", "vReqSoafRe5O")
|
192
189
|
|
@@ -78,19 +78,10 @@ class IntegrationServer
|
|
78
78
|
end
|
79
79
|
|
80
80
|
map "/digest-auth" do
|
81
|
-
|
81
|
+
# Rack::Auth::Digest is removed in Rack 3.1
|
82
|
+
run lambda { |env|
|
82
83
|
IntegrationServer.respond_with "digest-auth"
|
83
84
|
}
|
84
|
-
|
85
|
-
realm = 'digest-realm'
|
86
|
-
app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
|
87
|
-
username == 'admin' ? Digest::MD5.hexdigest("admin:#{realm}:secret") : nil
|
88
|
-
end
|
89
|
-
app.realm = realm
|
90
|
-
app.opaque = 'this-should-be-secret'
|
91
|
-
app.passwords_hashed = true
|
92
|
-
|
93
|
-
run app
|
94
85
|
end
|
95
86
|
|
96
87
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Harrington
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-07-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
version: '2.0'
|
21
21
|
- - "<"
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: '
|
23
|
+
version: '4'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
version: '2.0'
|
31
31
|
- - "<"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '4'
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: nkf
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,14 +79,14 @@ dependencies:
|
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 0.6.4
|
83
83
|
type: :development
|
84
84
|
prerelease: false
|
85
85
|
version_requirements: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: 0.6.4
|
90
90
|
- !ruby/object:Gem::Dependency
|
91
91
|
name: rake
|
92
92
|
requirement: !ruby/object:Gem::Requirement
|
@@ -262,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
262
262
|
- !ruby/object:Gem::Version
|
263
263
|
version: '0'
|
264
264
|
requirements: []
|
265
|
-
rubygems_version: 3.
|
265
|
+
rubygems_version: 3.5.3
|
266
266
|
signing_key:
|
267
267
|
specification_version: 4
|
268
268
|
summary: Common interface for Ruby's HTTP libraries
|