webmock 3.0.0 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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