webmock 3.14.0 → 3.24.0

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +207 -3
  3. data/README.md +65 -20
  4. data/lib/webmock/api.rb +2 -0
  5. data/lib/webmock/assertion_failure.rb +2 -0
  6. data/lib/webmock/callback_registry.rb +2 -0
  7. data/lib/webmock/config.rb +2 -0
  8. data/lib/webmock/cucumber.rb +2 -0
  9. data/lib/webmock/deprecation.rb +2 -0
  10. data/lib/webmock/errors.rb +2 -0
  11. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +16 -4
  12. data/lib/webmock/http_lib_adapters/curb_adapter.rb +4 -2
  13. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +17 -7
  14. data/lib/webmock/http_lib_adapters/excon_adapter.rb +2 -0
  15. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +2 -0
  16. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +2 -0
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +3 -3
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +17 -5
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +32 -9
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +10 -2
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +8 -2
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +7 -5
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +26 -25
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +2 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +28 -117
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +2 -0
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +3 -1
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +18 -2
  29. data/lib/webmock/matchers/any_arg_matcher.rb +2 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +2 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +2 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +2 -0
  33. data/lib/webmock/minitest.rb +2 -0
  34. data/lib/webmock/rack_response.rb +5 -1
  35. data/lib/webmock/request_body_diff.rb +2 -0
  36. data/lib/webmock/request_execution_verifier.rb +2 -0
  37. data/lib/webmock/request_pattern.rb +35 -12
  38. data/lib/webmock/request_registry.rb +2 -0
  39. data/lib/webmock/request_signature.rb +4 -2
  40. data/lib/webmock/request_signature_snippet.rb +2 -0
  41. data/lib/webmock/request_stub.rb +34 -0
  42. data/lib/webmock/response.rb +15 -13
  43. data/lib/webmock/responses_sequence.rb +2 -0
  44. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +2 -0
  45. data/lib/webmock/rspec/matchers/webmock_matcher.rb +2 -0
  46. data/lib/webmock/rspec/matchers.rb +2 -0
  47. data/lib/webmock/rspec.rb +2 -0
  48. data/lib/webmock/stub_registry.rb +2 -0
  49. data/lib/webmock/stub_request_snippet.rb +2 -0
  50. data/lib/webmock/test_unit.rb +2 -0
  51. data/lib/webmock/util/hash_counter.rb +12 -6
  52. data/lib/webmock/util/hash_keys_stringifier.rb +2 -0
  53. data/lib/webmock/util/hash_validator.rb +2 -0
  54. data/lib/webmock/util/headers.rb +23 -10
  55. data/lib/webmock/util/parsers/json.rb +72 -0
  56. data/lib/webmock/util/parsers/parse_error.rb +7 -0
  57. data/lib/webmock/util/parsers/xml.rb +16 -0
  58. data/lib/webmock/util/query_mapper.rb +2 -0
  59. data/lib/webmock/util/uri.rb +3 -1
  60. data/lib/webmock/util/values_stringifier.rb +2 -0
  61. data/lib/webmock/util/version_checker.rb +7 -5
  62. data/lib/webmock/version.rb +3 -1
  63. data/lib/webmock/webmock.rb +12 -0
  64. data/lib/webmock.rb +4 -2
  65. metadata +66 -182
  66. data/.gemtest +0 -0
  67. data/.github/workflows/CI.yml +0 -37
  68. data/.gitignore +0 -34
  69. data/.rspec-tm +0 -2
  70. data/Gemfile +0 -9
  71. data/Rakefile +0 -38
  72. data/lib/webmock/util/json.rb +0 -67
  73. data/minitest/test_helper.rb +0 -34
  74. data/minitest/test_webmock.rb +0 -9
  75. data/minitest/webmock_spec.rb +0 -60
  76. data/spec/acceptance/async_http_client/async_http_client_spec.rb +0 -375
  77. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +0 -73
  78. data/spec/acceptance/curb/curb_spec.rb +0 -499
  79. data/spec/acceptance/curb/curb_spec_helper.rb +0 -147
  80. data/spec/acceptance/em_http_request/em_http_request_spec.rb +0 -462
  81. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +0 -77
  82. data/spec/acceptance/excon/excon_spec.rb +0 -77
  83. data/spec/acceptance/excon/excon_spec_helper.rb +0 -52
  84. data/spec/acceptance/http_rb/http_rb_spec.rb +0 -93
  85. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +0 -54
  86. data/spec/acceptance/httpclient/httpclient_spec.rb +0 -217
  87. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +0 -57
  88. data/spec/acceptance/manticore/manticore_spec.rb +0 -107
  89. data/spec/acceptance/manticore/manticore_spec_helper.rb +0 -35
  90. data/spec/acceptance/net_http/net_http_shared.rb +0 -153
  91. data/spec/acceptance/net_http/net_http_spec.rb +0 -369
  92. data/spec/acceptance/net_http/net_http_spec_helper.rb +0 -64
  93. data/spec/acceptance/net_http/real_net_http_spec.rb +0 -20
  94. data/spec/acceptance/patron/patron_spec.rb +0 -125
  95. data/spec/acceptance/patron/patron_spec_helper.rb +0 -54
  96. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +0 -313
  97. data/spec/acceptance/shared/callbacks.rb +0 -148
  98. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +0 -36
  99. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +0 -95
  100. data/spec/acceptance/shared/precedence_of_stubs.rb +0 -15
  101. data/spec/acceptance/shared/request_expectations.rb +0 -930
  102. data/spec/acceptance/shared/returning_declared_responses.rb +0 -409
  103. data/spec/acceptance/shared/stubbing_requests.rb +0 -678
  104. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +0 -135
  105. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +0 -60
  106. data/spec/acceptance/webmock_shared.rb +0 -41
  107. data/spec/fixtures/test.txt +0 -1
  108. data/spec/quality_spec.rb +0 -84
  109. data/spec/spec_helper.rb +0 -48
  110. data/spec/support/example_curl_output.txt +0 -22
  111. data/spec/support/failures.rb +0 -9
  112. data/spec/support/my_rack_app.rb +0 -53
  113. data/spec/support/network_connection.rb +0 -19
  114. data/spec/support/webmock_server.rb +0 -70
  115. data/spec/unit/api_spec.rb +0 -175
  116. data/spec/unit/errors_spec.rb +0 -129
  117. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +0 -17
  118. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +0 -12
  119. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +0 -61
  120. data/spec/unit/matchers/hash_including_matcher_spec.rb +0 -87
  121. data/spec/unit/rack_response_spec.rb +0 -112
  122. data/spec/unit/request_body_diff_spec.rb +0 -90
  123. data/spec/unit/request_execution_verifier_spec.rb +0 -208
  124. data/spec/unit/request_pattern_spec.rb +0 -736
  125. data/spec/unit/request_registry_spec.rb +0 -95
  126. data/spec/unit/request_signature_snippet_spec.rb +0 -89
  127. data/spec/unit/request_signature_spec.rb +0 -155
  128. data/spec/unit/request_stub_spec.rb +0 -199
  129. data/spec/unit/response_spec.rb +0 -286
  130. data/spec/unit/stub_registry_spec.rb +0 -103
  131. data/spec/unit/stub_request_snippet_spec.rb +0 -115
  132. data/spec/unit/util/hash_counter_spec.rb +0 -39
  133. data/spec/unit/util/hash_keys_stringifier_spec.rb +0 -27
  134. data/spec/unit/util/headers_spec.rb +0 -28
  135. data/spec/unit/util/json_spec.rb +0 -33
  136. data/spec/unit/util/query_mapper_spec.rb +0 -157
  137. data/spec/unit/util/uri_spec.rb +0 -371
  138. data/spec/unit/util/version_checker_spec.rb +0 -65
  139. data/spec/unit/webmock_spec.rb +0 -60
  140. data/test/http_request.rb +0 -24
  141. data/test/shared_test.rb +0 -108
  142. data/test/test_helper.rb +0 -23
  143. data/test/test_webmock.rb +0 -12
  144. data/webmock.gemspec +0 -54
@@ -1,12 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Response
3
5
  def to_webmock
4
6
  webmock_response = ::WebMock::Response.new
5
7
 
6
8
  webmock_response.status = [status.to_i, reason]
9
+
7
10
  webmock_response.body = body.to_s
8
- webmock_response.headers = headers.to_h
11
+ # This call is used to reset the body of the response to enable it to be streamed if necessary.
12
+ # The `body.to_s` call above reads the body, which allows WebMock to trigger any registered callbacks.
13
+ # However, once the body is read to_s, it cannot be streamed again and attempting to do so
14
+ # will raise a "HTTP::StateError: body has already been consumed" error.
15
+ # To avoid this error, we replace the original body with a new one.
16
+ # The new body has its @stream attribute set to new Streamer, instead of the original Connection.
17
+ # Unfortunately, it's not possible to reset the original body to its initial streaming state.
18
+ # Therefore, this replacement is the best workaround currently available.
19
+ reset_body_to_allow_it_to_be_streamed!(webmock_response)
9
20
 
21
+ webmock_response.headers = headers.to_h
10
22
  webmock_response
11
23
  end
12
24
 
@@ -19,13 +31,7 @@ module HTTP
19
31
  # HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x
20
32
  # and 2.x use a positional argument, and 0.x don't support supplying
21
33
  # the encoding.
22
- body = if HTTP::VERSION < "1.0.0"
23
- Body.new(Streamer.new(webmock_response.body))
24
- elsif HTTP::VERSION < "3.0.0"
25
- Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
26
- else
27
- Body.new(Streamer.new(webmock_response.body), encoding: webmock_response.body.encoding)
28
- end
34
+ body = build_http_rb_response_body_from_webmock_response(webmock_response)
29
35
 
30
36
  return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
31
37
 
@@ -49,7 +55,18 @@ module HTTP
49
55
  })
50
56
  end
51
57
 
52
- private
58
+ def build_http_rb_response_body_from_webmock_response(webmock_response)
59
+ if HTTP::VERSION < "1.0.0"
60
+ Body.new(Streamer.new(webmock_response.body))
61
+ elsif HTTP::VERSION < "3.0.0"
62
+ Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
63
+ else
64
+ Body.new(
65
+ Streamer.new(webmock_response.body, encoding: webmock_response.body.encoding),
66
+ encoding: webmock_response.body.encoding
67
+ )
68
+ end
69
+ end
53
70
 
54
71
  def normalize_uri(uri)
55
72
  return unless uri
@@ -60,5 +77,11 @@ module HTTP
60
77
  uri
61
78
  end
62
79
  end
80
+
81
+ private
82
+
83
+ def reset_body_to_allow_it_to_be_streamed!(webmock_response)
84
+ @body = self.class.build_http_rb_response_body_from_webmock_response(webmock_response)
85
+ end
63
86
  end
64
87
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Response
3
5
  class Streamer
4
- def initialize(str)
6
+ def initialize(str, encoding: Encoding::BINARY)
5
7
  @io = StringIO.new str
8
+ @encoding = encoding
6
9
  end
7
10
 
8
11
  def readpartial(size = nil, outbuf = nil)
@@ -14,13 +17,18 @@ module HTTP
14
17
  end
15
18
  end
16
19
 
17
- @io.read size, outbuf
20
+ chunk = @io.read size, outbuf
21
+ chunk.force_encoding(@encoding) if chunk
18
22
  end
19
23
 
20
24
  def close
21
25
  @io.close
22
26
  end
23
27
 
28
+ def finished_request?
29
+ @io.eof?
30
+ end
31
+
24
32
  def sequence_id
25
33
  -1
26
34
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class WebMockPerform
3
- def initialize(request, &perform)
5
+ def initialize(request, options, &perform)
4
6
  @request = request
7
+ @options = options
5
8
  @perform = perform
6
9
  @request_signature = nil
7
10
  end
@@ -38,7 +41,10 @@ module HTTP
38
41
  webmock_response.raise_error_if_any
39
42
 
40
43
  invoke_callbacks(webmock_response, real_request: false)
41
- ::HTTP::Response.from_webmock @request, webmock_response, request_signature
44
+ response = ::HTTP::Response.from_webmock @request, webmock_response, request_signature
45
+
46
+ @options.features.each { |_name, feature| response = feature.wrap_response(response) }
47
+ response
42
48
  end
43
49
 
44
50
  def raise_timeout_error
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "http"
3
5
  rescue LoadError
@@ -29,9 +31,9 @@ if defined?(HTTP) && defined?(HTTP::VERSION)
29
31
  end
30
32
  end
31
33
 
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"
34
+ require_relative "http_rb/client"
35
+ require_relative "http_rb/request"
36
+ require_relative "http_rb/response"
37
+ require_relative "http_rb/streamer"
38
+ require_relative "http_rb/webmock"
37
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'httpclient'
3
5
  require 'jsonclient' # defined in 'httpclient' gem as well
@@ -43,6 +45,8 @@ if defined?(::HTTPClient)
43
45
  end
44
46
 
45
47
  module WebMockHTTPClients
48
+ WEBMOCK_HTTPCLIENT_RESPONSES = :webmock_httpclient_responses
49
+ WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES = :webmock_httpclient_request_signatures
46
50
 
47
51
  REQUEST_RESPONSE_LOCK = Mutex.new
48
52
 
@@ -55,12 +59,14 @@ if defined?(::HTTPClient)
55
59
  end
56
60
 
57
61
  def do_get(req, proxy, conn, stream = false, &block)
62
+ clear_thread_variables unless conn.async_thread
63
+
58
64
  request_signature = build_request_signature(req, :reuse_existing)
59
65
 
60
66
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
61
67
 
62
68
  if webmock_responses[request_signature]
63
- webmock_response = synchronize_request_response { webmock_responses.delete(request_signature) }
69
+ webmock_response = webmock_responses.delete(request_signature)
64
70
  response = build_httpclient_response(webmock_response, stream, req.header, &block)
65
71
  @request_filter.each do |filter|
66
72
  filter.filter_response(req, response)
@@ -71,7 +77,7 @@ if defined?(::HTTPClient)
71
77
  res
72
78
  elsif WebMock.net_connect_allowed?(request_signature.uri)
73
79
  # in case there is a nil entry in the hash...
74
- synchronize_request_response { webmock_responses.delete(request_signature) }
80
+ webmock_responses.delete(request_signature)
75
81
 
76
82
  res = if stream
77
83
  do_get_stream_without_webmock(req, proxy, conn, &block)
@@ -101,12 +107,16 @@ if defined?(::HTTPClient)
101
107
  end
102
108
 
103
109
  def do_request_async(method, uri, query, body, extheader)
110
+ clear_thread_variables
104
111
  req = create_request(method, uri, query, body, extheader)
105
112
  request_signature = build_request_signature(req)
106
- synchronize_request_response { webmock_request_signatures << request_signature }
113
+ webmock_request_signatures << request_signature
107
114
 
108
115
  if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
109
- super
116
+ conn = super
117
+ conn.async_thread[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] = Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES]
118
+ conn.async_thread[WEBMOCK_HTTPCLIENT_RESPONSES] = Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES]
119
+ conn
110
120
  else
111
121
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
112
122
  end
@@ -155,14 +165,14 @@ if defined?(::HTTPClient)
155
165
  end
156
166
 
157
167
  def build_request_signature(req, reuse_existing = false)
158
- uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
159
- uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query
160
- uri.port = req.header.request_uri.port
161
-
162
168
  @request_filter.each do |filter|
163
169
  filter.filter_request(req)
164
170
  end
165
171
 
172
+ uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
173
+ uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query
174
+ uri.port = req.header.request_uri.port
175
+
166
176
  headers = req.header.all.inject({}) do |hdrs, header|
167
177
  hdrs[header[0]] ||= []
168
178
  hdrs[header[0]] << header[1]
@@ -186,22 +196,18 @@ if defined?(::HTTPClient)
186
196
  end
187
197
 
188
198
  def webmock_responses
189
- @webmock_responses ||= Hash.new do |hash, request_signature|
190
- synchronize_request_response do
191
- hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
192
- end
199
+ Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] ||= Hash.new do |hash, request_signature|
200
+ hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
193
201
  end
194
202
  end
195
203
 
196
204
  def webmock_request_signatures
197
- @webmock_request_signatures ||= []
205
+ Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] ||= []
198
206
  end
199
207
 
200
208
  def previous_signature_for(signature)
201
- synchronize_request_response do
202
- return nil unless index = webmock_request_signatures.index(signature)
203
- webmock_request_signatures.delete_at(index)
204
- end
209
+ return nil unless index = webmock_request_signatures.index(signature)
210
+ webmock_request_signatures.delete_at(index)
205
211
  end
206
212
 
207
213
  private
@@ -217,14 +223,9 @@ if defined?(::HTTPClient)
217
223
  end
218
224
  end
219
225
 
220
- def synchronize_request_response
221
- if REQUEST_RESPONSE_LOCK.owned?
222
- yield
223
- else
224
- REQUEST_RESPONSE_LOCK.synchronize do
225
- yield
226
- end
227
- end
226
+ def clear_thread_variables
227
+ Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] = nil
228
+ Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] = nil
228
229
  end
229
230
  end
230
231
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'manticore'
3
5
  rescue LoadError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'net/http'
2
4
  require 'net/https'
3
5
  require 'stringio'
@@ -10,24 +12,19 @@ module WebMock
10
12
  adapter_for :net_http
11
13
 
12
14
  OriginalNetHTTP = Net::HTTP unless const_defined?(:OriginalNetHTTP)
13
- OriginalNetBufferedIO = Net::BufferedIO unless const_defined?(:OriginalNetBufferedIO)
14
15
 
15
16
  def self.enable!
16
- Net.send(:remove_const, :BufferedIO)
17
17
  Net.send(:remove_const, :HTTP)
18
18
  Net.send(:remove_const, :HTTPSession)
19
19
  Net.send(:const_set, :HTTP, @webMockNetHTTP)
20
20
  Net.send(:const_set, :HTTPSession, @webMockNetHTTP)
21
- Net.send(:const_set, :BufferedIO, Net::WebMockNetBufferedIO)
22
21
  end
23
22
 
24
23
  def self.disable!
25
- Net.send(:remove_const, :BufferedIO)
26
24
  Net.send(:remove_const, :HTTP)
27
25
  Net.send(:remove_const, :HTTPSession)
28
26
  Net.send(:const_set, :HTTP, OriginalNetHTTP)
29
27
  Net.send(:const_set, :HTTPSession, OriginalNetHTTP)
30
- Net.send(:const_set, :BufferedIO, OriginalNetBufferedIO)
31
28
 
32
29
  #copy all constants from @webMockNetHTTP to original Net::HTTP
33
30
  #in case any constants were added to @webMockNetHTTP instead of Net::HTTP
@@ -80,7 +77,7 @@ module WebMock
80
77
  @socket = Net::HTTP.socket_type.new
81
78
  WebMock::CallbackRegistry.invoke_callbacks(
82
79
  {lib: :net_http}, request_signature, webmock_response)
83
- build_net_http_response(webmock_response, &block)
80
+ build_net_http_response(webmock_response, request.uri, &block)
84
81
  elsif WebMock.net_connect_allowed?(request_signature.uri)
85
82
  check_right_http_connection
86
83
  after_request = lambda do |response|
@@ -98,13 +95,8 @@ module WebMock
98
95
  after_request.call(response)
99
96
  }
100
97
  if started?
101
- if WebMock::Config.instance.net_http_connect_on_start
102
- super_with_after_request.call
103
- else
104
- start_with_connect_without_finish {
105
- super_with_after_request.call
106
- }
107
- end
98
+ ensure_actual_connection
99
+ super_with_after_request.call
108
100
  else
109
101
  start_with_connect {
110
102
  super_with_after_request.call
@@ -119,39 +111,40 @@ module WebMock
119
111
  raise IOError, 'HTTP session already opened' if @started
120
112
  if block_given?
121
113
  begin
114
+ @socket = Net::HTTP.socket_type.new
122
115
  @started = true
123
116
  return yield(self)
124
117
  ensure
125
118
  do_finish
126
119
  end
127
120
  end
121
+ @socket = Net::HTTP.socket_type.new
128
122
  @started = true
129
123
  self
130
124
  end
131
125
 
132
126
 
133
- def start_with_connect_without_finish # :yield: http
134
- if block_given?
135
- begin
136
- do_start
137
- return yield(self)
138
- end
127
+ def ensure_actual_connection
128
+ if @socket.is_a?(StubSocket)
129
+ @socket&.close
130
+ @socket = nil
131
+ do_start
139
132
  end
140
- do_start
141
- self
142
133
  end
143
134
 
144
135
  alias_method :start_with_connect, :start
145
136
 
146
137
  def start(&block)
147
- if WebMock::Config.instance.net_http_connect_on_start
138
+ uri = Addressable::URI.parse(WebMock::NetHTTPUtility.get_uri(self))
139
+
140
+ if WebMock.net_http_connect_on_start?(uri)
148
141
  super(&block)
149
142
  else
150
143
  start_without_connect(&block)
151
144
  end
152
145
  end
153
146
 
154
- def build_net_http_response(webmock_response, &block)
147
+ def build_net_http_response(webmock_response, request_uri, &block)
155
148
  response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
156
149
  body = webmock_response.body
157
150
  body = nil if webmock_response.status[0].to_s == '204'
@@ -166,10 +159,12 @@ module WebMock
166
159
 
167
160
  response.instance_variable_set(:@read, true)
168
161
 
162
+ response.uri = request_uri
163
+
169
164
  response.extend Net::WebMockHTTPResponse
170
165
 
171
166
  if webmock_response.should_timeout
172
- raise timeout_exception, "execution expired"
167
+ raise Net::OpenTimeout, "execution expired"
173
168
  end
174
169
 
175
170
  webmock_response.raise_error_if_any
@@ -179,16 +174,6 @@ module WebMock
179
174
  response
180
175
  end
181
176
 
182
- def timeout_exception
183
- if defined?(Net::OpenTimeout)
184
- # Ruby 2.x
185
- Net::OpenTimeout
186
- else
187
- # Fallback, if things change
188
- Timeout::Error
189
- end
190
- end
191
-
192
177
  def build_webmock_response(net_http_response)
193
178
  webmock_response = WebMock::Response.new
194
179
  webmock_response.status = [
@@ -222,31 +207,21 @@ module WebMock
222
207
  end
223
208
  end
224
209
 
225
- # patch for StringIO behavior in Ruby 2.2.3
226
- # https://github.com/bblimke/webmock/issues/558
227
- class PatchedStringIO < StringIO #:nodoc:
228
-
229
- alias_method :orig_read_nonblock, :read_nonblock
230
-
231
- def read_nonblock(size, *args, **kwargs)
232
- args.reject! {|arg| !arg.is_a?(Hash)}
233
- orig_read_nonblock(size, *args, **kwargs)
234
- end
235
-
236
- end
237
-
238
210
  class StubSocket #:nodoc:
239
211
 
240
212
  attr_accessor :read_timeout, :continue_timeout, :write_timeout
241
213
 
242
214
  def initialize(*args)
215
+ @closed = false
243
216
  end
244
217
 
245
218
  def closed?
246
- @closed ||= true
219
+ @closed
247
220
  end
248
221
 
249
222
  def close
223
+ @closed = true
224
+ nil
250
225
  end
251
226
 
252
227
  def readuntil(*args)
@@ -258,57 +233,13 @@ class StubSocket #:nodoc:
258
233
 
259
234
  class StubIO
260
235
  def setsockopt(*args); end
236
+ def peer_cert; end
237
+ def peeraddr; ["AF_INET", 443, "127.0.0.1", "127.0.0.1"] end
238
+ def ssl_version; "TLSv1.3" end
239
+ def cipher; ["TLS_AES_128_GCM_SHA256", "TLSv1.3", 128, 128] end
261
240
  end
262
241
  end
263
242
 
264
- module Net #:nodoc: all
265
-
266
- class WebMockNetBufferedIO < BufferedIO
267
- def initialize(io, *args, **kwargs)
268
- io = case io
269
- when Socket, OpenSSL::SSL::SSLSocket, IO
270
- io
271
- when StringIO
272
- PatchedStringIO.new(io.string)
273
- when String
274
- PatchedStringIO.new(io)
275
- end
276
- raise "Unable to create local socket" unless io
277
-
278
- # Prior to 2.4.0 `BufferedIO` only takes a single argument (`io`) with no
279
- # options. Here we pass through our full set of arguments only if we're
280
- # on 2.4.0 or later, and use a simplified invocation otherwise.
281
- if RUBY_VERSION >= '2.4.0'
282
- super
283
- else
284
- super(io)
285
- end
286
- end
287
-
288
- if RUBY_VERSION >= '2.6.0'
289
- # https://github.com/ruby/ruby/blob/7d02441f0d6e5c9d0a73a024519eba4f69e36dce/lib/net/protocol.rb#L208
290
- # Modified version of method from ruby, so that nil is always passed into orig_read_nonblock to avoid timeout
291
- def rbuf_fill
292
- case rv = @io.read_nonblock(BUFSIZE, nil, exception: false)
293
- when String
294
- return if rv.nil?
295
- @rbuf << rv
296
- rv.clear
297
- return
298
- when :wait_readable
299
- @io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
300
- when :wait_writable
301
- @io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
302
- when nil
303
- raise EOFError, 'end of file reached'
304
- end while true
305
- end
306
- end
307
- end
308
-
309
- end
310
-
311
-
312
243
  module WebMock
313
244
  module NetHTTPUtility
314
245
 
@@ -325,7 +256,6 @@ module WebMock
325
256
  method = request.method.downcase.to_sym
326
257
 
327
258
  headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
328
- validate_headers(headers)
329
259
 
330
260
  if request.body_stream
331
261
  body = request.body_stream.read
@@ -341,7 +271,7 @@ module WebMock
341
271
  WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers)
342
272
  end
343
273
 
344
- def self.get_uri(net_http, path)
274
+ def self.get_uri(net_http, path = nil)
345
275
  protocol = net_http.use_ssl? ? "https" : "http"
346
276
 
347
277
  hostname = net_http.address
@@ -350,25 +280,6 @@ module WebMock
350
280
  "#{protocol}://#{hostname}:#{net_http.port}#{path}"
351
281
  end
352
282
 
353
- def self.validate_headers(headers)
354
- # For Ruby versions < 2.3.0, if you make a request with headers that are symbols
355
- # Net::HTTP raises a NoMethodError
356
- #
357
- # WebMock normalizes headers when creating a RequestSignature,
358
- # and will update all headers from symbols to strings.
359
- #
360
- # This could create a false positive in a test suite with WebMock.
361
- #
362
- # So before this point, WebMock raises an ArgumentError if any of the headers are symbols
363
- # instead of the cryptic NoMethodError "undefined method `split' ...` from Net::HTTP
364
- if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.3.0')
365
- header_as_symbol = headers.keys.find {|header| header.is_a? Symbol}
366
- if header_as_symbol
367
- raise ArgumentError.new("Net:HTTP does not accept headers as symbols")
368
- end
369
- end
370
- end
371
-
372
283
  def self.check_right_http_connection
373
284
  @was_right_http_connection_loaded = defined?(RightHttpConnection)
374
285
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This code is entierly copied from VCR (http://github.com/myronmarston/vcr) by courtesy of Myron Marston
2
4
 
3
5
  # A Net::HTTP response that has already been read raises an IOError when #read_body
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'patron'
3
5
  rescue LoadError
4
6
  # patron not found
5
7
  end
6
8
 
7
- if defined?(::Patron)
9
+ if defined?(::Patron::Session)
8
10
  module WebMock
9
11
  module HttpLibAdapters
10
12
  class PatronAdapter < ::WebMock::HttpLibAdapter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'typhoeus'
3
5
  rescue LoadError
@@ -94,7 +96,14 @@ if defined?(Typhoeus)
94
96
  status_message: "",
95
97
  body: "",
96
98
  headers: {},
97
- return_code: :operation_timedout
99
+ return_code: :operation_timedout,
100
+ total_time: 0.0,
101
+ starttransfer_time: 0.0,
102
+ appconnect_time: 0.0,
103
+ pretransfer_time: 0.0,
104
+ connect_time: 0.0,
105
+ namelookup_time: 0.0,
106
+ redirect_time: 0.0
98
107
  )
99
108
  else
100
109
  ::Typhoeus::Response.new(
@@ -102,7 +111,14 @@ if defined?(Typhoeus)
102
111
  status_message: webmock_response.status[1],
103
112
  body: webmock_response.body,
104
113
  headers: webmock_response.headers,
105
- effective_url: request_signature.uri
114
+ effective_url: request_signature.uri,
115
+ total_time: 0.0,
116
+ starttransfer_time: 0.0,
117
+ appconnect_time: 0.0,
118
+ pretransfer_time: 0.0,
119
+ connect_time: 0.0,
120
+ namelookup_time: 0.0,
121
+ redirect_time: 0.0
106
122
  )
107
123
  end
108
124
  response.mock = :webmock
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  module Matchers
3
5
  # this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  module Matchers
3
5
  # Base class for Hash matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  module Matchers
3
5
  # this is a based on RSpec::Mocks::ArgumentMatchers::HashExcludingMatcher
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  module Matchers
3
5
  # this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'minitest/test'
3
5
  test_class = Minitest::Test
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class RackResponse < Response
3
5
  def initialize(app)
@@ -45,7 +47,9 @@ module WebMock
45
47
  # Rack-specific variables
46
48
  env['rack.input'] = StringIO.new(body)
47
49
  env['rack.errors'] = $stderr
48
- env['rack.version'] = Rack::VERSION
50
+ if !Rack.const_defined?(:RELEASE) || Rack::RELEASE < "3"
51
+ env['rack.version'] = Rack::VERSION
52
+ end
49
53
  env['rack.url_scheme'] = uri.scheme
50
54
  env['rack.run_once'] = true
51
55
  env['rack.session'] = session
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "hashdiff"
2
4
  require "json"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class RequestExecutionVerifier
3
5