1
0
Fork 0
mirror of https://github.com/rest-client/rest-client.git synced 2022-11-09 13:49:40 -05:00

Remove args from Response and normalize method.

It is redundant and confusing to pass both the Request args and the
Request when creating a RestClient::Response. Instead, when the response
object needs to read args from the request, access them on the request
object itself. For determining the HTTP method of the request, switch to
calling RestClient::Request#method.

Also normalize the Request#method to be a lowercase string. This makes
handling of redirection and other method-specific functionality actually
work regardless of how the method was provided to Request.new (:get,
'GET', 'get').

Fixes: #461
Fixes: #462
Fixes: #463
This commit is contained in:
Andy Brody 2016-05-01 16:57:54 -04:00
parent c469b3629c
commit e85a954ed8
9 changed files with 80 additions and 43 deletions

View file

@ -5,7 +5,7 @@ module RestClient
module AbstractResponse module AbstractResponse
attr_reader :net_http_res, :args, :request attr_reader :net_http_res, :request
def inspect def inspect
raise NotImplementedError.new('must override in subclass') raise NotImplementedError.new('must override in subclass')
@ -31,9 +31,8 @@ module RestClient
@raw_headers ||= @net_http_res.to_hash @raw_headers ||= @net_http_res.to_hash
end end
def response_set_vars(net_http_res, args, request) def response_set_vars(net_http_res, request)
@net_http_res = net_http_res @net_http_res = net_http_res
@args = args
@request = request @request = request
# prime redirection history # prime redirection history
@ -67,17 +66,27 @@ module RestClient
end end
# Return the default behavior corresponding to the response code: # Return the default behavior corresponding to the response code:
# the response itself for code in 200..206, redirection for 301, 302 and 307 in get and head cases, redirection for 303 and an exception in other cases #
# For 20x status codes: return the response itself
#
# For 30x status codes:
# 301, 302, 307: redirect GET / HEAD if there is a Location header
# 303: redirect, changing method to GET, if there is a Location header
#
# For all other responses, raise a response exception
#
def return!(&block) def return!(&block)
if (200..207).include? code case code
when 200..207
self self
elsif [301, 302, 307].include? code when 301, 302, 307
unless [:get, :head].include? args[:method] case request.method
raise exception_with_response when 'get', 'head'
else
follow_redirection(&block) follow_redirection(&block)
else
raise exception_with_response
end end
elsif code == 303 when 303
follow_get_redirection(&block) follow_get_redirection(&block)
else else
raise exception_with_response raise exception_with_response
@ -96,13 +105,13 @@ module RestClient
# Follow a redirection response by making a new HTTP request to the # Follow a redirection response by making a new HTTP request to the
# redirection target. # redirection target.
def follow_redirection(&block) def follow_redirection(&block)
_follow_redirection(@args.dup, &block) _follow_redirection(request.args.dup, &block)
end end
# Follow a redirection response, but change the HTTP method to GET and drop # Follow a redirection response, but change the HTTP method to GET and drop
# the payload from the original request. # the payload from the original request.
def follow_get_redirection(&block) def follow_get_redirection(&block)
new_args = @args.dup new_args = request.args.dup
new_args[:method] = :get new_args[:method] = :get
new_args.delete(:payload) new_args.delete(:payload)

View file

@ -19,9 +19,8 @@ module RestClient
"<RestClient::RawResponse @code=#{code.inspect}, @file=#{file.inspect}, @request=#{request.inspect}>" "<RestClient::RawResponse @code=#{code.inspect}, @file=#{file.inspect}, @request=#{request.inspect}>"
end end
def initialize(tempfile, net_http_res, args, request) def initialize(tempfile, net_http_res, request)
@net_http_res = net_http_res @net_http_res = net_http_res
@args = args
@file = tempfile @file = tempfile
@request = request @request = request
end end

View file

@ -115,7 +115,7 @@ module RestClient
end end
def initialize args def initialize args
@method = args[:method] or raise ArgumentError, "must pass :method" @method = normalize_method(args[:method])
@headers = (args[:headers] || {}).dup @headers = (args[:headers] || {}).dup
if args[:url] if args[:url]
@url = process_url_params(args[:url], headers) @url = process_url_params(args[:url], headers)
@ -332,7 +332,7 @@ module RestClient
end end
def net_http_request_class(method) def net_http_request_class(method)
Net::HTTP.const_get(method.to_s.capitalize) Net::HTTP.const_get(method.capitalize, false)
end end
def net_http_do_request(http, req, body=nil, &block) def net_http_do_request(http, req, body=nil, &block)
@ -533,6 +533,22 @@ module RestClient
warned warned
end end
# Parse a method and return a normalized string version.
#
# Raise ArgumentError if the method is falsy, but otherwise do no
# validation.
#
# @param method [String, Symbol]
#
# @return [String]
#
# @see net_http_request_class
#
def normalize_method(method)
raise ArgumentError.new('must pass :method') unless method
method.to_s.downcase
end
def transmit uri, req, payload, & block def transmit uri, req, payload, & block
# We set this to true in the net/http block so that we can distinguish # We set this to true in the net/http block so that we can distinguish
@ -696,10 +712,10 @@ module RestClient
def process_result res, & block def process_result res, & block
if @raw_response if @raw_response
# We don't decode raw requests # We don't decode raw requests
response = RawResponse.new(@tf, res, args, self) response = RawResponse.new(@tf, res, self)
else else
decoded = Request.decode(res['content-encoding'], res.body) decoded = Request.decode(res['content-encoding'], res.body)
response = Response.create(decoded, res, args, self) response = Response.create(decoded, res, self)
end end
if block_given? if block_given?

View file

@ -38,10 +38,10 @@ module RestClient
"<RestClient::Response #{code.inspect} #{body_truncated(10).inspect}>" "<RestClient::Response #{code.inspect} #{body_truncated(10).inspect}>"
end end
def self.create(body, net_http_res, args, request) def self.create(body, net_http_res, request)
result = self.new(body || '') result = self.new(body || '')
result.response_set_vars(net_http_res, args, request) result.response_set_vars(net_http_res, request)
fix_encoding(result) fix_encoding(result)
result result

View file

@ -11,4 +11,9 @@ module Helpers
ensure ensure
$stderr = original_stderr $stderr = original_stderr
end end
def request_double(url: 'http://example.com', method: 'get')
double('request', url: url, method: method, user: nil, password: nil,
redirection_history: nil, args: {url: url, method: method})
end
end end

View file

@ -1,6 +1,6 @@
require_relative '_lib' require_relative '_lib'
describe RestClient::AbstractResponse do describe RestClient::AbstractResponse, :include_helpers do
class MyAbstractResponse class MyAbstractResponse
@ -8,9 +8,8 @@ describe RestClient::AbstractResponse do
attr_accessor :size attr_accessor :size
def initialize net_http_res, args, request def initialize net_http_res, request
@net_http_res = net_http_res @net_http_res = net_http_res
@args = args
@request = request @request = request
end end
@ -18,8 +17,8 @@ describe RestClient::AbstractResponse do
before do before do
@net_http_res = double('net http response') @net_http_res = double('net http response')
@request = double('restclient request', :url => 'http://example.com') @request = double('restclient request', url: 'http://example.com', method: 'get')
@response = MyAbstractResponse.new(@net_http_res, {}, @request) @response = MyAbstractResponse.new(@net_http_res, @request)
end end
it "fetches the numeric response code" do it "fetches the numeric response code" do
@ -95,8 +94,15 @@ describe RestClient::AbstractResponse do
it "should raise an error on a redirection after non-GET/HEAD requests" do it "should raise an error on a redirection after non-GET/HEAD requests" do
@net_http_res.should_receive(:code).and_return('301') @net_http_res.should_receive(:code).and_return('301')
@response.args.merge(:method => :put) @request.should_receive(:method).and_return('put')
@response.should_not_receive(:follow_redirection)
lambda { @response.return! }.should raise_error RestClient::RequestFailed lambda { @response.return! }.should raise_error RestClient::RequestFailed
end end
it "should follow 302 redirect" do
@net_http_res.should_receive(:code).and_return('302')
@response.should_receive(:follow_redirection).and_return('fake-redirection')
@response.return!.should eq 'fake-redirection'
end
end end
end end

View file

@ -5,7 +5,7 @@ describe RestClient::RawResponse do
@tf = double("Tempfile", :read => "the answer is 42", :open => true) @tf = double("Tempfile", :read => "the answer is 42", :open => true)
@net_http_res = double('net http response') @net_http_res = double('net http response')
@request = double('http request') @request = double('http request')
@response = RestClient::RawResponse.new(@tf, @net_http_res, {}, @request) @response = RestClient::RawResponse.new(@tf, @net_http_res, @request)
end end
it "behaves like string" do it "behaves like string" do

View file

@ -264,7 +264,7 @@ describe RestClient::Request, :include_helpers do
it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
klass = double("net:http class") klass = double("net:http class")
@request.should_receive(:net_http_request_class).with(:put).and_return(klass) @request.should_receive(:net_http_request_class).with('put').and_return(klass)
klass.should_receive(:new).and_return('result') klass.should_receive(:new).and_return('result')
@request.should_receive(:transmit).with(@request.uri, 'result', kind_of(RestClient::Payload::Base)) @request.should_receive(:transmit).with(@request.uri, 'result', kind_of(RestClient::Payload::Base))
@request.execute @request.execute
@ -273,7 +273,7 @@ describe RestClient::Request, :include_helpers do
it "IPv6: executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do it "IPv6: executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
@request = RestClient::Request.new(:method => :put, :url => 'http://[::1]/some/resource', :payload => 'payload') @request = RestClient::Request.new(:method => :put, :url => 'http://[::1]/some/resource', :payload => 'payload')
klass = double("net:http class") klass = double("net:http class")
@request.should_receive(:net_http_request_class).with(:put).and_return(klass) @request.should_receive(:net_http_request_class).with('put').and_return(klass)
if RUBY_VERSION >= "2.0.0" if RUBY_VERSION >= "2.0.0"
klass.should_receive(:new).with(kind_of(URI), kind_of(Hash)).and_return('result') klass.should_receive(:new).with(kind_of(URI), kind_of(Hash)).and_return('result')

View file

@ -4,8 +4,8 @@ describe RestClient::Response, :include_helpers do
before do before do
@net_http_res = double('net http response', :to_hash => {"Status" => ["200 OK"]}, :code => 200) @net_http_res = double('net http response', :to_hash => {"Status" => ["200 OK"]}, :code => 200)
@example_url = 'http://example.com' @example_url = 'http://example.com'
@request = double('http request', :user => nil, :password => nil, :url => @example_url, :redirection_history => nil) @request = request_double(url: @example_url, method: 'get')
@response = RestClient::Response.create('abc', @net_http_res, {}, @request) @response = RestClient::Response.create('abc', @net_http_res, @request)
end end
it "behaves like string" do it "behaves like string" do
@ -17,7 +17,7 @@ describe RestClient::Response, :include_helpers do
end end
it "accepts nil strings and sets it to empty for the case of HEAD" do it "accepts nil strings and sets it to empty for the case of HEAD" do
RestClient::Response.create(nil, @net_http_res, {}, @request).to_s.should eq "" RestClient::Response.create(nil, @net_http_res, @request).to_s.should eq ""
end end
describe 'header processing' do describe 'header processing' do
@ -29,8 +29,8 @@ describe RestClient::Response, :include_helpers do
it 'handles multiple headers by joining with comma' do it 'handles multiple headers by joining with comma' do
@net_http_res = double('net http response', :to_hash => {'My-Header' => ['foo', 'bar']}, :code => 200) @net_http_res = double('net http response', :to_hash => {'My-Header' => ['foo', 'bar']}, :code => 200)
@example_url = 'http://example.com' @example_url = 'http://example.com'
@request = double('http request', :user => nil, :password => nil, :url => @example_url, :redirection_history => nil) @request = request_double(url: @example_url, method: 'get')
@response = RestClient::Response.create('abc', @net_http_res, {}, @request) @response = RestClient::Response.create('abc', @net_http_res, @request)
@response.raw_headers['My-Header'].should eq ['foo', 'bar'] @response.raw_headers['My-Header'].should eq ['foo', 'bar']
@response.headers[:my_header].should eq 'foo, bar' @response.headers[:my_header].should eq 'foo, bar'
@ -42,14 +42,14 @@ describe RestClient::Response, :include_helpers do
header_val = "main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT".freeze header_val = "main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT".freeze
net_http_res = double('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => [header_val]}) net_http_res = double('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => [header_val]})
response = RestClient::Response.create('abc', net_http_res, {}, @request) response = RestClient::Response.create('abc', net_http_res, @request)
response.headers[:set_cookie].should eq [header_val] response.headers[:set_cookie].should eq [header_val]
response.cookies.should eq({ "main_page" => "main_page_no_rewrite" }) response.cookies.should eq({ "main_page" => "main_page_no_rewrite" })
end end
it "should correctly deal with multiple cookies [multiple Set-Cookie headers]" do it "should correctly deal with multiple cookies [multiple Set-Cookie headers]" do
net_http_res = double('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT", "remember_me=; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT", "user=somebody; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT"]}) net_http_res = double('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT", "remember_me=; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT", "user=somebody; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT"]})
response = RestClient::Response.create('abc', net_http_res, {}, @request) response = RestClient::Response.create('abc', net_http_res, @request)
response.headers[:set_cookie].should eq ["main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT", "remember_me=; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT", "user=somebody; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT"] response.headers[:set_cookie].should eq ["main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT", "remember_me=; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT", "user=somebody; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT"]
response.cookies.should eq({ response.cookies.should eq({
"main_page" => "main_page_no_rewrite", "main_page" => "main_page_no_rewrite",
@ -60,7 +60,7 @@ describe RestClient::Response, :include_helpers do
it "should correctly deal with multiple cookies [one Set-Cookie header with multiple cookies]" do it "should correctly deal with multiple cookies [one Set-Cookie header with multiple cookies]" do
net_http_res = double('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT, remember_me=; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT, user=somebody; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT"]}) net_http_res = double('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Sat, 10-Jan-2037 15:03:14 GMT, remember_me=; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT, user=somebody; path=/; expires=Sat, 10-Jan-2037 00:00:00 GMT"]})
response = RestClient::Response.create('abc', net_http_res, {}, @request) response = RestClient::Response.create('abc', net_http_res, @request)
response.cookies.should eq({ response.cookies.should eq({
"main_page" => "main_page_no_rewrite", "main_page" => "main_page_no_rewrite",
"remember_me" => "", "remember_me" => "",
@ -73,7 +73,7 @@ describe RestClient::Response, :include_helpers do
it "should return itself for normal codes" do it "should return itself for normal codes" do
(200..206).each do |code| (200..206).each do |code|
net_http_res = response_double(:code => '200') net_http_res = response_double(:code => '200')
resp = RestClient::Response.create('abc', net_http_res, {}, @request) resp = RestClient::Response.create('abc', net_http_res, @request)
resp.return! resp.return!
end end
end end
@ -82,7 +82,7 @@ describe RestClient::Response, :include_helpers do
RestClient::Exceptions::EXCEPTIONS_MAP.each_key do |code| RestClient::Exceptions::EXCEPTIONS_MAP.each_key do |code|
unless (200..207).include? code unless (200..207).include? code
net_http_res = response_double(:code => code.to_i) net_http_res = response_double(:code => code.to_i)
resp = RestClient::Response.create('abc', net_http_res, {}, @request) resp = RestClient::Response.create('abc', net_http_res, @request)
lambda { resp.return! }.should raise_error lambda { resp.return! }.should raise_error
end end
end end
@ -128,8 +128,9 @@ describe RestClient::Response, :include_helpers do
it "doesn't follow a 301 when the request is a post" do it "doesn't follow a 301 when the request is a post" do
net_http_res = response_double(:code => 301) net_http_res = response_double(:code => 301)
response = RestClient::Response.create('abc', net_http_res, response = RestClient::Response.create('abc', net_http_res,
{:method => :post}, @request) request_double(method: 'post'))
lambda { lambda {
response.return! response.return!
}.should raise_error(RestClient::MovedPermanently) }.should raise_error(RestClient::MovedPermanently)
@ -138,7 +139,7 @@ describe RestClient::Response, :include_helpers do
it "doesn't follow a 302 when the request is a post" do it "doesn't follow a 302 when the request is a post" do
net_http_res = response_double(:code => 302) net_http_res = response_double(:code => 302)
response = RestClient::Response.create('abc', net_http_res, response = RestClient::Response.create('abc', net_http_res,
{:method => :post}, @request) request_double(method: 'post'))
lambda { lambda {
response.return! response.return!
}.should raise_error(RestClient::Found) }.should raise_error(RestClient::Found)
@ -147,7 +148,8 @@ describe RestClient::Response, :include_helpers do
it "doesn't follow a 307 when the request is a post" do it "doesn't follow a 307 when the request is a post" do
net_http_res = response_double(:code => 307) net_http_res = response_double(:code => 307)
response = RestClient::Response.create('abc', net_http_res, response = RestClient::Response.create('abc', net_http_res,
{:method => :post}, @request) request_double(method: 'post'))
response.should_not_receive(:follow_redirection)
lambda { lambda {
response.return! response.return!
}.should raise_error(RestClient::TemporaryRedirect) }.should raise_error(RestClient::TemporaryRedirect)
@ -156,7 +158,7 @@ describe RestClient::Response, :include_helpers do
it "doesn't follow a redirection when the request is a put" do it "doesn't follow a redirection when the request is a put" do
net_http_res = response_double(:code => 301) net_http_res = response_double(:code => 301)
response = RestClient::Response.create('abc', net_http_res, response = RestClient::Response.create('abc', net_http_res,
{:method => :put}, @request) request_double(method: 'put'))
lambda { lambda {
response.return! response.return!
}.should raise_error(RestClient::MovedPermanently) }.should raise_error(RestClient::MovedPermanently)