webmock 3.7.1
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 +7 -0
- data/.gemtest +0 -0
- data/.gitignore +34 -0
- data/.rspec-tm +2 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +1698 -0
- data/Gemfile +9 -0
- data/LICENSE +20 -0
- data/README.md +1125 -0
- data/Rakefile +28 -0
- data/lib/webmock.rb +59 -0
- data/lib/webmock/api.rb +109 -0
- data/lib/webmock/assertion_failure.rb +11 -0
- data/lib/webmock/callback_registry.rb +35 -0
- data/lib/webmock/config.rb +18 -0
- data/lib/webmock/cucumber.rb +10 -0
- data/lib/webmock/deprecation.rb +9 -0
- data/lib/webmock/errors.rb +17 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +214 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +347 -0
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +228 -0
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +162 -0
- data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +7 -0
- data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +19 -0
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +14 -0
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +43 -0
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
- data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +242 -0
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +130 -0
- data/lib/webmock/http_lib_adapters/net_http.rb +361 -0
- data/lib/webmock/http_lib_adapters/net_http_response.rb +34 -0
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +130 -0
- data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +174 -0
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +17 -0
- data/lib/webmock/minitest.rb +41 -0
- data/lib/webmock/rack_response.rb +69 -0
- data/lib/webmock/request_body_diff.rb +64 -0
- data/lib/webmock/request_execution_verifier.rb +77 -0
- data/lib/webmock/request_pattern.rb +370 -0
- data/lib/webmock/request_registry.rb +35 -0
- data/lib/webmock/request_signature.rb +54 -0
- data/lib/webmock/request_signature_snippet.rb +61 -0
- data/lib/webmock/request_stub.rb +100 -0
- data/lib/webmock/response.rb +153 -0
- data/lib/webmock/responses_sequence.rb +40 -0
- data/lib/webmock/rspec.rb +41 -0
- data/lib/webmock/rspec/matchers.rb +27 -0
- data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +78 -0
- data/lib/webmock/rspec/matchers/webmock_matcher.rb +67 -0
- data/lib/webmock/stub_registry.rb +67 -0
- data/lib/webmock/stub_request_snippet.rb +38 -0
- data/lib/webmock/test_unit.rb +22 -0
- data/lib/webmock/util/hash_counter.rb +39 -0
- data/lib/webmock/util/hash_keys_stringifier.rb +25 -0
- data/lib/webmock/util/hash_validator.rb +17 -0
- data/lib/webmock/util/headers.rb +64 -0
- data/lib/webmock/util/json.rb +67 -0
- data/lib/webmock/util/query_mapper.rb +281 -0
- data/lib/webmock/util/uri.rb +110 -0
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/util/version_checker.rb +111 -0
- data/lib/webmock/version.rb +3 -0
- data/lib/webmock/webmock.rb +161 -0
- data/minitest/test_helper.rb +34 -0
- data/minitest/test_webmock.rb +9 -0
- data/minitest/webmock_spec.rb +60 -0
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +349 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +492 -0
- data/spec/acceptance/curb/curb_spec_helper.rb +147 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +406 -0
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +77 -0
- data/spec/acceptance/excon/excon_spec.rb +77 -0
- data/spec/acceptance/excon/excon_spec_helper.rb +50 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +82 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
- data/spec/acceptance/httpclient/httpclient_spec.rb +217 -0
- data/spec/acceptance/httpclient/httpclient_spec_helper.rb +57 -0
- data/spec/acceptance/manticore/manticore_spec.rb +56 -0
- data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
- data/spec/acceptance/net_http/net_http_shared.rb +153 -0
- data/spec/acceptance/net_http/net_http_spec.rb +331 -0
- data/spec/acceptance/net_http/net_http_spec_helper.rb +64 -0
- data/spec/acceptance/net_http/real_net_http_spec.rb +20 -0
- data/spec/acceptance/patron/patron_spec.rb +125 -0
- data/spec/acceptance/patron/patron_spec_helper.rb +54 -0
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +313 -0
- data/spec/acceptance/shared/callbacks.rb +148 -0
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +36 -0
- data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +95 -0
- data/spec/acceptance/shared/precedence_of_stubs.rb +15 -0
- data/spec/acceptance/shared/request_expectations.rb +930 -0
- data/spec/acceptance/shared/returning_declared_responses.rb +409 -0
- data/spec/acceptance/shared/stubbing_requests.rb +643 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +135 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +60 -0
- data/spec/acceptance/webmock_shared.rb +41 -0
- data/spec/fixtures/test.txt +1 -0
- data/spec/quality_spec.rb +84 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/support/example_curl_output.txt +22 -0
- data/spec/support/failures.rb +9 -0
- data/spec/support/my_rack_app.rb +53 -0
- data/spec/support/network_connection.rb +19 -0
- data/spec/support/webmock_server.rb +70 -0
- data/spec/unit/api_spec.rb +175 -0
- data/spec/unit/errors_spec.rb +129 -0
- data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +17 -0
- data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +12 -0
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
- data/spec/unit/rack_response_spec.rb +112 -0
- data/spec/unit/request_body_diff_spec.rb +90 -0
- data/spec/unit/request_execution_verifier_spec.rb +208 -0
- data/spec/unit/request_pattern_spec.rb +601 -0
- data/spec/unit/request_registry_spec.rb +95 -0
- data/spec/unit/request_signature_snippet_spec.rb +89 -0
- data/spec/unit/request_signature_spec.rb +155 -0
- data/spec/unit/request_stub_spec.rb +199 -0
- data/spec/unit/response_spec.rb +282 -0
- data/spec/unit/stub_registry_spec.rb +103 -0
- data/spec/unit/stub_request_snippet_spec.rb +115 -0
- data/spec/unit/util/hash_counter_spec.rb +39 -0
- data/spec/unit/util/hash_keys_stringifier_spec.rb +27 -0
- data/spec/unit/util/headers_spec.rb +28 -0
- data/spec/unit/util/json_spec.rb +33 -0
- data/spec/unit/util/query_mapper_spec.rb +157 -0
- data/spec/unit/util/uri_spec.rb +361 -0
- data/spec/unit/util/version_checker_spec.rb +65 -0
- data/spec/unit/webmock_spec.rb +19 -0
- data/test/http_request.rb +24 -0
- data/test/shared_test.rb +108 -0
- data/test/test_helper.rb +23 -0
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +45 -0
- metadata +496 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module EMHttpRequestSpecHelper
|
|
2
|
+
|
|
3
|
+
def failed
|
|
4
|
+
EventMachine.stop
|
|
5
|
+
fail
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def http_request(method, uri, options = {}, &block)
|
|
9
|
+
@http = nil
|
|
10
|
+
head = options[:headers] || {}
|
|
11
|
+
if options[:basic_auth]
|
|
12
|
+
head.merge!('authorization' => options[:basic_auth])
|
|
13
|
+
end
|
|
14
|
+
response = nil
|
|
15
|
+
error = nil
|
|
16
|
+
error_set = false
|
|
17
|
+
uri = Addressable::URI.heuristic_parse(uri)
|
|
18
|
+
EventMachine.run {
|
|
19
|
+
request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}")
|
|
20
|
+
http = request.send(method, {
|
|
21
|
+
timeout: 30,
|
|
22
|
+
body: options[:body],
|
|
23
|
+
file: options[:file],
|
|
24
|
+
query: options[:query],
|
|
25
|
+
head: head,
|
|
26
|
+
compressed: false
|
|
27
|
+
}, &block)
|
|
28
|
+
http.errback {
|
|
29
|
+
error_set = true
|
|
30
|
+
error = if http.respond_to?(:errors)
|
|
31
|
+
http.errors
|
|
32
|
+
else
|
|
33
|
+
http.error
|
|
34
|
+
end
|
|
35
|
+
failed
|
|
36
|
+
}
|
|
37
|
+
http.callback {
|
|
38
|
+
response = OpenStruct.new({
|
|
39
|
+
body: http.response,
|
|
40
|
+
headers: WebMock::Util::Headers.normalize_headers(extract_response_headers(http)),
|
|
41
|
+
message: http.response_header.http_reason,
|
|
42
|
+
status: http.response_header.status.to_s
|
|
43
|
+
})
|
|
44
|
+
EventMachine.stop
|
|
45
|
+
}
|
|
46
|
+
@http = http
|
|
47
|
+
}
|
|
48
|
+
raise error.to_s if error_set
|
|
49
|
+
response
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def client_timeout_exception_class
|
|
53
|
+
"WebMock timeout error"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def connection_refused_exception_class
|
|
57
|
+
RuntimeError
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def http_library
|
|
61
|
+
:em_http_request
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def extract_response_headers(http)
|
|
67
|
+
headers = {}
|
|
68
|
+
if http.response_header
|
|
69
|
+
http.response_header.each do |k,v|
|
|
70
|
+
v = v.join(", ") if v.is_a?(Array)
|
|
71
|
+
headers[k] = v
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
headers
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'acceptance/webmock_shared'
|
|
3
|
+
require 'acceptance/excon/excon_spec_helper'
|
|
4
|
+
|
|
5
|
+
describe "Excon" do
|
|
6
|
+
include ExconSpecHelper
|
|
7
|
+
include_context "with WebMock", :no_url_auth
|
|
8
|
+
|
|
9
|
+
it 'should allow Excon requests to use query hash paramters' do
|
|
10
|
+
stub_request(:get, "http://example.com/resource/?a=1&b=2").to_return(body: "abc")
|
|
11
|
+
expect(Excon.new('http://example.com').get(path: "resource/", query: {a: 1, b: 2}).body).to eq("abc")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'should support Excon :expects options' do
|
|
15
|
+
stub_request(:get, "http://example.com/").to_return(body: 'a')
|
|
16
|
+
expect { Excon.new('http://example.com').get(expects: 204) }.to raise_error(Excon::Errors::OK)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "with response_block" do
|
|
20
|
+
it "should support excon response_block for real requests", net_connect: true do
|
|
21
|
+
a = []
|
|
22
|
+
WebMock.allow_net_connect!
|
|
23
|
+
r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
|
|
24
|
+
get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
|
|
25
|
+
expect(a).to eq(["2", "0", "0", " ", "O", "K"])
|
|
26
|
+
expect(r.body).to eq("")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should support excon response_block" do
|
|
30
|
+
a = []
|
|
31
|
+
stub_request(:get, "http://example.com/").to_return(body: "abc")
|
|
32
|
+
r = Excon.new('http://example.com').get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
|
|
33
|
+
expect(a).to eq(['a', 'b', 'c'])
|
|
34
|
+
expect(r.body).to eq("")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should invoke callbacks with response body even if a real request is made", net_connect: true do
|
|
38
|
+
a = []
|
|
39
|
+
WebMock.allow_net_connect!
|
|
40
|
+
response = nil
|
|
41
|
+
WebMock.after_request { |_, res|
|
|
42
|
+
response = res
|
|
43
|
+
}
|
|
44
|
+
r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
|
|
45
|
+
get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
|
|
46
|
+
expect(response.body).to eq("200 OK")
|
|
47
|
+
expect(a).to eq(["2", "0", "0", " ", "O", "K"])
|
|
48
|
+
expect(r.body).to eq("")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
let(:file) { File.new(__FILE__) }
|
|
53
|
+
let(:file_contents) { File.read(__FILE__) }
|
|
54
|
+
|
|
55
|
+
it 'handles file uploads correctly' do
|
|
56
|
+
stub_request(:put, "http://example.com/upload").with(body: file_contents)
|
|
57
|
+
|
|
58
|
+
yielded_request_body = nil
|
|
59
|
+
WebMock.after_request do |req, res|
|
|
60
|
+
yielded_request_body = req.body
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Excon.new("http://example.com").put(path: "upload", body: file)
|
|
64
|
+
|
|
65
|
+
expect(yielded_request_body).to eq(file_contents)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '.request_params_from' do
|
|
69
|
+
|
|
70
|
+
it 'rejects invalid request keys' do
|
|
71
|
+
request_params = WebMock::HttpLibAdapters::ExconAdapter.request_params_from(body: :keep, fake: :reject)
|
|
72
|
+
expect(request_params).to eq(body: :keep)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
|
|
3
|
+
module ExconSpecHelper
|
|
4
|
+
|
|
5
|
+
def http_request(method, uri, options = {}, &block)
|
|
6
|
+
Excon.defaults[:ssl_verify_peer] = false
|
|
7
|
+
Excon.defaults[:ciphers] = 'DEFAULT'
|
|
8
|
+
uri = Addressable::URI.heuristic_parse(uri)
|
|
9
|
+
uri = uri.to_s.gsub(' ', '%20')
|
|
10
|
+
|
|
11
|
+
excon_options = {}
|
|
12
|
+
|
|
13
|
+
if basic_auth = options.delete(:basic_auth)
|
|
14
|
+
excon_options = {user: basic_auth[0], password: basic_auth[1]}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if Gem::Version.new(Excon::VERSION) < Gem::Version.new("0.29.0")
|
|
18
|
+
options = options.merge(method: method, nonblock: false) # Dup and merge
|
|
19
|
+
response = Excon.new(uri, excon_options).request(options, &block)
|
|
20
|
+
else
|
|
21
|
+
options = options.merge(method: method) # Dup and merge
|
|
22
|
+
response = Excon.new(uri, excon_options.merge(nonblock: false)).request(options, &block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
headers = WebMock::Util::Headers.normalize_headers(response.headers)
|
|
26
|
+
headers = headers.inject({}) do |res, (name, value)|
|
|
27
|
+
res[name] = value.is_a?(Array) ? value.flatten.join(', ') : value
|
|
28
|
+
res
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
OpenStruct.new \
|
|
32
|
+
body: response.body,
|
|
33
|
+
headers: headers,
|
|
34
|
+
status: response.status.to_s,
|
|
35
|
+
message: response.reason_phrase
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def client_timeout_exception_class
|
|
39
|
+
Excon::Errors::Timeout
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def connection_refused_exception_class
|
|
43
|
+
Excon::Errors::SocketError
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def http_library
|
|
47
|
+
:excon
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "acceptance/webmock_shared"
|
|
5
|
+
require "acceptance/http_rb/http_rb_spec_helper"
|
|
6
|
+
|
|
7
|
+
describe "HTTP.rb" do
|
|
8
|
+
include HttpRbSpecHelper
|
|
9
|
+
|
|
10
|
+
include_examples "with WebMock", :no_status_message
|
|
11
|
+
|
|
12
|
+
context "streaming body" do
|
|
13
|
+
let(:response) { HTTP.get "http://example.com" }
|
|
14
|
+
before { stub_simple_request "example.com", 302, {}, "abc" }
|
|
15
|
+
|
|
16
|
+
it "works as if it was streamed from socket" do
|
|
17
|
+
expect(response.body.readpartial 1).to eq "a"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "fails if body was already streamed" do
|
|
21
|
+
response.body.to_s
|
|
22
|
+
expect { response.body.readpartial 1 }.to raise_error(HTTP::StateError)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context "without following redirects" do
|
|
27
|
+
let(:response) { http_request(:get, "http://example.com") }
|
|
28
|
+
let(:headers) { response.headers }
|
|
29
|
+
|
|
30
|
+
it "stops on first request" do
|
|
31
|
+
stub_simple_request("example.com", 302, "Location" => "http://www.example.com")
|
|
32
|
+
stub_simple_request("www.example.com")
|
|
33
|
+
|
|
34
|
+
expect(headers).to include "Host" => "example.com"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context "following redirects" do
|
|
39
|
+
let(:options) { { follow: true } }
|
|
40
|
+
let(:response) { http_request(:get, "http://example.com", options) }
|
|
41
|
+
let(:headers) { response.headers }
|
|
42
|
+
|
|
43
|
+
it "returns response of destination" do
|
|
44
|
+
stub_simple_request("example.com", 302, "Location" => "http://www.example.com")
|
|
45
|
+
stub_simple_request("www.example.com")
|
|
46
|
+
|
|
47
|
+
expect(headers).to include "Host" => "www.example.com"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "restored request uri on replayed response object" do
|
|
52
|
+
it "keeps non-default port" do
|
|
53
|
+
stub_request :get, "example.com:1234/foo"
|
|
54
|
+
response = HTTP.get "http://example.com:1234/foo"
|
|
55
|
+
|
|
56
|
+
expect(response.uri.to_s).to eq "http://example.com:1234/foo"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "does not injects default port" do
|
|
60
|
+
stub_request :get, "example.com/foo"
|
|
61
|
+
response = HTTP.get "http://example.com/foo"
|
|
62
|
+
|
|
63
|
+
expect(response.uri.to_s).to eq "http://example.com/foo"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "strips out default port even if it was explicitly given" do
|
|
67
|
+
stub_request :get, "example.com/foo"
|
|
68
|
+
response = HTTP.get "http://example.com:80/foo"
|
|
69
|
+
|
|
70
|
+
expect(response.uri.to_s).to eq "http://example.com/foo"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context "streamer" do
|
|
75
|
+
it "can be closed" do
|
|
76
|
+
stub_request :get, "example.com/foo"
|
|
77
|
+
response = HTTP.get "http://example.com/foo"
|
|
78
|
+
|
|
79
|
+
response.connection.close
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "ostruct"
|
|
2
|
+
|
|
3
|
+
module HttpRbSpecHelper
|
|
4
|
+
def http_request(method, uri, options = {})
|
|
5
|
+
chain = HTTP
|
|
6
|
+
|
|
7
|
+
if basic_auth = options.delete(:basic_auth)
|
|
8
|
+
chain = chain.basic_auth(user: basic_auth[0], pass: basic_auth[1])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
ssl_ctx = OpenSSL::SSL::SSLContext.new
|
|
12
|
+
ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
13
|
+
|
|
14
|
+
response = chain.request(method, normalize_uri(uri), options.merge(ssl_context: ssl_ctx))
|
|
15
|
+
|
|
16
|
+
OpenStruct.new({
|
|
17
|
+
body: response.body.to_s,
|
|
18
|
+
headers: normalize_headers(response.headers.to_h),
|
|
19
|
+
status: response.code.to_s,
|
|
20
|
+
message: response.reason
|
|
21
|
+
})
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def client_timeout_exception_class
|
|
25
|
+
return Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
|
|
26
|
+
HTTP::TimeoutError
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def connection_refused_exception_class
|
|
30
|
+
return Errno::ECONNREFUSED if HTTP::VERSION < "1.0.0"
|
|
31
|
+
HTTP::ConnectionError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def http_library
|
|
35
|
+
:http_rb
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def normalize_uri(uri)
|
|
39
|
+
Addressable::URI.heuristic_parse(uri).normalize.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def normalize_headers(headers)
|
|
43
|
+
headers = Hash[headers.map { |k, v| [k, Array(v).join(", ")] }]
|
|
44
|
+
WebMock::Util::Headers.normalize_headers headers
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def stub_simple_request(host, status = 200, headers = {}, body = nil)
|
|
48
|
+
stub_request(:any, host).to_return({
|
|
49
|
+
status: status,
|
|
50
|
+
headers: headers.merge({ "Host" => host }),
|
|
51
|
+
body: body
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'acceptance/webmock_shared'
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
|
|
5
|
+
require 'acceptance/httpclient/httpclient_spec_helper'
|
|
6
|
+
|
|
7
|
+
describe "HTTPClient" do
|
|
8
|
+
include HTTPClientSpecHelper
|
|
9
|
+
|
|
10
|
+
before(:each) do
|
|
11
|
+
WebMock.reset_callbacks
|
|
12
|
+
HTTPClientSpecHelper.async_mode = false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
include_examples "with WebMock"
|
|
16
|
+
|
|
17
|
+
it "should raise a clearly readable error if request with multipart body is sent" do
|
|
18
|
+
stub_request(:post, 'www.example.com').with(body: {type: 'image'})
|
|
19
|
+
|
|
20
|
+
expect {
|
|
21
|
+
HTTPClient.new.post_content('www.example.com', type: 'image', file: File.new('spec/fixtures/test.txt'))
|
|
22
|
+
}.to raise_error(ArgumentError, "WebMock does not support matching body for multipart/form-data requests yet :(")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should yield block on response if block provided" do
|
|
26
|
+
stub_request(:get, "www.example.com").to_return(body: "abc")
|
|
27
|
+
response_body = ""
|
|
28
|
+
http_request(:get, "http://www.example.com/") do |body|
|
|
29
|
+
response_body = body
|
|
30
|
+
end
|
|
31
|
+
expect(response_body).to eq("abc")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should not yield block on empty response if block provided" do
|
|
35
|
+
stub_request(:get, "www.example.com").to_return(body: "")
|
|
36
|
+
response_body = ""
|
|
37
|
+
http_request(:get, "http://www.example.com/"){ raise }
|
|
38
|
+
expect(response_body).to eq("")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should match requests if headers are the same but in different order" do
|
|
42
|
+
stub_request(:get, "www.example.com").with(headers: {"a" => ["b", "c"]} )
|
|
43
|
+
expect(http_request(
|
|
44
|
+
:get, "http://www.example.com/",
|
|
45
|
+
headers: {"a" => ["c", "b"]}).status).to eq("200")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "when using async requests" do
|
|
49
|
+
before(:each) do
|
|
50
|
+
HTTPClientSpecHelper.async_mode = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
include_examples "with WebMock"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should work with get_content" do
|
|
57
|
+
stub_request(:get, 'www.example.com').to_return(status: 200, body: 'test', headers: {})
|
|
58
|
+
str = ''.dup
|
|
59
|
+
HTTPClient.get_content('www.example.com') do |content|
|
|
60
|
+
str << content
|
|
61
|
+
end
|
|
62
|
+
expect(str).to eq('test')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should work via JSONClient subclass" do
|
|
66
|
+
stub_request(:get, 'www.example.com').to_return(
|
|
67
|
+
status: 200,
|
|
68
|
+
body: '{"test": "foo"}',
|
|
69
|
+
headers: {'Content-Type' => 'application/json'}
|
|
70
|
+
)
|
|
71
|
+
content = JSONClient.get('www.example.com').content
|
|
72
|
+
expect(content).to eq("test" => "foo")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context "multipart bodies" do
|
|
76
|
+
let(:header) {{
|
|
77
|
+
'Accept' => 'application/json',
|
|
78
|
+
'Content-Type' => 'multipart/form-data'
|
|
79
|
+
}}
|
|
80
|
+
|
|
81
|
+
let(:body) {[
|
|
82
|
+
{
|
|
83
|
+
'Content-Type' => 'application/json',
|
|
84
|
+
'Content-Disposition' => 'form-data',
|
|
85
|
+
:content => '{"foo": "bar", "baz": 2}'
|
|
86
|
+
}
|
|
87
|
+
]}
|
|
88
|
+
|
|
89
|
+
let(:make_request) {HTTPClient.new.post("http://www.example.com", body: body, header: header)}
|
|
90
|
+
|
|
91
|
+
before do
|
|
92
|
+
stub_request(:post, "www.example.com")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should work with multipart bodies" do
|
|
96
|
+
make_request
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
context "Filters" do
|
|
102
|
+
class Filter
|
|
103
|
+
def filter_request(request)
|
|
104
|
+
request.header["Authorization"] = "Bearer 0123456789"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def filter_response(request, response)
|
|
108
|
+
response.header.set('X-Powered-By', 'webmock')
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
before do
|
|
113
|
+
@client = HTTPClient.new
|
|
114
|
+
@client.request_filter << Filter.new
|
|
115
|
+
stub_request(:get, 'www.example.com').with(headers: {'Authorization' => 'Bearer 0123456789'})
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "supports request filters" do
|
|
119
|
+
expect(@client.request(:get, 'http://www.example.com/').status).to eq(200)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "supports response filters" do
|
|
123
|
+
res = @client.request(:get, 'http://www.example.com/')
|
|
124
|
+
expect(res.header['X-Powered-By'].first).to eq('webmock')
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'when a client instance is re-used for another identical request' do
|
|
129
|
+
let(:client) { HTTPClient.new }
|
|
130
|
+
let(:webmock_server_url) {"http://#{WebMockServer.instance.host_with_port}/"}
|
|
131
|
+
|
|
132
|
+
before { WebMock.allow_net_connect! }
|
|
133
|
+
|
|
134
|
+
it 'invokes the global_stub_request hook for each request' do
|
|
135
|
+
# Mock time to ensure that date headers match
|
|
136
|
+
now = Time.now
|
|
137
|
+
allow(Time).to receive(:now).and_return(now)
|
|
138
|
+
|
|
139
|
+
request_signatures = []
|
|
140
|
+
WebMock.globally_stub_request do |request_sig|
|
|
141
|
+
request_signatures << request_sig
|
|
142
|
+
nil # to let the request be made for real
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
http_request(:get, webmock_server_url, client: client, headers: { "Cookie" => "bar=; foo=" })
|
|
146
|
+
|
|
147
|
+
if defined? HTTP::CookieJar
|
|
148
|
+
http_request(:get, webmock_server_url, client: client, headers: { "Cookie" => "bar=; foo=" })
|
|
149
|
+
else
|
|
150
|
+
# If http-cookie is not present, then the cookie headers will saved between requests
|
|
151
|
+
http_request(:get, webmock_server_url, client: client)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
expect(request_signatures.size).to eq(2)
|
|
155
|
+
# Verify the request signatures were identical as needed by this example
|
|
156
|
+
expect(request_signatures.first).to eq(request_signatures.last)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context 'session headers' do
|
|
161
|
+
it "client sends a User-Agent header when given an agent_name explicitly to the client" do
|
|
162
|
+
user_agent = "Client/0.1"
|
|
163
|
+
stub_request(:get, "www.example.com").with(headers: { 'User-agent' => "#{user_agent} #{HTTPClient::LIB_NAME}" })
|
|
164
|
+
HTTPClient.new(agent_name: user_agent).get("www.example.com")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "client sends the Accept, User-Agent, and Date by default" do
|
|
168
|
+
WebMock.disable_net_connect!
|
|
169
|
+
stub_request(:get, "www.example.com").with do |req|
|
|
170
|
+
req.headers["Accept"] == "*/*" &&
|
|
171
|
+
req.headers["User-Agent"] == "#{HTTPClient::DEFAULT_AGENT_NAME} #{HTTPClient::LIB_NAME}" &&
|
|
172
|
+
req.headers["Date"]
|
|
173
|
+
end
|
|
174
|
+
http_request(:get, "www.example.com")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "explicitly defined headers take precedence over session defaults" do
|
|
178
|
+
headers = { 'Accept' => 'foo/bar', 'User-Agent' => 'custom', 'Date' => 'today' }
|
|
179
|
+
stub_request(:get, "www.example.com").with(headers: headers)
|
|
180
|
+
HTTPClient.new.get("www.example.com", nil, headers)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context 'httpclient response header' do
|
|
185
|
+
it 'receives request_method, request_uri, and request_query from the request header' do
|
|
186
|
+
stub_request :get, 'www.example.com'
|
|
187
|
+
message = HTTPClient.new.get 'www.example.com'
|
|
188
|
+
expect(message.header.request_uri.to_s).to eq('www.example.com')
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context 'httpclient streams response', net_connect: true do
|
|
193
|
+
before do
|
|
194
|
+
WebMock.allow_net_connect!
|
|
195
|
+
WebMock.after_request(except: [:other_lib]) do |_, response|
|
|
196
|
+
@response = response
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'sets the full body on the webmock response' do
|
|
201
|
+
body = ''
|
|
202
|
+
HTTPClient.new.request(:get, 'http://www.example.com/') do |http_res, chunk|
|
|
203
|
+
body += chunk
|
|
204
|
+
end
|
|
205
|
+
expect(@response.body).to eq body
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context 'credentials' do
|
|
210
|
+
it 'are detected when manually specifying Authorization header' do
|
|
211
|
+
stub_request(:get, "http://www.example.com/").with(basic_auth: ['username', 'password']).to_return(status: 200)
|
|
212
|
+
|
|
213
|
+
headers = {'Authorization' => 'Basic dXNlcm5hbWU6cGFzc3dvcmQ='}
|
|
214
|
+
expect(http_request(:get, 'http://www.example.com/', {headers: headers}).status).to eql('200')
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|