mirror of
https://github.com/rest-client/rest-client.git
synced 2022-11-09 13:49:40 -05:00
Added raw_request support, which downloads the body to a tempfile rather than a string
This commit is contained in:
parent
3b01ab4733
commit
212ae67bfe
8 changed files with 173 additions and 64 deletions
|
@ -4,7 +4,9 @@ require 'zlib'
|
|||
require 'stringio'
|
||||
|
||||
require File.dirname(__FILE__) + '/restclient/request'
|
||||
require File.dirname(__FILE__) + '/restclient/mixin/response'
|
||||
require File.dirname(__FILE__) + '/restclient/response'
|
||||
require File.dirname(__FILE__) + '/restclient/raw_response'
|
||||
require File.dirname(__FILE__) + '/restclient/resource'
|
||||
require File.dirname(__FILE__) + '/restclient/exceptions'
|
||||
|
||||
|
|
43
lib/restclient/mixin/response.rb
Normal file
43
lib/restclient/mixin/response.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
module RestClient
|
||||
module Mixin
|
||||
module Response
|
||||
attr_reader :net_http_res
|
||||
|
||||
# HTTP status code, always 200 since RestClient throws exceptions for
|
||||
# other codes.
|
||||
def code
|
||||
@code ||= @net_http_res.code.to_i
|
||||
end
|
||||
|
||||
# A hash of the headers, beautified with symbols and underscores.
|
||||
# e.g. "Content-type" will become :content_type.
|
||||
def headers
|
||||
@headers ||= self.class.beautify_headers(@net_http_res.to_hash)
|
||||
end
|
||||
|
||||
# Hash of cookies extracted from response headers
|
||||
def cookies
|
||||
@cookies ||= (self.headers[:set_cookie] || "").split('; ').inject({}) do |out, raw_c|
|
||||
key, val = raw_c.split('=')
|
||||
unless %w(expires domain path secure).member?(key)
|
||||
out[key] = val
|
||||
end
|
||||
out
|
||||
end
|
||||
end
|
||||
|
||||
def self.included(receiver)
|
||||
receiver.extend(RestClient::Mixin::Response::ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def beautify_headers(headers)
|
||||
headers.inject({}) do |out, (key, value)|
|
||||
out[key.gsub(/-/, '_').to_sym] = value.first
|
||||
out
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
lib/restclient/raw_response.rb
Normal file
30
lib/restclient/raw_response.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
require File.dirname(__FILE__) + '/mixin/response'
|
||||
|
||||
module RestClient
|
||||
# The response from RestClient on a raw request looks like a string, but is
|
||||
# actually one of these. 99% of the time you're making a rest call all you
|
||||
# care about is the body, but on the occassion you want to fetch the
|
||||
# headers you can:
|
||||
#
|
||||
# RestClient.get('http://example.com').headers[:content_type]
|
||||
#
|
||||
# In addition, if you do not use the response as a string, you can access
|
||||
# a Tempfile object at res.file, which contains the path to the raw
|
||||
# downloaded request body.
|
||||
class RawResponse
|
||||
include RestClient::Mixin::Response
|
||||
|
||||
attr_reader :file
|
||||
|
||||
def initialize(tempfile, net_http_res)
|
||||
@net_http_res = net_http_res
|
||||
@file = tempfile
|
||||
end
|
||||
|
||||
def to_s
|
||||
@file.open
|
||||
@file.read
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,3 +1,5 @@
|
|||
require 'tempfile'
|
||||
|
||||
module RestClient
|
||||
# This class is used internally by RestClient to send the request, but you can also
|
||||
# access it internally if you'd like to use a method not directly supported by the
|
||||
|
@ -6,7 +8,7 @@ module RestClient
|
|||
# RestClient::Request.execute(:method => :head, :url => 'http://example.com')
|
||||
#
|
||||
class Request
|
||||
attr_reader :method, :url, :payload, :headers, :cookies, :user, :password, :timeout, :open_timeout
|
||||
attr_reader :method, :url, :payload, :headers, :cookies, :user, :password, :timeout, :open_timeout, :raw_response
|
||||
|
||||
def self.execute(args)
|
||||
new(args).execute
|
||||
|
@ -22,6 +24,7 @@ module RestClient
|
|||
@password = args[:password]
|
||||
@timeout = args[:timeout]
|
||||
@open_timeout = args[:open_timeout]
|
||||
@raw_response = args[:raw_response] || false
|
||||
end
|
||||
|
||||
def execute
|
||||
|
@ -103,10 +106,12 @@ module RestClient
|
|||
net.start do |http|
|
||||
res = http.request(req, payload)
|
||||
display_log response_log(res)
|
||||
string = process_result(res)
|
||||
result = process_result(res)
|
||||
|
||||
if string or @method == :head
|
||||
Response.new(string, res)
|
||||
if result.kind_of?(String) or @method == :head
|
||||
Response.new(result, res)
|
||||
elsif result.kind_of?(Tempfile)
|
||||
RawResponse.new(result, res)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -123,7 +128,28 @@ module RestClient
|
|||
|
||||
def process_result(res)
|
||||
if res.code =~ /\A2\d{2}\z/
|
||||
decode res['content-encoding'], res.body if res.body
|
||||
if @raw_response
|
||||
# Taken from Chef, which as in turn...
|
||||
# Stolen from http://www.ruby-forum.com/topic/166423
|
||||
# Kudos to _why!
|
||||
tf = Tempfile.new("rest-client")
|
||||
size, total = 0, res.header['Content-Length'].to_i
|
||||
res.read_body do |chunk|
|
||||
tf.write(chunk)
|
||||
size += chunk.size
|
||||
if size == 0
|
||||
display_log("Request for #{@url} done (0 length file)")
|
||||
elsif total == 0
|
||||
display_log("Request for #{@url} (zero content length)")
|
||||
else
|
||||
display_log("Request for #{@url} %d%% done (%d of %d)" % [(size * 100) / total, size, total])
|
||||
end
|
||||
end
|
||||
tf.close
|
||||
tf
|
||||
else
|
||||
decode res['content-encoding'], res.body if res.body
|
||||
end
|
||||
elsif %w(301 302 303).include? res.code
|
||||
url = res.header['Location']
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require File.dirname(__FILE__) + '/mixin/response'
|
||||
|
||||
module RestClient
|
||||
# The response from RestClient looks like a string, but is actually one of
|
||||
# these. 99% of the time you're making a rest call all you care about is
|
||||
|
@ -6,41 +8,13 @@ module RestClient
|
|||
# RestClient.get('http://example.com').headers[:content_type]
|
||||
#
|
||||
class Response < String
|
||||
attr_reader :net_http_res
|
||||
|
||||
include RestClient::Mixin::Response
|
||||
|
||||
def initialize(string, net_http_res)
|
||||
@net_http_res = net_http_res
|
||||
super(string || "")
|
||||
end
|
||||
|
||||
# HTTP status code, always 200 since RestClient throws exceptions for
|
||||
# other codes.
|
||||
def code
|
||||
@code ||= @net_http_res.code.to_i
|
||||
end
|
||||
|
||||
# A hash of the headers, beautified with symbols and underscores.
|
||||
# e.g. "Content-type" will become :content_type.
|
||||
def headers
|
||||
@headers ||= self.class.beautify_headers(@net_http_res.to_hash)
|
||||
end
|
||||
|
||||
# Hash of cookies extracted from response headers
|
||||
def cookies
|
||||
@cookies ||= (self.headers[:set_cookie] || "").split('; ').inject({}) do |out, raw_c|
|
||||
key, val = raw_c.split('=')
|
||||
unless %w(expires domain path secure).member?(key)
|
||||
out[key] = val
|
||||
end
|
||||
out
|
||||
end
|
||||
end
|
||||
|
||||
def self.beautify_headers(headers)
|
||||
headers.inject({}) do |out, (key, value)|
|
||||
out[key.gsub(/-/, '_').to_sym] = value.first
|
||||
out
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
46
spec/mixin/response_spec.rb
Normal file
46
spec/mixin/response_spec.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
require File.dirname(__FILE__) + '/../base'
|
||||
|
||||
class MockResponse
|
||||
include RestClient::Mixin::Response
|
||||
|
||||
def initialize(body, res)
|
||||
@net_http_res = res
|
||||
@body = @body
|
||||
end
|
||||
end
|
||||
|
||||
describe RestClient::Mixin::Response do
|
||||
before do
|
||||
@net_http_res = mock('net http response')
|
||||
@response = MockResponse.new('abc', @net_http_res)
|
||||
end
|
||||
|
||||
it "fetches the numeric response code" do
|
||||
@net_http_res.should_receive(:code).and_return('200')
|
||||
@response.code.should == 200
|
||||
end
|
||||
|
||||
it "beautifies the headers by turning the keys to symbols" do
|
||||
h = RestClient::Response.beautify_headers('content-type' => [ 'x' ])
|
||||
h.keys.first.should == :content_type
|
||||
end
|
||||
|
||||
it "beautifies the headers by turning the values to strings instead of one-element arrays" do
|
||||
h = RestClient::Response.beautify_headers('x' => [ 'text/html' ] )
|
||||
h.values.first.should == 'text/html'
|
||||
end
|
||||
|
||||
it "fetches the headers" do
|
||||
@net_http_res.should_receive(:to_hash).and_return('content-type' => [ 'text/html' ])
|
||||
@response.headers.should == { :content_type => 'text/html' }
|
||||
end
|
||||
|
||||
it "extracts cookies from response headers" do
|
||||
@net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
|
||||
@response.cookies.should == { 'session_id' => '1' }
|
||||
end
|
||||
|
||||
it "can access the net http result directly" do
|
||||
@response.net_http_res.should == @net_http_res
|
||||
end
|
||||
end
|
17
spec/raw_response_spec.rb
Normal file
17
spec/raw_response_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
require File.dirname(__FILE__) + '/base'
|
||||
|
||||
describe RestClient::RawResponse do
|
||||
before do
|
||||
@tf = mock("Tempfile", :read => "the answer is 42", :open => true)
|
||||
@net_http_res = mock('net http response')
|
||||
@response = RestClient::RawResponse.new(@tf, @net_http_res)
|
||||
end
|
||||
|
||||
it "behaves like string" do
|
||||
@response.to_s.should == 'the answer is 42'
|
||||
end
|
||||
|
||||
it "exposes a Tempfile" do
|
||||
@response.file.should == @tf
|
||||
end
|
||||
end
|
|
@ -10,35 +10,6 @@ describe RestClient::Response do
|
|||
@response.should == 'abc'
|
||||
end
|
||||
|
||||
it "fetches the numeric response code" do
|
||||
@net_http_res.should_receive(:code).and_return('200')
|
||||
@response.code.should == 200
|
||||
end
|
||||
|
||||
it "beautifies the headers by turning the keys to symbols" do
|
||||
h = RestClient::Response.beautify_headers('content-type' => [ 'x' ])
|
||||
h.keys.first.should == :content_type
|
||||
end
|
||||
|
||||
it "beautifies the headers by turning the values to strings instead of one-element arrays" do
|
||||
h = RestClient::Response.beautify_headers('x' => [ 'text/html' ] )
|
||||
h.values.first.should == 'text/html'
|
||||
end
|
||||
|
||||
it "fetches the headers" do
|
||||
@net_http_res.should_receive(:to_hash).and_return('content-type' => [ 'text/html' ])
|
||||
@response.headers.should == { :content_type => 'text/html' }
|
||||
end
|
||||
|
||||
it "extracts cookies from response headers" do
|
||||
@net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
|
||||
@response.cookies.should == { 'session_id' => '1' }
|
||||
end
|
||||
|
||||
it "can access the net http result directly" do
|
||||
@response.net_http_res.should == @net_http_res
|
||||
end
|
||||
|
||||
it "accepts nil strings and sets it to empty for the case of HEAD" do
|
||||
RestClient::Response.new(nil, @net_http_res).should == ""
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue