1
0
Fork 0
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:
Adam Jacob 2009-03-16 15:28:20 -07:00
parent 3b01ab4733
commit 212ae67bfe
8 changed files with 173 additions and 64 deletions

View file

@ -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'

View 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

View 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

View file

@ -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']

View file

@ -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

View 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
View 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

View file

@ -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