httpi 2.4.2 → 2.4.3
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/.travis.yml +7 -1
- data/CHANGELOG.md +8 -0
- data/Gemfile +3 -0
- data/Rakefile +5 -3
- data/lib/httpi/adapter/excon.rb +5 -1
- data/lib/httpi/adapter/http.rb +6 -2
- data/lib/httpi/adapter/net_http.rb +7 -3
- data/lib/httpi/auth/ssl.rb +9 -1
- data/lib/httpi/logger.rb +6 -1
- data/lib/httpi/version.rb +1 -1
- data/spec/httpi/adapter/curb_spec.rb +1 -2
- data/spec/httpi/adapter/net_http_spec.rb +27 -0
- data/spec/httpi/auth/ssl_spec.rb +1 -1
- data/spec/integration/excon_spec.rb +108 -0
- data/spec/integration/http_spec.rb +109 -0
- data/spec/integration/net_http_persistent_spec.rb +3 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 613b8bb3b0fa730f0920296081683075b6de1c34164bd347a1d93d5928d4af0b
|
4
|
+
data.tar.gz: 84d1328228a15c27afee7a4bbe04d3a29079129b7227697c6993728c96b4a4c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4ea5d0acbfb93fd0599268946a062cb20d162195469bc92d548b241a75902b4b5f2131a5d95fc21c9165b8cf68058bc156126e265676d083b888558f4e32d35
|
7
|
+
data.tar.gz: '069622ebb89c624bb74d12a883506af7b38553f016a90c223a91a0230fb3b11115b665bafcdcf8c0f3a26c121abdf817581ff7fcddb68acd727ad9218ead6ba8'
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
### 2.4.3
|
2
|
+
|
3
|
+
* Fix: [#171](https://github.com/savonrb/httpi/pull/171) bug with rubyntlm v0.6.0
|
4
|
+
* Fix: [#181](https://github.com/savonrb/httpi/pull/181) excon and http adapters
|
5
|
+
* Feature: [#183](https://github.com/savonrb/httpi/pull/183) Logging ssl information of a request
|
6
|
+
* Fix: [#187](https://github.com/savonrb/httpi/pull/187) only require ntlm if ntlm auth was requested
|
7
|
+
|
8
|
+
|
1
9
|
### 2.4.2
|
2
10
|
|
3
11
|
* Feature: [#165](https://github.com/savonrb/httpi/pull/165) Extended net_http adapter ssl options with cert_store and ca_path
|
data/Gemfile
CHANGED
@@ -3,6 +3,9 @@ gemspec
|
|
3
3
|
|
4
4
|
gem 'jruby-openssl', :platforms => :jruby
|
5
5
|
|
6
|
+
# compatibility restrictions for http clients under existing travis test environments
|
7
|
+
gem 'public_suffix', '~> 2.0' # or remove rubies < 2.1 from travis.yml
|
8
|
+
|
6
9
|
# http clients
|
7
10
|
gem 'httpclient', '~> 2.3', :require => false
|
8
11
|
gem 'curb', '~> 0.8', :require => false, :platforms => :ruby
|
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/lib/httpi/adapter/excon.rb
CHANGED
@@ -77,7 +77,11 @@ module HTTPI
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def respond_with(response)
|
80
|
-
|
80
|
+
headers = response.headers.dup
|
81
|
+
if (cookies = response.data[:cookies]) && !cookies.empty?
|
82
|
+
headers["Set-Cookie"] = cookies
|
83
|
+
end
|
84
|
+
Response.new response.status, headers, response.body
|
81
85
|
end
|
82
86
|
|
83
87
|
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
|
@@ -18,7 +18,7 @@ module HTTPI
|
|
18
18
|
|
19
19
|
register :net_http, :deps => %w(net/https)
|
20
20
|
def initialize(request)
|
21
|
-
check_net_ntlm_version!
|
21
|
+
check_net_ntlm_version! if request.auth.ntlm?
|
22
22
|
@request = request
|
23
23
|
@client = create_client
|
24
24
|
end
|
@@ -55,11 +55,15 @@ module HTTPI
|
|
55
55
|
end
|
56
56
|
|
57
57
|
private
|
58
|
+
def ntlm_version
|
59
|
+
Net::NTLM::VERSION::STRING
|
60
|
+
end
|
61
|
+
|
58
62
|
def check_net_ntlm_version!
|
59
63
|
begin
|
60
64
|
require 'net/ntlm'
|
61
|
-
require 'net/ntlm/version' unless Net::NTLM.const_defined?(:VERSION)
|
62
|
-
unless
|
65
|
+
require 'net/ntlm/version' unless Net::NTLM.const_defined?(:VERSION, false)
|
66
|
+
unless ntlm_version >= '0.3.2'
|
63
67
|
raise ArgumentError, 'Invalid version of rubyntlm. Please use v0.3.2+.'
|
64
68
|
end
|
65
69
|
rescue LoadError
|
data/lib/httpi/auth/ssl.rb
CHANGED
@@ -10,7 +10,15 @@ module HTTPI
|
|
10
10
|
|
11
11
|
VERIFY_MODES = [:none, :peer, :fail_if_no_peer_cert, :client_once]
|
12
12
|
CERT_TYPES = [:pem, :der]
|
13
|
-
|
13
|
+
|
14
|
+
# Fix for
|
15
|
+
# httpi/auth/ssl.rb:13: warning: constant OpenSSL::SSL::SSLContext::METHODS is deprecated
|
16
|
+
ssl_context = OpenSSL::SSL::SSLContext
|
17
|
+
SSL_VERSIONS = if ssl_context.const_defined? :METHODS_MAP
|
18
|
+
ssl_context.const_get(:METHODS_MAP).keys
|
19
|
+
else
|
20
|
+
ssl_context::METHODS.reject { |method| method.match(/server|client/) }
|
21
|
+
end.sort.reverse
|
14
22
|
|
15
23
|
# Returns whether SSL configuration is present.
|
16
24
|
def present?
|
data/lib/httpi/logger.rb
CHANGED
@@ -43,8 +43,13 @@ module HTTPI
|
|
43
43
|
protected
|
44
44
|
|
45
45
|
def log_request(method, request, adapter)
|
46
|
-
log("HTTPI #{method.to_s.upcase} request to #{request.url.host} (#{adapter})")
|
46
|
+
log("HTTPI #{request_ssl_info(request)} #{method.to_s.upcase} request to #{request.url.host} (#{adapter})")
|
47
47
|
end
|
48
48
|
|
49
|
+
def request_ssl_info(request)
|
50
|
+
if request.auth && request.auth.ssl
|
51
|
+
"#{request.auth.ssl.ssl_version}/#{request.auth.ssl.verify_mode}"
|
52
|
+
end
|
53
|
+
end
|
49
54
|
end
|
50
55
|
end
|
data/lib/httpi/version.rb
CHANGED
@@ -257,8 +257,7 @@ unless RUBY_PLATFORM =~ /java/
|
|
257
257
|
end
|
258
258
|
|
259
259
|
it 'to 2 when ssl_version is specified as SSLv2/SSLv23' do
|
260
|
-
version =
|
261
|
-
version = version.select { |method| method.to_s.match(/SSLv2|SSLv23/) }.first
|
260
|
+
version = HTTPI::Auth::SSL::SSL_VERSIONS.select { |method| method.to_s.match(/SSLv2|SSLv23/) }.first
|
262
261
|
request.auth.ssl.ssl_version = version
|
263
262
|
curb.expects(:ssl_version=).with(2)
|
264
263
|
|
@@ -132,6 +132,33 @@ describe HTTPI::Adapter::NetHTTP do
|
|
132
132
|
to raise_error(HTTPI::NotSupportedError, /Net::NTLM is not available/)
|
133
133
|
end
|
134
134
|
|
135
|
+
it 'does not require ntlm when ntlm authenication is not requested' do
|
136
|
+
HTTPI::Adapter::NetHTTP.any_instance.stubs(:check_net_ntlm_version!).raises(RuntimeError)
|
137
|
+
request = HTTPI::Request.new(@server.url)
|
138
|
+
expect(request.auth.ntlm?).to be false
|
139
|
+
|
140
|
+
# make sure a request doesn't call ntlm check if we don't ask for it.
|
141
|
+
expect { HTTPI.get(request, adapter) }.not_to raise_error
|
142
|
+
HTTPI::Adapter::NetHTTP.any_instance.unstub(:check_net_ntlm_version!)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'does check ntlm when ntlm authentication is requested' do
|
146
|
+
request = HTTPI::Request.new(@server.url + "ntlm-auth")
|
147
|
+
request.auth.ntlm("tester", "vReqSoafRe5O")
|
148
|
+
|
149
|
+
expect { HTTPI.get(request, adapter) }.not_to raise_error
|
150
|
+
|
151
|
+
# the check should also verify that the version of ntlm is supported and still fail if it isn't
|
152
|
+
HTTPI::Adapter::NetHTTP.any_instance.stubs(:ntlm_version).returns("0.1.1")
|
153
|
+
|
154
|
+
request = HTTPI::Request.new(@server.url + "ntlm-auth")
|
155
|
+
request.auth.ntlm("tester", "vReqSoafRe5O")
|
156
|
+
|
157
|
+
expect { HTTPI.get(request, adapter) }.to raise_error(ArgumentError, /Invalid version/)
|
158
|
+
|
159
|
+
HTTPI::Adapter::NetHTTP.any_instance.unstub(:ntlm_version)
|
160
|
+
end
|
161
|
+
|
135
162
|
it "does not crash when authenticate header is missing (on second request)" do
|
136
163
|
request = HTTPI::Request.new(@server.url + 'ntlm-auth')
|
137
164
|
request.auth.ntlm("tester", "vReqSoafRe5O")
|
data/spec/httpi/auth/ssl_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require "httpi/auth/ssl"
|
|
3
3
|
|
4
4
|
describe HTTPI::Auth::SSL do
|
5
5
|
before(:all) do
|
6
|
-
@ssl_versions =
|
6
|
+
@ssl_versions = HTTPI::Auth::SSL::SSL_VERSIONS
|
7
7
|
end
|
8
8
|
|
9
9
|
describe "VERIFY_MODES" do
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe HTTPI::Adapter::HTTPClient do
|
5
|
+
|
6
|
+
subject(:adapter) { :excon }
|
7
|
+
|
8
|
+
context "http requests" do
|
9
|
+
before :all do
|
10
|
+
@server = IntegrationServer.run
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
@server.stop
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sends and receives HTTP headers" do
|
18
|
+
request = HTTPI::Request.new(@server.url + "x-header")
|
19
|
+
request.headers["X-Header"] = "HTTPI"
|
20
|
+
|
21
|
+
response = HTTPI.get(request, adapter)
|
22
|
+
expect(response.body).to include("HTTPI")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "it supports headers with multiple values" do
|
26
|
+
request = HTTPI::Request.new(@server.url + "cookies")
|
27
|
+
|
28
|
+
response = HTTPI.get(request, adapter)
|
29
|
+
cookies = ["cookie1=chip1; path=/", "cookie2=chip2; path=/"]
|
30
|
+
expect(response.headers["Set-Cookie"]).to eq(cookies)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "executes GET requests" do
|
34
|
+
response = HTTPI.get(@server.url, adapter)
|
35
|
+
expect(response.body).to eq("get")
|
36
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "executes POST requests" do
|
40
|
+
response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
|
41
|
+
expect(response.body).to eq("post")
|
42
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "executes HEAD requests" do
|
46
|
+
response = HTTPI.head(@server.url, adapter)
|
47
|
+
expect(response.code).to eq(200)
|
48
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "executes PUT requests" do
|
52
|
+
response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
|
53
|
+
expect(response.body).to eq("put")
|
54
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "executes DELETE requests" do
|
58
|
+
response = HTTPI.delete(@server.url, adapter)
|
59
|
+
expect(response.body).to eq("delete")
|
60
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "supports basic authentication" do
|
64
|
+
request = HTTPI::Request.new(@server.url + "basic-auth")
|
65
|
+
request.auth.basic("admin", "secret")
|
66
|
+
|
67
|
+
response = HTTPI.get(request, adapter)
|
68
|
+
expect(response.body).to eq("basic-auth")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "supports chunked response" do
|
72
|
+
request = HTTPI::Request.new(@server.url)
|
73
|
+
res = ""
|
74
|
+
request.on_body do |body|
|
75
|
+
res += body
|
76
|
+
end
|
77
|
+
response = HTTPI.post(request, adapter)
|
78
|
+
expect(res).to eq("post")
|
79
|
+
expect(response.body).to eq("")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if RUBY_PLATFORM =~ /java/
|
84
|
+
pending "Puma Server complains: SSL not supported on JRuby"
|
85
|
+
else
|
86
|
+
context "https requests" do
|
87
|
+
before :all do
|
88
|
+
@server = IntegrationServer.run(:ssl => true)
|
89
|
+
end
|
90
|
+
after :all do
|
91
|
+
@server.stop
|
92
|
+
end
|
93
|
+
|
94
|
+
it "raises when no certificate was set up" do
|
95
|
+
expect { HTTPI.post(@server.url, "", adapter) }.to raise_error(HTTPI::SSLError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "works when set up properly" do
|
99
|
+
request = HTTPI::Request.new(@server.url)
|
100
|
+
request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
|
101
|
+
|
102
|
+
response = HTTPI.get(request, adapter)
|
103
|
+
expect(response.body).to eq("get")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe HTTPI::Adapter::HTTP do
|
5
|
+
|
6
|
+
subject(:adapter) { :http }
|
7
|
+
|
8
|
+
context "http requests" do
|
9
|
+
before :all do
|
10
|
+
@server = IntegrationServer.run
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
@server.stop
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sends and receives HTTP headers" do
|
18
|
+
request = HTTPI::Request.new(@server.url + "x-header")
|
19
|
+
request.headers["X-Header"] = "HTTPI"
|
20
|
+
|
21
|
+
response = HTTPI.get(request, adapter)
|
22
|
+
expect(response.body).to include("HTTPI")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "it supports headers with multiple values" do
|
26
|
+
request = HTTPI::Request.new(@server.url + "cookies")
|
27
|
+
|
28
|
+
response = HTTPI.get(request, adapter)
|
29
|
+
cookies = ["cookie1=chip1; path=/", "cookie2=chip2; path=/"]
|
30
|
+
expect(response.headers["Set-Cookie"]).to eq(cookies)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "executes GET requests" do
|
34
|
+
response = HTTPI.get(@server.url, adapter)
|
35
|
+
expect(response.body).to eq("get")
|
36
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "executes POST requests" do
|
40
|
+
response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
|
41
|
+
expect(response.body).to eq("post")
|
42
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "executes HEAD requests" do
|
46
|
+
response = HTTPI.head(@server.url, adapter)
|
47
|
+
expect(response.code).to eq(200)
|
48
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "executes PUT requests" do
|
52
|
+
response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
|
53
|
+
expect(response.body).to eq("put")
|
54
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "executes DELETE requests" do
|
58
|
+
response = HTTPI.delete(@server.url, adapter)
|
59
|
+
expect(response.body).to eq("delete")
|
60
|
+
expect(response.headers["Content-Type"]).to eq("text/plain")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "supports basic authentication" do
|
64
|
+
request = HTTPI::Request.new(@server.url + "basic-auth")
|
65
|
+
request.auth.basic("admin", "secret")
|
66
|
+
|
67
|
+
response = HTTPI.get(request, adapter)
|
68
|
+
expect(response.body).to eq("basic-auth")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "supports chunked response" do
|
72
|
+
skip("Needs investigation")
|
73
|
+
request = HTTPI::Request.new(@server.url)
|
74
|
+
res = ""
|
75
|
+
request.on_body do |body|
|
76
|
+
res += body
|
77
|
+
end
|
78
|
+
response = HTTPI.post(request, adapter)
|
79
|
+
expect(res).to eq("post")
|
80
|
+
expect(response.body).to eq("")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
if RUBY_PLATFORM =~ /java/
|
85
|
+
pending "Puma Server complains: SSL not supported on JRuby"
|
86
|
+
else
|
87
|
+
context "https requests" do
|
88
|
+
before :all do
|
89
|
+
@server = IntegrationServer.run(:ssl => true)
|
90
|
+
end
|
91
|
+
after :all do
|
92
|
+
@server.stop
|
93
|
+
end
|
94
|
+
|
95
|
+
it "raises when no certificate was set up" do
|
96
|
+
expect { HTTPI.post(@server.url, "", adapter) }.to raise_error(HTTPI::SSLError)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "works when set up properly" do
|
100
|
+
request = HTTPI::Request.new(@server.url)
|
101
|
+
request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
|
102
|
+
|
103
|
+
response = HTTPI.get(request, adapter)
|
104
|
+
expect(response.body).to eq("get")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -37,7 +37,9 @@ describe HTTPI::Adapter::NetHTTP do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it "executes POST requests" do
|
40
|
-
|
40
|
+
request = HTTPI::Request.new(url: @server.url, open_timeout: 1, read_timeout: 1, body: "<some>xml</some>")
|
41
|
+
|
42
|
+
response = HTTPI.post(request, adapter)
|
41
43
|
expect(response.body).to eq("post")
|
42
44
|
expect(response.headers["Content-Type"]).to eq("text/plain")
|
43
45
|
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: 2.4.
|
4
|
+
version: 2.4.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:
|
12
|
+
date: 2018-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -186,9 +186,11 @@ files:
|
|
186
186
|
- spec/httpi/response_spec.rb
|
187
187
|
- spec/integration/curb_spec.rb
|
188
188
|
- spec/integration/em_http_spec.rb
|
189
|
+
- spec/integration/excon_spec.rb
|
189
190
|
- spec/integration/fixtures/ca_all.pem
|
190
191
|
- spec/integration/fixtures/server.cert
|
191
192
|
- spec/integration/fixtures/server.key
|
193
|
+
- spec/integration/http_spec.rb
|
192
194
|
- spec/integration/httpclient_spec.rb
|
193
195
|
- spec/integration/net_http_persistent_spec.rb
|
194
196
|
- spec/integration/net_http_spec.rb
|
@@ -218,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
220
|
version: '0'
|
219
221
|
requirements: []
|
220
222
|
rubyforge_project: httpi
|
221
|
-
rubygems_version: 2.
|
223
|
+
rubygems_version: 2.7.3
|
222
224
|
signing_key:
|
223
225
|
specification_version: 4
|
224
226
|
summary: Common interface for Ruby's HTTP libraries
|