webmock 1.8.6 → 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 (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
data/Rakefile CHANGED
@@ -1,65 +1,38 @@
1
- #!/usr/bin/env rake
2
-
3
1
  require 'bundler'
4
2
  Bundler::GemHelper.install_tasks
5
3
 
6
- namespace :rvm do
7
- desc 'Run specs against 1.8.6, REE, 1.8.7, 1.9.2 and jRuby'
8
- task :specs do
9
- # JCF: I'd love to be able to use RVM's `rvm {rubies} specs` command but
10
- # the require tests in spec/other_net_http_libs_spec.rb break when doing
11
- # so.
12
- spec_files = Dir[File.dirname(__FILE__) + '/spec/**/*_spec.rb'].join(' ')
13
- sh "rvm 1.8.6@webmock,ree@webmock,1.8.7@webmock,1.9.2@webmock,jruby@webmock exec rspec #{spec_files}"
14
- end
15
- end
16
-
17
4
  require "rspec/core/rake_task"
18
5
  RSpec::Core::RakeTask.new(:spec) do |t|
19
- t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
6
+ t.rspec_opts = %w[
7
+ --force-color
8
+ --format progress
9
+ --require ./spec/spec_helper.rb
10
+ ]
20
11
  t.pattern = 'spec/**/*_spec.rb'
21
12
  end
22
13
 
23
14
  RSpec::Core::RakeTask.new(:spec_http_without_webmock) do |t|
24
- t.rspec_opts = ["-c", "-f progress", "-r ./spec/acceptance/net_http/real_net_http_spec.rb"]
15
+ t.rspec_opts = %w[
16
+ --force-color
17
+ --format progress
18
+ --require ./spec/acceptance/net_http/real_net_http_spec.rb
19
+ ]
25
20
  t.pattern = 'spec/acceptance/net_http/real_net_http_spec.rb'
26
21
  end
27
22
 
28
-
29
- task :em_http_request_0_x_spec do
30
- sh "EM_HTTP_REQUEST_0_X=true bundle install && bundle exec rspec spec/acceptance/em_http_request/em_http_request_spec.rb" if RUBY_VERSION <= "1.8.7"
31
- end
32
-
33
23
  require 'rake/testtask'
34
24
  Rake::TestTask.new(:test) do |test|
35
25
  test.test_files = FileList["test/**/*.rb"].exclude("test/test_helper.rb")
26
+ test.options = "--use-color"
36
27
  test.verbose = false
37
28
  test.warning = false
38
29
  end
39
30
 
40
31
  Rake::TestTask.new(:minitest) do |test|
41
32
  test.test_files = FileList["minitest/**/*.rb"].exclude("test/test_helper.rb")
33
+ test.options = "--pride"
42
34
  test.verbose = false
43
35
  test.warning = false
44
36
  end
45
37
 
46
-
47
- task :default => [:spec, :spec_http_without_webmock, :test, :minitest]
48
-
49
- require 'rdoc/task'
50
- RDoc::Task.new do |rdoc|
51
- $:.push File.expand_path('../lib', __FILE__)
52
- require 'webmock/version'
53
-
54
- rdoc.rdoc_dir = 'rdoc'
55
- rdoc.title = "webmock #{WebMock::VERSION}"
56
- rdoc.rdoc_files.include('README*')
57
- rdoc.rdoc_files.include('lib/webmock/webmock.rb')
58
- end
59
-
60
-
61
- task :require_ruby_18 do
62
- raise "This must be run on Ruby 1.8" unless RUBY_VERSION =~ /^1\.8/
63
- end
64
-
65
- task :release => [:require_ruby_18]
38
+ task default: [:spec, :spec_http_without_webmock, :test, :minitest]
data/lib/webmock/api.rb CHANGED
@@ -17,46 +17,92 @@ module WebMock
17
17
  alias :request :a_request
18
18
  end
19
19
 
20
-
21
20
  def assert_requested(*args, &block)
22
21
  if not args[0].is_a?(WebMock::RequestStub)
23
- args = convert_uri_method_and_options_to_request_and_options(*args, &block)
22
+ args = convert_uri_method_and_options_to_request_and_options(args[0], args[1], args[2], &block)
23
+ elsif block
24
+ raise ArgumentError, "assert_requested with a stub object, doesn't accept blocks"
24
25
  end
25
- assert_request_requested(*args, &block)
26
+ assert_request_requested(*args)
26
27
  end
27
28
 
28
29
  def assert_not_requested(*args, &block)
29
30
  if not args[0].is_a?(WebMock::RequestStub)
30
- args = convert_uri_method_and_options_to_request_and_options(*args, &block)
31
+ args = convert_uri_method_and_options_to_request_and_options(args[0], args[1], args[2], &block)
32
+ elsif block
33
+ raise ArgumentError, "assert_not_requested with a stub object, doesn't accept blocks"
34
+ end
35
+ assert_request_not_requested(*args)
36
+ end
37
+ alias refute_requested assert_not_requested
38
+
39
+ # Similar to RSpec::Mocks::ArgumentMatchers#hash_including()
40
+ #
41
+ # Matches a hash that includes the specified key(s) or key/value pairs.
42
+ # Ignores any additional keys.
43
+ #
44
+ # @example
45
+ #
46
+ # object.should_receive(:message).with(hash_including(:key => val))
47
+ # object.should_receive(:message).with(hash_including(:key))
48
+ # object.should_receive(:message).with(hash_including(:key, :key2 => val2))
49
+ def hash_including(*args)
50
+ if defined?(super)
51
+ super
52
+ else
53
+ WebMock::Matchers::HashIncludingMatcher.new(anythingize_lonely_keys(*args))
31
54
  end
32
- assert_request_not_requested(*args, &block)
33
55
  end
34
56
 
35
- def hash_including(expected)
36
- if defined?(::RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher)
37
- RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher.new(expected)
38
- elsif defined?(::Spec::Mocks::ArgumentMatchers::HashIncludingMatcher)
39
- Spec::Mocks::ArgumentMatchers::HashIncludingMatcher.new(expected)
57
+ def hash_excluding(*args)
58
+ if defined?(super)
59
+ super
40
60
  else
41
- WebMock::Matchers::HashIncludingMatcher.new(expected)
61
+ WebMock::Matchers::HashExcludingMatcher.new(anythingize_lonely_keys(*args))
42
62
  end
43
63
  end
44
64
 
65
+ def remove_request_stub(stub)
66
+ WebMock::StubRegistry.instance.remove_request_stub(stub)
67
+ end
68
+
69
+ def reset_executed_requests!
70
+ WebMock::RequestRegistry.instance.reset!
71
+ end
72
+
45
73
  private
46
74
 
47
- def convert_uri_method_and_options_to_request_and_options(*args, &block)
48
- request = WebMock::RequestPattern.new(*args).with(&block)
49
- [request, args[2] || {}]
75
+ def convert_uri_method_and_options_to_request_and_options(method, uri, options, &block)
76
+ options ||= {}
77
+ options_for_pattern = options.dup
78
+ [:times, :at_least_times, :at_most_times].each { |key| options_for_pattern.delete(key) }
79
+ request = WebMock::RequestPattern.new(method, uri, options_for_pattern)
80
+ request = request.with(&block) if block
81
+ [request, options]
50
82
  end
51
83
 
52
84
  def assert_request_requested(request, options = {})
53
- verifier = WebMock::RequestExecutionVerifier.new(request, options.delete(:times) || 1)
85
+ times = options.delete(:times)
86
+ at_least_times = options.delete(:at_least_times)
87
+ at_most_times = options.delete(:at_most_times)
88
+ times = 1 if times.nil? && at_least_times.nil? && at_most_times.nil?
89
+ verifier = WebMock::RequestExecutionVerifier.new(request, times, at_least_times, at_most_times)
54
90
  WebMock::AssertionFailure.failure(verifier.failure_message) unless verifier.matches?
55
91
  end
56
92
 
57
93
  def assert_request_not_requested(request, options = {})
58
- verifier = WebMock::RequestExecutionVerifier.new(request, options.delete(:times))
59
- WebMock::AssertionFailure.failure(verifier.negative_failure_message) unless verifier.does_not_match?
94
+ times = options.delete(:times)
95
+ at_least_times = options.delete(:at_least_times)
96
+ at_most_times = options.delete(:at_most_times)
97
+ verifier = WebMock::RequestExecutionVerifier.new(request, times, at_least_times, at_most_times)
98
+ WebMock::AssertionFailure.failure(verifier.failure_message_when_negated) unless verifier.does_not_match?
99
+ end
100
+
101
+ #this is a based on RSpec::Mocks::ArgumentMatchers#anythingize_lonely_keys
102
+ def anythingize_lonely_keys(*args)
103
+ hash = args.last.class == Hash ? args.delete_at(-1) : {}
104
+ args.each { | arg | hash[arg] = WebMock::Matchers::AnyArgMatcher.new(nil) }
105
+ hash
60
106
  end
61
107
 
62
108
  end
@@ -3,7 +3,7 @@ module WebMock
3
3
  @@callbacks = []
4
4
 
5
5
  def self.add_callback(options, block)
6
- @@callbacks << {:options => options, :block => block}
6
+ @@callbacks << {options: options, block: block}
7
7
  end
8
8
 
9
9
  def self.callbacks
@@ -2,9 +2,17 @@ module WebMock
2
2
  class Config
3
3
  include Singleton
4
4
 
5
+ def initialize
6
+ @show_stubbing_instructions = true
7
+ @show_body_diff = true
8
+ end
9
+
5
10
  attr_accessor :allow_net_connect
6
11
  attr_accessor :allow_localhost
7
12
  attr_accessor :allow
8
13
  attr_accessor :net_http_connect_on_start
14
+ attr_accessor :show_stubbing_instructions
15
+ attr_accessor :query_values_notation
16
+ attr_accessor :show_body_diff
9
17
  end
10
18
  end
@@ -1,6 +1,8 @@
1
1
  require 'webmock'
2
2
  require 'webmock/rspec/matchers'
3
3
 
4
+ WebMock.enable!
5
+
4
6
  World(WebMock::API, WebMock::Matchers)
5
7
 
6
8
  After do
@@ -1,33 +1,17 @@
1
1
  module WebMock
2
2
 
3
- class NetConnectNotAllowedError < StandardError
3
+ class NetConnectNotAllowedError < Exception
4
4
  def initialize(request_signature)
5
- text = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
6
- text << "\n\n"
7
- text << stubbing_instructions(request_signature)
8
- text << request_stubs
9
- text << "\n\n" + "="*60
5
+ request_signature_snippet = RequestSignatureSnippet.new(request_signature)
6
+ text = [
7
+ "Real HTTP connections are disabled. Unregistered request: #{request_signature}",
8
+ request_signature_snippet.stubbing_instructions,
9
+ request_signature_snippet.request_stubs,
10
+ "="*60
11
+ ].compact.join("\n\n")
10
12
  super(text)
11
13
  end
12
14
 
13
- private
14
-
15
- def request_stubs
16
- return "" if WebMock::StubRegistry.instance.request_stubs.empty?
17
- text = "\n\nregistered request stubs:\n"
18
- WebMock::StubRegistry.instance.request_stubs.each do |stub|
19
- text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
20
- end
21
- text
22
- end
23
-
24
- def stubbing_instructions(request_signature)
25
- text = ""
26
- request_stub = RequestStub.from_request_signature(request_signature)
27
- text << "You can stub this request with the following snippet:\n\n"
28
- text << WebMock::StubRequestSnippet.new(request_stub).to_s
29
- text
30
- end
31
15
  end
32
16
 
33
17
  end
@@ -0,0 +1,216 @@
1
+ begin
2
+ require 'async'
3
+ require 'async/http'
4
+ rescue LoadError
5
+ # async-http not found
6
+ end
7
+
8
+ if defined?(Async::HTTP)
9
+ module WebMock
10
+ module HttpLibAdapters
11
+ class AsyncHttpClientAdapter < HttpLibAdapter
12
+ adapter_for :async_http_client
13
+
14
+ OriginalAsyncHttpClient = Async::HTTP::Client unless const_defined?(:OriginalAsyncHttpClient)
15
+
16
+ class << self
17
+ def enable!
18
+ Async::HTTP.send(:remove_const, :Client)
19
+ Async::HTTP.send(:const_set, :Client, Async::HTTP::WebMockClientWrapper)
20
+ end
21
+
22
+ def disable!
23
+ Async::HTTP.send(:remove_const, :Client)
24
+ Async::HTTP.send(:const_set, :Client, OriginalAsyncHttpClient)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module Async
32
+ module HTTP
33
+ class WebMockClientWrapper < Client
34
+ def initialize(
35
+ endpoint,
36
+ protocol = endpoint.protocol,
37
+ scheme = endpoint.scheme,
38
+ authority = endpoint.authority,
39
+ **options
40
+ )
41
+ webmock_endpoint = WebMockEndpoint.new(scheme, authority, protocol)
42
+
43
+ @network_client = WebMockClient.new(endpoint, **options)
44
+ @webmock_client = WebMockClient.new(webmock_endpoint, **options)
45
+
46
+ @scheme = scheme
47
+ @authority = authority
48
+ end
49
+
50
+ def call(request)
51
+ request.scheme ||= self.scheme
52
+ request.authority ||= self.authority
53
+
54
+ request_signature = build_request_signature(request)
55
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
56
+ webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
57
+ net_connect_allowed = WebMock.net_connect_allowed?(request_signature.uri)
58
+ real_request = false
59
+
60
+ if webmock_response
61
+ webmock_response.raise_error_if_any
62
+ raise Async::TimeoutError, 'WebMock timeout error' if webmock_response.should_timeout
63
+ WebMockApplication.add_webmock_response(request, webmock_response)
64
+ response = @webmock_client.call(request)
65
+ elsif net_connect_allowed
66
+ response = @network_client.call(request)
67
+ real_request = true
68
+ else
69
+ raise WebMock::NetConnectNotAllowedError.new(request_signature) unless webmock_response
70
+ end
71
+
72
+ if WebMock::CallbackRegistry.any_callbacks?
73
+ webmock_response ||= build_webmock_response(response)
74
+ WebMock::CallbackRegistry.invoke_callbacks(
75
+ {
76
+ lib: :async_http_client,
77
+ real_request: real_request
78
+ },
79
+ request_signature,
80
+ webmock_response
81
+ )
82
+ end
83
+
84
+ response
85
+ end
86
+
87
+ def close
88
+ @network_client.close
89
+ @webmock_client.close
90
+ end
91
+
92
+ private
93
+
94
+ def build_request_signature(request)
95
+ body = request.read
96
+ request.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
97
+ WebMock::RequestSignature.new(
98
+ request.method.downcase.to_sym,
99
+ "#{request.scheme}://#{request.authority}#{request.path}",
100
+ headers: request.headers.to_h,
101
+ body: body
102
+ )
103
+ end
104
+
105
+ def build_webmock_response(response)
106
+ body = response.read
107
+ response.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
108
+
109
+ webmock_response = WebMock::Response.new
110
+ webmock_response.status = [
111
+ response.status,
112
+ ::Protocol::HTTP1::Reason::DESCRIPTIONS[response.status]
113
+ ]
114
+ webmock_response.headers = build_webmock_response_headers(response)
115
+ webmock_response.body = body
116
+ webmock_response
117
+ end
118
+
119
+ def build_webmock_response_headers(response)
120
+ response.headers.each.each_with_object({}) do |(k, v), o|
121
+ o[k] ||= []
122
+ o[k] << v
123
+ end
124
+ end
125
+ end
126
+
127
+ class WebMockClient < Client
128
+ end
129
+
130
+ class WebMockEndpoint
131
+ def initialize(scheme, authority, protocol)
132
+ @scheme = scheme
133
+ @authority = authority
134
+ @protocol = protocol
135
+ end
136
+
137
+ attr :scheme, :authority, :protocol
138
+
139
+ def connect
140
+ server_socket, client_socket = create_connected_sockets
141
+ Async(transient: true) do
142
+ accept_socket(server_socket)
143
+ end
144
+ client_socket
145
+ end
146
+
147
+ def inspect
148
+ "\#<#{self.class}> #{scheme}://#{authority} protocol=#{protocol}"
149
+ end
150
+
151
+ private
152
+
153
+ def create_connected_sockets
154
+ Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM).tap do |sockets|
155
+ sockets.each do |socket|
156
+ socket.instance_variable_set :@alpn_protocol, nil
157
+ socket.instance_eval do
158
+ def alpn_protocol
159
+ nil # means HTTP11 will be used for HTTPS
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ def accept_socket(socket)
167
+ server = Async::HTTP::Server.new(WebMockApplication, self)
168
+ server.accept(socket, socket.remote_address)
169
+ end
170
+ end
171
+
172
+ module WebMockApplication
173
+ WEBMOCK_REQUEST_ID_HEADER = 'x-webmock-request-id'.freeze
174
+
175
+ class << self
176
+ def call(request)
177
+ request.read
178
+ webmock_response = get_webmock_response(request)
179
+ build_response(webmock_response)
180
+ end
181
+
182
+ def add_webmock_response(request, webmock_response)
183
+ webmock_request_id = request.object_id.to_s
184
+ request.headers.add(WEBMOCK_REQUEST_ID_HEADER, webmock_request_id)
185
+ webmock_responses[webmock_request_id] = webmock_response
186
+ end
187
+
188
+ def get_webmock_response(request)
189
+ webmock_request_id = request.headers[WEBMOCK_REQUEST_ID_HEADER][0]
190
+ webmock_responses.fetch(webmock_request_id)
191
+ end
192
+
193
+ private
194
+
195
+ def webmock_responses
196
+ @webmock_responses ||= {}
197
+ end
198
+
199
+ def build_response(webmock_response)
200
+ headers = (webmock_response.headers || {}).each_with_object([]) do |(k, value), o|
201
+ Array(value).each do |v|
202
+ o.push [k, v]
203
+ end
204
+ end
205
+
206
+ ::Protocol::HTTP::Response[
207
+ webmock_response.status[0],
208
+ headers,
209
+ webmock_response.body
210
+ ]
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end