webmock 1.8.6 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/CI.yml +37 -0
  3. data/.gitignore +6 -0
  4. data/CHANGELOG.md +1198 -0
  5. data/Gemfile +3 -15
  6. data/README.md +761 -305
  7. data/Rakefile +13 -40
  8. data/lib/webmock/api.rb +63 -17
  9. data/lib/webmock/callback_registry.rb +1 -1
  10. data/lib/webmock/config.rb +8 -0
  11. data/lib/webmock/cucumber.rb +2 -0
  12. data/lib/webmock/errors.rb +8 -24
  13. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  14. data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
  15. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
  16. data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
  29. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
  33. data/lib/webmock/minitest.rb +29 -3
  34. data/lib/webmock/rack_response.rb +14 -7
  35. data/lib/webmock/request_body_diff.rb +64 -0
  36. data/lib/webmock/request_execution_verifier.rb +38 -17
  37. data/lib/webmock/request_pattern.rb +158 -38
  38. data/lib/webmock/request_registry.rb +3 -3
  39. data/lib/webmock/request_signature.rb +7 -3
  40. data/lib/webmock/request_signature_snippet.rb +61 -0
  41. data/lib/webmock/request_stub.rb +9 -6
  42. data/lib/webmock/response.rb +30 -15
  43. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
  44. data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
  45. data/lib/webmock/rspec/matchers.rb +0 -1
  46. data/lib/webmock/rspec.rb +11 -2
  47. data/lib/webmock/stub_registry.rb +31 -10
  48. data/lib/webmock/stub_request_snippet.rb +14 -6
  49. data/lib/webmock/test_unit.rb +4 -4
  50. data/lib/webmock/util/hash_counter.rb +20 -6
  51. data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
  52. data/lib/webmock/util/hash_validator.rb +17 -0
  53. data/lib/webmock/util/headers.rb +23 -2
  54. data/lib/webmock/util/json.rb +20 -7
  55. data/lib/webmock/util/query_mapper.rb +281 -0
  56. data/lib/webmock/util/uri.rb +29 -19
  57. data/lib/webmock/util/values_stringifier.rb +20 -0
  58. data/lib/webmock/util/version_checker.rb +40 -2
  59. data/lib/webmock/version.rb +1 -1
  60. data/lib/webmock/webmock.rb +56 -17
  61. data/lib/webmock.rb +56 -46
  62. data/minitest/test_helper.rb +8 -3
  63. data/minitest/test_webmock.rb +4 -1
  64. data/minitest/webmock_spec.rb +16 -6
  65. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  66. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  67. data/spec/acceptance/curb/curb_spec.rb +227 -68
  68. data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
  69. data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
  70. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
  71. data/spec/acceptance/excon/excon_spec.rb +66 -4
  72. data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
  73. data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
  74. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  75. data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
  76. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
  77. data/spec/acceptance/manticore/manticore_spec.rb +107 -0
  78. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  79. data/spec/acceptance/net_http/net_http_shared.rb +52 -24
  80. data/spec/acceptance/net_http/net_http_spec.rb +164 -50
  81. data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
  82. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  83. data/spec/acceptance/patron/patron_spec.rb +29 -40
  84. data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
  85. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
  86. data/spec/acceptance/shared/callbacks.rb +32 -30
  87. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
  88. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
  89. data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
  90. data/spec/acceptance/shared/request_expectations.rb +560 -296
  91. data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
  92. data/spec/acceptance/shared/stubbing_requests.rb +385 -154
  93. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
  94. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
  95. data/spec/acceptance/webmock_shared.rb +2 -2
  96. data/spec/fixtures/test.txt +1 -0
  97. data/spec/quality_spec.rb +27 -3
  98. data/spec/spec_helper.rb +11 -20
  99. data/spec/support/failures.rb +9 -0
  100. data/spec/support/my_rack_app.rb +8 -3
  101. data/spec/support/network_connection.rb +7 -13
  102. data/spec/support/webmock_server.rb +8 -3
  103. data/spec/unit/api_spec.rb +175 -0
  104. data/spec/unit/errors_spec.rb +116 -19
  105. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
  106. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
  107. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  108. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  109. data/spec/unit/rack_response_spec.rb +54 -16
  110. data/spec/unit/request_body_diff_spec.rb +90 -0
  111. data/spec/unit/request_execution_verifier_spec.rb +147 -39
  112. data/spec/unit/request_pattern_spec.rb +462 -198
  113. data/spec/unit/request_registry_spec.rb +29 -9
  114. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  115. data/spec/unit/request_signature_spec.rb +91 -49
  116. data/spec/unit/request_stub_spec.rb +71 -70
  117. data/spec/unit/response_spec.rb +100 -81
  118. data/spec/unit/stub_registry_spec.rb +37 -20
  119. data/spec/unit/stub_request_snippet_spec.rb +51 -31
  120. data/spec/unit/util/hash_counter_spec.rb +6 -6
  121. data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
  122. data/spec/unit/util/headers_spec.rb +4 -4
  123. data/spec/unit/util/json_spec.rb +29 -3
  124. data/spec/unit/util/query_mapper_spec.rb +157 -0
  125. data/spec/unit/util/uri_spec.rb +150 -36
  126. data/spec/unit/util/version_checker_spec.rb +15 -9
  127. data/spec/unit/webmock_spec.rb +57 -4
  128. data/test/http_request.rb +3 -3
  129. data/test/shared_test.rb +45 -13
  130. data/test/test_helper.rb +1 -1
  131. data/test/test_webmock.rb +6 -0
  132. data/webmock.gemspec +30 -11
  133. metadata +308 -199
  134. data/.rvmrc +0 -1
  135. data/.travis.yml +0 -11
  136. data/Guardfile +0 -24
  137. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
  138. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +0 -210
@@ -5,7 +5,7 @@ rescue LoadError
5
5
  end
6
6
 
7
7
  if defined?(Curl)
8
- WebMock::VersionChecker.new('Curb', Gem.loaded_specs['curb'].version.to_s, '0.7.16').check_version!
8
+ WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '0.9.1', ['0.8.7']).check_version!
9
9
 
10
10
  module WebMock
11
11
  module HttpLibAdapters
@@ -55,14 +55,13 @@ if defined?(Curl)
55
55
  module Curl
56
56
  class WebMockCurlEasy < Curl::Easy
57
57
  def curb_or_webmock
58
-
59
58
  request_signature = build_request_signature
60
59
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
61
60
 
62
61
  if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
63
62
  build_curb_response(webmock_response)
64
63
  WebMock::CallbackRegistry.invoke_callbacks(
65
- {:lib => :curb}, request_signature, webmock_response)
64
+ {lib: :curb}, request_signature, webmock_response)
66
65
  invoke_curb_callbacks
67
66
  true
68
67
  elsif WebMock.net_connect_allowed?(request_signature.uri)
@@ -70,7 +69,7 @@ if defined?(Curl)
70
69
  if WebMock::CallbackRegistry.any_callbacks?
71
70
  webmock_response = build_webmock_response
72
71
  WebMock::CallbackRegistry.invoke_callbacks(
73
- {:lib => :curb, :real_request => true}, request_signature,
72
+ {lib: :curb, real_request: true}, request_signature,
74
73
  webmock_response)
75
74
  end
76
75
  res
@@ -84,11 +83,11 @@ if defined?(Curl)
84
83
 
85
84
  uri = WebMock::Util::URI.heuristic_parse(self.url)
86
85
  uri.path = uri.normalized_path.gsub("[^:]//","/")
87
- uri.user = self.username
88
- uri.password = self.password
86
+
87
+ headers = headers_as_hash(self.headers).merge(basic_auth_headers)
89
88
 
90
89
  request_body = case method
91
- when :post
90
+ when :post, :patch
92
91
  self.post_body || @post_body
93
92
  when :put
94
93
  @put_data
@@ -96,15 +95,56 @@ if defined?(Curl)
96
95
  nil
97
96
  end
98
97
 
98
+ if defined?( @on_debug )
99
+ @on_debug.call("Trying 127.0.0.1...\r\n", 0)
100
+ @on_debug.call('Connected to ' + uri.hostname + "\r\n", 0)
101
+ @debug_method = method.upcase
102
+ @debug_path = uri.path
103
+ @debug_host = uri.hostname
104
+ http_request = ["#{@debug_method} #{@debug_path} HTTP/1.1"]
105
+ http_request << "Host: #{uri.hostname}"
106
+ headers.each do |name, value|
107
+ http_request << "#{name}: #{value}"
108
+ end
109
+ @on_debug.call(http_request.join("\r\n") + "\r\n\r\n", 2)
110
+ if request_body
111
+ @on_debug.call(request_body + "\r\n", 4)
112
+ @on_debug.call(
113
+ "upload completely sent off: #{request_body.bytesize}"\
114
+ " out of #{request_body.bytesize} bytes\r\n", 0
115
+ )
116
+ end
117
+ end
118
+
99
119
  request_signature = WebMock::RequestSignature.new(
100
120
  method,
101
121
  uri.to_s,
102
- :body => request_body,
103
- :headers => self.headers
122
+ body: request_body,
123
+ headers: headers
104
124
  )
105
125
  request_signature
106
126
  end
107
127
 
128
+ def headers_as_hash(headers)
129
+ if headers.is_a?(Array)
130
+ headers.inject({}) {|hash, header|
131
+ name, value = header.split(":").map(&:strip)
132
+ hash[name] = value
133
+ hash
134
+ }
135
+ else
136
+ headers
137
+ end
138
+ end
139
+
140
+ def basic_auth_headers
141
+ if self.username
142
+ {'Authorization' => WebMock::Util::Headers.basic_auth_header(self.username, self.password)}
143
+ else
144
+ {}
145
+ end
146
+ end
147
+
108
148
  def build_curb_response(webmock_response)
109
149
  raise Curl::Err::TimeoutError if webmock_response.should_timeout
110
150
  webmock_response.raise_error_if_any
@@ -112,11 +152,17 @@ if defined?(Curl)
112
152
  @body_str = webmock_response.body
113
153
  @response_code = webmock_response.status[0]
114
154
 
115
- @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n"
155
+ @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n".dup
156
+
157
+ @on_debug.call(@header_str, 1) if defined?( @on_debug )
158
+
116
159
  if webmock_response.headers
117
160
  @header_str << webmock_response.headers.map do |k,v|
118
- "#{k}: #{v.is_a?(Array) ? v.join(", ") : v}"
161
+ header = "#{k}: #{v.is_a?(Array) ? v.join(", ") : v}"
162
+ @on_debug.call(header + "\r\n", 1) if defined?( @on_debug )
163
+ header
119
164
  end.join("\r\n")
165
+ @on_debug.call("\r\n", 1) if defined?( @on_debug )
120
166
 
121
167
  location = webmock_response.headers['Location']
122
168
  if self.follow_location? && location
@@ -125,6 +171,7 @@ if defined?(Curl)
125
171
  end
126
172
 
127
173
  @content_type = webmock_response.headers["Content-Type"]
174
+ @transfer_encoding = webmock_response.headers["Transfer-Encoding"]
128
175
  end
129
176
 
130
177
  @last_effective_url ||= self.url
@@ -135,30 +182,54 @@ if defined?(Curl)
135
182
  self.url = location
136
183
 
137
184
  curb_or_webmock do
138
- send( "http_#{@webmock_method}_without_webmock" )
185
+ send( :http, {'method' => @webmock_method} )
139
186
  end
140
187
 
141
188
  self.url = first_url
142
189
  end
143
190
 
144
191
  def invoke_curb_callbacks
145
- @on_progress.call(0.0,1.0,0.0,1.0) if @on_progress
146
- @on_header.call(self.header_str) if @on_header
147
- @on_body.call(self.body_str) if @on_body
148
- @on_complete.call(self) if @on_complete
192
+ @on_progress.call(0.0,1.0,0.0,1.0) if defined?( @on_progress )
193
+ self.header_str.lines.each { |header_line| @on_header.call header_line } if defined?( @on_header )
194
+ if defined?( @on_body )
195
+ if chunked_response?
196
+ self.body_str.each do |chunk|
197
+ @on_body.call(chunk)
198
+ end
199
+ else
200
+ @on_body.call(self.body_str)
201
+ end
202
+ end
203
+ @on_complete.call(self) if defined?( @on_complete )
149
204
 
150
205
  case response_code
151
206
  when 200..299
152
- @on_success.call(self) if @on_success
153
- when 400..599
154
- @on_failure.call(self, self.response_code) if @on_failure
207
+ @on_success.call(self) if defined?( @on_success )
208
+ when 400..499
209
+ @on_missing.call(self, self.response_code) if defined?( @on_missing )
210
+ when 500..599
211
+ @on_failure.call(self, self.response_code) if defined?( @on_failure )
155
212
  end
156
213
  end
157
214
 
215
+ def chunked_response?
216
+ defined?( @transfer_encoding ) && @transfer_encoding == 'chunked' && self.body_str.respond_to?(:each)
217
+ end
218
+
158
219
  def build_webmock_response
159
220
  status, headers =
160
221
  WebMock::HttpLibAdapters::CurbAdapter.parse_header_string(self.header_str)
161
222
 
223
+ if defined?( @on_debug )
224
+ http_response = ["HTTP/1.0 #{@debug_method} #{@debug_path}"]
225
+ headers.each do |name, value|
226
+ http_response << "#{name}: #{value}"
227
+ end
228
+ http_response << self.body_str
229
+ @on_debug.call(http_response.join("\r\n") + "\r\n", 3)
230
+ @on_debug.call("Connection #0 to host #{@debug_host} left intact\r\n", 0)
231
+ end
232
+
162
233
  webmock_response = WebMock::Response.new
163
234
  webmock_response.status = [self.response_code, status]
164
235
  webmock_response.body = self.body_str
@@ -170,117 +241,110 @@ if defined?(Curl)
170
241
  ### Mocks of Curl::Easy methods below here.
171
242
  ###
172
243
 
173
- def http_with_webmock(method)
244
+ def http(method)
174
245
  @webmock_method = method
175
- http_without_webmock(method)
246
+ super
176
247
  end
177
- alias_method :http_without_webmock, :http
178
- alias_method :http, :http_with_webmock
179
248
 
180
249
  %w[ get head delete ].each do |verb|
181
- define_method "http_#{verb}_with_webmock" do
250
+ define_method "http_#{verb}" do
182
251
  @webmock_method = verb
183
- send( "http_#{verb}_without_webmock" )
252
+ super()
184
253
  end
185
-
186
- alias_method "http_#{verb}_without_webmock", "http_#{verb}"
187
- alias_method "http_#{verb}", "http_#{verb}_with_webmock"
188
254
  end
189
255
 
190
- def http_put_with_webmock data = nil
256
+ def http_put data = nil
191
257
  @webmock_method = :put
192
258
  @put_data = data if data
193
- http_put_without_webmock(data)
259
+ super
194
260
  end
195
- alias_method :http_put_without_webmock, :http_put
196
- alias_method :http_put, :http_put_with_webmock
261
+ alias put http_put
197
262
 
198
- def http_post_with_webmock *data
263
+ def http_post *data
199
264
  @webmock_method = :post
200
265
  @post_body = data.join('&') if data && !data.empty?
201
- http_post_without_webmock(*data)
266
+ super
202
267
  end
203
- alias_method :http_post_without_webmock, :http_post
204
- alias_method :http_post, :http_post_with_webmock
268
+ alias post http_post
205
269
 
206
-
207
- def perform_with_webmock
270
+ def perform
208
271
  @webmock_method ||= :get
209
- curb_or_webmock do
210
- perform_without_webmock
211
- end
272
+ curb_or_webmock { super }
273
+ ensure
274
+ reset_webmock_method
212
275
  end
213
- alias :perform_without_webmock :perform
214
- alias :perform :perform_with_webmock
215
276
 
216
- def put_data_with_webmock= data
277
+ def put_data= data
217
278
  @webmock_method = :put
218
279
  @put_data = data
219
- self.put_data_without_webmock = data
280
+ super
220
281
  end
221
- alias_method :put_data_without_webmock=, :put_data=
222
- alias_method :put_data=, :put_data_with_webmock=
223
282
 
224
- def post_body_with_webmock= data
283
+ def post_body= data
225
284
  @webmock_method = :post
226
- self.post_body_without_webmock = data
285
+ super
227
286
  end
228
- alias_method :post_body_without_webmock=, :post_body=
229
- alias_method :post_body=, :post_body_with_webmock=
230
287
 
231
- def delete_with_webmock= value
288
+ def delete= value
232
289
  @webmock_method = :delete if value
233
- self.delete_without_webmock = value
290
+ super
234
291
  end
235
- alias_method :delete_without_webmock=, :delete=
236
- alias_method :delete=, :delete_with_webmock=
237
292
 
238
- def head_with_webmock= value
293
+ def head= value
239
294
  @webmock_method = :head if value
240
- self.head_without_webmock = value
295
+ super
296
+ end
297
+
298
+ def verbose=(verbose)
299
+ @verbose = verbose
300
+ end
301
+
302
+ def verbose?
303
+ @verbose ||= false
241
304
  end
242
- alias_method :head_without_webmock=, :head=
243
- alias_method :head=, :head_with_webmock=
244
305
 
245
- def body_str_with_webmock
246
- @body_str || body_str_without_webmock
306
+ def body_str
307
+ @body_str ||= super
247
308
  end
248
- alias :body_str_without_webmock :body_str
249
- alias :body_str :body_str_with_webmock
309
+ alias body body_str
250
310
 
251
- def response_code_with_webmock
252
- @response_code || response_code_without_webmock
311
+ def response_code
312
+ @response_code ||= super
253
313
  end
254
- alias :response_code_without_webmock :response_code
255
- alias :response_code :response_code_with_webmock
256
314
 
257
- def header_str_with_webmock
258
- @header_str || header_str_without_webmock
315
+ def header_str
316
+ @header_str ||= super
259
317
  end
260
- alias :header_str_without_webmock :header_str
261
- alias :header_str :header_str_with_webmock
318
+ alias head header_str
262
319
 
263
- def last_effective_url_with_webmock
264
- @last_effective_url || last_effective_url_without_webmock
320
+ def last_effective_url
321
+ @last_effective_url ||= super
265
322
  end
266
- alias :last_effective_url_without_webmock :last_effective_url
267
- alias :last_effective_url :last_effective_url_with_webmock
268
323
 
269
- def content_type_with_webmock
270
- @content_type || content_type_without_webmock
324
+ def content_type
325
+ @content_type ||= super
271
326
  end
272
- alias :content_type_without_webmock :content_type
273
- alias :content_type :content_type_with_webmock
274
327
 
275
- %w[ success failure header body complete progress ].each do |callback|
328
+ %w[ success failure missing header body complete progress debug ].each do |callback|
276
329
  class_eval <<-METHOD, __FILE__, __LINE__
277
- def on_#{callback}_with_webmock &block
330
+ def on_#{callback} &block
278
331
  @on_#{callback} = block
279
- on_#{callback}_without_webmock &block
332
+ super
280
333
  end
281
334
  METHOD
282
- alias_method "on_#{callback}_without_webmock", "on_#{callback}"
283
- alias_method "on_#{callback}", "on_#{callback}_with_webmock"
335
+ end
336
+
337
+ def reset_webmock_method
338
+ @webmock_method = :get
339
+ end
340
+
341
+ def reset
342
+ instance_variable_set(:@body_str, nil)
343
+ instance_variable_set(:@content_type, nil)
344
+ instance_variable_set(:@header_str, nil)
345
+ instance_variable_set(:@last_effective_url, nil)
346
+ instance_variable_set(:@response_code, nil)
347
+ super
284
348
  end
285
349
  end
286
350
  end
@@ -4,8 +4,228 @@ rescue LoadError
4
4
  # em-http-request not found
5
5
  end
6
6
 
7
- if defined?(EventMachine::HttpConnection)
8
- require File.expand_path(File.dirname(__FILE__) + '/em_http_request/em_http_request_1_x')
9
- else
10
- require File.expand_path(File.dirname(__FILE__) + '/em_http_request/em_http_request_0_x')
7
+ if defined?(EventMachine::HttpClient)
8
+ module WebMock
9
+ module HttpLibAdapters
10
+ class EmHttpRequestAdapter < HttpLibAdapter
11
+ adapter_for :em_http_request
12
+
13
+ OriginalHttpClient = EventMachine::HttpClient unless const_defined?(:OriginalHttpClient)
14
+ OriginalHttpConnection = EventMachine::HttpConnection unless const_defined?(:OriginalHttpConnection)
15
+
16
+ def self.enable!
17
+ EventMachine.send(:remove_const, :HttpConnection)
18
+ EventMachine.send(:const_set, :HttpConnection, EventMachine::WebMockHttpConnection)
19
+ EventMachine.send(:remove_const, :HttpClient)
20
+ EventMachine.send(:const_set, :HttpClient, EventMachine::WebMockHttpClient)
21
+ end
22
+
23
+ def self.disable!
24
+ EventMachine.send(:remove_const, :HttpConnection)
25
+ EventMachine.send(:const_set, :HttpConnection, OriginalHttpConnection)
26
+ EventMachine.send(:remove_const, :HttpClient)
27
+ EventMachine.send(:const_set, :HttpClient, OriginalHttpClient)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ module EventMachine
34
+ if defined?(Synchrony) && HTTPMethods.instance_methods.include?(:aget)
35
+ # have to make the callbacks fire on the next tick in order
36
+ # to avoid the dreaded "double resume" exception
37
+ module HTTPMethods
38
+ %w[get head post delete put].each do |type|
39
+ class_eval %[
40
+ def #{type}(options = {}, &blk)
41
+ f = Fiber.current
42
+
43
+ conn = setup_request(:#{type}, options, &blk)
44
+ conn.callback { EM.next_tick { f.resume(conn) } }
45
+ conn.errback { EM.next_tick { f.resume(conn) } }
46
+
47
+ Fiber.yield
48
+ end
49
+ ]
50
+ end
51
+ end
52
+ end
53
+
54
+ class WebMockHttpConnection < HttpConnection
55
+ def activate_connection(client)
56
+ request_signature = client.request_signature
57
+
58
+ if client.stubbed_webmock_response
59
+ conn = HttpStubConnection.new rand(10000)
60
+ post_init
61
+
62
+ @deferred = false
63
+ @conn = conn
64
+
65
+ conn.parent = self
66
+ conn.pending_connect_timeout = @connopts.connect_timeout
67
+ conn.comm_inactivity_timeout = @connopts.inactivity_timeout
68
+
69
+ finalize_request(client)
70
+ @conn.set_deferred_status :succeeded
71
+ elsif WebMock.net_connect_allowed?(request_signature.uri)
72
+ super
73
+ else
74
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
75
+ end
76
+ end
77
+
78
+ def drop_client
79
+ @clients.shift
80
+ end
81
+ end
82
+
83
+ class WebMockHttpClient < EventMachine::HttpClient
84
+ include HttpEncoding
85
+
86
+ def uri
87
+ @req.uri
88
+ end
89
+
90
+ def setup(response, uri, error = nil)
91
+ @last_effective_url = @uri = uri
92
+ if error
93
+ on_error(error)
94
+ @conn.drop_client
95
+ fail(self)
96
+ else
97
+ @conn.receive_data(response)
98
+ succeed(self)
99
+ end
100
+ end
101
+
102
+ def connection_completed
103
+ @state = :response_header
104
+ send_request(request_signature.headers, request_signature.body)
105
+ end
106
+
107
+ def send_request(head, body)
108
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
109
+
110
+ if stubbed_webmock_response
111
+ WebMock::CallbackRegistry.invoke_callbacks({lib: :em_http_request}, request_signature, stubbed_webmock_response)
112
+ @uri ||= nil
113
+ EM.next_tick {
114
+ setup(make_raw_response(stubbed_webmock_response), @uri,
115
+ stubbed_webmock_response.should_timeout ? Errno::ETIMEDOUT : nil)
116
+ }
117
+ self
118
+ elsif WebMock.net_connect_allowed?(request_signature.uri)
119
+ super
120
+ else
121
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
122
+ end
123
+ end
124
+
125
+ def unbind(reason = nil)
126
+ if !stubbed_webmock_response && WebMock::CallbackRegistry.any_callbacks?
127
+ webmock_response = build_webmock_response
128
+ WebMock::CallbackRegistry.invoke_callbacks(
129
+ {lib: :em_http_request, real_request: true},
130
+ request_signature,
131
+ webmock_response)
132
+ end
133
+ @request_signature = nil
134
+ remove_instance_variable(:@stubbed_webmock_response)
135
+
136
+ super
137
+ end
138
+
139
+ def request_signature
140
+ @request_signature ||= build_request_signature
141
+ end
142
+
143
+ def stubbed_webmock_response
144
+ unless defined?(@stubbed_webmock_response)
145
+ @stubbed_webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
146
+ end
147
+
148
+ @stubbed_webmock_response
149
+ end
150
+
151
+ def get_response_cookie(name)
152
+ name = name.to_s
153
+
154
+ raw_cookie = response_header.cookie
155
+ raw_cookie = [raw_cookie] if raw_cookie.is_a? String
156
+
157
+ cookie = raw_cookie.select { |c| c.start_with? name }.first
158
+ cookie and cookie.split('=', 2)[1]
159
+ end
160
+
161
+ private
162
+
163
+ def build_webmock_response
164
+ webmock_response = WebMock::Response.new
165
+ webmock_response.status = [response_header.status, response_header.http_reason]
166
+ webmock_response.headers = response_header
167
+ webmock_response.body = response
168
+ webmock_response
169
+ end
170
+
171
+ def build_request_signature
172
+ headers, body = build_request, @req.body
173
+
174
+ @conn.middleware.select {|m| m.respond_to?(:request) }.each do |m|
175
+ headers, body = m.request(self, headers, body)
176
+ end
177
+
178
+ method = @req.method
179
+ uri = @req.uri.clone
180
+ query = @req.query
181
+
182
+ uri.query = encode_query(@req.uri, query).slice(/\?(.*)/, 1)
183
+
184
+ body = form_encode_body(body) if body.is_a?(Hash)
185
+
186
+ if headers['authorization'] && headers['authorization'].is_a?(Array)
187
+ headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(headers.delete('authorization'))
188
+ end
189
+
190
+ WebMock::RequestSignature.new(
191
+ method.downcase.to_sym,
192
+ uri.to_s,
193
+ body: body || (@req.file && File.read(@req.file)),
194
+ headers: headers
195
+ )
196
+ end
197
+
198
+ def make_raw_response(response)
199
+ response.raise_error_if_any
200
+
201
+ status, headers, body = response.status, response.headers, response.body
202
+ headers ||= {}
203
+
204
+ response_string = []
205
+ response_string << "HTTP/1.1 #{status[0]} #{status[1]}"
206
+
207
+ headers["Content-Length"] = body.bytesize unless headers["Content-Length"]
208
+ headers.each do |header, value|
209
+ if header =~ /set-cookie/i
210
+ [value].flatten.each do |cookie|
211
+ response_string << "#{header}: #{cookie}"
212
+ end
213
+ else
214
+ value = value.join(", ") if value.is_a?(Array)
215
+
216
+ # WebMock's internal processing will not handle the body
217
+ # correctly if the header indicates that it is chunked, unless
218
+ # we also create all the chunks.
219
+ # It's far easier just to remove the header.
220
+ next if header =~ /transfer-encoding/i && value =~/chunked/i
221
+
222
+ response_string << "#{header}: #{value}"
223
+ end
224
+ end if headers
225
+
226
+ response_string << "" << body
227
+ response_string.join("\n")
228
+ end
229
+ end
230
+ end
11
231
  end