webmock 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
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,67 @@
1
+ # This is a copy of https://github.com/jnunemaker/crack/blob/master/lib/crack/json.rb
2
+ # with date parsing removed
3
+ # Copyright (c) 2004-2008 David Heinemeier Hansson
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7
+
8
+ module WebMock
9
+ module Util
10
+ class JSON
11
+ class ParseError < StandardError; end
12
+
13
+ def self.parse(json)
14
+ yaml = unescape(convert_json_to_yaml(json))
15
+ YAML.load(yaml)
16
+ rescue ArgumentError => e
17
+ raise ParseError, "Invalid JSON string: #{yaml}, Error: #{e.inspect}"
18
+ end
19
+
20
+ protected
21
+ def self.unescape(str)
22
+ str.gsub(/\\u([0-9a-f]{4})/) { [$1.hex].pack("U") }
23
+ end
24
+
25
+ # Ensure that ":" and "," are always followed by a space
26
+ def self.convert_json_to_yaml(json) #:nodoc:
27
+ scanner, quoting, marks, times = StringScanner.new(json), false, [], []
28
+ while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
29
+ case char = scanner[1]
30
+ when '"', "'"
31
+ if !quoting
32
+ quoting = char
33
+ elsif quoting == char
34
+ quoting = false
35
+ end
36
+ when ":",","
37
+ marks << scanner.pos - 1 unless quoting
38
+ when "\\"
39
+ scanner.skip(/\\/)
40
+ end
41
+ end
42
+
43
+ if marks.empty?
44
+ json.gsub(/\\\//, '/')
45
+ else
46
+ left_pos = [-1].push(*marks)
47
+ right_pos = marks << json.bytesize
48
+ output = []
49
+
50
+ left_pos.each_with_index do |left, i|
51
+ if json.respond_to?(:byteslice)
52
+ output << json.byteslice(left.succ..right_pos[i])
53
+ else
54
+ output << json[left.succ..right_pos[i]]
55
+ end
56
+ end
57
+
58
+ output = output * " "
59
+
60
+ times.each { |i| output[i-1] = ' ' }
61
+ output.gsub!(/\\\//, '/')
62
+ output
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,281 @@
1
+ module WebMock::Util
2
+ class QueryMapper
3
+ class << self
4
+ #This class is based on Addressable::URI pre 2.3.0
5
+
6
+ ##
7
+ # Converts the query component to a Hash value.
8
+ #
9
+ # @option [Symbol] notation
10
+ # May be one of <code>:flat</code>, <code>:dot</code>, or
11
+ # <code>:subscript</code>. The <code>:dot</code> notation is not
12
+ # supported for assignment. Default value is <code>:subscript</code>.
13
+ #
14
+ # @return [Hash, Array] The query string parsed as a Hash or Array object.
15
+ #
16
+ # @example
17
+ # WebMock::Util::QueryMapper.query_to_values("?one=1&two=2&three=3")
18
+ # #=> {"one" => "1", "two" => "2", "three" => "3"}
19
+ # WebMock::Util::QueryMapper("?one[two][three]=four").query_values
20
+ # #=> {"one" => {"two" => {"three" => "four"}}}
21
+ # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
22
+ # :notation => :dot
23
+ # )
24
+ # #=> {"one" => {"two" => {"three" => "four"}}}
25
+ # WebMock::Util::QueryMapper.query_to_values("?one[two][three]=four",
26
+ # :notation => :flat
27
+ # )
28
+ # #=> {"one[two][three]" => "four"}
29
+ # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
30
+ # :notation => :flat
31
+ # )
32
+ # #=> {"one.two.three" => "four"}
33
+ # WebMock::Util::QueryMapper(
34
+ # "?one[two][three][]=four&one[two][three][]=five"
35
+ # )
36
+ # #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
37
+ # WebMock::Util::QueryMapper.query_to_values(
38
+ # "?one=two&one=three").query_values(:notation => :flat_array)
39
+ # #=> [['one', 'two'], ['one', 'three']]
40
+ def query_to_values(query, options={})
41
+ return nil if query.nil?
42
+ query = query.dup.force_encoding('utf-8') if query.respond_to?(:force_encoding)
43
+
44
+ options[:notation] ||= :subscript
45
+
46
+ if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
47
+ raise ArgumentError,
48
+ 'Invalid notation. Must be one of: ' +
49
+ '[:flat, :dot, :subscript, :flat_array].'
50
+ end
51
+
52
+ empty_accumulator = :flat_array == options[:notation] ? [] : {}
53
+
54
+ query_array = collect_query_parts(query)
55
+
56
+ query_hash = collect_query_hash(query_array, empty_accumulator, options)
57
+
58
+ normalize_query_hash(query_hash, empty_accumulator, options)
59
+ end
60
+
61
+ def normalize_query_hash(query_hash, empty_accumulator, options)
62
+ query_hash.inject(empty_accumulator.dup) do |accumulator, (key, value)|
63
+ if options[:notation] == :flat_array
64
+ accumulator << [key, value]
65
+ else
66
+ accumulator[key] = value.kind_of?(Hash) ? dehash(value) : value
67
+ end
68
+ accumulator
69
+ end
70
+ end
71
+
72
+ def collect_query_parts(query)
73
+ query_parts = query.split('&').map do |pair|
74
+ pair.split('=', 2) if pair && !pair.empty?
75
+ end
76
+ query_parts.compact
77
+ end
78
+
79
+ def collect_query_hash(query_array, empty_accumulator, options)
80
+ query_array.compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
81
+ value = if value.nil?
82
+ nil
83
+ else
84
+ ::Addressable::URI.unencode_component(value.tr('+', ' '))
85
+ end
86
+ key = Addressable::URI.unencode_component(key)
87
+ key = key.dup.force_encoding(Encoding::ASCII_8BIT) if key.respond_to?(:force_encoding)
88
+ self.__send__("fill_accumulator_for_#{options[:notation]}", accumulator, key, value)
89
+ accumulator
90
+ end
91
+ end
92
+
93
+ def fill_accumulator_for_flat(accumulator, key, value)
94
+ if accumulator[key]
95
+ raise ArgumentError, "Key was repeated: #{key.inspect}"
96
+ end
97
+ accumulator[key] = value
98
+ end
99
+
100
+ def fill_accumulator_for_flat_array(accumulator, key, value)
101
+ accumulator << [key, value]
102
+ end
103
+
104
+ def fill_accumulator_for_dot(accumulator, key, value)
105
+ array_value = false
106
+ subkeys = key.split(".")
107
+ current_hash = accumulator
108
+ subkeys[0..-2].each do |subkey|
109
+ current_hash[subkey] = {} unless current_hash[subkey]
110
+ current_hash = current_hash[subkey]
111
+ end
112
+ if array_value
113
+ if current_hash[subkeys.last] && !current_hash[subkeys.last].is_a?(Array)
114
+ current_hash[subkeys.last] = [current_hash[subkeys.last]]
115
+ end
116
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
117
+ current_hash[subkeys.last] << value
118
+ else
119
+ current_hash[subkeys.last] = value
120
+ end
121
+ end
122
+
123
+ def fill_accumulator_for_subscript(accumulator, key, value)
124
+ current_node = accumulator
125
+ subkeys = key.split(/(?=\[[^\[\]]+)/)
126
+ subkeys[0..-2].each do |subkey|
127
+ node = subkey =~ /\[\]\z/ ? [] : {}
128
+ subkey = subkey.gsub(/[\[\]]/, '')
129
+ if current_node.is_a? Array
130
+ container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(subkey) }
131
+ if container
132
+ current_node = container[subkey]
133
+ else
134
+ current_node << {subkey => node}
135
+ current_node = node
136
+ end
137
+ else
138
+ current_node[subkey] = node unless current_node[subkey]
139
+ current_node = current_node[subkey]
140
+ end
141
+ end
142
+ last_key = subkeys.last
143
+ array_value = !!(last_key =~ /\[\]$/)
144
+ last_key = last_key.gsub(/[\[\]]/, '')
145
+ if current_node.is_a? Array
146
+ last_container = current_node.select { |n| n.is_a?(Hash) }.last
147
+ if last_container && !last_container.has_key?(last_key)
148
+ if array_value
149
+ last_container[last_key] ||= []
150
+ last_container[last_key] << value
151
+ else
152
+ last_container[last_key] = value
153
+ end
154
+ else
155
+ if array_value
156
+ current_node << {last_key => [value]}
157
+ else
158
+ current_node << {last_key => value}
159
+ end
160
+ end
161
+ else
162
+ if array_value
163
+ current_node[last_key] ||= []
164
+ current_node[last_key] << value unless value.nil?
165
+ else
166
+ current_node[last_key] = value
167
+ end
168
+ end
169
+ end
170
+
171
+ ##
172
+ # Sets the query component for this URI from a Hash object.
173
+ # This method produces a query string using the :subscript notation.
174
+ # An empty Hash will result in a nil query.
175
+ #
176
+ # @param [Hash, #to_hash, Array] new_query_values The new query values.
177
+ def values_to_query(new_query_values, options = {})
178
+ options[:notation] ||= :subscript
179
+ return if new_query_values.nil?
180
+
181
+ unless new_query_values.is_a?(Array)
182
+ unless new_query_values.respond_to?(:to_hash)
183
+ raise TypeError,
184
+ "Can't convert #{new_query_values.class} into Hash."
185
+ end
186
+ new_query_values = new_query_values.to_hash
187
+ new_query_values = new_query_values.inject([]) do |object, (key, value)|
188
+ key = key.to_s if key.is_a?(::Symbol) || key.nil?
189
+ if value.is_a?(Array) && value.empty?
190
+ object << [key.to_s + '[]']
191
+ elsif value.is_a?(Array)
192
+ value.each { |v| object << [key.to_s + '[]', v] }
193
+ elsif value.is_a?(Hash)
194
+ value.each { |k, v| object << ["#{key.to_s}[#{k}]", v]}
195
+ else
196
+ object << [key.to_s, value]
197
+ end
198
+ object
199
+ end
200
+ # Useful default for OAuth and caching.
201
+ # Only to be used for non-Array inputs. Arrays should preserve order.
202
+ begin
203
+ new_query_values.sort! # may raise for non-comparable values
204
+ rescue NoMethodError, ArgumentError
205
+ # ignore
206
+ end
207
+ end
208
+
209
+ buffer = ''.dup
210
+ new_query_values.each do |parent, value|
211
+ encoded_parent = ::Addressable::URI.encode_component(
212
+ parent.dup, ::Addressable::URI::CharacterClasses::UNRESERVED
213
+ )
214
+ buffer << "#{to_query(encoded_parent, value, options)}&"
215
+ end
216
+ buffer.chop
217
+ end
218
+
219
+ def dehash(hash)
220
+ hash.each do |(key, value)|
221
+ if value.is_a?(::Hash)
222
+ hash[key] = self.dehash(value)
223
+ end
224
+ end
225
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
226
+ hash.sort.inject([]) do |accu, (_, value)|
227
+ accu << value; accu
228
+ end
229
+ else
230
+ hash
231
+ end
232
+ end
233
+
234
+ ##
235
+ # Joins and converts parent and value into a properly encoded and
236
+ # ordered URL query.
237
+ #
238
+ # @private
239
+ # @param [String] parent an URI encoded component.
240
+ # @param [Array, Hash, Symbol, #to_str] value
241
+ #
242
+ # @return [String] a properly escaped and ordered URL query.
243
+
244
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
245
+ def to_query(parent, value, options = {})
246
+ options[:notation] ||= :subscript
247
+ case value
248
+ when ::Hash
249
+ value = value.map do |key, val|
250
+ [
251
+ ::Addressable::URI.encode_component(key.to_s.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
252
+ val
253
+ ]
254
+ end
255
+ value.sort!
256
+ buffer = ''.dup
257
+ value.each do |key, val|
258
+ new_parent = options[:notation] != :flat_array ? "#{parent}[#{key}]" : parent
259
+ buffer << "#{to_query(new_parent, val, options)}&"
260
+ end
261
+ buffer.chop
262
+ when ::Array
263
+ buffer = ''.dup
264
+ value.each_with_index do |val, i|
265
+ new_parent = options[:notation] != :flat_array ? "#{parent}[#{i}]" : parent
266
+ buffer << "#{to_query(new_parent, val, options)}&"
267
+ end
268
+ buffer.chop
269
+ when NilClass
270
+ parent
271
+ else
272
+ encoded_value = Addressable::URI.encode_component(
273
+ value.to_s.dup, Addressable::URI::CharacterClasses::UNRESERVED
274
+ )
275
+ "#{parent}=#{encoded_value}"
276
+ end
277
+ end
278
+ end
279
+
280
+ end
281
+ end
@@ -0,0 +1,110 @@
1
+ module WebMock
2
+
3
+ module Util
4
+
5
+ class URI
6
+ module CharacterClasses
7
+ USERINFO = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS + "\\:"
8
+ end
9
+
10
+ ADDRESSABLE_URIS = Hash.new do |hash, key|
11
+ hash[key] = Addressable::URI.heuristic_parse(key)
12
+ end
13
+
14
+ NORMALIZED_URIS = Hash.new do |hash, uri|
15
+ normalized_uri = WebMock::Util::URI.heuristic_parse(uri)
16
+ if normalized_uri.query_values
17
+ sorted_query_values = sort_query_values(WebMock::Util::QueryMapper.query_to_values(normalized_uri.query, notation: Config.instance.query_values_notation) || {})
18
+ normalized_uri.query = WebMock::Util::QueryMapper.values_to_query(sorted_query_values, notation: WebMock::Config.instance.query_values_notation)
19
+ end
20
+ normalized_uri = normalized_uri.normalize #normalize! is slower
21
+ normalized_uri.query = normalized_uri.query.gsub("+", "%2B") if normalized_uri.query
22
+ normalized_uri.port = normalized_uri.inferred_port unless normalized_uri.port
23
+ hash[uri] = normalized_uri
24
+ end
25
+
26
+ def self.heuristic_parse(uri)
27
+ ADDRESSABLE_URIS[uri].dup
28
+ end
29
+
30
+ def self.normalize_uri(uri)
31
+ return uri if uri.is_a?(Regexp)
32
+ uri = 'http://' + uri unless uri.match('^https?://') if uri.is_a?(String)
33
+ NORMALIZED_URIS[uri].dup
34
+ end
35
+
36
+ def self.variations_of_uri_as_strings(uri_object, only_with_scheme: false)
37
+ normalized_uri = normalize_uri(uri_object.dup).freeze
38
+ uris = [ normalized_uri ]
39
+
40
+ if normalized_uri.path == '/'
41
+ uris = uris_with_trailing_slash_and_without(uris)
42
+ end
43
+
44
+ if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme]
45
+ uris = uris_with_inferred_port_and_without(uris)
46
+ end
47
+
48
+ uris = uris_encoded_and_unencoded(uris)
49
+
50
+ if normalized_uri.scheme == "http" && !only_with_scheme
51
+ uris = uris_with_scheme_and_without(uris)
52
+ end
53
+
54
+ uris.map {|uri| uri.to_s.gsub(/^\/\//,'') }.uniq
55
+ end
56
+
57
+ def self.strip_default_port_from_uri_string(uri_string)
58
+ case uri_string
59
+ when %r{^http://} then uri_string.sub(%r{:80(/|$)}, '\1')
60
+ when %r{^https://} then uri_string.sub(%r{:443(/|$)}, '\1')
61
+ else uri_string
62
+ end
63
+ end
64
+
65
+ def self.encode_unsafe_chars_in_userinfo(userinfo)
66
+ Addressable::URI.encode_component(userinfo, WebMock::Util::URI::CharacterClasses::USERINFO)
67
+ end
68
+
69
+ def self.is_uri_localhost?(uri)
70
+ uri.is_a?(Addressable::URI) &&
71
+ %w(localhost 127.0.0.1 0.0.0.0).include?(uri.host)
72
+ end
73
+
74
+ private
75
+
76
+ def self.sort_query_values(query_values)
77
+ sorted_query_values = query_values.sort
78
+ query_values.is_a?(Hash) ? Hash[*sorted_query_values.inject([]) { |values, pair| values + pair}] : sorted_query_values
79
+ end
80
+
81
+ def self.uris_with_inferred_port_and_without(uris)
82
+ uris.map { |uri|
83
+ [ uri, uri.omit(:port)]
84
+ }.flatten
85
+ end
86
+
87
+ def self.uris_encoded_and_unencoded(uris)
88
+ uris.map do |uri|
89
+ [ uri.to_s, Addressable::URI.unencode(uri, String).freeze ]
90
+ end.flatten
91
+ end
92
+
93
+ def self.uris_with_scheme_and_without(uris)
94
+ uris.map { |uri|
95
+ uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
96
+ [ uri, uri.gsub(%r{^https?://},"").freeze ]
97
+ }.flatten
98
+ end
99
+
100
+ def self.uris_with_trailing_slash_and_without(uris)
101
+ uris = uris.map { |uri|
102
+ uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
103
+ [ uri, uri.omit(:path).freeze ]
104
+ }.flatten
105
+ end
106
+
107
+ end
108
+ end
109
+
110
+ end