webmock 3.14.0 → 3.24.0

Sign up to get free protection for your applications and to get access to all the features.
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