webmock icon indicating copy to clipboard operation
webmock copied to clipboard

`uri` method on mocked `Net::HTTPOK` incorrectly returns nil

Open abevoelker opened this issue 10 years ago • 7 comments

require 'webmock'
include WebMock::API

WebMock.allow_net_connect!

target_uri = URI.parse('http://www.google.com')

real_request = Net::HTTP.get_response(target_uri) # => #<Net::HTTPOK 200 OK readbody=true>
real_request.uri # => #<URI::HTTP http://www.google.com>

WebMock.disable_net_connect!
stub_request(:get, "http://www.google.com/").
  to_return(:status => 200, :body => "", :headers => {})

mocked_request = Net::HTTP.get_response(target_uri) # => #<Net::HTTPOK 200  readbody=true>
mocked_request.uri # => nil

I believe the method is inherited from HTTPResponse#uri.

This problem occurred on Ruby 2.2.0 and webmock version 1.21.0 as well as master (currently 9e7162f6).

abevoelker avatar Apr 26 '15 05:04 abevoelker

@abevoelker thank you for reporting that. I'd need to investigate more to find out what the root cause is, unless someone investigates that before me.

bblimke avatar Apr 28 '15 09:04 bblimke

No rush, I was able to avoid it by restructuring my code. Thanks for the neat library!

abevoelker avatar Apr 28 '15 13:04 abevoelker

I have something similar going on in my Net::HTTP wrapper. I attach the last_response instance variable to the response body to know the last resolved url in case of redirects. And it seems to have uri unset:

(byebug) head.instance_variable_get(:@last_response)
#<Net::HTTPOK 200  readbody=true>
(byebug) head.instance_variable_get(:@last_response).uri
nil

UPD: maybe somewhere here https://github.com/bblimke/webmock/blob/d03e76ae41f6d7fbbe31ff45cb077234de4f94aa/lib/webmock/http_lib_adapters/net_http.rb#L83 it should add uri to the response. Or maybe response.uri = @uri here: https://github.com/bblimke/webmock/blob/d03e76ae41f6d7fbbe31ff45cb077234de4f94aa/lib/webmock/http_lib_adapters/net_http.rb#L179

Still there is a problem after this edit that if there is a chain of 302 redirects, the .uri gives me the 2nd of 4 uris. Seems like the @uri variable changes by the first redirect Location and does not change afterwards:

p response.uri = @uri
stub_request(:head, "https://flic.kr/p/vPvCWJ").                          to_return status: [302, "Found"], headers: {location: "https://www.flickr.com/photo.gne?short=vPvCWJ"}
stub_request(:head, "https://www.flickr.com/photo.gne?short=vPvCWJ").     to_return status: [302, "Found"], headers: {location: "/photo.gne?rb=1&short=vPvCWJ"}
stub_request(:head, "https://www.flickr.com/photo.gne?rb=1&short=vPvCWJ").to_return status: [302, "Found"], headers: {location: "/photos/mlopezphotography/19572004110/"}
stub_request(:head, "https://www.flickr.com/photos/mlopezphotography/19572004110/")
#<URI::HTTPS https://flic.kr/p/vPvCWJ>
#<URI::HTTPS https://www.flickr.com/photo.gne?short=vPvCWJ>
#<URI::HTTPS https://www.flickr.com/photo.gne?short=vPvCWJ>
#<URI::HTTPS https://www.flickr.com/photo.gne?short=vPvCWJ>

Not sure yet if this behaviour is in webmock or on my side but I know for sure that it was ok (and I had the 4th uri in the end) in my tests before webmock. Maybe @uri is just a wrong thing to assign.

UPD2: it works if I assign the request.uri instead of @uri. The monkey patch is:

WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get(:@webMockNetHTTP).class_eval do
  old_request = instance_method :request
  define_method :request do |request, &block|
    old_request.bind(self).(request, &block).tap do |response|
      response.uri = request.uri
    end
  end
end

Nakilon avatar Dec 30 '20 10:12 Nakilon

Is this something that could get made into a PR and merged in? I just hit it in a code where I want to follow redirects and update the model's url to the last one. Monkey patch still works, but feels like this should be fixed in core, no?

miharekar avatar Mar 21 '23 11:03 miharekar

FWIW, I built a test project that reproduces the nil uri behavior: https://github.com/Automattic/webmock-uri-demo.

mokagio avatar Oct 04 '23 05:10 mokagio

@Nakilon's monkey-patching suggestion worked for me. Thanks! RuboCop suggested a slight change, implementing the lambda call rule:

# Monkey-patch WebMock to work around response.uri not being set.
# See https://github.com/bblimke/webmock/issues/469
WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get(:@webMockNetHTTP).class_eval do
  old_request = instance_method :request
  define_method :request do |request, &block|
    old_request.bind(self).call(request, &block).tap do |response|
      response.uri = request.uri
    end
  end
end

mokagio avatar Oct 06 '23 02:10 mokagio

The fix is now released in version 3.21.1

bblimke avatar Feb 20 '24 03:02 bblimke