diff --git a/lib/httparty.rb b/lib/httparty.rb index 8664cbf..c12148c 100644 --- a/lib/httparty.rb +++ b/lib/httparty.rb @@ -127,6 +127,29 @@ module HTTParty end end + # Declare whether or not to follow redirects. When true, an + # {HTTParty::RedirectionTooDeep} error will raise upon encountering a + # redirect. You can then gain access to the response object via + # HTTParty::RedirectionTooDeep#response. + # + # @see HTTParty::ResponseError#response + # + # @example + # class Foo + # include HTTParty + # base_uri 'http://google.com' + # no_follow true + # end + # + # begin + # Foo.get('/') + # rescue HTTParty::RedirectionTooDeep => e + # puts e.response.body + # end + def no_follow(value = false) + default_options[:no_follow] = value + end + # Allows setting a PEM file to be used # # class Foo diff --git a/lib/httparty/request.rb b/lib/httparty/request.rb index 774f3ae..67ac94f 100644 --- a/lib/httparty/request.rb +++ b/lib/httparty/request.rb @@ -13,13 +13,13 @@ module HTTParty SupportedURISchemes = [URI::HTTP, URI::HTTPS] - attr_accessor :http_method, :path, :options + attr_accessor :http_method, :path, :options, :last_response def initialize(http_method, path, o={}) self.http_method = http_method self.path = path self.options = { - :limit => o.delete(:no_follow) ? 0 : 5, + :limit => o.delete(:no_follow) ? 1 : 5, :default_params => {}, :parser => Parser }.merge(o) @@ -56,7 +56,8 @@ module HTTParty def perform validate setup_raw_request - handle_response(get_response) + get_response + handle_response end private @@ -113,9 +114,8 @@ module HTTParty end def get_response - response = perform_actual_request - options[:format] ||= format_from_mimetype(response['content-type']) - response + self.last_response = perform_actual_request + options[:format] ||= format_from_mimetype(last_response['content-type']) end def query_string(uri) @@ -133,26 +133,26 @@ module HTTParty end # Raises exception Net::XXX (http error code) if an http error occured - def handle_response(response) - case response + def handle_response + case last_response when Net::HTTPMultipleChoice, # 300 Net::HTTPMovedPermanently, # 301 Net::HTTPFound, # 302 Net::HTTPSeeOther, # 303 Net::HTTPUseProxy, # 305 Net::HTTPTemporaryRedirect - if response.key?('location') + if last_response.key?('location') options[:limit] -= 1 - self.path = response['location'] + self.path = last_response['location'] @redirect = true self.http_method = Net::HTTP::Get - capture_cookies(response) + capture_cookies(last_response) perform else - response + last_response end else - Response.new(parse_response(response.body), response.body, response.code, response.message, response.to_hash) + Response.new(parse_response(last_response.body), last_response.body, last_response.code, last_response.message, last_response.to_hash) end end @@ -179,7 +179,7 @@ module HTTParty end def validate - raise HTTParty::RedirectionTooDeep, 'HTTP redirects too deep' if options[:limit].to_i <= 0 + raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0 raise ArgumentError, 'only get, post, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method) raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash) raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash) diff --git a/spec/httparty_spec.rb b/spec/httparty_spec.rb index 6bc9b26..97a683a 100644 --- a/spec/httparty_spec.rb +++ b/spec/httparty_spec.rb @@ -304,42 +304,60 @@ describe HTTParty do end end + describe "#no_follow" do + it "sets no_follow to false by default" do + @klass.no_follow + @klass.default_options[:no_follow].should be_false + end + + it "sets the no_follow option to true" do + @klass.no_follow true + @klass.default_options[:no_follow].should be_true + end + end + describe "with explicit override of automatic redirect handling" do + before do + @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml, :no_follow => true) + @redirect = stub_response 'first redirect', 302 + @redirect['location'] = 'http://foo.com/bar' + HTTParty::Request.stub(:new => @request) + end it "should fail with redirected GET" do lambda do - @klass.get('/foo', :no_follow => true) - end.should raise_error(HTTParty::RedirectionTooDeep) + @error = @klass.get('/foo', :no_follow => true) + end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'} end it "should fail with redirected POST" do lambda do @klass.post('/foo', :no_follow => true) - end.should raise_error(HTTParty::RedirectionTooDeep) + end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'} end it "should fail with redirected DELETE" do lambda do @klass.delete('/foo', :no_follow => true) - end.should raise_error(HTTParty::RedirectionTooDeep) + end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'} end it "should fail with redirected PUT" do lambda do @klass.put('/foo', :no_follow => true) - end.should raise_error(HTTParty::RedirectionTooDeep) + end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'} end it "should fail with redirected HEAD" do lambda do @klass.head('/foo', :no_follow => true) - end.should raise_error(HTTParty::RedirectionTooDeep) + end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'} end it "should fail with redirected OPTIONS" do lambda do @klass.options('/foo', :no_follow => true) - end.should raise_error(HTTParty::RedirectionTooDeep) + end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'} end end