1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
require 'vcr/util/version_checker'
require 'vcr/request_handler'
require 'webmock'
VCR::VersionChecker.new('WebMock', WebMock.version, '1.8.0').check_version!
WebMock.enable!
module VCR
class LibraryHooks
# @private
module WebMock
extend self
def with_global_hook_disabled(request)
global_hook_disabled_requests << request
begin
yield
ensure
global_hook_disabled_requests.delete(request)
end
end
def global_hook_disabled?(request)
requests = Thread.current[:_vcr_webmock_disabled_requests]
requests && requests.include?(request)
end
def global_hook_disabled_requests
Thread.current[:_vcr_webmock_disabled_requests] ||= []
end
# @private
module Helpers
def vcr_request_for(webmock_request)
VCR::Request.new \
webmock_request.method,
webmock_request.uri.to_s,
webmock_request.body,
request_headers_for(webmock_request)
end
# @private
def vcr_response_for(webmock_response)
VCR::Response.new \
VCR::ResponseStatus.new(*webmock_response.status),
webmock_response.headers,
webmock_response.body,
nil
end
if defined?(::Excon)
# @private
def request_headers_for(webmock_request)
return nil unless webmock_request.headers
# WebMock hooks deeply into a Excon at a place where it manually adds a "Host"
# header, but this isn't a header we actually care to store...
webmock_request.headers.dup.tap do |headers|
headers.delete("Host")
end
end
else
# @private
def request_headers_for(webmock_request)
webmock_request.headers
end
end
def typed_request_for(webmock_request, remove = false)
if webmock_request.instance_variables.find { |v| v.to_sym == :@__typed_vcr_request }
meth = remove ? :remove_instance_variable : :instance_variable_get
return webmock_request.send(meth, :@__typed_vcr_request)
end
warn <<-EOS.gsub(/^\s+\|/, '')
|WARNING: There appears to be a bug in WebMock's after_request hook
| and VCR is attempting to work around it. Some VCR features
| may not work properly.
EOS
Request::Typed.new(vcr_request_for(webmock_request), :unknown)
end
end
class RequestHandler < ::VCR::RequestHandler
include Helpers
attr_reader :request
def initialize(request)
@request = request
end
private
def externally_stubbed?
# prevent infinite recursion...
VCR::LibraryHooks::WebMock.with_global_hook_disabled(request) do
::WebMock.registered_request?(request)
end
end
def set_typed_request_for_after_hook(*args)
super
request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
end
def vcr_request
@vcr_request ||= vcr_request_for(request)
end
def on_externally_stubbed_request
# nil allows WebMock to handle the request
nil
end
def on_unhandled_request
invoke_after_request_hook(nil)
super
end
def on_stubbed_by_vcr_request
{
:body => stubbed_response.body.dup, # Excon mutates the body, so we must dup it :-(
:status => [stubbed_response.status.code.to_i, stubbed_response.status.message],
:headers => stubbed_response.headers
}
end
end
extend Helpers
::WebMock.globally_stub_request do |req|
global_hook_disabled?(req) ? nil : RequestHandler.new(req).handle
end
::WebMock.after_request(:real_requests_only => true) do |request, response|
unless VCR.library_hooks.disabled?(:webmock)
http_interaction = VCR::HTTPInteraction.new \
typed_request_for(request), vcr_response_for(response)
VCR.record_http_interaction(http_interaction)
end
end
::WebMock.after_request do |request, response|
unless VCR.library_hooks.disabled?(:webmock)
VCR.configuration.invoke_hook \
:after_http_request,
typed_request_for(request, :remove),
vcr_response_for(response)
end
end
end
end
end
# @private
module WebMock
class << self
# ensure HTTP requests are always allowed; VCR takes care of disallowing
# them at the appropriate times in its hook
def net_connect_allowed_with_vcr?(*args)
VCR.turned_on? ? true : net_connect_allowed_without_vcr?(*args)
end
alias net_connect_allowed_without_vcr? net_connect_allowed?
alias net_connect_allowed? net_connect_allowed_with_vcr?
end unless respond_to?(:net_connect_allowed_with_vcr?)
end
|