File: manticore_adapter.rb

package info (click to toggle)
ruby-webmock 3.25.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,172 kB
  • sloc: ruby: 12,829; makefile: 6
file content (147 lines) | stat: -rw-r--r-- 5,302 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
# frozen_string_literal: true

begin
  require 'manticore'
rescue LoadError
  # manticore not found
end

if defined?(Manticore)
  module WebMock
    module HttpLibAdapters
      class ManticoreAdapter < HttpLibAdapter
        adapter_for :manticore

        OriginalManticoreClient = Manticore::Client

        def self.enable!
          Manticore.send(:remove_const, :Client)
          Manticore.send(:const_set, :Client, WebMockManticoreClient)
          Manticore.instance_variable_set(:@manticore_facade, WebMockManticoreClient.new)
        end

        def self.disable!
          Manticore.send(:remove_const, :Client)
          Manticore.send(:const_set, :Client, OriginalManticoreClient)
          Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new)
        end

        class StubbedTimeoutResponse < Manticore::StubbedResponse
          def call
            @handlers[:failure].call(Manticore::ConnectTimeout.new("Too slow (mocked timeout)"))
          end
        end

        class WebMockManticoreClient < Manticore::Client
          def request(klass, url, options={}, &block)
            super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options))
          end

          private

          def format_options(options)
            return options unless headers = options[:headers]

            options.merge(headers: join_array_values(headers))
          end

          def join_array_values(headers)
            headers.reduce({}) do |h, (k,v)|
              v = v.join(', ') if v.is_a?(Array)
              h.merge(k => v)
            end
          end

          def response_object_for(request, context, &block)
            request_signature = generate_webmock_request_signature(request, context)
            WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)

            if webmock_response = registered_response_for(request_signature)
              webmock_response.raise_error_if_any
              manticore_response = generate_manticore_response(webmock_response)
              manticore_response.on_success do
                WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: false}, request_signature, webmock_response)
              end

            elsif real_request_allowed?(request_signature.uri)
              manticore_response = Manticore::Response.new(self, request, context, &block)
              manticore_response.on_complete do |completed_response|
                webmock_response = generate_webmock_response(completed_response)
                WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: true}, request_signature, webmock_response)
              end

            else
              raise WebMock::NetConnectNotAllowedError.new(request_signature)
            end

            manticore_response
          end

          def registered_response_for(request_signature)
            WebMock::StubRegistry.instance.response_for_request(request_signature)
          end

          def real_request_allowed?(uri)
            WebMock.net_connect_allowed?(uri)
          end

          def generate_webmock_request_signature(request, context)
            method = request.method.downcase
            uri = request.uri.to_s
            body = read_body(request)
            headers = split_array_values(request.headers)

            if context.get_credentials_provider && credentials = context.get_credentials_provider.get_credentials(AuthScope::ANY)
              headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(credentials.get_user_name,credentials.get_password)
            end

            WebMock::RequestSignature.new(method, uri, {body: body, headers: headers})
          end

          def read_body(request)
            if request.respond_to?(:entity) && !request.entity.nil?
              Manticore::EntityConverter.new.read_entity(request.entity)
            end
          end

          def split_array_values(headers = [])
            headers.each_with_object({}) do |(k, v), h|
              h[k] = case v
                     when /,/ then v.split(',').map(&:strip)
                     else v
                     end
            end
          end

          def generate_manticore_response(webmock_response)
            if webmock_response.should_timeout
              StubbedTimeoutResponse.new
            else
              Manticore::StubbedResponse.stub(
                code: webmock_response.status[0],
                body: webmock_response.body,
                headers: webmock_response.headers,
                cookies: {}
              )
            end
          end

          def generate_webmock_response(manticore_response)
            webmock_response = WebMock::Response.new
            webmock_response.status = [manticore_response.code, manticore_response.message]
            webmock_response.headers = manticore_response.headers

            # The attempt to read the body could fail if manticore is used in a streaming mode
            webmock_response.body = begin
              manticore_response.body
            rescue ::Manticore::StreamClosedException
              nil
            end

            webmock_response
          end
        end
      end
    end
  end
end