webmock 3.0.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/CI.yml +37 -0
  3. data/CHANGELOG.md +416 -0
  4. data/Gemfile +1 -1
  5. data/README.md +157 -31
  6. data/Rakefile +12 -4
  7. data/lib/webmock/api.rb +12 -0
  8. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  9. data/lib/webmock/http_lib_adapters/curb_adapter.rb +17 -3
  10. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
  11. data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
  12. data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
  13. data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
  14. data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
  15. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +6 -2
  16. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +2 -2
  17. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
  18. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
  19. data/lib/webmock/http_lib_adapters/net_http.rb +54 -14
  20. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  21. data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
  22. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  23. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  24. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  25. data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
  26. data/lib/webmock/rack_response.rb +1 -1
  27. data/lib/webmock/request_body_diff.rb +1 -1
  28. data/lib/webmock/request_execution_verifier.rb +2 -3
  29. data/lib/webmock/request_pattern.rb +108 -46
  30. data/lib/webmock/request_registry.rb +1 -1
  31. data/lib/webmock/request_signature.rb +1 -1
  32. data/lib/webmock/request_signature_snippet.rb +4 -4
  33. data/lib/webmock/response.rb +11 -5
  34. data/lib/webmock/rspec.rb +10 -3
  35. data/lib/webmock/stub_registry.rb +26 -11
  36. data/lib/webmock/stub_request_snippet.rb +10 -6
  37. data/lib/webmock/test_unit.rb +1 -3
  38. data/lib/webmock/util/hash_counter.rb +4 -4
  39. data/lib/webmock/util/headers.rb +17 -2
  40. data/lib/webmock/util/json.rb +1 -2
  41. data/lib/webmock/util/query_mapper.rb +9 -7
  42. data/lib/webmock/util/uri.rb +10 -10
  43. data/lib/webmock/util/values_stringifier.rb +20 -0
  44. data/lib/webmock/version.rb +1 -1
  45. data/lib/webmock/webmock.rb +10 -3
  46. data/lib/webmock.rb +53 -48
  47. data/minitest/webmock_spec.rb +2 -2
  48. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  49. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  50. data/spec/acceptance/curb/curb_spec.rb +33 -0
  51. data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
  52. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  53. data/spec/acceptance/excon/excon_spec.rb +4 -2
  54. data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
  55. data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
  56. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
  57. data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
  58. data/spec/acceptance/manticore/manticore_spec.rb +51 -0
  59. data/spec/acceptance/net_http/net_http_shared.rb +1 -1
  60. data/spec/acceptance/net_http/net_http_spec.rb +53 -1
  61. data/spec/acceptance/patron/patron_spec.rb +7 -0
  62. data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
  63. data/spec/acceptance/shared/callbacks.rb +3 -2
  64. data/spec/acceptance/shared/request_expectations.rb +14 -0
  65. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  66. data/spec/acceptance/shared/stubbing_requests.rb +95 -0
  67. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
  68. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
  69. data/spec/support/webmock_server.rb +1 -0
  70. data/spec/unit/api_spec.rb +103 -3
  71. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  72. data/spec/unit/request_execution_verifier_spec.rb +12 -12
  73. data/spec/unit/request_pattern_spec.rb +195 -49
  74. data/spec/unit/request_signature_snippet_spec.rb +2 -2
  75. data/spec/unit/response_spec.rb +22 -18
  76. data/spec/unit/stub_request_snippet_spec.rb +30 -10
  77. data/spec/unit/util/query_mapper_spec.rb +13 -0
  78. data/spec/unit/util/uri_spec.rb +74 -2
  79. data/spec/unit/webmock_spec.rb +54 -5
  80. data/test/shared_test.rb +15 -2
  81. data/test/test_webmock.rb +6 -0
  82. data/webmock.gemspec +11 -3
  83. metadata +66 -17
  84. data/.travis.yml +0 -20
@@ -33,7 +33,7 @@ module WebMock
33
33
  NORMALIZED_URIS[uri].dup
34
34
  end
35
35
 
36
- def self.variations_of_uri_as_strings(uri_object)
36
+ def self.variations_of_uri_as_strings(uri_object, only_with_scheme: false)
37
37
  normalized_uri = normalize_uri(uri_object.dup).freeze
38
38
  uris = [ normalized_uri ]
39
39
 
@@ -41,13 +41,13 @@ module WebMock
41
41
  uris = uris_with_trailing_slash_and_without(uris)
42
42
  end
43
43
 
44
- uris = uris_encoded_and_unencoded(uris)
45
-
46
44
  if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme]
47
45
  uris = uris_with_inferred_port_and_without(uris)
48
46
  end
49
47
 
50
- if normalized_uri.scheme == "http"
48
+ uris = uris_encoded_and_unencoded(uris)
49
+
50
+ if normalized_uri.scheme == "http" && !only_with_scheme
51
51
  uris = uris_with_scheme_and_without(uris)
52
52
  end
53
53
 
@@ -80,27 +80,27 @@ module WebMock
80
80
 
81
81
  def self.uris_with_inferred_port_and_without(uris)
82
82
  uris.map { |uri|
83
- uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
84
- [ uri, uri.gsub(%r{(:80)|(:443)}, "").freeze ]
83
+ [ uri, uri.omit(:port)]
85
84
  }.flatten
86
85
  end
87
86
 
88
87
  def self.uris_encoded_and_unencoded(uris)
89
88
  uris.map do |uri|
90
- [ uri.to_s, Addressable::URI.unencode(uri, String).freeze ]
89
+ [
90
+ uri.to_s.force_encoding(Encoding::ASCII_8BIT),
91
+ Addressable::URI.unencode(uri, String).force_encoding(Encoding::ASCII_8BIT).freeze
92
+ ]
91
93
  end.flatten
92
94
  end
93
95
 
94
96
  def self.uris_with_scheme_and_without(uris)
95
97
  uris.map { |uri|
96
- uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
97
98
  [ uri, uri.gsub(%r{^https?://},"").freeze ]
98
99
  }.flatten
99
100
  end
100
101
 
101
102
  def self.uris_with_trailing_slash_and_without(uris)
102
- uris = uris.map { |uri|
103
- uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
103
+ uris.map { |uri|
104
104
  [ uri, uri.omit(:path).freeze ]
105
105
  }.flatten
106
106
  end
@@ -0,0 +1,20 @@
1
+ class WebMock::Util::ValuesStringifier
2
+ def self.stringify_values(value)
3
+ case value
4
+ when String, Numeric, TrueClass, FalseClass
5
+ value.to_s
6
+ when Hash
7
+ Hash[
8
+ value.map do |k, v|
9
+ [k, stringify_values(v)]
10
+ end
11
+ ]
12
+ when Array
13
+ value.map do |v|
14
+ stringify_values(v)
15
+ end
16
+ else
17
+ value
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '3.0.0' unless defined?(::WebMock::VERSION)
2
+ VERSION = '3.14.0' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -53,12 +53,19 @@ module WebMock
53
53
  Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start]
54
54
  end
55
55
 
56
+ class << self
57
+ alias :enable_net_connect! :allow_net_connect!
58
+ alias :disallow_net_connect! :disable_net_connect!
59
+ end
60
+
56
61
  def self.net_connect_allowed?(uri = nil)
62
+ return !!Config.instance.allow_net_connect if uri.nil?
63
+
57
64
  if uri.is_a?(String)
58
65
  uri = WebMock::Util::URI.normalize_uri(uri)
59
66
  end
60
67
 
61
- Config.instance.allow_net_connect ||
68
+ !!Config.instance.allow_net_connect ||
62
69
  ( Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri) ||
63
70
  Config.instance.allow && net_connect_explicit_allowed?(Config.instance.allow, uri) )
64
71
  end
@@ -133,8 +140,8 @@ module WebMock
133
140
  puts WebMock::RequestExecutionVerifier.executed_requests_message
134
141
  end
135
142
 
136
- def self.globally_stub_request(&block)
137
- WebMock::StubRegistry.instance.register_global_stub(&block)
143
+ def self.globally_stub_request(order = :before_local_stubs, &block)
144
+ WebMock::StubRegistry.instance.register_global_stub(order, &block)
138
145
  end
139
146
 
140
147
  %w(
data/lib/webmock.rb CHANGED
@@ -4,51 +4,56 @@ require 'addressable/uri'
4
4
  require 'addressable/template'
5
5
  require 'crack/xml'
6
6
 
7
- require 'webmock/deprecation'
8
- require 'webmock/version'
9
-
10
- require 'webmock/errors'
11
-
12
- require 'webmock/util/query_mapper'
13
- require 'webmock/util/uri'
14
- require 'webmock/util/headers'
15
- require 'webmock/util/hash_counter'
16
- require 'webmock/util/hash_keys_stringifier'
17
- require 'webmock/util/json'
18
- require 'webmock/util/version_checker'
19
- require 'webmock/util/hash_validator'
20
-
21
- require 'webmock/matchers/hash_including_matcher'
22
-
23
- require 'webmock/request_pattern'
24
- require 'webmock/request_signature'
25
- require 'webmock/responses_sequence'
26
- require 'webmock/request_stub'
27
- require 'webmock/response'
28
- require 'webmock/rack_response'
29
-
30
- require 'webmock/stub_request_snippet'
31
- require 'webmock/request_signature_snippet'
32
- require 'webmock/request_body_diff'
33
-
34
- require 'webmock/assertion_failure'
35
- require 'webmock/request_execution_verifier'
36
- require 'webmock/config'
37
- require 'webmock/callback_registry'
38
- require 'webmock/request_registry'
39
- require 'webmock/stub_registry'
40
- require 'webmock/api'
41
-
42
- require 'webmock/http_lib_adapters/http_lib_adapter_registry'
43
- require 'webmock/http_lib_adapters/http_lib_adapter'
44
- require 'webmock/http_lib_adapters/net_http'
45
- require 'webmock/http_lib_adapters/http_rb_adapter'
46
- require 'webmock/http_lib_adapters/httpclient_adapter'
47
- require 'webmock/http_lib_adapters/patron_adapter'
48
- require 'webmock/http_lib_adapters/curb_adapter'
49
- require 'webmock/http_lib_adapters/em_http_request_adapter'
50
- require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
51
- require 'webmock/http_lib_adapters/excon_adapter'
52
- require 'webmock/http_lib_adapters/manticore_adapter'
53
-
54
- require 'webmock/webmock'
7
+ require_relative 'webmock/deprecation'
8
+ require_relative 'webmock/version'
9
+
10
+ require_relative 'webmock/errors'
11
+
12
+ require_relative 'webmock/util/query_mapper'
13
+ require_relative 'webmock/util/uri'
14
+ require_relative 'webmock/util/headers'
15
+ require_relative 'webmock/util/hash_counter'
16
+ require_relative 'webmock/util/hash_keys_stringifier'
17
+ require_relative 'webmock/util/values_stringifier'
18
+ require_relative 'webmock/util/json'
19
+ require_relative 'webmock/util/version_checker'
20
+ require_relative 'webmock/util/hash_validator'
21
+
22
+ require_relative 'webmock/matchers/hash_argument_matcher'
23
+ require_relative 'webmock/matchers/hash_excluding_matcher'
24
+ require_relative 'webmock/matchers/hash_including_matcher'
25
+ require_relative 'webmock/matchers/any_arg_matcher'
26
+
27
+ require_relative 'webmock/request_pattern'
28
+ require_relative 'webmock/request_signature'
29
+ require_relative 'webmock/responses_sequence'
30
+ require_relative 'webmock/request_stub'
31
+ require_relative 'webmock/response'
32
+ require_relative 'webmock/rack_response'
33
+
34
+ require_relative 'webmock/stub_request_snippet'
35
+ require_relative 'webmock/request_signature_snippet'
36
+ require_relative 'webmock/request_body_diff'
37
+
38
+ require_relative 'webmock/assertion_failure'
39
+ require_relative 'webmock/request_execution_verifier'
40
+ require_relative 'webmock/config'
41
+ require_relative 'webmock/callback_registry'
42
+ require_relative 'webmock/request_registry'
43
+ require_relative 'webmock/stub_registry'
44
+ require_relative 'webmock/api'
45
+
46
+ require_relative 'webmock/http_lib_adapters/http_lib_adapter_registry'
47
+ require_relative 'webmock/http_lib_adapters/http_lib_adapter'
48
+ require_relative 'webmock/http_lib_adapters/net_http'
49
+ require_relative 'webmock/http_lib_adapters/http_rb_adapter'
50
+ require_relative 'webmock/http_lib_adapters/httpclient_adapter'
51
+ require_relative 'webmock/http_lib_adapters/patron_adapter'
52
+ require_relative 'webmock/http_lib_adapters/curb_adapter'
53
+ require_relative 'webmock/http_lib_adapters/em_http_request_adapter'
54
+ require_relative 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
55
+ require_relative 'webmock/http_lib_adapters/excon_adapter'
56
+ require_relative 'webmock/http_lib_adapters/manticore_adapter'
57
+ require_relative 'webmock/http_lib_adapters/async_http_client_adapter'
58
+
59
+ require_relative 'webmock/webmock'
@@ -43,7 +43,7 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
43
43
 
44
44
  it "should verify that expect request didn't occur" do
45
45
  expected_message = "The request GET http://www.example.com/ was expected to execute 1 time but it executed 0 times"
46
- expected_message << "\n\nThe following requests were made:\n\nNo requests were made.\n============================================================"
46
+ expected_message += "\n\nThe following requests were made:\n\nNo requests were made.\n============================================================"
47
47
  assert_fail(expected_message) do
48
48
  assert_requested(:get, "http://www.example.com")
49
49
  end
@@ -51,7 +51,7 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
51
51
 
52
52
  it "should verify that expect stub didn't occur" do
53
53
  expected_message = "The request ANY http://www.example.com/ was expected to execute 1 time but it executed 0 times"
54
- expected_message << "\n\nThe following requests were made:\n\nNo requests were made.\n============================================================"
54
+ expected_message += "\n\nThe following requests were made:\n\nNo requests were made.\n============================================================"
55
55
  assert_fail(expected_message) do
56
56
  assert_requested(@stub_http)
57
57
  end
@@ -0,0 +1,375 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'acceptance/webmock_shared'
4
+ require_relative './async_http_client_spec_helper'
5
+
6
+ require 'protocol/http/body/file'
7
+
8
+ Async.logger.debug! if ENV['ASYNC_LOGGER_DEBUG']
9
+
10
+ unless RUBY_PLATFORM =~ /java/
11
+ describe 'Async::HTTP::Client' do
12
+ include AsyncHttpClientSpecHelper
13
+
14
+ include_context "with WebMock",
15
+ :no_status_message,
16
+ :no_url_auth,
17
+ :no_content_length_header
18
+
19
+ it 'works' do
20
+ stub_request(:get, 'http://www.example.com')
21
+ expect(make_request(:get, 'http://www.example.com')).to eq(
22
+ status: 200,
23
+ headers: {},
24
+ body: nil
25
+ )
26
+ end
27
+
28
+ it 'works with request path' do
29
+ stub_request(:get, 'http://www.example.com/foo')
30
+ expect(make_request(:get, 'http://www.example.com/foo')).to eq(
31
+ status: 200,
32
+ headers: {},
33
+ body: nil
34
+ )
35
+ end
36
+
37
+ it 'works with request query' do
38
+ stub_request(:get, 'http://www.example.com/').with(
39
+ query: {
40
+ 'foo' => 'bar'
41
+ }
42
+ )
43
+ expect(make_request(:get, 'http://www.example.com/?foo=bar')).to eq(
44
+ status: 200,
45
+ headers: {},
46
+ body: nil
47
+ )
48
+ end
49
+
50
+ it 'works with request headers' do
51
+ stub_request(:get, 'http://www.example.com').with(
52
+ headers: {
53
+ 'X-Token' => 'Token'
54
+ }
55
+ )
56
+ expect(
57
+ make_request :get, 'http://www.example.com',
58
+ headers: {
59
+ 'X-Token' => 'Token'
60
+ }
61
+ ).to eq(
62
+ status: 200,
63
+ headers: {},
64
+ body: nil
65
+ )
66
+ end
67
+
68
+ it 'works with request body as text' do
69
+ stub_request(:post, 'http://www.example.com').with(
70
+ body: 'x'*10_000
71
+ )
72
+ expect(
73
+ make_request :post, 'http://www.example.com',
74
+ body: 'x'*10_000
75
+ ).to eq(
76
+ status: 200,
77
+ headers: {},
78
+ body: nil
79
+ )
80
+ end
81
+
82
+ it 'works with request body as file' do
83
+ stub_request(:post, "www.example.com").with(
84
+ body: File.read(__FILE__)
85
+ )
86
+ expect(
87
+ make_request :post, "http://www.example.com",
88
+ body: ::Protocol::HTTP::Body::File.open(__FILE__, block_size: 32)
89
+ ).to eq(
90
+ status: 200,
91
+ headers: {},
92
+ body: nil
93
+ )
94
+ end
95
+
96
+ it 'works with response status' do
97
+ stub_request(:get, 'http://www.example.com').to_return(
98
+ status: 400
99
+ )
100
+ expect(make_request(:get, 'http://www.example.com')).to eq(
101
+ status: 400,
102
+ headers: {},
103
+ body: nil
104
+ )
105
+ end
106
+
107
+ it 'works with response headers' do
108
+ stub_request(:get, 'http://www.example.com').to_return(
109
+ headers: {
110
+ 'X-Token' => 'TOKEN'
111
+ }
112
+ )
113
+ expect(make_request(:get, 'http://www.example.com')).to eq(
114
+ status: 200,
115
+ headers: {
116
+ 'x-token' => ['TOKEN']
117
+ },
118
+ body: nil
119
+ )
120
+ end
121
+
122
+ it 'works with response body' do
123
+ stub_request(:get, 'http://www.example.com').to_return(
124
+ body: 'abc'
125
+ )
126
+ expect(make_request(:get, 'http://www.example.com')).to eq(
127
+ status: 200,
128
+ headers: {},
129
+ body: 'abc'
130
+ )
131
+ end
132
+
133
+ it 'works with to_timeout' do
134
+ stub_request(:get, 'http://www.example.com').to_timeout
135
+ expect { make_request(:get, 'http://www.example.com') }.to raise_error Async::TimeoutError
136
+ end
137
+
138
+ it 'does not invoke "after real request" callbacks for stubbed requests' do
139
+ WebMock.allow_net_connect!
140
+ stub_request(:get, 'http://www.example.com').to_return(body: 'abc')
141
+
142
+ callback_invoked = false
143
+ WebMock.after_request(real_requests_only: true) { |_| callback_invoked = true }
144
+
145
+ make_request(:get, 'http://www.example.com')
146
+ expect(callback_invoked).to eq(false)
147
+ end
148
+
149
+ it 'does invoke "after request" callbacks for stubbed requests' do
150
+ WebMock.allow_net_connect!
151
+ stub_request(:get, 'http://www.example.com').to_return(body: 'abc')
152
+
153
+ callback_invoked = false
154
+ WebMock.after_request(real_requests_only: false) { |_| callback_invoked = true }
155
+
156
+ make_request(:get, 'http://www.example.com')
157
+ expect(callback_invoked).to eq(true)
158
+ end
159
+
160
+ context 'scheme and protocol' do
161
+ let(:default_response_headers) { {} }
162
+
163
+ before do
164
+ stub_request(
165
+ :get, "#{scheme}://www.example.com"
166
+ ).and_return(
167
+ body: 'BODY'
168
+ )
169
+ end
170
+
171
+ subject do
172
+ make_request(:get, "#{scheme}://www.example.com", protocol: protocol)
173
+ end
174
+
175
+ shared_examples :common do
176
+ specify do
177
+ expect(subject).to eq(
178
+ status: 200,
179
+ headers: default_response_headers,
180
+ body: 'BODY'
181
+ )
182
+ end
183
+ end
184
+
185
+ context 'http scheme' do
186
+ let(:scheme) { 'http' }
187
+
188
+ context 'default protocol' do
189
+ let(:protocol) { nil }
190
+
191
+ include_examples :common
192
+ end
193
+
194
+ context 'HTTP10 protocol' do
195
+ let(:protocol) { Async::HTTP::Protocol::HTTP10 }
196
+ let(:default_response_headers) { {"connection"=>["keep-alive"]} }
197
+
198
+ include_examples :common
199
+ end
200
+
201
+ context 'HTTP11 protocol' do
202
+ let(:protocol) { Async::HTTP::Protocol::HTTP11 }
203
+
204
+ include_examples :common
205
+ end
206
+
207
+ context 'HTTP2 protocol' do
208
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
209
+
210
+ include_examples :common
211
+ end
212
+ end
213
+
214
+ context 'https scheme' do
215
+ let(:scheme) { 'https' }
216
+
217
+ context 'default protocol' do
218
+ let(:protocol) { nil }
219
+
220
+ include_examples :common
221
+ end
222
+
223
+ context 'HTTP10 protocol' do
224
+ let(:protocol) { Async::HTTP::Protocol::HTTP10 }
225
+ let(:default_response_headers) { {"connection"=>["keep-alive"]} }
226
+
227
+ include_examples :common
228
+ end
229
+
230
+ context 'HTTP11 protocol' do
231
+ let(:protocol) { Async::HTTP::Protocol::HTTP11 }
232
+
233
+ include_examples :common
234
+ end
235
+
236
+ context 'HTTP2 protocol' do
237
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
238
+
239
+ include_examples :common
240
+ end
241
+
242
+ context 'HTTPS protocol' do
243
+ let(:protocol) { Async::HTTP::Protocol::HTTPS }
244
+
245
+ include_examples :common
246
+ end
247
+ end
248
+ end
249
+
250
+ context 'multiple requests' do
251
+ let(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
252
+ let(:requests_count) { 3 }
253
+
254
+ shared_examples :common do
255
+ before do
256
+ requests_count.times do |index|
257
+ stub_request(
258
+ :get, "http://www.example.com/foo#{index}"
259
+ ).to_return(
260
+ status: 200 + index,
261
+ headers: {'X-Token' => "foo#{index}"},
262
+ body: "FOO#{index}"
263
+ )
264
+ end
265
+ end
266
+
267
+ specify do
268
+ expect(subject).to eq(
269
+ 0 => {
270
+ status: 200,
271
+ headers: {'x-token' => ['foo0']},
272
+ body: 'FOO0'
273
+ },
274
+ 1 => {
275
+ status: 201,
276
+ headers: {'x-token' => ['foo1']},
277
+ body: 'FOO1'
278
+ },
279
+ 2 => {
280
+ status: 202,
281
+ headers: {'x-token' => ['foo2']},
282
+ body: 'FOO2'
283
+ }
284
+ )
285
+ end
286
+ end
287
+
288
+ context 'sequential' do
289
+ subject do
290
+ responses = {}
291
+ Async do |task|
292
+ Async::HTTP::Client.open(endpoint, protocol) do |client|
293
+ requests_count.times do |index|
294
+ response = client.get "/foo#{index}"
295
+ responses[index] = response_to_hash(response)
296
+ end
297
+ end
298
+ end
299
+ responses
300
+ end
301
+
302
+ context 'HTTP1 protocol' do
303
+ let(:protocol) { Async::HTTP::Protocol::HTTP1 }
304
+
305
+ include_examples :common
306
+ end
307
+
308
+ context 'HTTP2 protocol' do
309
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
310
+
311
+ include_examples :common
312
+ end
313
+ end
314
+
315
+ context 'asynchronous' do
316
+ subject do
317
+ responses = {}
318
+ Async do |task|
319
+ Async::HTTP::Client.open(endpoint, protocol) do |client|
320
+ tasks = requests_count.times.map do |index|
321
+ task.async do
322
+ response = client.get "/foo#{index}"
323
+ responses[index] = response_to_hash(response)
324
+ end
325
+ end
326
+
327
+ tasks.map(&:wait)
328
+ end
329
+ end
330
+ responses
331
+ end
332
+
333
+ context 'HTTP1 protocol' do
334
+ let(:protocol) { Async::HTTP::Protocol::HTTP1 }
335
+
336
+ include_examples :common
337
+ end
338
+
339
+ context 'HTTP2 protocol' do
340
+ let(:protocol) { Async::HTTP::Protocol::HTTP2 }
341
+
342
+ include_examples :common
343
+ end
344
+ end
345
+ end
346
+
347
+ def make_request(method, url, protocol: nil, headers: {}, body: nil)
348
+ Async do
349
+ endpoint = Async::HTTP::Endpoint.parse(url)
350
+
351
+ begin
352
+ Async::HTTP::Client.open(endpoint, protocol || endpoint.protocol) do |client|
353
+ response = client.send(
354
+ method,
355
+ endpoint.path,
356
+ headers,
357
+ body
358
+ )
359
+ response_to_hash(response)
360
+ end
361
+ rescue Async::TimeoutError => e
362
+ e
363
+ end
364
+ end.wait
365
+ end
366
+
367
+ def response_to_hash(response)
368
+ {
369
+ status: response.status,
370
+ headers: response.headers.to_h,
371
+ body: response.read
372
+ }
373
+ end
374
+ end
375
+ end
@@ -0,0 +1,73 @@
1
+ module AsyncHttpClientSpecHelper
2
+ def http_request(method, url, options = {}, &block)
3
+ endpoint = Async::HTTP::Endpoint.parse(url)
4
+
5
+ path = endpoint.path
6
+ path = path + "?" + options[:query] if options[:query]
7
+
8
+ headers = (options[:headers] || {}).each_with_object([]) do |(k, v), o|
9
+ Array(v).each do |v|
10
+ o.push [k, v]
11
+ end
12
+ end
13
+ headers.push(
14
+ ['authorization', 'Basic ' + Base64.strict_encode64(options[:basic_auth].join(':'))]
15
+ ) if options[:basic_auth]
16
+
17
+ body = options[:body]
18
+
19
+ Async do
20
+ begin
21
+ Async::HTTP::Client.open(endpoint) do |client|
22
+ response = client.send(
23
+ method,
24
+ path,
25
+ headers,
26
+ body
27
+ )
28
+
29
+ OpenStruct.new(
30
+ build_hash_response(response)
31
+ )
32
+ end
33
+ rescue Exception => e
34
+ e
35
+ end
36
+ end.wait
37
+ end
38
+
39
+ def client_timeout_exception_class
40
+ Async::TimeoutError
41
+ end
42
+
43
+ def connection_refused_exception_class
44
+ Errno::ECONNREFUSED
45
+ end
46
+
47
+ def http_library
48
+ :async_http_client
49
+ end
50
+
51
+ private
52
+
53
+ def build_hash_response(response)
54
+ {
55
+
56
+ status: response.status.to_s,
57
+ message: Protocol::HTTP1::Reason::DESCRIPTIONS[response.status],
58
+ headers: build_response_headers(response),
59
+ body: response.read
60
+ }
61
+ end
62
+
63
+ def build_response_headers(response)
64
+ response.headers.each.each_with_object({}) do |(k, v), o|
65
+ o[k] ||= []
66
+ o[k] << v
67
+ end.tap do |o|
68
+ o.each do |k, v|
69
+ o[k] = v.join(', ')
70
+ end
71
+ end
72
+ end
73
+ end