mustwin-vcr 2.9.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 +7 -0
- data/features/about_these_examples.md +18 -0
- data/features/cassettes/allow_unused_http_interactions.feature +100 -0
- data/features/cassettes/automatic_re_recording.feature +72 -0
- data/features/cassettes/decompress.feature +74 -0
- data/features/cassettes/dynamic_erb.feature +100 -0
- data/features/cassettes/exclusive.feature +126 -0
- data/features/cassettes/format.feature +323 -0
- data/features/cassettes/freezing_time.feature +68 -0
- data/features/cassettes/naming.feature +28 -0
- data/features/cassettes/no_cassette.feature +152 -0
- data/features/cassettes/update_content_length_header.feature +112 -0
- data/features/configuration/allow_http_connections_when_no_cassette.feature +55 -0
- data/features/configuration/cassette_library_dir.feature +31 -0
- data/features/configuration/debug_logging.feature +59 -0
- data/features/configuration/default_cassette_options.feature +100 -0
- data/features/configuration/filter_sensitive_data.feature +153 -0
- data/features/configuration/hook_into.feature +172 -0
- data/features/configuration/ignore_request.feature +192 -0
- data/features/configuration/preserve_exact_body_bytes.feature +108 -0
- data/features/configuration/query_parser.feature +84 -0
- data/features/configuration/uri_parser.feature +89 -0
- data/features/getting_started.md +82 -0
- data/features/hooks/after_http_request.feature +58 -0
- data/features/hooks/around_http_request.feature +57 -0
- data/features/hooks/before_http_request.feature +63 -0
- data/features/hooks/before_playback.feature +184 -0
- data/features/hooks/before_record.feature +172 -0
- data/features/http_libraries/em_http_request.feature +250 -0
- data/features/http_libraries/net_http.feature +179 -0
- data/features/middleware/faraday.feature +56 -0
- data/features/middleware/rack.feature +92 -0
- data/features/record_modes/all.feature +82 -0
- data/features/record_modes/new_episodes.feature +79 -0
- data/features/record_modes/none.feature +72 -0
- data/features/record_modes/once.feature +95 -0
- data/features/request_matching/README.md +30 -0
- data/features/request_matching/body.feature +91 -0
- data/features/request_matching/body_as_json.feature +90 -0
- data/features/request_matching/custom_matcher.feature +135 -0
- data/features/request_matching/headers.feature +85 -0
- data/features/request_matching/host.feature +95 -0
- data/features/request_matching/identical_request_sequence.feature +89 -0
- data/features/request_matching/method.feature +96 -0
- data/features/request_matching/path.feature +96 -0
- data/features/request_matching/playback_repeats.feature +98 -0
- data/features/request_matching/query.feature +97 -0
- data/features/request_matching/uri.feature +94 -0
- data/features/request_matching/uri_without_param.feature +101 -0
- data/features/step_definitions/cli_steps.rb +193 -0
- data/features/support/env.rb +44 -0
- data/features/support/http_lib_filters.rb +53 -0
- data/features/test_frameworks/cucumber.feature +211 -0
- data/features/test_frameworks/rspec_macro.feature +81 -0
- data/features/test_frameworks/rspec_metadata.feature +150 -0
- data/features/test_frameworks/test_unit.feature +49 -0
- data/lib/vcr.rb +347 -0
- data/lib/vcr/cassette.rb +291 -0
- data/lib/vcr/cassette/erb_renderer.rb +55 -0
- data/lib/vcr/cassette/http_interaction_list.rb +108 -0
- data/lib/vcr/cassette/migrator.rb +118 -0
- data/lib/vcr/cassette/persisters.rb +42 -0
- data/lib/vcr/cassette/persisters/file_system.rb +64 -0
- data/lib/vcr/cassette/serializers.rb +57 -0
- data/lib/vcr/cassette/serializers/json.rb +48 -0
- data/lib/vcr/cassette/serializers/psych.rb +48 -0
- data/lib/vcr/cassette/serializers/syck.rb +61 -0
- data/lib/vcr/cassette/serializers/yaml.rb +50 -0
- data/lib/vcr/configuration.rb +555 -0
- data/lib/vcr/deprecations.rb +109 -0
- data/lib/vcr/errors.rb +266 -0
- data/lib/vcr/extensions/net_http_response.rb +36 -0
- data/lib/vcr/library_hooks.rb +18 -0
- data/lib/vcr/library_hooks/excon.rb +27 -0
- data/lib/vcr/library_hooks/fakeweb.rb +196 -0
- data/lib/vcr/library_hooks/faraday.rb +51 -0
- data/lib/vcr/library_hooks/typhoeus.rb +120 -0
- data/lib/vcr/library_hooks/typhoeus_0.4.rb +103 -0
- data/lib/vcr/library_hooks/webmock.rb +164 -0
- data/lib/vcr/middleware/excon.rb +221 -0
- data/lib/vcr/middleware/excon/legacy_methods.rb +33 -0
- data/lib/vcr/middleware/faraday.rb +118 -0
- data/lib/vcr/middleware/rack.rb +79 -0
- data/lib/vcr/request_handler.rb +114 -0
- data/lib/vcr/request_ignorer.rb +43 -0
- data/lib/vcr/request_matcher_registry.rb +149 -0
- data/lib/vcr/structs.rb +578 -0
- data/lib/vcr/tasks/vcr.rake +9 -0
- data/lib/vcr/test_frameworks/cucumber.rb +64 -0
- data/lib/vcr/test_frameworks/rspec.rb +47 -0
- data/lib/vcr/util/hooks.rb +61 -0
- data/lib/vcr/util/internet_connection.rb +43 -0
- data/lib/vcr/util/logger.rb +59 -0
- data/lib/vcr/util/variable_args_block_caller.rb +13 -0
- data/lib/vcr/util/version_checker.rb +48 -0
- data/lib/vcr/version.rb +34 -0
- data/spec/acceptance/threading_spec.rb +34 -0
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
- data/spec/fixtures/cassette_spec/empty.yml +0 -0
- data/spec/fixtures/cassette_spec/example.yml +111 -0
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +111 -0
- data/spec/fixtures/fake_example_responses.yml +110 -0
- data/spec/fixtures/match_requests_on.yml +187 -0
- data/spec/lib/vcr/cassette/erb_renderer_spec.rb +53 -0
- data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +295 -0
- data/spec/lib/vcr/cassette/migrator_spec.rb +195 -0
- data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +69 -0
- data/spec/lib/vcr/cassette/persisters_spec.rb +39 -0
- data/spec/lib/vcr/cassette/serializers_spec.rb +176 -0
- data/spec/lib/vcr/cassette_spec.rb +618 -0
- data/spec/lib/vcr/configuration_spec.rb +326 -0
- data/spec/lib/vcr/deprecations_spec.rb +85 -0
- data/spec/lib/vcr/errors_spec.rb +162 -0
- data/spec/lib/vcr/extensions/net_http_response_spec.rb +86 -0
- data/spec/lib/vcr/library_hooks/excon_spec.rb +104 -0
- data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +169 -0
- data/spec/lib/vcr/library_hooks/faraday_spec.rb +68 -0
- data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +36 -0
- data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +162 -0
- data/spec/lib/vcr/library_hooks/webmock_spec.rb +118 -0
- data/spec/lib/vcr/library_hooks_spec.rb +51 -0
- data/spec/lib/vcr/middleware/faraday_spec.rb +182 -0
- data/spec/lib/vcr/middleware/rack_spec.rb +115 -0
- data/spec/lib/vcr/request_ignorer_spec.rb +70 -0
- data/spec/lib/vcr/request_matcher_registry_spec.rb +345 -0
- data/spec/lib/vcr/structs_spec.rb +732 -0
- data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +107 -0
- data/spec/lib/vcr/test_frameworks/rspec_spec.rb +83 -0
- data/spec/lib/vcr/util/hooks_spec.rb +158 -0
- data/spec/lib/vcr/util/internet_connection_spec.rb +37 -0
- data/spec/lib/vcr/util/version_checker_spec.rb +31 -0
- data/spec/lib/vcr/version_spec.rb +27 -0
- data/spec/lib/vcr_spec.rb +349 -0
- data/spec/monkey_patches.rb +182 -0
- data/spec/spec_helper.rb +62 -0
- data/spec/support/configuration_stubbing.rb +8 -0
- data/spec/support/cucumber_helpers.rb +35 -0
- data/spec/support/fixnum_extension.rb +10 -0
- data/spec/support/http_library_adapters.rb +289 -0
- data/spec/support/limited_uri.rb +21 -0
- data/spec/support/ruby_interpreter.rb +7 -0
- data/spec/support/shared_example_groups/excon.rb +63 -0
- data/spec/support/shared_example_groups/hook_into_http_library.rb +594 -0
- data/spec/support/shared_example_groups/request_hooks.rb +59 -0
- data/spec/support/sinatra_app.rb +86 -0
- data/spec/support/vcr_localhost_server.rb +76 -0
- data/spec/support/vcr_stub_helpers.rb +17 -0
- metadata +677 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
require 'vcr/util/version_checker'
|
|
2
|
+
require 'vcr/request_handler'
|
|
3
|
+
require 'webmock'
|
|
4
|
+
|
|
5
|
+
VCR::VersionChecker.new('WebMock', WebMock.version, '1.8.0').check_version!
|
|
6
|
+
|
|
7
|
+
module VCR
|
|
8
|
+
class LibraryHooks
|
|
9
|
+
# @private
|
|
10
|
+
module WebMock
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
attr_accessor :global_hook_disabled
|
|
14
|
+
alias global_hook_disabled? global_hook_disabled
|
|
15
|
+
|
|
16
|
+
def with_global_hook_disabled
|
|
17
|
+
self.global_hook_disabled = true
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
yield
|
|
21
|
+
ensure
|
|
22
|
+
self.global_hook_disabled = false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @private
|
|
27
|
+
module Helpers
|
|
28
|
+
def vcr_request_for(webmock_request)
|
|
29
|
+
VCR::Request.new \
|
|
30
|
+
webmock_request.method,
|
|
31
|
+
webmock_request.uri.to_s,
|
|
32
|
+
webmock_request.body,
|
|
33
|
+
request_headers_for(webmock_request)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @private
|
|
37
|
+
def vcr_response_for(webmock_response)
|
|
38
|
+
VCR::Response.new \
|
|
39
|
+
VCR::ResponseStatus.new(*webmock_response.status),
|
|
40
|
+
webmock_response.headers,
|
|
41
|
+
webmock_response.body,
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if defined?(::Excon)
|
|
46
|
+
# @private
|
|
47
|
+
def request_headers_for(webmock_request)
|
|
48
|
+
return nil unless webmock_request.headers
|
|
49
|
+
|
|
50
|
+
# WebMock hooks deeply into a Excon at a place where it manually adds a "Host"
|
|
51
|
+
# header, but this isn't a header we actually care to store...
|
|
52
|
+
webmock_request.headers.dup.tap do |headers|
|
|
53
|
+
headers.delete("Host")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
# @private
|
|
58
|
+
def request_headers_for(webmock_request)
|
|
59
|
+
webmock_request.headers
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def typed_request_for(webmock_request, remove = false)
|
|
64
|
+
if webmock_request.instance_variables.find { |v| v.to_sym == :@__typed_vcr_request }
|
|
65
|
+
meth = remove ? :remove_instance_variable : :instance_variable_get
|
|
66
|
+
return webmock_request.send(meth, :@__typed_vcr_request)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
warn <<-EOS.gsub(/^\s+\|/, '')
|
|
70
|
+
|WARNING: There appears to be a bug in WebMock's after_request hook
|
|
71
|
+
| and VCR is attempting to work around it. Some VCR features
|
|
72
|
+
| may not work properly.
|
|
73
|
+
EOS
|
|
74
|
+
|
|
75
|
+
Request::Typed.new(vcr_request_for(webmock_request), :unknown)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class RequestHandler < ::VCR::RequestHandler
|
|
80
|
+
include Helpers
|
|
81
|
+
|
|
82
|
+
attr_reader :request
|
|
83
|
+
def initialize(request)
|
|
84
|
+
@request = request
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def externally_stubbed?
|
|
90
|
+
# prevent infinite recursion...
|
|
91
|
+
VCR::LibraryHooks::WebMock.with_global_hook_disabled do
|
|
92
|
+
::WebMock.registered_request?(request)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def set_typed_request_for_after_hook(*args)
|
|
97
|
+
super
|
|
98
|
+
request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def vcr_request
|
|
102
|
+
@vcr_request ||= vcr_request_for(request)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def on_externally_stubbed_request
|
|
106
|
+
# nil allows WebMock to handle the request
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def on_unhandled_request
|
|
111
|
+
invoke_after_request_hook(nil)
|
|
112
|
+
super
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def on_stubbed_by_vcr_request
|
|
116
|
+
{
|
|
117
|
+
:body => stubbed_response.body.dup, # Excon mutates the body, so we must dup it :-(
|
|
118
|
+
:status => [stubbed_response.status.code.to_i, stubbed_response.status.message],
|
|
119
|
+
:headers => stubbed_response.headers
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
extend Helpers
|
|
125
|
+
|
|
126
|
+
::WebMock.globally_stub_request do |req|
|
|
127
|
+
global_hook_disabled? ? nil : RequestHandler.new(req).handle
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
::WebMock.after_request(:real_requests_only => true) do |request, response|
|
|
131
|
+
unless VCR.library_hooks.disabled?(:webmock)
|
|
132
|
+
http_interaction = VCR::HTTPInteraction.new \
|
|
133
|
+
typed_request_for(request), vcr_response_for(response)
|
|
134
|
+
|
|
135
|
+
VCR.record_http_interaction(http_interaction)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
::WebMock.after_request do |request, response|
|
|
140
|
+
unless VCR.library_hooks.disabled?(:webmock)
|
|
141
|
+
VCR.configuration.invoke_hook \
|
|
142
|
+
:after_http_request,
|
|
143
|
+
typed_request_for(request, :remove),
|
|
144
|
+
vcr_response_for(response)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @private
|
|
152
|
+
module WebMock
|
|
153
|
+
class << self
|
|
154
|
+
# ensure HTTP requests are always allowed; VCR takes care of disallowing
|
|
155
|
+
# them at the appropriate times in its hook
|
|
156
|
+
def net_connect_allowed_with_vcr?(*args)
|
|
157
|
+
VCR.turned_on? ? true : net_connect_allowed_without_vcr?(*args)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
alias net_connect_allowed_without_vcr? net_connect_allowed?
|
|
161
|
+
alias net_connect_allowed? net_connect_allowed_with_vcr?
|
|
162
|
+
end unless respond_to?(:net_connect_allowed_with_vcr?)
|
|
163
|
+
end
|
|
164
|
+
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
require 'excon'
|
|
2
|
+
require 'vcr/request_handler'
|
|
3
|
+
require 'vcr/util/version_checker'
|
|
4
|
+
|
|
5
|
+
VCR::VersionChecker.new('Excon', Excon::VERSION, '0.25.2').check_version!
|
|
6
|
+
|
|
7
|
+
module VCR
|
|
8
|
+
# Contains middlewares for use with different libraries.
|
|
9
|
+
module Middleware
|
|
10
|
+
# Contains Excon middlewares.
|
|
11
|
+
module Excon
|
|
12
|
+
# One part of the Excon middleware that uses VCR to record
|
|
13
|
+
# and replay HTTP requests made through Excon.
|
|
14
|
+
#
|
|
15
|
+
# @private
|
|
16
|
+
class Request < ::Excon::Middleware::Base
|
|
17
|
+
# @private
|
|
18
|
+
def request_call(params)
|
|
19
|
+
params[:vcr_request_handler] = request_handler = RequestHandler.new
|
|
20
|
+
request_handler.before_request(params)
|
|
21
|
+
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# One part of the Excon middleware that uses VCR to record
|
|
27
|
+
# and replay HTTP requests made through Excon.
|
|
28
|
+
#
|
|
29
|
+
# @private
|
|
30
|
+
class Response < ::Excon::Middleware::Base
|
|
31
|
+
# @private
|
|
32
|
+
def response_call(params)
|
|
33
|
+
complete_request(params)
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def error_call(params)
|
|
38
|
+
complete_request(params)
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def complete_request(params)
|
|
45
|
+
if handler = params.delete(:vcr_request_handler)
|
|
46
|
+
handler.after_request(params[:response])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Handles a single Excon request.
|
|
52
|
+
#
|
|
53
|
+
# @private
|
|
54
|
+
class RequestHandler < ::VCR::RequestHandler
|
|
55
|
+
def initialize
|
|
56
|
+
@request_params = nil
|
|
57
|
+
@response_body_reader = nil
|
|
58
|
+
@should_record = false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Performs before_request processing based on the provided
|
|
62
|
+
# request_params.
|
|
63
|
+
#
|
|
64
|
+
# @private
|
|
65
|
+
def before_request(request_params)
|
|
66
|
+
@request_params = request_params
|
|
67
|
+
@response_body_reader = create_response_body_reader
|
|
68
|
+
handle
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Performs after_request processing based on the provided response.
|
|
72
|
+
#
|
|
73
|
+
# @private
|
|
74
|
+
def after_request(response)
|
|
75
|
+
vcr_response = vcr_response_for(response)
|
|
76
|
+
|
|
77
|
+
if should_record?
|
|
78
|
+
VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, vcr_response))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
invoke_after_request_hook(vcr_response)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ensure_response_body_can_be_read_for_error_case
|
|
85
|
+
# Excon does not invoke the `:response_block` when an error
|
|
86
|
+
# has occurred, so we need to be sure to use the non-streaming
|
|
87
|
+
# body reader.
|
|
88
|
+
@response_body_reader = NonStreamingResponseBodyReader
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
attr_reader :request_params, :response_body_reader
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def externally_stubbed?
|
|
96
|
+
!!::Excon.stub_for(request_params)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def should_record?
|
|
100
|
+
@should_record
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def on_stubbed_by_vcr_request
|
|
104
|
+
request_params[:response] = {
|
|
105
|
+
:body => stubbed_response.body.dup, # Excon mutates the body, so we must dup it :-(
|
|
106
|
+
:headers => normalized_headers(stubbed_response.headers || {}),
|
|
107
|
+
:status => stubbed_response.status.code
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def on_recordable_request
|
|
112
|
+
@should_record = true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def create_response_body_reader
|
|
116
|
+
block = request_params[:response_block]
|
|
117
|
+
return NonStreamingResponseBodyReader unless block
|
|
118
|
+
|
|
119
|
+
StreamingResponseBodyReader.new(block).tap do |response_block_wrapper|
|
|
120
|
+
request_params[:response_block] = response_block_wrapper
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def vcr_request
|
|
125
|
+
@vcr_request ||= begin
|
|
126
|
+
headers = request_params[:headers].dup
|
|
127
|
+
headers.delete("Host")
|
|
128
|
+
|
|
129
|
+
VCR::Request.new \
|
|
130
|
+
request_params[:method],
|
|
131
|
+
uri,
|
|
132
|
+
request_params[:body],
|
|
133
|
+
headers
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def vcr_response_for(response)
|
|
138
|
+
return nil if response.nil?
|
|
139
|
+
|
|
140
|
+
VCR::Response.new(
|
|
141
|
+
VCR::ResponseStatus.new(response.fetch(:status), nil),
|
|
142
|
+
response.fetch(:headers),
|
|
143
|
+
response_body_reader.read_body_from(response),
|
|
144
|
+
nil
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def normalized_headers(headers)
|
|
149
|
+
normalized = {}
|
|
150
|
+
headers.each do |k, v|
|
|
151
|
+
v = v.join(', ') if v.respond_to?(:join)
|
|
152
|
+
normalized[k] = v
|
|
153
|
+
end
|
|
154
|
+
normalized
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
if defined?(::Excon::Utils) && ::Excon::Utils.respond_to?(:request_uri)
|
|
158
|
+
def uri
|
|
159
|
+
@uri ||= "#{::Excon::Utils.request_uri(request_params)}"
|
|
160
|
+
end
|
|
161
|
+
else
|
|
162
|
+
require 'vcr/middleware/excon/legacy_methods'
|
|
163
|
+
include LegacyMethods
|
|
164
|
+
|
|
165
|
+
def uri
|
|
166
|
+
@uri ||= "#{request_params[:scheme]}://#{request_params[:host]}:#{request_params[:port]}#{request_params[:path]}#{query}"
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Wraps an Excon streaming `:response_block`, so that we can
|
|
172
|
+
# accumulate the response as it streams back from the real HTTP
|
|
173
|
+
# server in order to record it.
|
|
174
|
+
#
|
|
175
|
+
# @private
|
|
176
|
+
class StreamingResponseBodyReader
|
|
177
|
+
def initialize(response_block)
|
|
178
|
+
@response_block = response_block
|
|
179
|
+
@chunks = []
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# @private
|
|
183
|
+
def call(chunk, remaining_bytes, total_bytes)
|
|
184
|
+
@chunks << chunk
|
|
185
|
+
@response_block.call(chunk, remaining_bytes, total_bytes)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Provides a duck-typed interface that matches that of
|
|
189
|
+
# `NonStreamingResponseBodyReader`. The request handler
|
|
190
|
+
# will use this to get the response body.
|
|
191
|
+
#
|
|
192
|
+
# @private
|
|
193
|
+
def read_body_from(response_params)
|
|
194
|
+
if @chunks.none?
|
|
195
|
+
# Not sure why, but sometimes the body comes through the params
|
|
196
|
+
# instead of via the streaming block even when the block was
|
|
197
|
+
# configured.
|
|
198
|
+
response_params[:body]
|
|
199
|
+
else
|
|
200
|
+
@chunks.join('')
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Reads the body when no streaming is done.
|
|
206
|
+
#
|
|
207
|
+
# @private
|
|
208
|
+
class NonStreamingResponseBodyReader
|
|
209
|
+
# Provides a duck-typed interface that matches that of
|
|
210
|
+
# `StreamingResponseBodyReader`. The request handler
|
|
211
|
+
# will use this to get the response body.
|
|
212
|
+
#
|
|
213
|
+
# @private
|
|
214
|
+
def self.read_body_from(response_params)
|
|
215
|
+
response_params.fetch(:body)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module VCR
|
|
2
|
+
module Middleware
|
|
3
|
+
module Excon
|
|
4
|
+
# Contains legacy methods only needed when integrating with older versions
|
|
5
|
+
# of Excon.
|
|
6
|
+
# @api private
|
|
7
|
+
module LegacyMethods
|
|
8
|
+
# based on:
|
|
9
|
+
# https://github.com/geemus/excon/blob/v0.7.8/lib/excon/connection.rb#L117-132
|
|
10
|
+
def query
|
|
11
|
+
@query ||= case request_params[:query]
|
|
12
|
+
when String
|
|
13
|
+
"?#{request_params[:query]}"
|
|
14
|
+
when Hash
|
|
15
|
+
qry = '?'
|
|
16
|
+
for key, values in request_params[:query]
|
|
17
|
+
if values.nil?
|
|
18
|
+
qry << key.to_s << '&'
|
|
19
|
+
else
|
|
20
|
+
for value in [*values]
|
|
21
|
+
qry << key.to_s << '=' << CGI.escape(value.to_s) << '&'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
qry.chop! # remove trailing '&'
|
|
26
|
+
else
|
|
27
|
+
''
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'vcr/util/version_checker'
|
|
3
|
+
require 'vcr/request_handler'
|
|
4
|
+
|
|
5
|
+
VCR::VersionChecker.new('Faraday', Faraday::VERSION, '0.7.0').check_version!
|
|
6
|
+
|
|
7
|
+
module VCR
|
|
8
|
+
# Contains middlewares for use with different libraries.
|
|
9
|
+
module Middleware
|
|
10
|
+
# Faraday middleware that VCR uses to record and replay HTTP requests made through
|
|
11
|
+
# Faraday.
|
|
12
|
+
#
|
|
13
|
+
# @note You can either insert this middleware into the Faraday middleware stack
|
|
14
|
+
# yourself or configure {VCR::Configuration#hook_into} to hook into `:faraday`.
|
|
15
|
+
class Faraday
|
|
16
|
+
include VCR::Deprecations::Middleware::Faraday
|
|
17
|
+
|
|
18
|
+
# Constructs a new instance of the Faraday middleware.
|
|
19
|
+
#
|
|
20
|
+
# @param [#call] app the faraday app
|
|
21
|
+
def initialize(app)
|
|
22
|
+
super
|
|
23
|
+
@app = app
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Handles the HTTP request being made through Faraday
|
|
27
|
+
#
|
|
28
|
+
# @param [Hash] env the Faraday request env hash
|
|
29
|
+
def call(env)
|
|
30
|
+
return @app.call(env) if VCR.library_hooks.disabled?(:faraday)
|
|
31
|
+
RequestHandler.new(@app, env).handle
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @private
|
|
35
|
+
class RequestHandler < ::VCR::RequestHandler
|
|
36
|
+
attr_reader :app, :env
|
|
37
|
+
def initialize(app, env)
|
|
38
|
+
@app, @env = app, env
|
|
39
|
+
@has_on_complete_hook = false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def handle
|
|
43
|
+
# Faraday must be exlusive here in case another library hook is being used.
|
|
44
|
+
# We don't want double recording/double playback.
|
|
45
|
+
VCR.library_hooks.exclusive_hook = :faraday
|
|
46
|
+
super
|
|
47
|
+
ensure
|
|
48
|
+
response = defined?(@vcr_response) ? @vcr_response : nil
|
|
49
|
+
invoke_after_request_hook(response) unless delay_finishing?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def delay_finishing?
|
|
55
|
+
!!env[:parallel_manager] && @has_on_complete_hook
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def vcr_request
|
|
59
|
+
@vcr_request ||= VCR::Request.new \
|
|
60
|
+
env[:method],
|
|
61
|
+
env[:url].to_s,
|
|
62
|
+
raw_body_from(env[:body]),
|
|
63
|
+
env[:request_headers]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def raw_body_from(body)
|
|
67
|
+
return body unless body.respond_to?(:read)
|
|
68
|
+
|
|
69
|
+
body.read.tap do |b|
|
|
70
|
+
body.rewind if body.respond_to?(:rewind)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def response_for(response)
|
|
75
|
+
VCR::Response.new(
|
|
76
|
+
VCR::ResponseStatus.new(response.status, nil),
|
|
77
|
+
response.headers,
|
|
78
|
+
raw_body_from(response.body),
|
|
79
|
+
nil
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def on_ignored_request
|
|
84
|
+
response = app.call(env)
|
|
85
|
+
@vcr_response = response_for(response)
|
|
86
|
+
response
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def on_stubbed_by_vcr_request
|
|
90
|
+
headers = env[:response_headers] ||= ::Faraday::Utils::Headers.new
|
|
91
|
+
headers.update stubbed_response.headers if stubbed_response.headers
|
|
92
|
+
env.update :status => stubbed_response.status.code, :body => stubbed_response.body
|
|
93
|
+
|
|
94
|
+
@vcr_response = stubbed_response
|
|
95
|
+
|
|
96
|
+
faraday_response = ::Faraday::Response.new
|
|
97
|
+
faraday_response.finish(env)
|
|
98
|
+
env[:response] = faraday_response
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def on_recordable_request
|
|
102
|
+
@has_on_complete_hook = true
|
|
103
|
+
response = app.call(env)
|
|
104
|
+
response.on_complete do
|
|
105
|
+
@vcr_response = response_for(response)
|
|
106
|
+
VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, @vcr_response))
|
|
107
|
+
invoke_after_request_hook(@vcr_response) if delay_finishing?
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def invoke_after_request_hook(response)
|
|
112
|
+
super
|
|
113
|
+
VCR.library_hooks.exclusive_hook = nil
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|