Permalink
Browse files

Added support for matching escaped and non escaped URLs

  • Loading branch information...
1 parent 144d620 commit fba5e3ed33e47925b127721912af576642812d25 Bartosz Blimke committed Nov 22, 2009
View
@@ -8,6 +8,7 @@ Features
* Stubbing requests and setting requests expectations
* Matching requests based on method, url, headers and body
+* Matching not escaped and escaped urls
* Support for Test::Unit and RSpec (and can be easily extended to other frameworks)
* Support for Net::Http and other http libraries based on Net::Http
* Adding other http library adapters is easy
@@ -187,7 +188,7 @@ Credits
-------
Thank you Fakeweb! This library is based on the idea taken from [FakeWeb](fakeweb.rubyforge.org).
-I took couple of solutions from that project. I also copied some code i.e Net:Http adapter or url normalisation function.
+I took couple of solutions from that project. I also copied some code i.e Net:Http adapter.
Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed.
I also preferred some things to work differently i.e request stub precedence.
View
@@ -1,5 +1,8 @@
require 'singleton'
+require 'rubygems'
+require 'addressable/uri'
+
require 'webmock/http_lib_adapters/net_http'
require 'webmock/errors'
@@ -61,7 +61,7 @@ def request_with_webmock(request, body = nil, &block)
protocol = use_ssl? ? "https" : "http"
path = request.path
- path = URI.parse(request.path).request_uri if request.path =~ /^http/
+ path = Addressable::URI.heuristic_parse(request.path).request_uri if request.path =~ /^http/
if request["authorization"] =~ /^Basic /
userinfo = WebMock::Utility.decode_userinfo_from_header(request["authorization"])
@@ -1,10 +1,10 @@
module WebMock
class RequestProfile < Struct.new(:method, :uri, :body, :headers)
-
+
def initialize(method, uri, body = nil, headers = nil)
super
- self.uri = WebMock::URL.normalize_uri(self.uri) unless self.uri.is_a?(URI)
+ self.uri = WebMock::URL.normalize_uri(self.uri) unless self.uri.is_a?(Addressable::URI)
self.headers = Utility.normalize_headers(self.headers)
end
@@ -35,11 +35,10 @@ def to_s
def match_url(other)
raise "Can't match regexp request profile" if self.uri.is_a?(Regexp)
- @uris_to_check ||= WebMock::URL.variations_of_uri_as_strings(self.uri)
- if other.uri.is_a?(URI)
- @uris_to_check.include?(other.uri.to_s)
+ if other.uri.is_a?(Addressable::URI)
+ URL.normalize_uri(uri) === URL.normalize_uri(other.uri)
elsif other.uri.is_a?(Regexp)
- @uris_to_check.any? { |u| u.match(other.uri) }
+ WebMock::URL.variations_of_uri_as_strings(self.uri).any? { |u| u.match(other.uri) }
else
false
end
View
@@ -4,41 +4,53 @@ class URL
def self.normalize_uri(uri)
return uri if uri.is_a?(Regexp)
- normalized_uri =
- case uri
- when URI then uri
- when String
- uri = 'http://' + uri unless uri.match('^https?://')
- URI.parse(uri)
- end
- normalized_uri.query = sort_query_params(normalized_uri.query)
- normalized_uri.normalize
+ uri = 'http://' + uri unless uri.match('^https?://') if uri.is_a?(String)
+ normalized_uri = Addressable::URI.heuristic_parse(uri)
+ normalized_uri.query_values = normalized_uri.query_values if normalized_uri.query_values
+ normalized_uri.normalize!
+ normalized_uri.port = normalized_uri.inferred_port unless normalized_uri.port && normalized_uri.inferred_port
+ normalized_uri
end
def self.variations_of_uri_as_strings(uri_object)
- normalized_uri = normalize_uri(uri_object.dup)
- normalized_uri_string = normalized_uri.to_s
-
- variations = [normalized_uri_string]
-
- # if the port is implied in the original, add a copy with an explicit port
- if normalized_uri.default_port == normalized_uri.port
- variations << normalized_uri_string.sub(
- /#{Regexp.escape(normalized_uri.request_uri)}$/,
- ":#{normalized_uri.port}#{normalized_uri.request_uri}")
+ normalized_uri = normalize_uri(uri_object.dup).freeze
+ uris = [ normalized_uri ]
+
+ if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme]
+ uris = uris_with_inferred_port_and_without(uris)
end
+
+ if normalized_uri.scheme == "http"
+ uris = uris_with_scheme_and_without(uris)
+ end
+
+ if normalized_uri.path == '/' && normalized_uri.query == nil
+ uris = uris_with_trailing_slash_and_without(uris)
+ end
+
+ uris = uris_encoded_and_unencoded(uris)
- variations
+ uris.map {|uri| uri.to_s.gsub(/^\/\//,'') }.uniq
end
private
- def self.sort_query_params(query)
- if query.nil? || query.empty?
- nil
- else
- query.split('&').sort.join('&')
- end
+ def self.uris_with_inferred_port_and_without(uris)
+ uris.map { |uri| [ uri, uri.omit(:port).freeze ] }.flatten
+ end
+
+ def self.uris_encoded_and_unencoded(uris)
+ uris.map do |uri|
+ [ uri, Addressable::URI.heuristic_parse(Addressable::URI.unencode(uri)).freeze ]
+ end.flatten
+ end
+
+ def self.uris_with_scheme_and_without(uris)
+ uris.map { |uri| [ uri, uri.omit(:scheme).freeze ] }.flatten
+ end
+
+ def self.uris_with_trailing_slash_and_without(uris)
+ uris = uris.map { |uri| [ uri, uri.omit(:path).freeze ] }.flatten
end
end
@@ -5,10 +5,14 @@
include WebMock
def http_request(method, url, options = {})
- url = URI.parse(url)
+ begin
+ url = URI.parse(url)
+ rescue
+ url = Addressable::URI.heuristic_parse(url)
+ end
response = nil
clazz = Net::HTTP.const_get("#{method.to_s.capitalize}")
- req = clazz.new(url.path, options[:headers])
+ req = clazz.new("#{url.path}#{url.query ? '?' : ''}#{url.query}", options[:headers])
req.basic_auth url.user, url.password if url.user
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true if url.scheme == "https"
@@ -21,7 +25,6 @@ def http_request(method, url, options = {})
:status => response.code })
end
-
describe "Webmock with Net:HTTP" do
it_should_behave_like "WebMock"
@@ -14,7 +14,7 @@
it "should have assigned uri without normalization if uri is URI" do
URL.should_not_receive(:normalize_uri)
- uri = URI.parse("www.google.com")
+ uri = Addressable::URI.parse("www.google.com")
profile = RequestProfile.new(:get, uri)
profile.uri.should == uri
end
@@ -30,9 +30,9 @@
end
- it "should report string" do
+ it "should report string describing itself" do
RequestProfile.new(:get, "www.google.com", "abc", {'A' => 'a', 'B' => 'b'}).to_s.should ==
- "GET http://www.google.com/ with body 'abc' with headers {'A'=>'a', 'B'=>'b'}"
+ "GET http://www.google.com:80/ with body 'abc' with headers {'A'=>'a', 'B'=>'b'}"
end
@@ -75,6 +75,21 @@
RequestProfile.new(:get, "www.google.com").
should match(RequestProfile.new(:get, "www.google.com"))
end
+
+ it "should match if uri matches other escaped using uri" do
+ RequestProfile.new(:get, "www.google.com/big image.jpg").
+ should match(RequestProfile.new(:get, "www.google.com/big%20image.jpg"))
+ end
+
+ it "should match if unescaped uri matches other uri" do
+ RequestProfile.new(:get, "www.google.com/big%20image.jpg").
+ should match(RequestProfile.new(:get, "www.google.com/big image.jpg"))
+ end
+
+ it "should match if unescaped uri matches other regexp uri" do
+ RequestProfile.new(:get, "www.google.com/big%20image.jpg").
+ should match(RequestProfile.new(:get, /.*big image.jpg.*/))
+ end
it "should match if uri matches other regex uri" do
RequestProfile.new(:get, "www.google.com").
@@ -95,6 +110,32 @@
RequestProfile.new(:get, "www.google.com?a=1&b=2").
should match(RequestProfile.new(:get, "www.google.com?b=2&a=1"))
end
+
+ describe "when parameters are escaped" do
+
+ it "should match if uri with non escaped parameters is the same as other uri with escaped parameters" do
+ RequestProfile.new(:get, "www.google.com/?a=a b").
+ should match(RequestProfile.new(:get, "www.google.com/?a=a%20b"))
+ end
+
+ it "should match if uri with escaped parameters is the same as other uri with non escaped parameters" do
+ RequestProfile.new(:get, "www.google.com/?a=a%20b").
+ should match(RequestProfile.new(:get, "www.google.com/?a=a b"))
+ end
+
+ it "should match if other regexp is for non escaped parameters but uri has escaped parameters" do
+ RequestProfile.new(:get, "www.google.com/?a=a%20b").
+ should match(RequestProfile.new(:get, /.*a=a b.*/))
+ end
+
+ it "should match if other regexp is for escaped parameters but uri has non escaped parameters" do
+ RequestProfile.new(:get, "www.google.com/?a=a b").
+ should match(RequestProfile.new(:get, /.*a=a%20b.*/))
+ end
+
+ end
+
+
it "should match for same bodies" do
RequestProfile.new(:get, "www.google.com", "abc").
View
@@ -3,6 +3,7 @@
require 'webmock'
require 'spec'
require 'spec/autorun'
+require 'addressable/uri'
include WebMock
@@ -56,3 +57,4 @@ def setup_expectations_for_real_google_request(options = {})
:response_body => "<title>Google fake response</title>" }
setup_expectations_for_real_request(defaults.merge(options))
end
+
Oops, something went wrong.

0 comments on commit fba5e3e

Please sign in to comment.