Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: bblimke/webmock
base: v1.8.8
...
head fork: bblimke/webmock
compare: v1.8.9
Checking mergeability… Don't worry, you can still create the pull request.
  • 9 commits
  • 21 files changed
  • 0 commit comments
  • 2 contributors
Commits on Jul 23, 2012
@bblimke Allow failures for Rubinius builds on Travis CI. ff99707
@bblimke Updated jruby-openssl dependency to the latest version. d709d7c
@bblimke Ignore errors on JRuby 1.9 on Travis using latest jruby-openssl gem. c31a3c0
Commits on Aug 07, 2012
@myronmarston myronmarston Clear cached webmock response in HTTPClient instance.
When the a request was not stubbed, the `nil` value of the webmock response was
being cached in the `webmock_responses` hash. Then, when a second request was 
made with the same HTTPClient instance and an identical signature, it was not 
checking for a stub again, even though there may have been one for the second request (e.g. when using a global stub hook or if another stub is registered between the 1st and 2nd requests).

Fixes myronmarston/vcr#190.
653d92f
Commits on Aug 13, 2012
@myronmarston myronmarston Prefer super to alias method chain.
WebMock was using alias method chain in lots of situations where it didn't need to, since most of the adapters subclass the HTTP client.
caeb0da
Commits on Aug 14, 2012
@bblimke Merge pull request #199 from bblimke/vcr-issue-190
Clear cached webmock response in HTTPClient instance.
430f60d
@bblimke Merge pull request #201 from bblimke/prefer_super
Prefer super to alias method chain.
f964815
Commits on Aug 15, 2012
@bblimke Added support for Addressable >= 2.3.0. Addressable 2.3.0 removed sup…
…port for multiple query value notations and broke backwards compatibility.


sporkmonger/addressable@f51e290
sporkmonger/addressable#77
64eefeb
@bblimke Version 1.8.9 43098e1
View
5 .travis.yml
@@ -7,5 +7,10 @@ rvm:
- jruby-18mode
- rbx-18mode
- rbx-19mode
+matrix:
+ allow_failures:
+ - rvm: rbx-18mode
+ - rvm: rbx-19mode
+ - rvm: jruby-19mode
script: "bundle exec rake && rake em_http_request_0_x_spec"
View
12 CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## 1.8.9
+
+* Fixed problem with caching nil responses when the same HTTPClient instance is used.
+
+ Thanks to [Myron Marston](https://github.com/myronmarston)
+
+* Added support for Addressable >= 2.3.0. Addressable 2.3.0 removed support for multiple query value notations and broke backwards compatibility.
+
+ https://github.com/sporkmonger/addressable/commit/f51e290b5f68a98293327a7da84eb9e2d5f21c62
+ https://github.com/sporkmonger/addressable/issues/77
+
+
## 1.8.8
* Fixed Net::HTTP adapter so that it returns `nil` for an empty body response.
View
2  Gemfile
@@ -17,5 +17,5 @@ group :test do
end
platforms :jruby do
- gem 'jruby-openssl', '~> 0.7.4.0'
+ gem 'jruby-openssl', '~> 0.7.7'
end
View
1  README.md
@@ -712,6 +712,7 @@ People who submitted patches and new features or suggested improvements. Many th
* Julien Boyer
* Kevin Glowacz
* Hans Hasselberg
+* Andrew France
For a full list of contributors you can visit the
[contributors](https://github.com/bblimke/webmock/contributors) page.
View
1  lib/webmock.rb
@@ -8,6 +8,7 @@
require 'webmock/errors'
+require 'webmock/util/query_mapper'
require 'webmock/util/uri'
require 'webmock/util/headers'
require 'webmock/util/hash_counter'
View
94 lib/webmock/http_lib_adapters/curb_adapter.rb
@@ -170,117 +170,83 @@ def build_webmock_response
### Mocks of Curl::Easy methods below here.
###
- def http_with_webmock(method)
+ def http(method)
@webmock_method = method
- http_without_webmock(method)
+ super
end
- alias_method :http_without_webmock, :http
- alias_method :http, :http_with_webmock
%w[ get head delete ].each do |verb|
- define_method "http_#{verb}_with_webmock" do
+ define_method "http_#{verb}" do
@webmock_method = verb
- send( "http_#{verb}_without_webmock" )
+ super()
end
-
- alias_method "http_#{verb}_without_webmock", "http_#{verb}"
- alias_method "http_#{verb}", "http_#{verb}_with_webmock"
end
- def http_put_with_webmock data = nil
+ def http_put data = nil
@webmock_method = :put
@put_data = data if data
- http_put_without_webmock(data)
+ super
end
- alias_method :http_put_without_webmock, :http_put
- alias_method :http_put, :http_put_with_webmock
- def http_post_with_webmock *data
+ def http_post *data
@webmock_method = :post
@post_body = data.join('&') if data && !data.empty?
- http_post_without_webmock(*data)
+ super
end
- alias_method :http_post_without_webmock, :http_post
- alias_method :http_post, :http_post_with_webmock
-
- def perform_with_webmock
+ def perform
@webmock_method ||= :get
- curb_or_webmock do
- perform_without_webmock
- end
+ curb_or_webmock { super }
end
- alias :perform_without_webmock :perform
- alias :perform :perform_with_webmock
- def put_data_with_webmock= data
+ def put_data= data
@webmock_method = :put
@put_data = data
- self.put_data_without_webmock = data
+ super
end
- alias_method :put_data_without_webmock=, :put_data=
- alias_method :put_data=, :put_data_with_webmock=
- def post_body_with_webmock= data
+ def post_body= data
@webmock_method = :post
- self.post_body_without_webmock = data
+ super
end
- alias_method :post_body_without_webmock=, :post_body=
- alias_method :post_body=, :post_body_with_webmock=
- def delete_with_webmock= value
+ def delete= value
@webmock_method = :delete if value
- self.delete_without_webmock = value
+ super
end
- alias_method :delete_without_webmock=, :delete=
- alias_method :delete=, :delete_with_webmock=
- def head_with_webmock= value
+ def head= value
@webmock_method = :head if value
- self.head_without_webmock = value
+ super
end
- alias_method :head_without_webmock=, :head=
- alias_method :head=, :head_with_webmock=
- def body_str_with_webmock
- @body_str || body_str_without_webmock
+ def body_str
+ @body_str || super
end
- alias :body_str_without_webmock :body_str
- alias :body_str :body_str_with_webmock
- def response_code_with_webmock
- @response_code || response_code_without_webmock
+ def response_code
+ @response_code || super
end
- alias :response_code_without_webmock :response_code
- alias :response_code :response_code_with_webmock
- def header_str_with_webmock
- @header_str || header_str_without_webmock
+ def header_str
+ @header_str || super
end
- alias :header_str_without_webmock :header_str
- alias :header_str :header_str_with_webmock
- def last_effective_url_with_webmock
- @last_effective_url || last_effective_url_without_webmock
+ def last_effective_url
+ @last_effective_url || super
end
- alias :last_effective_url_without_webmock :last_effective_url
- alias :last_effective_url :last_effective_url_with_webmock
- def content_type_with_webmock
- @content_type || content_type_without_webmock
+ def content_type
+ @content_type || super
end
- alias :content_type_without_webmock :content_type
- alias :content_type :content_type_with_webmock
%w[ success failure header body complete progress ].each do |callback|
class_eval <<-METHOD, __FILE__, __LINE__
- def on_#{callback}_with_webmock &block
+ def on_#{callback} &block
@on_#{callback} = block
- on_#{callback}_without_webmock &block
+ super
end
METHOD
- alias_method "on_#{callback}_without_webmock", "on_#{callback}"
- alias_method "on_#{callback}", "on_#{callback}_with_webmock"
end
end
end
View
8 lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb
@@ -47,7 +47,7 @@ def close_connection
end
end
- def send_request_with_webmock(&block)
+ def send_request(&block)
request_signature = build_request_signature
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
@@ -61,7 +61,7 @@ def send_request_with_webmock(&block)
webmock_response.should_timeout ? "WebMock timeout error" : nil)
client
elsif WebMock.net_connect_allowed?(request_signature.uri)
- http = send_request_without_webmock(&block)
+ http = super
http.callback {
if WebMock::CallbackRegistry.any_callbacks?
webmock_response = build_webmock_response(http)
@@ -76,10 +76,6 @@ def send_request_with_webmock(&block)
end
end
- alias_method :send_request_without_webmock, :send_request
- alias_method :send_request, :send_request_with_webmock
-
-
private
def build_webmock_response(http)
View
13 lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb
@@ -48,7 +48,7 @@ def #{type}(options = {}, &blk)
end
class WebMockHttpConnection < HttpConnection
- def webmock_activate_connection(client)
+ def activate_connection(client)
request_signature = client.request_signature
if client.stubbed_webmock_response
@@ -65,13 +65,11 @@ def webmock_activate_connection(client)
finalize_request(client)
@conn.set_deferred_status :succeeded
elsif WebMock.net_connect_allowed?(request_signature.uri)
- real_activate_connection(client)
+ super
else
raise WebMock::NetConnectNotAllowedError.new(request_signature)
end
end
- alias_method :real_activate_connection, :activate_connection
- alias_method :activate_connection, :webmock_activate_connection
end
class WebMockHttpClient < EventMachine::HttpClient
@@ -92,7 +90,7 @@ def setup(response, uri, error = nil)
end
end
- def send_request_with_webmock(head, body)
+ def send_request(head, body)
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
if stubbed_webmock_response
@@ -104,15 +102,12 @@ def send_request_with_webmock(head, body)
}
self
elsif WebMock.net_connect_allowed?(request_signature.uri)
- send_request_without_webmock(head, body)
+ super
else
raise WebMock::NetConnectNotAllowedError.new(request_signature)
end
end
- alias_method :send_request_without_webmock, :send_request
- alias_method :send_request, :send_request_with_webmock
-
def set_deferred_status(status, *args)
if status == :succeeded && !stubbed_webmock_response && WebMock::CallbackRegistry.any_callbacks?
webmock_response = build_webmock_response
View
2  lib/webmock/http_lib_adapters/excon_adapter.rb
@@ -42,7 +42,7 @@ def self.build_request(params)
params = params.dup
method = (params.delete(:method) || :get).to_s.downcase.to_sym
params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash)
- uri = Addressable::URI.new(params).to_s
+ uri = Addressable::URI.new(params).to_s
WebMock::RequestSignature.new method, uri, :body => params[:body], :headers => params[:headers]
end
View
30 lib/webmock/http_lib_adapters/httpclient_adapter.rb
@@ -28,16 +28,18 @@ def self.disable!
class WebMockHTTPClient < HTTPClient
+ alias_method :do_get_block_without_webmock, :do_get_block
+ alias_method :do_get_stream_without_webmock, :do_get_stream
- def do_get_block_with_webmock(req, proxy, conn, &block)
- do_get_with_webmock(req, proxy, conn, false, &block)
+ def do_get_block(req, proxy, conn, &block)
+ do_get(req, proxy, conn, false, &block)
end
- def do_get_stream_with_webmock(req, proxy, conn, &block)
- do_get_with_webmock(req, proxy, conn, true, &block)
+ def do_get_stream(req, proxy, conn, &block)
+ do_get(req, proxy, conn, true, &block)
end
- def do_get_with_webmock(req, proxy, conn, stream = false, &block)
+ def do_get(req, proxy, conn, stream = false, &block)
request_signature = build_request_signature(req, :reuse_existing)
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
@@ -53,6 +55,9 @@ def do_get_with_webmock(req, proxy, conn, stream = false, &block)
{:lib => :httpclient}, request_signature, webmock_response)
res
elsif WebMock.net_connect_allowed?(request_signature.uri)
+ # in case there is a nil entry in the hash...
+ webmock_responses.delete(request_signature)
+
res = if stream
do_get_stream_without_webmock(req, proxy, conn, &block)
else
@@ -72,27 +77,18 @@ def do_get_with_webmock(req, proxy, conn, stream = false, &block)
end
end
- def do_request_async_with_webmock(method, uri, query, body, extheader)
+ def do_request_async(method, uri, query, body, extheader)
req = create_request(method, uri, query, body, extheader)
request_signature = build_request_signature(req)
webmock_request_signatures << request_signature
if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
- do_request_async_without_webmock(method, uri, query, body, extheader)
+ super
else
raise WebMock::NetConnectNotAllowedError.new(request_signature)
end
end
- alias_method :do_get_block_without_webmock, :do_get_block
- alias_method :do_get_block, :do_get_block_with_webmock
-
- alias_method :do_get_stream_without_webmock, :do_get_stream
- alias_method :do_get_stream, :do_get_stream_with_webmock
-
- alias_method :do_request_async_without_webmock, :do_request_async
- alias_method :do_request_async, :do_request_async_with_webmock
-
def build_httpclient_response(webmock_response, stream = false, &block)
body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
response = HTTP::Message.new_response(body)
@@ -136,7 +132,7 @@ def build_webmock_response(httpclient_response)
def build_request_signature(req, reuse_existing = false)
uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
- uri.query_values = req.header.request_query if req.header.request_query
+ uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query) if req.header.request_query
uri.port = req.header.request_uri.port
uri = uri.omit(:userinfo)
View
24 lib/webmock/http_lib_adapters/net_http.rb
@@ -32,11 +32,9 @@ def self.disable!
@webMockNetHTTP = Class.new(Net::HTTP) do
class << self
- def socket_type_with_webmock
+ def socket_type
StubSocket
end
- alias_method :socket_type_without_webmock, :socket_type
- alias_method :socket_type, :socket_type_with_webmock
if Module.method(:const_defined?).arity == 1
def const_defined?(name)
@@ -63,7 +61,7 @@ def constants(inherit=true)
end
end
- def request_with_webmock(request, body = nil, &block)
+ 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)
@@ -88,19 +86,17 @@ def request_with_webmock(request, body = nil, &block)
response = if (started? && !WebMock::Config.instance.net_http_connect_on_start) || !started?
@started = false #otherwise start_with_connect wouldn't execute and connect
start_with_connect {
- response = request_without_webmock(request, nil)
+ response = super(request, nil, &nil)
after_request.call(response)
}
else
- response = request_without_webmock(request, nil)
+ response = super(request, nil, &nil)
after_request.call(response)
end
else
raise WebMock::NetConnectNotAllowedError.new(request_signature)
end
end
- alias_method :request_without_webmock, :request
- alias_method :request, :request_with_webmock
def start_without_connect
raise IOError, 'HTTP session already opened' if @started
@@ -116,15 +112,15 @@ def start_without_connect
self
end
- def start_with_conditional_connect(&block)
+ alias_method :start_with_connect, :start
+
+ def start(&block)
if WebMock::Config.instance.net_http_connect_on_start
- start_with_connect(&block)
+ super(&block)
else
start_without_connect(&block)
end
end
- alias_method :start_with_connect, :start
- alias_method :start, :start_with_conditional_connect
def build_net_http_response(webmock_response, &block)
response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
@@ -204,7 +200,7 @@ def readuntil(*args)
module Net #:nodoc: all
class WebMockNetBufferedIO < BufferedIO
- def initialize_with_webmock(io, debug_output = nil)
+ def initialize(io, debug_output = nil)
@read_timeout = 60
@rbuf = ''
@debug_output = debug_output
@@ -217,8 +213,6 @@ def initialize_with_webmock(io, debug_output = nil)
end
raise "Unable to create local socket" unless @io
end
- alias_method :initialize_without_webmock, :initialize
- alias_method :initialize, :initialize_with_webmock
end
end
View
7 lib/webmock/http_lib_adapters/patron_adapter.rb
@@ -13,7 +13,7 @@ class PatronAdapter < ::WebMock::HttpLibAdapter
OriginalPatronSession = ::Patron::Session unless const_defined?(:OriginalPatronSession)
class WebMockPatronSession < ::Patron::Session
- def handle_request_with_webmock(req)
+ def handle_request(req)
request_signature =
WebMock::HttpLibAdapters::PatronAdapter.build_request_signature(req)
@@ -28,7 +28,7 @@ def handle_request_with_webmock(req)
{:lib => :patron}, request_signature, webmock_response)
res
elsif WebMock.net_connect_allowed?(request_signature.uri)
- res = handle_request_without_webmock(req)
+ res = super
if WebMock::CallbackRegistry.any_callbacks?
webmock_response = WebMock::HttpLibAdapters::PatronAdapter.
build_webmock_response(res)
@@ -41,9 +41,6 @@ def handle_request_with_webmock(req)
raise WebMock::NetConnectNotAllowedError.new(request_signature)
end
end
-
- alias_method :handle_request_without_webmock, :handle_request
- alias_method :handle_request, :handle_request_with_webmock
end
def self.enable!
View
12 lib/webmock/request_pattern.rb
@@ -95,7 +95,7 @@ def add_query_params(query_params)
elsif rSpecHashIncludingMatcher?(query_params)
WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params)
else
- Addressable::URI.parse('?' + query_params).query_values
+ WebMock::Util::QueryMapper.query_to_values(query_params)
end
end
@@ -109,7 +109,7 @@ def to_s
class URIRegexpPattern < URIPattern
def matches?(uri)
WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } &&
- (@query_params.nil? || @query_params == uri.query_values)
+ (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query))
end
def to_s
@@ -123,7 +123,8 @@ class URIStringPattern < URIPattern
def matches?(uri)
if @pattern.is_a?(Addressable::URI)
if @query_params
- uri.omit(:query) === @pattern && (@query_params.nil? || @query_params == uri.query_values)
+ uri.omit(:query) === @pattern &&
+ (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query))
else
uri === @pattern
end
@@ -135,7 +136,8 @@ def matches?(uri)
def add_query_params(query_params)
super
if @query_params.is_a?(Hash) || @query_params.is_a?(String)
- @pattern.query_values = (@pattern.query_values || {}).merge(@query_params)
+ query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query) || {}).merge(@query_params)
+ @pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash)
@query_params = nil
end
end
@@ -199,7 +201,7 @@ def body_as_hash(body, content_type)
when :xml then
Crack::XML.parse(body)
else
- Addressable::URI.parse('?' + body).query_values
+ WebMock::Util::QueryMapper.query_to_values(body)
end
end
View
2  lib/webmock/request_stub.rb
@@ -81,7 +81,7 @@ def self.from_request_signature(signature)
if signature.body.to_s != ''
body = if signature.url_encoded?
- Addressable::URI.parse('?' + signature.body).query_values
+ WebMock::Util::QueryMapper.query_to_values(signature.body)
else
signature.body
end
View
188 lib/webmock/util/query_mapper.rb
@@ -0,0 +1,188 @@
+module WebMock::Util
+ class QueryMapper
+ #This class is based on Addressable::URI pre 2.3.0
+
+ ##
+ # Converts the query component to a Hash value.
+ #
+ # @option [Symbol] notation
+ # May be one of <code>:flat</code>, <code>:dot</code>, or
+ # <code>:subscript</code>. The <code>:dot</code> notation is not
+ # supported for assignment. Default value is <code>:subscript</code>.
+ #
+ # @return [Hash, Array] The query string parsed as a Hash or Array object.
+ #
+ # @example
+ # WebMock::Util::QueryMapper.query_to_values("?one=1&two=2&three=3")
+ # #=> {"one" => "1", "two" => "2", "three" => "3"}
+ # WebMock::Util::QueryMapper("?one[two][three]=four").query_values
+ # #=> {"one" => {"two" => {"three" => "four"}}}
+ # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
+ # :notation => :dot
+ # )
+ # #=> {"one" => {"two" => {"three" => "four"}}}
+ # WebMock::Util::QueryMapper.query_to_values("?one[two][three]=four",
+ # :notation => :flat
+ # )
+ # #=> {"one[two][three]" => "four"}
+ # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
+ # :notation => :flat
+ # )
+ # #=> {"one.two.three" => "four"}
+ # WebMock::Util::QueryMapper(
+ # "?one[two][three][]=four&one[two][three][]=five"
+ # )
+ # #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
+ # WebMock::Util::QueryMapper.query_to_values(
+ # "?one=two&one=three").query_values(:notation => :flat_array)
+ # #=> [['one', 'two'], ['one', 'three']]
+ def self.query_to_values(query, options={})
+ defaults = {:notation => :subscript}
+ options = defaults.merge(options)
+ if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
+ raise ArgumentError,
+ "Invalid notation. Must be one of: " +
+ "[:flat, :dot, :subscript, :flat_array]."
+ end
+ dehash = lambda do |hash|
+ hash.each do |(key, value)|
+ if value.kind_of?(Hash)
+ hash[key] = dehash.call(value)
+ end
+ end
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
+ hash.sort.inject([]) do |accu, (_, value)|
+ accu << value; accu
+ end
+ else
+ hash
+ end
+ end
+ return nil if query == nil
+ empty_accumulator = :flat_array == options[:notation] ? [] : {}
+ return ((query.split("&").map do |pair|
+ pair.split("=", 2) if pair && !pair.empty?
+ end).compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
+ value = true if value.nil?
+ key = Addressable::URI.unencode_component(key)
+ if value != true
+ value = Addressable::URI.unencode_component(value.gsub(/\+/, " "))
+ end
+ if options[:notation] == :flat
+ if accumulator[key]
+ raise ArgumentError, "Key was repeated: #{key.inspect}"
+ end
+ accumulator[key] = value
+ elsif options[:notation] == :flat_array
+ accumulator << [key, value]
+ else
+ if options[:notation] == :dot
+ array_value = false
+ subkeys = key.split(".")
+ elsif options[:notation] == :subscript
+ array_value = !!(key =~ /\[\]$/)
+ subkeys = key.split(/[\[\]]+/)
+ end
+ current_hash = accumulator
+ for i in 0...(subkeys.size - 1)
+ subkey = subkeys[i]
+ current_hash[subkey] = {} unless current_hash[subkey]
+ current_hash = current_hash[subkey]
+ end
+ if array_value
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
+ current_hash[subkeys.last] << value
+ else
+ current_hash[subkeys.last] = value
+ end
+ end
+ accumulator
+ end).inject(empty_accumulator.dup) do |accumulator, (key, value)|
+ if options[:notation] == :flat_array
+ accumulator << [key, value]
+ else
+ accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
+ end
+ accumulator
+ end
+ end
+
+ ##
+ # Sets the query component for this URI from a Hash object.
+ # This method produces a query string using the :subscript notation.
+ # An empty Hash will result in a nil query.
+ #
+ # @param [Hash, #to_hash, Array] new_query_values The new query values.
+ def self.values_to_query(new_query_values)
+ if new_query_values == nil
+ return nil
+ end
+
+ if !new_query_values.is_a?(Array)
+ if !new_query_values.respond_to?(:to_hash)
+ raise TypeError,
+ "Can't convert #{new_query_values.class} into Hash."
+ end
+ new_query_values = new_query_values.to_hash
+ new_query_values = new_query_values.map do |key, value|
+ key = key.to_s if key.kind_of?(Symbol)
+ [key, value]
+ end
+ # Useful default for OAuth and caching.
+ # Only to be used for non-Array inputs. Arrays should preserve order.
+ new_query_values.sort!
+ end
+
+ ##
+ # Joins and converts parent and value into a properly encoded and
+ # ordered URL query.
+ #
+ # @private
+ # @param [String] parent an URI encoded component.
+ # @param [Array, Hash, Symbol, #to_str] value
+ #
+ # @return [String] a properly escaped and ordered URL query.
+ to_query = lambda do |parent, value|
+ if value.is_a?(Hash)
+ value = value.map do |key, val|
+ [
+ Addressable::URI.encode_component(key, Addressable::URI::CharacterClasses::UNRESERVED),
+ val
+ ]
+ end
+ value.sort!
+ buffer = ""
+ value.each do |key, val|
+ new_parent = "#{parent}[#{key}]"
+ buffer << "#{to_query.call(new_parent, val)}&"
+ end
+ return buffer.chop
+ elsif value.is_a?(Array)
+ buffer = ""
+ value.each_with_index do |val, i|
+ new_parent = "#{parent}[#{i}]"
+ buffer << "#{to_query.call(new_parent, val)}&"
+ end
+ return buffer.chop
+ elsif value == true
+ return parent
+ else
+ encoded_value = Addressable::URI.encode_component(
+ value, Addressable::URI::CharacterClasses::UNRESERVED
+ )
+ return "#{parent}=#{encoded_value}"
+ end
+ end
+
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
+ buffer = ""
+ new_query_values.each do |parent, value|
+ encoded_parent = Addressable::URI.encode_component(
+ parent, Addressable::URI::CharacterClasses::UNRESERVED
+ )
+ buffer << "#{to_query.call(encoded_parent, value)}&"
+ end
+ return buffer.chop
+ end
+ end
+end
View
19 lib/webmock/util/uri.rb
@@ -1,23 +1,22 @@
-module Addressable
- class URI
- module CharacterClasses
- USERINFO = UNRESERVED + SUB_DELIMS + "\\:"
- end
- end
-end
-
module WebMock
module Util
class URI
+ module CharacterClasses
+ USERINFO = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS + "\\:"
+ end
+
ADDRESSABLE_URIS = Hash.new do |hash, key|
hash[key] = Addressable::URI.heuristic_parse(key)
end
NORMALIZED_URIS = Hash.new do |hash, uri|
normalized_uri = WebMock::Util::URI.heuristic_parse(uri)
- normalized_uri.query_values = sort_query_values(normalized_uri.query_values) if normalized_uri.query_values
+ if normalized_uri.query_values
+ sorted_query_values = sort_query_values(WebMock::Util::QueryMapper.query_to_values(normalized_uri.query) || {})
+ normalized_uri.query = WebMock::Util::QueryMapper.values_to_query(sorted_query_values)
+ end
normalized_uri = normalized_uri.normalize #normalize! is slower
normalized_uri.port = normalized_uri.inferred_port unless normalized_uri.port
hash[uri] = normalized_uri
@@ -63,7 +62,7 @@ def self.strip_default_port_from_uri_string(uri_string)
end
def self.encode_unsafe_chars_in_userinfo(userinfo)
- Addressable::URI.encode_component(userinfo, Addressable::URI::CharacterClasses::USERINFO)
+ Addressable::URI.encode_component(userinfo, WebMock::Util::URI::CharacterClasses::USERINFO)
end
def self.is_uri_localhost?(uri)
View
2  lib/webmock/version.rb
@@ -1,3 +1,3 @@
module WebMock
- VERSION = '1.8.8' unless defined?(::WebMock::VERSION)
+ VERSION = '1.8.9' unless defined?(::WebMock::VERSION)
end
View
27 spec/acceptance/httpclient/httpclient_spec.rb
@@ -73,4 +73,31 @@ def filter_response(request, response)
end
end
+ context 'when a client instance is re-used for another identical request' do
+ let(:client) { HTTPClient.new }
+ let(:webmock_server_url) {"http://#{WebMockServer.instance.host_with_port}/"}
+
+ before { WebMock.allow_net_connect! }
+
+ it 'invokes the global_stub_request hook for each request' do
+ request_signatures = []
+ WebMock.globally_stub_request do |request_sig|
+ request_signatures << request_sig
+ nil # to let the request be made for real
+ end
+
+ # To make two requests that have the same request signature, the headers must match.
+ # Since the webmock server has a Set-Cookie header, the 2nd request will automatically
+ # include a Cookie header (due to how httpclient works), so we have to set the header
+ # manually on the first request but not on the 2nd request.
+ http_request(:get, webmock_server_url, :client => client,
+ :headers => { "Cookie" => "bar=; foo=" })
+ http_request(:get, webmock_server_url, :client => client)
+
+ request_signatures.should have(2).signatures
+ # Verify the request signatures were identical as needed by this example
+ request_signatures.first.should eq(request_signatures.last)
+ end
+ end
+
end
View
4 spec/acceptance/httpclient/httpclient_spec_helper.rb
@@ -5,11 +5,11 @@ class << self
def http_request(method, uri, options = {}, &block)
uri = Addressable::URI.heuristic_parse(uri)
- c = HTTPClient.new
+ c = options.fetch(:client) { HTTPClient.new }
c.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
c.set_basic_auth(nil, uri.user, uri.password) if uri.user
params = [method, "#{uri.omit(:userinfo, :query).normalize.to_s}",
- uri.query_values, options[:body], options[:headers] || {}]
+ WebMock::Util::QueryMapper.query_to_values(uri.query), options[:body], options[:headers] || {}]
if HTTPClientSpecHelper.async_mode
connection = c.request_async(*params)
connection.join
View
6 spec/unit/util/uri_spec.rb
@@ -170,19 +170,19 @@
it "should successfully handle array parameters" do
uri_string = 'http://www.example.com:80/path?a[]=b&a[]=c'
uri = WebMock::Util::URI.normalize_uri(uri_string)
- uri.query_values.should == {"a"=>["b", "c"]}
+ WebMock::Util::QueryMapper.query_to_values(uri.query).should == {"a"=>["b", "c"]}
end
it "should successfully handle hash parameters" do
uri_string = 'http://www.example.com:80/path?a[d]=b&a[e]=c&a[b][c]=1'
uri = WebMock::Util::URI.normalize_uri(uri_string)
- uri.query_values.should == {"a"=>{"d"=>"b", "e"=>"c", "b"=>{"c"=>"1"}}}
+ WebMock::Util::QueryMapper.query_to_values(uri.query).should == {"a"=>{"d"=>"b", "e"=>"c", "b"=>{"c"=>"1"}}}
end
it "should successfully handle nested hash parameters" do
uri_string = 'http://www.example.com:80/path?one[two][three][]=four&one[two][three][]=five'
uri = WebMock::Util::URI.normalize_uri(uri_string)
- uri.query_values.should == {"one"=>{"two"=>{"three" => ["four", "five"]}}}
+ WebMock::Util::QueryMapper.query_to_values(uri.query).should == {"one"=>{"two"=>{"three" => ["four", "five"]}}}
end
end
View
2  webmock.gemspec
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
s.rubyforge_project = 'webmock'
- s.add_dependency 'addressable', '~> 2.2.8'
+ s.add_dependency 'addressable', '>= 2.2.7'
s.add_dependency 'crack', '>=0.1.7'
s.add_development_dependency 'rspec', '~> 2.10'

No commit comments for this range

Something went wrong with that request. Please try again.