forked from dependabot/dependabot-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add custom WebMock handler for Gem::Net::HTTP
- Loading branch information
Showing
2 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
238 changes: 238 additions & 0 deletions
238
bundler/helpers/v1/spec/helpers/gem_net_http_adapter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
# typed: false | ||
|
||
require "rubygems/vendored_net_http" | ||
|
||
module WebMock | ||
module HttpLibAdapters | ||
class GemNetHttpAdapter < HttpLibAdapter | ||
adapter_for :gem_net_http | ||
|
||
OriginalGemNetHTTP = ::Gem::Net::HTTP unless const_defined?(:OriginalGemNetHTTP) | ||
|
||
def self.enable! | ||
::Gem::Net.send(:remove_const, :HTTP) | ||
::Gem::Net.send(:remove_const, :HTTPSession) | ||
::Gem::Net.send(:const_set, :HTTP, @webMockNetHTTP) | ||
::Gem::Net.send(:const_set, :HTTPSession, @webMockNetHTTP) | ||
end | ||
|
||
def self.disable! | ||
::Gem::Net.send(:remove_const, :HTTP) | ||
::Gem::Net.send(:remove_const, :HTTPSession) | ||
::Gem::Net.send(:const_set, :HTTP, OriginalGemNetHTTP) | ||
::Gem::Net.send(:const_set, :HTTPSession, OriginalGemNetHTTP) | ||
|
||
# copy all constants from @webMockNetHTTP to original Net::HTTP | ||
# in case any constants were added to @webMockNetHTTP instead of Net::HTTP | ||
# after WebMock was enabled. | ||
# i.e Net::HTTP::DigestAuth | ||
@webMockNetHTTP.constants.each do |constant| | ||
unless OriginalGemNetHTTP.constants.map(&:to_s).include?(constant.to_s) | ||
OriginalGemNetHTTP.send(:const_set, constant, @webMockNetHTTP.const_get(constant)) | ||
end | ||
end | ||
end | ||
|
||
@webMockNetHTTP = Class.new(::Gem::Net::HTTP) do | ||
class << self | ||
def socket_type | ||
StubSocket | ||
end | ||
|
||
if Module.method(:const_defined?).arity == 1 | ||
def const_defined?(name) | ||
super || superclass.const_defined?(name) | ||
end | ||
else | ||
def const_defined?(name, inherit = true) | ||
super || superclass.const_defined?(name, inherit) | ||
end | ||
end | ||
|
||
if Module.method(:const_get).arity != 1 | ||
def const_get(name, inherit = true) | ||
super | ||
rescue NameError | ||
superclass.const_get(name, inherit) | ||
end | ||
end | ||
|
||
if Module.method(:constants).arity != 0 | ||
def constants(inherit = true) | ||
(super + superclass.constants(inherit)).uniq | ||
end | ||
end | ||
end | ||
|
||
def request(request, body = nil, &block) | ||
request_signature = WebMock::NetHTTPUtility.request_signature_from_request(self, request, body) | ||
|
||
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) | ||
|
||
if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) | ||
@socket = ::Gem::Net::HTTP.socket_type.new | ||
WebMock::CallbackRegistry.invoke_callbacks( | ||
{ lib: :net_http }, request_signature, webmock_response | ||
) | ||
build_net_http_response(webmock_response, request.uri, &block) | ||
elsif WebMock.net_connect_allowed?(request_signature.uri) | ||
check_right_http_connection | ||
after_request = lambda do |response| | ||
if WebMock::CallbackRegistry.any_callbacks? | ||
webmock_response = build_webmock_response(response) | ||
WebMock::CallbackRegistry.invoke_callbacks( | ||
{ lib: :net_http, real_request: true }, request_signature, webmock_response | ||
) | ||
end | ||
response.extend Net::WebMockHTTPResponse | ||
yield response if block | ||
response | ||
end | ||
super_with_after_request = lambda { | ||
response = super(request, nil, &nil) | ||
after_request.call(response) | ||
} | ||
if started? | ||
ensure_actual_connection | ||
super_with_after_request.call | ||
else | ||
start_with_connect do | ||
super_with_after_request.call | ||
end | ||
end | ||
else | ||
raise WebMock::NetConnectNotAllowedError.new(request_signature) | ||
end | ||
end | ||
|
||
def start_without_connect | ||
raise IOError, "HTTP session already opened" if @started | ||
|
||
if block_given? | ||
begin | ||
@socket = ::Gem::Net::HTTP.socket_type.new | ||
@started = true | ||
return yield(self) | ||
ensure | ||
do_finish | ||
end | ||
end | ||
@socket = ::Gem::Net::HTTP.socket_type.new | ||
@started = true | ||
self | ||
end | ||
|
||
def ensure_actual_connection | ||
return unless @socket.is_a?(StubSocket) | ||
|
||
@socket&.close | ||
@socket = nil | ||
do_start | ||
end | ||
|
||
alias_method :start_with_connect, :start | ||
|
||
def start(&block) | ||
uri = Addressable::URI.parse(WebMock::NetHTTPUtility.get_uri(self)) | ||
|
||
if WebMock.net_http_connect_on_start?(uri) | ||
super(&block) | ||
else | ||
start_without_connect(&block) | ||
end | ||
end | ||
|
||
def build_net_http_response(webmock_response, request_uri) | ||
response = ::Gem::Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", | ||
webmock_response.status[0].to_s, webmock_response.status[1]) | ||
body = webmock_response.body | ||
body = nil if webmock_response.status[0].to_s == "204" | ||
|
||
response.instance_variable_set(:@body, body) | ||
webmock_response.headers.to_a.each do |name, values| | ||
values = [values] unless values.is_a?(Array) | ||
values.each do |value| | ||
response.add_field(name, value) | ||
end | ||
end | ||
|
||
response.instance_variable_set(:@read, true) | ||
|
||
response.uri = request_uri | ||
|
||
response.extend Net::WebMockHTTPResponse | ||
|
||
raise Net::OpenTimeout, "execution expired" if webmock_response.should_timeout | ||
|
||
webmock_response.raise_error_if_any | ||
|
||
yield response if block_given? | ||
|
||
response | ||
end | ||
|
||
def build_webmock_response(net_http_response) | ||
webmock_response = WebMock::Response.new | ||
webmock_response.status = [ | ||
net_http_response.code.to_i, | ||
net_http_response.message | ||
] | ||
webmock_response.headers = net_http_response.to_hash | ||
webmock_response.body = net_http_response.body | ||
webmock_response | ||
end | ||
|
||
def check_right_http_connection | ||
return if @@alredy_checked_for_right_http_connection ||= false | ||
|
||
WebMock::NetHTTPUtility.puts_warning_for_right_http_if_needed | ||
@@alredy_checked_for_right_http_connection = true | ||
end | ||
end | ||
@webMockNetHTTP.version_1_2 | ||
[ | ||
[:Get, ::Gem::Net::HTTP::Get], | ||
[:Post, ::Gem::Net::HTTP::Post], | ||
[:Put, ::Gem::Net::HTTP::Put], | ||
[:Delete, ::Gem::Net::HTTP::Delete], | ||
[:Head, ::Gem::Net::HTTP::Head], | ||
[:Options, ::Gem::Net::HTTP::Options] | ||
].each do |c| | ||
@webMockNetHTTP.const_set(c[0], c[1]) | ||
end | ||
end | ||
end | ||
|
||
class StubSocket # :nodoc: | ||
attr_accessor :read_timeout | ||
attr_accessor :continue_timeout | ||
attr_accessor :write_timeout | ||
|
||
def initialize(*_args) | ||
@closed = false | ||
end | ||
|
||
def closed? | ||
@closed | ||
end | ||
|
||
def close | ||
@closed = true | ||
nil | ||
end | ||
|
||
def readuntil(*args); end | ||
|
||
def io | ||
@io ||= StubIO.new | ||
end | ||
|
||
class StubIO | ||
def setsockopt(*args); end | ||
def peer_cert; end | ||
def peeraddr = ["AF_INET", 443, "127.0.0.1", "127.0.0.1"] | ||
def ssl_version = "TLSv1.3" | ||
def cipher = ["TLS_AES_128_GCM_SHA256", "TLSv1.3", 128, 128] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters