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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +34 -0
  4. data/.rspec-tm +2 -0
  5. data/.travis.yml +19 -0
  6. data/CHANGELOG.md +1698 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE +20 -0
  9. data/README.md +1125 -0
  10. data/Rakefile +28 -0
  11. data/lib/webmock.rb +59 -0
  12. data/lib/webmock/api.rb +109 -0
  13. data/lib/webmock/assertion_failure.rb +11 -0
  14. data/lib/webmock/callback_registry.rb +35 -0
  15. data/lib/webmock/config.rb +18 -0
  16. data/lib/webmock/cucumber.rb +10 -0
  17. data/lib/webmock/deprecation.rb +9 -0
  18. data/lib/webmock/errors.rb +17 -0
  19. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +214 -0
  20. data/lib/webmock/http_lib_adapters/curb_adapter.rb +347 -0
  21. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +228 -0
  22. data/lib/webmock/http_lib_adapters/excon_adapter.rb +162 -0
  23. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +7 -0
  24. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +19 -0
  25. data/lib/webmock/http_lib_adapters/http_rb/client.rb +14 -0
  26. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  27. data/lib/webmock/http_lib_adapters/http_rb/response.rb +43 -0
  28. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  29. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  30. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  31. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +242 -0
  32. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +130 -0
  33. data/lib/webmock/http_lib_adapters/net_http.rb +361 -0
  34. data/lib/webmock/http_lib_adapters/net_http_response.rb +34 -0
  35. data/lib/webmock/http_lib_adapters/patron_adapter.rb +130 -0
  36. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +174 -0
  37. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  38. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  39. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  40. data/lib/webmock/matchers/hash_including_matcher.rb +17 -0
  41. data/lib/webmock/minitest.rb +41 -0
  42. data/lib/webmock/rack_response.rb +69 -0
  43. data/lib/webmock/request_body_diff.rb +64 -0
  44. data/lib/webmock/request_execution_verifier.rb +77 -0
  45. data/lib/webmock/request_pattern.rb +370 -0
  46. data/lib/webmock/request_registry.rb +35 -0
  47. data/lib/webmock/request_signature.rb +54 -0
  48. data/lib/webmock/request_signature_snippet.rb +61 -0
  49. data/lib/webmock/request_stub.rb +100 -0
  50. data/lib/webmock/response.rb +153 -0
  51. data/lib/webmock/responses_sequence.rb +40 -0
  52. data/lib/webmock/rspec.rb +41 -0
  53. data/lib/webmock/rspec/matchers.rb +27 -0
  54. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +78 -0
  55. data/lib/webmock/rspec/matchers/webmock_matcher.rb +67 -0
  56. data/lib/webmock/stub_registry.rb +67 -0
  57. data/lib/webmock/stub_request_snippet.rb +38 -0
  58. data/lib/webmock/test_unit.rb +22 -0
  59. data/lib/webmock/util/hash_counter.rb +39 -0
  60. data/lib/webmock/util/hash_keys_stringifier.rb +25 -0
  61. data/lib/webmock/util/hash_validator.rb +17 -0
  62. data/lib/webmock/util/headers.rb +64 -0
  63. data/lib/webmock/util/json.rb +67 -0
  64. data/lib/webmock/util/query_mapper.rb +281 -0
  65. data/lib/webmock/util/uri.rb +110 -0
  66. data/lib/webmock/util/values_stringifier.rb +20 -0
  67. data/lib/webmock/util/version_checker.rb +111 -0
  68. data/lib/webmock/version.rb +3 -0
  69. data/lib/webmock/webmock.rb +161 -0
  70. data/minitest/test_helper.rb +34 -0
  71. data/minitest/test_webmock.rb +9 -0
  72. data/minitest/webmock_spec.rb +60 -0
  73. data/spec/acceptance/async_http_client/async_http_client_spec.rb +349 -0
  74. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  75. data/spec/acceptance/curb/curb_spec.rb +492 -0
  76. data/spec/acceptance/curb/curb_spec_helper.rb +147 -0
  77. data/spec/acceptance/em_http_request/em_http_request_spec.rb +406 -0
  78. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +77 -0
  79. data/spec/acceptance/excon/excon_spec.rb +77 -0
  80. data/spec/acceptance/excon/excon_spec_helper.rb +50 -0
  81. data/spec/acceptance/http_rb/http_rb_spec.rb +82 -0
  82. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  83. data/spec/acceptance/httpclient/httpclient_spec.rb +217 -0
  84. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +57 -0
  85. data/spec/acceptance/manticore/manticore_spec.rb +56 -0
  86. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  87. data/spec/acceptance/net_http/net_http_shared.rb +153 -0
  88. data/spec/acceptance/net_http/net_http_spec.rb +331 -0
  89. data/spec/acceptance/net_http/net_http_spec_helper.rb +64 -0
  90. data/spec/acceptance/net_http/real_net_http_spec.rb +20 -0
  91. data/spec/acceptance/patron/patron_spec.rb +125 -0
  92. data/spec/acceptance/patron/patron_spec_helper.rb +54 -0
  93. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +313 -0
  94. data/spec/acceptance/shared/callbacks.rb +148 -0
  95. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +36 -0
  96. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +95 -0
  97. data/spec/acceptance/shared/precedence_of_stubs.rb +15 -0
  98. data/spec/acceptance/shared/request_expectations.rb +930 -0
  99. data/spec/acceptance/shared/returning_declared_responses.rb +409 -0
  100. data/spec/acceptance/shared/stubbing_requests.rb +643 -0
  101. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +135 -0
  102. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +60 -0
  103. data/spec/acceptance/webmock_shared.rb +41 -0
  104. data/spec/fixtures/test.txt +1 -0
  105. data/spec/quality_spec.rb +84 -0
  106. data/spec/spec_helper.rb +48 -0
  107. data/spec/support/example_curl_output.txt +22 -0
  108. data/spec/support/failures.rb +9 -0
  109. data/spec/support/my_rack_app.rb +53 -0
  110. data/spec/support/network_connection.rb +19 -0
  111. data/spec/support/webmock_server.rb +70 -0
  112. data/spec/unit/api_spec.rb +175 -0
  113. data/spec/unit/errors_spec.rb +129 -0
  114. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +17 -0
  115. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +12 -0
  116. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  117. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  118. data/spec/unit/rack_response_spec.rb +112 -0
  119. data/spec/unit/request_body_diff_spec.rb +90 -0
  120. data/spec/unit/request_execution_verifier_spec.rb +208 -0
  121. data/spec/unit/request_pattern_spec.rb +601 -0
  122. data/spec/unit/request_registry_spec.rb +95 -0
  123. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  124. data/spec/unit/request_signature_spec.rb +155 -0
  125. data/spec/unit/request_stub_spec.rb +199 -0
  126. data/spec/unit/response_spec.rb +282 -0
  127. data/spec/unit/stub_registry_spec.rb +103 -0
  128. data/spec/unit/stub_request_snippet_spec.rb +115 -0
  129. data/spec/unit/util/hash_counter_spec.rb +39 -0
  130. data/spec/unit/util/hash_keys_stringifier_spec.rb +27 -0
  131. data/spec/unit/util/headers_spec.rb +28 -0
  132. data/spec/unit/util/json_spec.rb +33 -0
  133. data/spec/unit/util/query_mapper_spec.rb +157 -0
  134. data/spec/unit/util/uri_spec.rb +361 -0
  135. data/spec/unit/util/version_checker_spec.rb +65 -0
  136. data/spec/unit/webmock_spec.rb +19 -0
  137. data/test/http_request.rb +24 -0
  138. data/test/shared_test.rb +108 -0
  139. data/test/test_helper.rb +23 -0
  140. data/test/test_webmock.rb +6 -0
  141. data/webmock.gemspec +45 -0
  142. metadata +496 -0
@@ -0,0 +1,162 @@
1
+ begin
2
+ require 'excon'
3
+ rescue LoadError
4
+ # excon not found
5
+ end
6
+
7
+ if defined?(Excon)
8
+ WebMock::VersionChecker.new('Excon', Excon::VERSION, '0.27.5').check_version!
9
+
10
+ module WebMock
11
+ module HttpLibAdapters
12
+
13
+ class ExconAdapter < HttpLibAdapter
14
+ PARAMS_TO_DELETE = [:expects, :idempotent,
15
+ :instrumentor_name, :instrumentor,
16
+ :response_block,
17
+ :__construction_args, :stack,
18
+ :connection, :response]
19
+
20
+ adapter_for :excon
21
+
22
+ instance_exec do
23
+ @original_excon_mock_default = nil
24
+ @stub = nil
25
+ end
26
+
27
+ def self.enable!
28
+ self.add_excon_stub
29
+ end
30
+
31
+ def self.disable!
32
+ self.remove_excon_stub
33
+ end
34
+
35
+ def self.add_excon_stub
36
+ if not @stub
37
+ @original_excon_mock_default = ::Excon.defaults[:mock]
38
+ ::Excon.defaults[:mock] = true
39
+ @stub = ::Excon.stub({}) do |params|
40
+ self.handle_request(params)
41
+ end
42
+ end
43
+ end
44
+
45
+ def self.remove_excon_stub
46
+ ::Excon.defaults[:mock] = @original_excon_mock_default
47
+ @original_excon_mock_default = nil
48
+ Excon.stubs.delete(@stub)
49
+ @stub = nil
50
+ end
51
+
52
+ def self.handle_request(params)
53
+ mock_request = self.build_request params.dup
54
+ WebMock::RequestRegistry.instance.requested_signatures.put(mock_request)
55
+
56
+ if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request)
57
+ self.perform_callbacks(mock_request, mock_response, real_request: false)
58
+ response = self.real_response(mock_response)
59
+ response
60
+ elsif WebMock.net_connect_allowed?(mock_request.uri)
61
+ conn = new_excon_connection(params)
62
+ real_response = conn.request(request_params_from(params.merge(mock: false)))
63
+
64
+ ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), real_request: true)
65
+
66
+ real_response.data
67
+ else
68
+ raise WebMock::NetConnectNotAllowedError.new(mock_request)
69
+ end
70
+ end
71
+
72
+ def self.new_excon_connection(params)
73
+ # Ensure the connection is constructed with the exact same args
74
+ # that the orginal connection was constructed with.
75
+ args = params.fetch(:__construction_args)
76
+ ::Excon::Connection.new(connection_params_from args.merge(mock: false))
77
+ end
78
+
79
+ def self.connection_params_from(hash)
80
+ hash = hash.dup
81
+ PARAMS_TO_DELETE.each { |key| hash.delete(key) }
82
+ hash
83
+ end
84
+
85
+ def self.request_params_from(hash)
86
+ hash = hash.dup
87
+ if defined?(Excon::VALID_REQUEST_KEYS)
88
+ hash.reject! {|key,_| !Excon::VALID_REQUEST_KEYS.include?(key) }
89
+ end
90
+ PARAMS_TO_DELETE.each { |key| hash.delete(key) }
91
+ hash
92
+ end
93
+
94
+ def self.to_query(hash)
95
+ string = "".dup
96
+ for key, values in hash
97
+ if values.nil?
98
+ string << key.to_s << '&'
99
+ else
100
+ for value in [*values]
101
+ string << key.to_s << '=' << CGI.escape(value.to_s) << '&'
102
+ end
103
+ end
104
+ end
105
+ string.chop! # remove trailing '&'
106
+ end
107
+
108
+ def self.build_request(params)
109
+ params = params.dup
110
+ params.delete(:user)
111
+ params.delete(:password)
112
+ method = (params.delete(:method) || :get).to_s.downcase.to_sym
113
+ params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash)
114
+ uri = Addressable::URI.new(params).to_s
115
+ WebMock::RequestSignature.new method, uri, body: body_from(params), headers: params[:headers]
116
+ end
117
+
118
+ def self.body_from(params)
119
+ body = params[:body]
120
+ return body unless body.respond_to?(:read)
121
+
122
+ contents = body.read
123
+ body.rewind if body.respond_to?(:rewind)
124
+ contents
125
+ end
126
+
127
+ def self.real_response(mock)
128
+ raise Excon::Errors::Timeout if mock.should_timeout
129
+ mock.raise_error_if_any
130
+ {
131
+ body: mock.body,
132
+ status: mock.status[0].to_i,
133
+ reason_phrase: mock.status[1],
134
+ headers: mock.headers || {}
135
+ }
136
+ end
137
+
138
+ def self.mock_response(real)
139
+ mock = WebMock::Response.new
140
+ mock.status = [real.status, real.reason_phrase]
141
+ mock.headers = real.headers
142
+ mock.body = real.body.dup
143
+ mock
144
+ end
145
+
146
+ def self.perform_callbacks(request, response, options = {})
147
+ return unless WebMock::CallbackRegistry.any_callbacks?
148
+ WebMock::CallbackRegistry.invoke_callbacks(options.merge(lib: :excon), request, response)
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ Excon::Connection.class_eval do
155
+ def self.new(args = {})
156
+ args.delete(:__construction_args)
157
+ super(args).tap do |instance|
158
+ instance.data[:__construction_args] = args
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,7 @@
1
+ module WebMock
2
+ class HttpLibAdapter
3
+ def self.adapter_for(lib)
4
+ WebMock::HttpLibAdapterRegistry.instance.register(lib, self)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module WebMock
2
+ class HttpLibAdapterRegistry
3
+ include Singleton
4
+
5
+ attr_accessor :http_lib_adapters
6
+
7
+ def initialize
8
+ @http_lib_adapters = {}
9
+ end
10
+
11
+ def register(lib, adapter)
12
+ @http_lib_adapters[lib] = adapter
13
+ end
14
+
15
+ def each_adapter(&block)
16
+ @http_lib_adapters.each(&block)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ module HTTP
2
+ class Client
3
+ alias_method :__perform__, :perform
4
+
5
+ def perform(request, options)
6
+ return __perform__(request, options) unless webmock_enabled?
7
+ WebMockPerform.new(request) { __perform__(request, options) }.exec
8
+ end
9
+
10
+ def webmock_enabled?
11
+ ::WebMock::HttpLibAdapters::HttpRbAdapter.enabled?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module HTTP
2
+ class Request
3
+ def webmock_signature
4
+ request_body = if defined?(HTTP::Request::Body)
5
+ ''.tap { |string| body.each { |part| string << part } }
6
+ else
7
+ body
8
+ end
9
+
10
+ ::WebMock::RequestSignature.new(verb, uri.to_s, {
11
+ headers: headers.to_h,
12
+ body: request_body
13
+ })
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ module HTTP
2
+ class Response
3
+ def to_webmock
4
+ webmock_response = ::WebMock::Response.new
5
+
6
+ webmock_response.status = [status.to_i, reason]
7
+ webmock_response.body = body.to_s
8
+ webmock_response.headers = headers.to_h
9
+
10
+ webmock_response
11
+ end
12
+
13
+ class << self
14
+ def from_webmock(webmock_response, request_signature = nil)
15
+ status = Status.new(webmock_response.status.first)
16
+ headers = webmock_response.headers || {}
17
+ body = Body.new Streamer.new webmock_response.body
18
+ uri = normalize_uri(request_signature && request_signature.uri)
19
+
20
+ return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
21
+
22
+ new({
23
+ status: status,
24
+ version: "1.1",
25
+ headers: headers,
26
+ body: body,
27
+ uri: uri
28
+ })
29
+ end
30
+
31
+ private
32
+
33
+ def normalize_uri(uri)
34
+ return unless uri
35
+
36
+ uri = Addressable::URI.parse uri
37
+ uri.port = nil if uri.default_port && uri.port == uri.default_port
38
+
39
+ uri
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module HTTP
2
+ class Response
3
+ class Streamer
4
+ def initialize(str)
5
+ @io = StringIO.new str
6
+ end
7
+
8
+ def readpartial(size = nil)
9
+ unless size
10
+ if defined?(HTTP::Client::BUFFER_SIZE)
11
+ size = HTTP::Client::BUFFER_SIZE
12
+ elsif defined?(HTTP::Connection::BUFFER_SIZE)
13
+ size = HTTP::Connection::BUFFER_SIZE
14
+ end
15
+ end
16
+
17
+ @io.read size
18
+ end
19
+
20
+ def close
21
+ @io.close
22
+ end
23
+
24
+ def sequence_id
25
+ -1
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ module HTTP
2
+ class WebMockPerform
3
+ def initialize(request, &perform)
4
+ @request = request
5
+ @perform = perform
6
+ @request_signature = nil
7
+ end
8
+
9
+ def exec
10
+ replay || perform || halt
11
+ end
12
+
13
+ def request_signature
14
+ unless @request_signature
15
+ @request_signature = @request.webmock_signature
16
+ register_request(@request_signature)
17
+ end
18
+
19
+ @request_signature
20
+ end
21
+
22
+ protected
23
+
24
+ def response_for_request(signature)
25
+ ::WebMock::StubRegistry.instance.response_for_request(signature)
26
+ end
27
+
28
+ def register_request(signature)
29
+ ::WebMock::RequestRegistry.instance.requested_signatures.put(signature)
30
+ end
31
+
32
+ def replay
33
+ webmock_response = response_for_request request_signature
34
+
35
+ return unless webmock_response
36
+
37
+ raise_timeout_error if webmock_response.should_timeout
38
+ webmock_response.raise_error_if_any
39
+
40
+ invoke_callbacks(webmock_response, real_request: false)
41
+ ::HTTP::Response.from_webmock webmock_response, request_signature
42
+ end
43
+
44
+ def raise_timeout_error
45
+ raise Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
46
+ raise HTTP::TimeoutError, "connection error: #{Errno::ETIMEDOUT.new}"
47
+ end
48
+
49
+ def perform
50
+ return unless ::WebMock.net_connect_allowed?(request_signature.uri)
51
+ response = @perform.call
52
+ invoke_callbacks(response.to_webmock, real_request: true)
53
+ response
54
+ end
55
+
56
+ def halt
57
+ raise ::WebMock::NetConnectNotAllowedError.new request_signature
58
+ end
59
+
60
+ def invoke_callbacks(webmock_response, options = {})
61
+ ::WebMock::CallbackRegistry.invoke_callbacks(
62
+ options.merge({ lib: :http_rb }),
63
+ request_signature,
64
+ webmock_response
65
+ )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ begin
2
+ require "http"
3
+ rescue LoadError
4
+ # HTTP gem not found
5
+ end
6
+
7
+ if defined?(HTTP) && defined?(HTTP::VERSION)
8
+ WebMock::VersionChecker.new("HTTP Gem", HTTP::VERSION, "0.6.0").check_version!
9
+
10
+ module WebMock
11
+ module HttpLibAdapters
12
+ class HttpRbAdapter < HttpLibAdapter
13
+ adapter_for :http_rb
14
+
15
+ class << self
16
+ def enable!
17
+ @enabled = true
18
+ end
19
+
20
+ def disable!
21
+ @enabled = false
22
+ end
23
+
24
+ def enabled?
25
+ @enabled
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require "webmock/http_lib_adapters/http_rb/client"
33
+ require "webmock/http_lib_adapters/http_rb/request"
34
+ require "webmock/http_lib_adapters/http_rb/response"
35
+ require "webmock/http_lib_adapters/http_rb/streamer"
36
+ require "webmock/http_lib_adapters/http_rb/webmock"
37
+ end
@@ -0,0 +1,242 @@
1
+ begin
2
+ require 'httpclient'
3
+ require 'jsonclient' # defined in 'httpclient' gem as well
4
+ rescue LoadError
5
+ # httpclient not found
6
+ # or jsonclient not defined (in old versions of httclient gem)
7
+ end
8
+
9
+ if defined?(::HTTPClient)
10
+
11
+ module WebMock
12
+ module HttpLibAdapters
13
+ class HTTPClientAdapter < HttpLibAdapter
14
+ adapter_for :httpclient
15
+
16
+ unless const_defined?(:OriginalHttpClient)
17
+ OriginalHttpClient = ::HTTPClient
18
+ end
19
+
20
+ unless const_defined?(:OriginalJsonClient)
21
+ OriginalJsonClient = ::JSONClient if defined?(::JSONClient)
22
+ end
23
+
24
+ def self.enable!
25
+ Object.send(:remove_const, :HTTPClient)
26
+ Object.send(:const_set, :HTTPClient, WebMockHTTPClient)
27
+ if defined? ::JSONClient
28
+ Object.send(:remove_const, :JSONClient)
29
+ Object.send(:const_set, :JSONClient, WebMockJSONClient)
30
+ end
31
+ end
32
+
33
+ def self.disable!
34
+ Object.send(:remove_const, :HTTPClient)
35
+ Object.send(:const_set, :HTTPClient, OriginalHttpClient)
36
+ if defined? ::JSONClient
37
+ Object.send(:remove_const, :JSONClient)
38
+ Object.send(:const_set, :JSONClient, OriginalJsonClient)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ module WebMockHTTPClients
46
+ def do_get_block(req, proxy, conn, &block)
47
+ do_get(req, proxy, conn, false, &block)
48
+ end
49
+
50
+ def do_get_stream(req, proxy, conn, &block)
51
+ do_get(req, proxy, conn, true, &block)
52
+ end
53
+
54
+ def do_get(req, proxy, conn, stream = false, &block)
55
+ request_signature = build_request_signature(req, :reuse_existing)
56
+
57
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
58
+
59
+ if webmock_responses[request_signature]
60
+ webmock_response = webmock_responses.delete(request_signature)
61
+ response = build_httpclient_response(webmock_response, stream, req.header, &block)
62
+ @request_filter.each do |filter|
63
+ filter.filter_response(req, response)
64
+ end
65
+ res = conn.push(response)
66
+ WebMock::CallbackRegistry.invoke_callbacks(
67
+ {lib: :httpclient}, request_signature, webmock_response)
68
+ res
69
+ elsif WebMock.net_connect_allowed?(request_signature.uri)
70
+ # in case there is a nil entry in the hash...
71
+ webmock_responses.delete(request_signature)
72
+
73
+ res = if stream
74
+ do_get_stream_without_webmock(req, proxy, conn, &block)
75
+ elsif block
76
+ body = ''
77
+ do_get_block_without_webmock(req, proxy, conn) do |http_res, chunk|
78
+ if chunk && chunk.bytesize > 0
79
+ body += chunk
80
+ block.call(http_res, chunk)
81
+ end
82
+ end
83
+ else
84
+ do_get_block_without_webmock(req, proxy, conn)
85
+ end
86
+ res = conn.pop
87
+ conn.push(res)
88
+ if WebMock::CallbackRegistry.any_callbacks?
89
+ webmock_response = build_webmock_response(res, body)
90
+ WebMock::CallbackRegistry.invoke_callbacks(
91
+ {lib: :httpclient, real_request: true}, request_signature,
92
+ webmock_response)
93
+ end
94
+ res
95
+ else
96
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
97
+ end
98
+ end
99
+
100
+ def do_request_async(method, uri, query, body, extheader)
101
+ req = create_request(method, uri, query, body, extheader)
102
+ request_signature = build_request_signature(req)
103
+ webmock_request_signatures << request_signature
104
+
105
+ if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
106
+ super
107
+ else
108
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
109
+ end
110
+ end
111
+
112
+ def build_httpclient_response(webmock_response, stream = false, req_header = nil, &block)
113
+ body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
114
+ response = HTTP::Message.new_response(body, req_header)
115
+ response.header.init_response(webmock_response.status[0])
116
+ response.reason=webmock_response.status[1]
117
+ webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) }
118
+
119
+ raise HTTPClient::TimeoutError if webmock_response.should_timeout
120
+ webmock_response.raise_error_if_any
121
+
122
+ block.call(response, body) if block && body && body.bytesize > 0
123
+
124
+ response
125
+ end
126
+
127
+ def build_webmock_response(httpclient_response, body = nil)
128
+ webmock_response = WebMock::Response.new
129
+ webmock_response.status = [httpclient_response.status, httpclient_response.reason]
130
+
131
+ webmock_response.headers = {}.tap do |hash|
132
+ httpclient_response.header.all.each do |(key, value)|
133
+ if hash.has_key?(key)
134
+ hash[key] = Array(hash[key]) + [value]
135
+ else
136
+ hash[key] = value
137
+ end
138
+ end
139
+ end
140
+
141
+ if body
142
+ webmock_response.body = body
143
+ elsif httpclient_response.content.respond_to?(:read)
144
+ webmock_response.body = httpclient_response.content.read
145
+ body = HTTP::Message::Body.new
146
+ body.init_response(StringIO.new(webmock_response.body))
147
+ httpclient_response.body = body
148
+ else
149
+ webmock_response.body = httpclient_response.content
150
+ end
151
+ webmock_response
152
+ end
153
+
154
+ def build_request_signature(req, reuse_existing = false)
155
+ uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
156
+ uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query
157
+ uri.port = req.header.request_uri.port
158
+
159
+ @request_filter.each do |filter|
160
+ filter.filter_request(req)
161
+ end
162
+
163
+ headers = req.header.all.inject({}) do |hdrs, header|
164
+ hdrs[header[0]] ||= []
165
+ hdrs[header[0]] << header[1]
166
+ hdrs
167
+ end
168
+ headers = headers_from_session(uri).merge(headers)
169
+
170
+ signature = WebMock::RequestSignature.new(
171
+ req.header.request_method.downcase.to_sym,
172
+ uri.to_s,
173
+ body: req.http_body.dump,
174
+ headers: headers
175
+ )
176
+
177
+ # reuse a previous identical signature object if we stored one for later use
178
+ if reuse_existing && previous_signature = previous_signature_for(signature)
179
+ return previous_signature
180
+ end
181
+
182
+ signature
183
+ end
184
+
185
+ def webmock_responses
186
+ @webmock_responses ||= Hash.new do |hash, request_signature|
187
+ hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
188
+ end
189
+ end
190
+
191
+ def webmock_request_signatures
192
+ @webmock_request_signatures ||= []
193
+ end
194
+
195
+ def previous_signature_for(signature)
196
+ return nil unless index = webmock_request_signatures.index(signature)
197
+ webmock_request_signatures.delete_at(index)
198
+ end
199
+
200
+ private
201
+
202
+ # some of the headers sent by HTTPClient are derived from
203
+ # the client session
204
+ def headers_from_session(uri)
205
+ session_headers = HTTP::Message::Headers.new
206
+ @session_manager.send(:open, uri).send(:set_header, MessageMock.new(session_headers))
207
+ session_headers.all.inject({}) do |hdrs, header|
208
+ hdrs[header[0]] = header[1]
209
+ hdrs
210
+ end
211
+ end
212
+ end
213
+
214
+ class WebMockHTTPClient < HTTPClient
215
+ alias_method :do_get_block_without_webmock, :do_get_block
216
+ alias_method :do_get_stream_without_webmock, :do_get_stream
217
+
218
+ include WebMockHTTPClients
219
+ end
220
+
221
+ if defined? ::JSONClient
222
+ class WebMockJSONClient < JSONClient
223
+ alias_method :do_get_block_without_webmock, :do_get_block
224
+ alias_method :do_get_stream_without_webmock, :do_get_stream
225
+
226
+ include WebMockHTTPClients
227
+ end
228
+ end
229
+
230
+
231
+ # Mocks a HTTPClient HTTP::Message
232
+ class MessageMock
233
+ attr_reader :header
234
+
235
+ def initialize(headers)
236
+ @header = headers
237
+ end
238
+
239
+ def http_version=(value);end
240
+ end
241
+
242
+ end