File: webmock.rb

package info (click to toggle)
ruby-vcr 6.0.0%2Breally5.0.0-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,320 kB
  • sloc: ruby: 8,456; sh: 177; makefile: 7
file content (172 lines) | stat: -rw-r--r-- 5,091 bytes parent folder | download
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