diff --git a/README.md b/README.md index f4a16ffc1..1ae3834e8 100644 --- a/README.md +++ b/README.md @@ -462,22 +462,67 @@ Net::HTTP.get('www.something.com', '/') # ===> Failure Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium? ``` -### External requests can be disabled while allowing any hostname or port or parts thereof +### External requests can be disabled while allowing specific requests + +Allowed requests can be specified in a number of ways. + +With a `String` specifying a host name: + +```ruby +WebMock.disable_net_connect!(:allow => 'www.example.org') + +RestClient.get('www.something.com', '/') # ===> Failure +RestClient.get('www.example.org', '/') # ===> Allowed +RestClient.get('www.example.org:8080', '/') # ===> Allowed +``` + +With a `String` specifying a host name and a port: ```ruby -WebMock.disable_net_connect!(:allow => "www.example.org:8080") +WebMock.disable_net_connect!(:allow => 'www.example.org:8080') RestClient.get('www.something.com', '/') # ===> Failure +RestClient.get('www.example.org', '/') # ===> Failure +RestClient.get('www.example.org:8080', '/') # ===> Allowed +``` + +With a `Regexp` matching the URI: + +```ruby +WebMock.disable_net_connect!(:allow => %r{ample.org/foo}) -RestClient.get('www.example.org', '/') # ===> Failure. +RestClient.get('www.example.org', '/foo/bar') # ===> Allowed +RestClient.get('sample.org', '/foo') # ===> Allowed +RestClient.get('sample.org', '/bar') # ===> Failure +``` -RestClient.get('www.example.org:8080', '/') # ===> Allowed +With an object that responds to `#call`, receiving a `URI` object and returning a boolean: -WebMock.disable_net_connect!(:allow => /ample.org/) +```ruby +blacklist = ['google.com', 'facebook.com', 'apple.com'] +allowed_sites = lambda{|uri| + blacklist.none?{|site| uri.host.include?(site) } +} +WebMock.disable_net_connect!(:allow => allowed_sites) + +RestClient.get('www.example.org', '/') # ===> Allowed +RestClient.get('www.facebook.com', '/') # ===> Failure +RestClient.get('apple.com', '/') # ===> Failure +``` -WebMock.disable_net_connect!(:allow => [/ample.org/, /googl/]) +With an `Array` of any of the above: -RestClient.get('www.example.org', '/') # ===> Allowed +```ruby +WebMock.disable_net_connect!(:allow => [ + lambda{|uri| uri.host.length % 2 == 0 }, + /ample.org/, + 'bbc.co.uk', +]) + +RestClient.get('www.example.org', '/') # ===> Allowed +RestClient.get('bbc.co.uk', '/') # ===> Allowed +RestClient.get('bbc.com', '/') # ===> Allowed +RestClient.get('www.bbc.com', '/') # ===> Failure ``` ## Connecting on Net::HTTP.start diff --git a/lib/webmock/webmock.rb b/lib/webmock/webmock.rb index 444c92d79..4124be54e 100644 --- a/lib/webmock/webmock.rb +++ b/lib/webmock/webmock.rb @@ -72,6 +72,10 @@ def self.net_connect_explicit_allowed?(allowed, uri=nil) when String allowed == uri.host || allowed == "#{uri.host}:#{uri.port}" + else + if allowed.respond_to?(:call) + allowed.call(uri) + end end end diff --git a/spec/acceptance/shared/allowing_and_disabling_net_connect.rb b/spec/acceptance/shared/allowing_and_disabling_net_connect.rb index bba21fbd6..080a8b6d1 100644 --- a/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +++ b/spec/acceptance/shared/allowing_and_disabling_net_connect.rb @@ -88,64 +88,172 @@ end end - describe "is not allowed with exception for allowed domains" do - let(:host_with_port){ WebMockServer.instance.host_with_port } + describe "is not allowed, with exceptions" do + describe "allowing by host string" do + before :each do + WebMock.disable_net_connect!(:allow => 'httpstat.us') + end - before(:each) do - WebMock.disable_net_connect!(:allow => ["www.example.org", "httpstat.us", host_with_port]) - end + context "when the host is not allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'disallowed.example.com/foo').to_return(:body => "abc") + expect(http_request(:get, 'http://disallowed.example.com/foo').body).to eq("abc") + end - context "when the host is not allowed" do - it "should return stubbed response if request was stubbed" do - stub_request(:get, "www.example.com").to_return(:body => "abc") - expect(http_request(:get, "http://www.example.com/").body).to eq("abc") + it "should raise exception if request was not stubbed" do + expect { + http_request(:get, 'http://disallowed.example.com/') + }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://disallowed.example.com)) + end end - it "should raise exception if request was not stubbed" do - expect { - http_request(:get, "http://www.example.com/") - }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://www.example.com/)) + context "when the host is allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'httpstat.us/200').to_return(:body => "abc") + expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc") + end + + # WARNING: this makes a real HTTP request! + it "should make a real request to allowed host", :net_connect => true do + expect(http_request(:get, "http://httpstat.us/200").status).to eq('200') + end end end - context "when the host with port is not allowed" do - it "should return stubbed response if request was stubbed" do - stub_request(:get, "http://localhost:2345").to_return(:body => "abc") - expect(http_request(:get, "http://localhost:2345/").body).to eq("abc") + describe "allowing by host:port string" do + def replace_with_different_port(uri) + uri.sub(%r{:(\d+)}){|m0, m1| ':' + ($~[1].to_i + 1).to_s } + end + + let(:allowed_host_with_port) { WebMockServer.instance.host_with_port } + let(:disallowed_host_with_port) { replace_with_different_port(allowed_host_with_port) } + + before :each do + WebMock.disable_net_connect!(:allow => allowed_host_with_port) + end + + context "when the host is not allowed" do + it "should return stubbed response if request was stubbed" do + request_url = "http://#{disallowed_host_with_port}/foo" + stub_request(:get, request_url).to_return(:body => "abc") + expect(http_request(:get, request_url).body).to eq("abc") + end + + it "should raise exception if request was not stubbed" do + request_url = "http://#{disallowed_host_with_port}/foo" + expect { + http_request(:get, request_url) + }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET #{request_url})) + end end - it "should raise exception if request was not stubbed" do - expect { - http_request(:get, "http://localhost:2345/") - }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://localhost:2345/)) + context "when the host is allowed" do + it "should return stubbed response if request was stubbed" do + request_url = "http://#{allowed_host_with_port}/foo" + stub_request(:get, request_url).to_return(:body => "abc") + expect(http_request(:get, request_url).body).to eq('abc') + end + + it "should make a real request to allowed host", :net_connect => true do + request_url = "http://#{allowed_host_with_port}/foo" + expect(http_request(:get, request_url).status).to eq('200') + end end end - context "when the host is allowed" do - it "should raise exception if request was not stubbed" do - expect { - http_request(:get, "http://www.example.com/") - }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://www.example.com/)) + describe "allowing by regular expression" do + before :each do + WebMock.disable_net_connect!(:allow => %r{httpstat}) + end + + context "when the host is not allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'disallowed.example.com/foo').to_return(:body => "abc") + expect(http_request(:get, 'http://disallowed.example.com/foo').body).to eq("abc") + end + + it "should raise exception if request was not stubbed" do + expect { + http_request(:get, 'http://disallowed.example.com/') + }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://disallowed.example.com)) + end end - it "should make a real request to allowed host", :net_connect => true do - expect(http_request(:get, "http://httpstat.us/200").status).to eq("200") + context "when the host is allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'httpstat.us/200').to_return(:body => "abc") + expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc") + end + + # WARNING: this makes a real HTTP request! + it "should make a real request to allowed host", :net_connect => true do + expect(http_request(:get, "http://httpstat.us/200").status).to eq('200') + end end end - context "when the host with port is allowed" do - it "should make a real request to allowed host", :net_connect => true do - expect(http_request(:get, "http://#{host_with_port}/").status).to eq("200") + describe "allowing by a callable" do + before :each do + WebMock.disable_net_connect!(:allow => lambda{|url| url.to_str.include?('httpstat') }) + end + + context "when the host is not allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'disallowed.example.com/foo').to_return(:body => "abc") + expect(http_request(:get, 'http://disallowed.example.com/foo').body).to eq("abc") + end + + it "should raise exception if request was not stubbed" do + expect { + http_request(:get, 'http://disallowed.example.com/') + }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://disallowed.example.com)) + end + end + + context "when the host is allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'httpstat.us/200').to_return(:body => "abc") + expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc") + end + + # WARNING: this makes a real HTTP request! + it "should make a real request to allowed host", :net_connect => true do + expect(http_request(:get, "http://httpstat.us/200").status).to eq('200') + end end end - context "when the host is allowed but not port" do - it "should make a real request to allowed host", :net_connect => true do - expect { - http_request(:get, "http://localhost:123/") - }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://localhost:123/)) + describe "allowing by a list of the above" do + before :each do + WebMock.disable_net_connect!(:allow => [lambda{|_| false }, %r{foobar}, 'httpstat.us']) + end + + context "when the host is not allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'disallowed.example.com/foo').to_return(:body => "abc") + expect(http_request(:get, 'http://disallowed.example.com/foo').body).to eq("abc") + end + + it "should raise exception if request was not stubbed" do + expect { + http_request(:get, 'http://disallowed.example.com/') + }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://disallowed.example.com)) + end + end + + context "when the host is allowed" do + it "should return stubbed response if request was stubbed" do + stub_request(:get, 'httpstat.us/200').to_return(:body => "abc") + expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc") + end + + # WARNING: this makes a real HTTP request! + it "should make a real request to allowed host", :net_connect => true do + expect(http_request(:get, "http://httpstat.us/200").status).to eq('200') + end end end + end end end