mirror of
https://github.com/rest-client/rest-client.git
synced 2022-11-09 13:49:40 -05:00
Merge branch 'master' into rpanachi/1.2.0
This commit is contained in:
commit
da0da219a5
22 changed files with 1002 additions and 623 deletions
126
README.rdoc
126
README.rdoc
|
@ -1,6 +1,6 @@
|
|||
= REST Client -- simple DSL for accessing REST resources
|
||||
= REST Client -- simple DSL for accessing HTTP and REST resources
|
||||
|
||||
A simple REST client for Ruby, inspired by the Sinatra's microframework style
|
||||
A simple HTTP and REST client for Ruby, inspired by the Sinatra's microframework style
|
||||
of specifying actions: get, put, post, delete.
|
||||
|
||||
== Usage: Raw URL
|
||||
|
@ -51,6 +51,51 @@ See RestClient::Resource module docs for details.
|
|||
|
||||
See RestClient::Resource docs for details.
|
||||
|
||||
== Exceptions
|
||||
|
||||
* for results code between 200 and 206 a RestClient::Response will be returned
|
||||
* for results code between 301 and 303 the redirection will be automatically followed
|
||||
* for other result codes a RestClient::Exception holding the Response will be raised, a specific exception class will be thrown for know error codes
|
||||
|
||||
RestClient.get 'http://example.com/resource'
|
||||
➔ RestClient::ResourceNotFound: RestClient::ResourceNotFound
|
||||
|
||||
begin
|
||||
RestClient.get 'http://example.com/resource'
|
||||
rescue => e
|
||||
e.response
|
||||
end
|
||||
➔ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>404 Not Found</title>..."
|
||||
|
||||
== Result handling
|
||||
|
||||
A block can be passed to the RestClient method, this block will then be called with the Response.
|
||||
Response.return! can be called to invoke the default response's behavior (return the Response for 200..206, raise an exception in other cases).
|
||||
|
||||
# Don't raise exceptions but return the response
|
||||
RestClient.get('http://example.com/resource'){|response| response}
|
||||
➔ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>404 Not Found</title>..."
|
||||
|
||||
# Manage a specific error code
|
||||
RestClient.get('http://my-rest-service.com/resource'){ |response|
|
||||
case response.code
|
||||
when 200
|
||||
p "It worked !"
|
||||
response
|
||||
when 423
|
||||
raise SomeCustomExceptionIfYouWant
|
||||
else
|
||||
response.return!
|
||||
end
|
||||
}
|
||||
|
||||
== Non-normalized URIs.
|
||||
|
||||
If you want to use non-normalized URIs, you can normalize them with the addressable gem (http://addressable.rubyforge.org/api/).
|
||||
|
||||
require 'addressable/uri'
|
||||
RestClient.get(Addressable::URI.parse("http://www.詹姆斯.com/").normalize.to_str)
|
||||
|
||||
== Lower-level access
|
||||
|
||||
For cases not covered by the general API, you can use the RestClient::Resource class which provide a lower-level API, see the class' rdoc for more information.
|
||||
|
@ -60,17 +105,17 @@ For cases not covered by the general API, you can use the RestClient::Resource c
|
|||
The restclient shell command gives an IRB session with RestClient already loaded:
|
||||
|
||||
$ restclient
|
||||
>> RestClient.get 'http://example.com'
|
||||
➔ RestClient.get 'http://example.com'
|
||||
|
||||
Specify a URL argument for get/post/put/delete on that resource:
|
||||
|
||||
$ restclient http://example.com
|
||||
>> put '/resource', 'data'
|
||||
➔ put '/resource', 'data'
|
||||
|
||||
Add a user and password for authenticated resources:
|
||||
|
||||
$ restclient https://example.com user pass
|
||||
>> delete '/private/resource'
|
||||
➔ delete '/private/resource'
|
||||
|
||||
Create ~/.restclient for named sessions:
|
||||
|
||||
|
@ -87,11 +132,78 @@ Then invoke:
|
|||
|
||||
$ restclient private_site
|
||||
|
||||
Use as a one-off, curl-style:
|
||||
|
||||
$ restclient get http://example.com/resource > output_body
|
||||
|
||||
$ restclient put http://example.com/resource < input_body
|
||||
|
||||
== Logging
|
||||
|
||||
To enable logging you can
|
||||
|
||||
* set RestClient.log with a ruby Logger
|
||||
* or set an environment variable to avoid modifying the code (in this case you can use a file name, "stdout" or "stderr"):
|
||||
|
||||
$ RESTCLIENT_LOG=stdout path/to/my/program
|
||||
|
||||
Either produces logs like this:
|
||||
|
||||
RestClient.get "http://some/resource"
|
||||
# => 200 OK | text/html 250 bytes
|
||||
RestClient.put "http://some/resource", "payload"
|
||||
# => 401 Unauthorized | application/xml 340 bytes
|
||||
|
||||
Note that these logs are valid Ruby, so you can paste them into the restclient
|
||||
shell or a script to replay your sequence of rest calls.
|
||||
|
||||
== Proxy
|
||||
|
||||
All calls to RestClient, including Resources, will use the proxy specified by
|
||||
RestClient.proxy:
|
||||
|
||||
RestClient.proxy = "http://proxy.example.com/"
|
||||
RestClient.get "http://some/resource"
|
||||
# => response from some/resource as proxied through proxy.example.com
|
||||
|
||||
Often the proxy url is set in an environment variable, so you can do this to
|
||||
use whatever proxy the system is configured to use:
|
||||
|
||||
RestClient.proxy = ENV['http_proxy']
|
||||
|
||||
== Cookies
|
||||
|
||||
Request and Response objects know about HTTP cookies, and will automatically
|
||||
extract and set headers for them as needed:
|
||||
|
||||
response = RestClient.get 'http://example.com/action_which_sets_session_id'
|
||||
response.cookies
|
||||
# => {"_applicatioN_session_id" => "1234"}
|
||||
|
||||
response2 = RestClient.post(
|
||||
'http://localhost:3000/',
|
||||
{:param1 => "foo"},
|
||||
{:cookies => {:session_id => "1234"}}
|
||||
)
|
||||
# ...response body
|
||||
|
||||
== SSL Client Certificates
|
||||
|
||||
RestClient::Resource.new(
|
||||
'https://example.com',
|
||||
:ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")),
|
||||
:ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
|
||||
:ssl_ca_file => "ca_certificate.pem",
|
||||
:verify_ssl => OpenSSL::SSL::VERIFY_PEER
|
||||
).get
|
||||
|
||||
Self-signed certificates can be generated with the openssl command-line tool.
|
||||
|
||||
== Meta
|
||||
|
||||
Written by Adam Wiggins, major modifications by Blake Mizerany, maintained by Archiloque
|
||||
Written by Adam Wiggins, major modifications by Blake Mizerany, maintained by Julien Kirch
|
||||
|
||||
Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, Rafael Souza, Rick Olson, Aman Gupta, François Beausoleil and Nick Plante.
|
||||
Patches contributed by many, including Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, Rafael Souza, Rick Olson, Aman Gupta, François Beausoleil and Nick Plante.
|
||||
|
||||
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
|
|
6
Rakefile
6
Rakefile
|
@ -3,9 +3,9 @@ require 'rake'
|
|||
require 'jeweler'
|
||||
|
||||
Jeweler::Tasks.new do |s|
|
||||
s.name = "rest-client"
|
||||
s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
||||
s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
|
||||
s.name = "rest-client-next"
|
||||
s.description = "A simple HTTP and REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
||||
s.summary = "Simple HTTP and REST client for Ruby, inspired by microframework syntax for specifying actions."
|
||||
s.author = "Adam Wiggins"
|
||||
s.email = "rest.client@librelist.com"
|
||||
s.homepage = "http://github.com/archiloque/rest-client"
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.2.0
|
||||
1.3.0
|
||||
|
|
19
history.md
19
history.md
|
@ -1,9 +1,26 @@
|
|||
# 1.4.0
|
||||
|
||||
- Response is no more a String, and the mixin is replaced by an abstract_response, existing calls are redirected to response body with a warning.
|
||||
|
||||
The response change may be breaking in rare cases.
|
||||
|
||||
# 1.3.0
|
||||
|
||||
- a block can be used to process a request's result, this enable to handle custom error codes or paththrought (design by Cyril Rohr)
|
||||
- cleaner log API, add a warning for some cases but should be compatible
|
||||
- accept multiple "Set-Cookie" headers, see http://www.ietf.org/rfc/rfc2109.txt (patch provided by Cyril Rohr)
|
||||
- remove "Content-Length" and "Content-Type" headers when following a redirection (patch provided by haarts)
|
||||
- all http error codes have now a corresponding exception class and all of them contain the Reponse -> this means that the raised exception can be different
|
||||
- changed "Content-Disposition: multipart/form-data" to "Content-Disposition: form-data" per RFC 2388 (patch provided by Kyle Crawford)
|
||||
|
||||
The only breaking change should be the exception classes, but as the new classes inherits from the existing ones, the breaking cases should be rare.
|
||||
|
||||
# 1.2.0
|
||||
|
||||
- formatting changed from tabs to spaces
|
||||
- logged requests now include generated headers
|
||||
- accept and content-type headers can now be specified using extentions: RestClient.post "http://example.com/resource", { 'x' => 1 }.to_json, :content_type => :json, :accept => :json
|
||||
- should be 1.1.1 but renammed to 1.2.0 because 1.1.X versions has already been packaged on Debian
|
||||
- should be 1.1.1 but renamed to 1.2.0 because 1.1.X versions has already been packaged on Debian
|
||||
|
||||
# 1.1.0
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ rescue LoadError => e
|
|||
raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
|
||||
end
|
||||
|
||||
require File.dirname(__FILE__) + '/restclient/exceptions'
|
||||
require File.dirname(__FILE__) + '/restclient/request'
|
||||
require File.dirname(__FILE__) + '/restclient/mixin/response'
|
||||
require File.dirname(__FILE__) + '/restclient/abstract_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'
|
||||
require File.dirname(__FILE__) + '/restclient/payload'
|
||||
require File.dirname(__FILE__) + '/restclient/net_http_ext'
|
||||
|
||||
|
@ -64,40 +64,38 @@ require File.dirname(__FILE__) + '/restclient/net_http_ext'
|
|||
#
|
||||
module RestClient
|
||||
|
||||
def self.get(url, headers={})
|
||||
Request.execute(:method => :get, :url => url, :headers => headers)
|
||||
def self.get(url, headers={}, &block)
|
||||
Request.execute(:method => :get, :url => url, :headers => headers, &block)
|
||||
end
|
||||
|
||||
def self.post(url, payload, headers={})
|
||||
Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
|
||||
def self.post(url, payload, headers={}, &block)
|
||||
Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
|
||||
end
|
||||
|
||||
def self.put(url, payload, headers={})
|
||||
Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
|
||||
def self.put(url, payload, headers={}, &block)
|
||||
Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers, &block)
|
||||
end
|
||||
|
||||
def self.delete(url, headers={})
|
||||
Request.execute(:method => :delete, :url => url, :headers => headers)
|
||||
def self.delete(url, headers={}, &block)
|
||||
Request.execute(:method => :delete, :url => url, :headers => headers, &block)
|
||||
end
|
||||
|
||||
def self.head(url, headers={})
|
||||
Request.execute(:method => :head, :url => url, :headers => headers)
|
||||
def self.head(url, headers={}, &block)
|
||||
Request.execute(:method => :head, :url => url, :headers => headers, &block)
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_accessor :proxy
|
||||
end
|
||||
|
||||
# Print log of RestClient calls. Value can be stdout, stderr, or a filename.
|
||||
# Setup the log for RestClient calls.
|
||||
# Value should be a logger but can can be stdout, stderr, or a filename.
|
||||
# You can also configure logging by the environment variable RESTCLIENT_LOG.
|
||||
def self.log=(log)
|
||||
@@log = log
|
||||
end
|
||||
|
||||
def self.log # :nodoc:
|
||||
return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
|
||||
return @@log if defined? @@log
|
||||
nil
|
||||
def self.log= log
|
||||
if log.is_a? String
|
||||
warn "[warning] You should set the log with a logger"
|
||||
end
|
||||
@@log = create_log log
|
||||
end
|
||||
|
||||
def self.version
|
||||
|
@ -105,4 +103,49 @@ module RestClient
|
|||
return File.read(version_path).chomp if File.file?(version_path)
|
||||
"0.0.0"
|
||||
end
|
||||
|
||||
# Create a log that respond to << like a logger
|
||||
# param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it)
|
||||
def self.create_log param
|
||||
if param
|
||||
if param.is_a? String
|
||||
if param == 'stdout'
|
||||
stdout_logger = Class.new do
|
||||
def << obj
|
||||
STDOUT.puts obj
|
||||
end
|
||||
end
|
||||
stdout_logger.new
|
||||
elsif param == 'stderr'
|
||||
stderr_logger = Class.new do
|
||||
def << obj
|
||||
STDERR.puts obj
|
||||
end
|
||||
end
|
||||
stderr_logger.new
|
||||
else
|
||||
file_logger = Class.new do
|
||||
attr_writer :target_file
|
||||
def << obj
|
||||
File.open(@target_file, 'a') { |f| f.puts obj }
|
||||
end
|
||||
end
|
||||
logger = file_logger.new
|
||||
logger.target_file = param
|
||||
logger
|
||||
end
|
||||
else
|
||||
param
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@env_log = create_log ENV['RESTCLIENT_LOG']
|
||||
|
||||
@@log = nil
|
||||
|
||||
def self.log # :nodoc:
|
||||
@@env_log || @@log
|
||||
end
|
||||
|
||||
end
|
||||
|
|
67
lib/restclient/abstract_response.rb
Normal file
67
lib/restclient/abstract_response.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
module RestClient
|
||||
|
||||
class AbstractResponse
|
||||
|
||||
attr_reader :net_http_res
|
||||
|
||||
def initialize net_http_res
|
||||
@net_http_res = net_http_res
|
||||
end
|
||||
|
||||
# HTTP status code
|
||||
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
|
||||
|
||||
# The raw headers.
|
||||
def raw_headers
|
||||
@raw_headers ||= @net_http_res.to_hash
|
||||
end
|
||||
|
||||
# Hash of cookies extracted from response headers
|
||||
def cookies
|
||||
@cookies ||= (self.headers[:set_cookie] || []).inject({}) do |out, cookie_content|
|
||||
# correctly parse comma-separated cookies containing HTTP dates (which also contain a comma)
|
||||
cookie_content.split(/,\s*/).inject([""]) { |array, blob|
|
||||
blob =~ /expires=.+?$/ ? array.push(blob) : array.last.concat(blob)
|
||||
array
|
||||
}.each do |cookie|
|
||||
next if cookie.empty?
|
||||
key, *val = cookie.split(";").first.split("=")
|
||||
out[key] = val.join("=")
|
||||
end
|
||||
out
|
||||
end
|
||||
end
|
||||
|
||||
# Return the default behavior corresponding to the response code:
|
||||
# the response itself for code in 200..206 and an exception in other cases
|
||||
def return!
|
||||
if (200..206).include? code
|
||||
self
|
||||
elsif Exceptions::EXCEPTIONS_MAP[code]
|
||||
raise Exceptions::EXCEPTIONS_MAP[code], self
|
||||
else
|
||||
raise RequestFailed, self
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{code} #{STATUSES[code]} | #{(headers[:content_type] || '').gsub(/;.*$/, '')} #{size} bytes\n"
|
||||
end
|
||||
|
||||
|
||||
def AbstractResponse.beautify_headers(headers)
|
||||
headers.inject({}) do |out, (key, value)|
|
||||
out[key.gsub(/-/, '_').downcase.to_sym] = %w{set-cookie}.include?(key.downcase) ? value : value.first
|
||||
out
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,76 +1,80 @@
|
|||
module RestClient
|
||||
|
||||
|
||||
STATUSES = {100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
203 => 'Non-Authoritative Information',
|
||||
204 => 'No Content',
|
||||
205 => 'Reset Content',
|
||||
206 => 'Partial Content',
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Resource Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Timeout',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Request Entity Too Large',
|
||||
414 => 'Request-URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Requested Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Timeout',
|
||||
505 => 'HTTP Version Not Supported'}
|
||||
|
||||
# This is the base RestClient exception class. Rescue it if you want to
|
||||
# catch any exception that your request might raise
|
||||
# You can get the status code by e.http_code, or see anything about the
|
||||
# response via e.response.
|
||||
# For example, the entire result body (which is
|
||||
# probably an HTML error page) is e.response.
|
||||
class Exception < RuntimeError
|
||||
def message(default=nil)
|
||||
self.class::ErrorMessage
|
||||
end
|
||||
end
|
||||
attr_accessor :message, :response
|
||||
|
||||
# Base RestClient exception when there's a response available
|
||||
class ExceptionWithResponse < Exception
|
||||
attr_accessor :response
|
||||
|
||||
def initialize(response=nil)
|
||||
def initialize response = nil
|
||||
@response = response
|
||||
end
|
||||
|
||||
def http_code
|
||||
# return integer for compatibility
|
||||
@response.code.to_i if @response
|
||||
end
|
||||
|
||||
def http_body
|
||||
RestClient::Request.decode(@response['content-encoding'], @response.body) if @response
|
||||
@response.body
|
||||
end
|
||||
end
|
||||
|
||||
# A redirect was encountered; caught by execute to retry with the new url.
|
||||
class Redirect < Exception
|
||||
ErrorMessage = "Redirect"
|
||||
|
||||
attr_accessor :url
|
||||
|
||||
def initialize(url)
|
||||
@url = url
|
||||
def inspect
|
||||
"#{self.class} : #{http_code} #{message}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class NotModified < ExceptionWithResponse
|
||||
ErrorMessage = 'NotModified'
|
||||
# Compatibility
|
||||
class ExceptionWithResponse < Exception
|
||||
end
|
||||
|
||||
# Authorization is required to access the resource specified.
|
||||
class Unauthorized < ExceptionWithResponse
|
||||
ErrorMessage = 'Unauthorized'
|
||||
end
|
||||
|
||||
# No resource was found at the given URL.
|
||||
class ResourceNotFound < ExceptionWithResponse
|
||||
ErrorMessage = 'Resource not found'
|
||||
end
|
||||
|
||||
# The server broke the connection prior to the request completing. Usually
|
||||
# this means it crashed, or sometimes that your network connection was
|
||||
# severed before it could complete.
|
||||
class ServerBrokeConnection < Exception
|
||||
ErrorMessage = 'Server broke connection'
|
||||
end
|
||||
|
||||
# The server took too long to respond.
|
||||
class RequestTimeout < Exception
|
||||
ErrorMessage = 'Request timed out'
|
||||
end
|
||||
|
||||
# The request failed, meaning the remote HTTP server returned a code other
|
||||
# than success, unauthorized, or redirect.
|
||||
#
|
||||
# The exception message attempts to extract the error from the XML, using
|
||||
# format returned by Rails: <errors><error>some message</error></errors>
|
||||
#
|
||||
# You can get the status code by e.http_code, or see anything about the
|
||||
# response via e.response. For example, the entire result body (which is
|
||||
# probably an HTML error page) is e.response.body.
|
||||
# The request failed with an error code not managed by the code
|
||||
class RequestFailed < ExceptionWithResponse
|
||||
|
||||
def message
|
||||
"HTTP status code #{http_code}"
|
||||
end
|
||||
|
@ -79,6 +83,45 @@ module RestClient
|
|||
message
|
||||
end
|
||||
end
|
||||
|
||||
# We will a create an exception for each status code, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
module Exceptions
|
||||
# Map http status codes to the corresponding exception class
|
||||
EXCEPTIONS_MAP = {}
|
||||
end
|
||||
|
||||
STATUSES.each_pair do |code, message|
|
||||
|
||||
# Compatibility
|
||||
superclass = ([304, 401, 404].include? code) ? ExceptionWithResponse : RequestFailed
|
||||
klass = Class.new(superclass) do
|
||||
send(:define_method, :message) {message}
|
||||
end
|
||||
klass_constant = const_set message.gsub(/ /, '').gsub(/-/, ''), klass
|
||||
Exceptions::EXCEPTIONS_MAP[code] = klass_constant
|
||||
end
|
||||
|
||||
# A redirect was encountered; caught by execute to retry with the new url.
|
||||
class Redirect < Exception
|
||||
|
||||
message = 'Redirect'
|
||||
|
||||
attr_accessor :url
|
||||
|
||||
def initialize(url)
|
||||
@url = url
|
||||
end
|
||||
end
|
||||
|
||||
# The server broke the connection prior to the request completing. Usually
|
||||
# this means it crashed, or sometimes that your network connection was
|
||||
# severed before it could complete.
|
||||
class ServerBrokeConnection < Exception
|
||||
message = 'Server broke connection'
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
# backwards compatibility
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
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
|
||||
|
||||
# The raw headers.
|
||||
def raw_headers
|
||||
@raw_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(/-/, '_').downcase.to_sym] = value.first
|
||||
out
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -91,7 +91,7 @@ module RestClient
|
|||
end
|
||||
|
||||
def short_inspect
|
||||
(size > 100 ? "#{size} byte length" : inspect)
|
||||
(size > 100 ? "#{size} byte(s) length" : inspect)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -141,7 +141,7 @@ module RestClient
|
|||
end
|
||||
|
||||
def create_regular_field(s, k, v)
|
||||
s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"")
|
||||
s.write("Content-Disposition: form-data; name=\"#{k}\"")
|
||||
s.write(EOL)
|
||||
s.write(EOL)
|
||||
s.write(v)
|
||||
|
@ -149,7 +149,7 @@ module RestClient
|
|||
|
||||
def create_file_field(s, k, v)
|
||||
begin
|
||||
s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"; filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
|
||||
s.write("Content-Disposition: form-data; name=\"#{k}\"; filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
|
||||
s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
|
||||
s.write(EOL)
|
||||
while data = v.read(8124)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
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
|
||||
|
@ -11,13 +9,12 @@ module RestClient
|
|||
# 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
|
||||
class RawResponse < AbstractResponse
|
||||
|
||||
attr_reader :file
|
||||
|
||||
def initialize(tempfile, net_http_res)
|
||||
@net_http_res = net_http_res
|
||||
def initialize tempfile, net_http_res
|
||||
super net_http_res
|
||||
@file = tempfile
|
||||
end
|
||||
|
||||
|
@ -26,5 +23,9 @@ module RestClient
|
|||
@file.read
|
||||
end
|
||||
|
||||
def size
|
||||
File.size file
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,16 +20,18 @@ module RestClient
|
|||
# * :timeout and :open_timeout
|
||||
# * :ssl_client_cert, :ssl_client_key, :ssl_ca_file
|
||||
class Request
|
||||
|
||||
attr_reader :method, :url, :payload, :headers, :processed_headers,
|
||||
:cookies, :user, :password, :timeout, :open_timeout,
|
||||
:verify_ssl, :ssl_client_cert, :ssl_client_key, :ssl_ca_file,
|
||||
:raw_response
|
||||
|
||||
def self.execute(args)
|
||||
new(args).execute
|
||||
|
||||
def self.execute(args, &block)
|
||||
new(args).execute &block
|
||||
end
|
||||
|
||||
def initialize(args)
|
||||
def initialize args
|
||||
@method = args[:method] or raise ArgumentError, "must pass :method"
|
||||
@url = args[:url] or raise ArgumentError, "must pass :url"
|
||||
@headers = args[:headers] || {}
|
||||
|
@ -48,23 +50,25 @@ module RestClient
|
|||
@processed_headers = make_headers headers
|
||||
end
|
||||
|
||||
def execute
|
||||
execute_inner
|
||||
def execute &block
|
||||
execute_inner &block
|
||||
rescue Redirect => e
|
||||
@processed_headers.delete "Content-Length"
|
||||
@processed_headers.delete "Content-Type"
|
||||
@url = e.url
|
||||
@method = :get
|
||||
@payload = nil
|
||||
execute
|
||||
execute &block
|
||||
end
|
||||
|
||||
def execute_inner
|
||||
def execute_inner &block
|
||||
uri = parse_url_with_auth(url)
|
||||
transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload
|
||||
transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, &block
|
||||
end
|
||||
|
||||
def make_headers user_headers
|
||||
unless @cookies.empty?
|
||||
user_headers[:cookie] = @cookies.map {|key, val| "#{key.to_s}=#{val}" }.join('; ')
|
||||
user_headers[:cookie] = @cookies.map {|(key, val)| "#{key.to_s}=#{val}" }.sort.join(",")
|
||||
end
|
||||
|
||||
headers = default_headers.merge(user_headers).inject({}) do |final, (key, value)|
|
||||
|
@ -73,13 +77,13 @@ module RestClient
|
|||
target_value = value.to_s
|
||||
final[target_key] = MIME::Types.type_for_extension target_value
|
||||
elsif 'ACCEPT' == target_key.upcase
|
||||
# Accept can be composed of several comma-separated values
|
||||
if value.is_a? Array
|
||||
target_values = value
|
||||
else
|
||||
target_values = value.to_s.split ','
|
||||
end
|
||||
final[target_key] = target_values.map{ |ext| MIME::Types.type_for_extension(ext.to_s.strip)}.join(', ')
|
||||
# Accept can be composed of several comma-separated values
|
||||
if value.is_a? Array
|
||||
target_values = value
|
||||
else
|
||||
target_values = value.to_s.split ','
|
||||
end
|
||||
final[target_key] = target_values.map{ |ext| MIME::Types.type_for_extension(ext.to_s.strip)}.join(', ')
|
||||
else
|
||||
final[target_key] = value.to_s
|
||||
end
|
||||
|
@ -132,8 +136,8 @@ module RestClient
|
|||
end
|
||||
end
|
||||
|
||||
def transmit(uri, req, payload)
|
||||
setup_credentials(req)
|
||||
def transmit uri, req, payload, &block
|
||||
setup_credentials req
|
||||
|
||||
net = net_http_class.new(uri.host, uri.port)
|
||||
net.use_ssl = uri.is_a?(URI::HTTPS)
|
||||
|
@ -148,20 +152,12 @@ module RestClient
|
|||
net.read_timeout = @timeout if @timeout
|
||||
net.open_timeout = @open_timeout if @open_timeout
|
||||
|
||||
display_log request_log
|
||||
log_request
|
||||
|
||||
net.start do |http|
|
||||
res = http.request(req, payload) { |http_response| fetch_body(http_response) }
|
||||
result = process_result(res)
|
||||
display_log response_log(res)
|
||||
|
||||
if result.kind_of?(String) or @method == :head
|
||||
Response.new(result, res)
|
||||
elsif @raw_response
|
||||
RawResponse.new(@tf, res)
|
||||
else
|
||||
Response.new(nil, res)
|
||||
end
|
||||
log_response res
|
||||
process_result res, &block
|
||||
end
|
||||
rescue EOFError
|
||||
raise RestClient::ServerBrokeConnection
|
||||
|
@ -181,14 +177,16 @@ module RestClient
|
|||
@tf = Tempfile.new("rest-client")
|
||||
size, total = 0, http_response.header['Content-Length'].to_i
|
||||
http_response.read_body do |chunk|
|
||||
@tf.write(chunk)
|
||||
@tf.write chunk
|
||||
size += chunk.size
|
||||
if size == 0
|
||||
display_log("#{@method} #{@url} done (0 length file)")
|
||||
elsif total == 0
|
||||
display_log("#{@method} #{@url} (zero content length)")
|
||||
else
|
||||
display_log("#{@method} #{@url} %d%% done (%d of %d)" % [(size * 100) / total, size, total])
|
||||
if RestClient.log
|
||||
if size == 0
|
||||
RestClient.log << "#{@method} #{@url} done (0 length file\n)"
|
||||
elsif total == 0
|
||||
RestClient.log << "#{@method} #{@url} (zero content length)\n"
|
||||
else
|
||||
RestClient.log << "#{@method} #{@url} %d%% done (%d of %d)\n" % [(size * 100) / total, size, total]
|
||||
end
|
||||
end
|
||||
end
|
||||
@tf.close
|
||||
|
@ -199,13 +197,17 @@ module RestClient
|
|||
http_response
|
||||
end
|
||||
|
||||
def process_result(res)
|
||||
if res.code =~ /\A2\d{2}\z/
|
||||
def process_result res
|
||||
if @raw_response
|
||||
# We don't decode raw requests
|
||||
unless @raw_response
|
||||
self.class.decode res['content-encoding'], res.body if res.body
|
||||
end
|
||||
elsif %w(301 302 303).include? res.code
|
||||
response = RawResponse.new(@tf, res)
|
||||
else
|
||||
response = Response.new(Request.decode(res['content-encoding'], res.body), res)
|
||||
end
|
||||
|
||||
code = res.code.to_i
|
||||
|
||||
if (301..303).include? code
|
||||
url = res.header['Location']
|
||||
|
||||
if url !~ /^http/
|
||||
|
@ -213,53 +215,40 @@ module RestClient
|
|||
uri.path = "/#{url}".squeeze('/')
|
||||
url = uri.to_s
|
||||
end
|
||||
|
||||
raise Redirect, url
|
||||
elsif res.code == "304"
|
||||
raise NotModified, res
|
||||
elsif res.code == "401"
|
||||
raise Unauthorized, res
|
||||
elsif res.code == "404"
|
||||
raise ResourceNotFound, res
|
||||
else
|
||||
raise RequestFailed, res
|
||||
if block_given?
|
||||
yield response
|
||||
else
|
||||
response.return!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.decode(content_encoding, body)
|
||||
def self.decode content_encoding, body
|
||||
if content_encoding == 'gzip' and not body.empty?
|
||||
Zlib::GzipReader.new(StringIO.new(body)).read
|
||||
elsif content_encoding == 'deflate'
|
||||
Zlib::Inflate.new.inflate(body)
|
||||
Zlib::Inflate.new.inflate body
|
||||
else
|
||||
body
|
||||
end
|
||||
end
|
||||
|
||||
def request_log
|
||||
def log_request
|
||||
if RestClient.log
|
||||
out = []
|
||||
out << "RestClient.#{method} #{url.inspect}"
|
||||
out << "headers: #{processed_headers.inspect}"
|
||||
out << "paylod: #{payload.short_inspect}" if payload
|
||||
out.join(', ')
|
||||
out << payload.short_inspect if payload
|
||||
out << processed_headers.inspect.gsub(/^\{/, '').gsub(/\}$/, '')
|
||||
RestClient.log << out.join(', ') + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
def response_log(res)
|
||||
size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
|
||||
"# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes"
|
||||
end
|
||||
|
||||
def display_log(msg)
|
||||
return unless log_to = RestClient.log
|
||||
|
||||
if log_to == 'stdout'
|
||||
STDOUT.puts msg
|
||||
elsif log_to == 'stderr'
|
||||
STDERR.puts msg
|
||||
else
|
||||
File.open(log_to, 'a') { |f| f.puts msg }
|
||||
def log_response res
|
||||
if RestClient.log
|
||||
size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
|
||||
RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -274,7 +263,7 @@ module MIME
|
|||
|
||||
# Return the first found content-type for a value considered as an extension or the value itself
|
||||
def type_for_extension ext
|
||||
candidates = @extension_index[ext]
|
||||
candidates = @extension_index[ext]
|
||||
candidates.empty? ? ext : candidates[0].content_type
|
||||
end
|
||||
|
||||
|
|
|
@ -34,10 +34,11 @@ module RestClient
|
|||
# site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
|
||||
#
|
||||
class Resource
|
||||
attr_reader :url, :options
|
||||
attr_reader :url, :options, :block
|
||||
|
||||
def initialize(url, options={}, backwards_compatibility=nil)
|
||||
def initialize(url, options={}, backwards_compatibility=nil, &block)
|
||||
@url = url
|
||||
@block = block
|
||||
if options.class == Hash
|
||||
@options = options
|
||||
else # compatibility with previous versions
|
||||
|
@ -45,38 +46,38 @@ module RestClient
|
|||
end
|
||||
end
|
||||
|
||||
def get(additional_headers={}, &b)
|
||||
def get(additional_headers={}, &block)
|
||||
headers = (options[:headers] || {}).merge(additional_headers)
|
||||
Request.execute(options.merge(
|
||||
:method => :get,
|
||||
:url => url,
|
||||
:headers => headers), &b)
|
||||
:headers => headers), &(block || @block))
|
||||
end
|
||||
|
||||
def post(payload, additional_headers={}, &b)
|
||||
def post(payload, additional_headers={}, &block)
|
||||
headers = (options[:headers] || {}).merge(additional_headers)
|
||||
Request.execute(options.merge(
|
||||
:method => :post,
|
||||
:url => url,
|
||||
:payload => payload,
|
||||
:headers => headers), &b)
|
||||
:headers => headers), &(block || @block))
|
||||
end
|
||||
|
||||
def put(payload, additional_headers={}, &b)
|
||||
def put(payload, additional_headers={}, &block)
|
||||
headers = (options[:headers] || {}).merge(additional_headers)
|
||||
Request.execute(options.merge(
|
||||
:method => :put,
|
||||
:url => url,
|
||||
:payload => payload,
|
||||
:headers => headers), &b)
|
||||
:headers => headers), &(block || @block))
|
||||
end
|
||||
|
||||
def delete(additional_headers={}, &b)
|
||||
def delete(additional_headers={}, &block)
|
||||
headers = (options[:headers] || {}).merge(additional_headers)
|
||||
Request.execute(options.merge(
|
||||
:method => :delete,
|
||||
:url => url,
|
||||
:headers => headers), &b)
|
||||
:headers => headers), &(block || @block))
|
||||
end
|
||||
|
||||
def to_s
|
||||
|
|
|
@ -1,19 +1,31 @@
|
|||
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
|
||||
# the body, but on the occassion you want to fetch the headers you can:
|
||||
#
|
||||
# RestClient.get('http://example.com').headers[:content_type]
|
||||
#
|
||||
class Response < String
|
||||
|
||||
include RestClient::Mixin::Response
|
||||
# A Response from RestClient, you can access the response body, the code or the headers.
|
||||
#
|
||||
class Response < AbstractResponse
|
||||
|
||||
def initialize(string, net_http_res)
|
||||
@net_http_res = net_http_res
|
||||
super(string || "")
|
||||
attr_reader :body
|
||||
|
||||
def initialize body, net_http_res
|
||||
super net_http_res
|
||||
@body = body || ""
|
||||
end
|
||||
|
||||
def method_missing symbol, *args
|
||||
if body.respond_to? symbol
|
||||
warn "[warning] The Response is no more a String, please update your code"
|
||||
body.send symbol, *args
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
body.to_s
|
||||
end
|
||||
|
||||
def size
|
||||
body.size
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{rest-client}
|
||||
s.version = "1.2.0"
|
||||
s.version = "1.3.0"
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["Adam Wiggins", "Archiloque"]
|
||||
s.date = %q{2010-01-3}
|
||||
s.authors = ["Adam Wiggins", "Julien Kirch"]
|
||||
s.date = %q{2010-01-25}
|
||||
s.default_executable = %q{restclient}
|
||||
s.description = %q{A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete.}
|
||||
s.description = %q{A simple Simple HTTP and REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete.}
|
||||
s.email = %q{rest.client@librelist.com}
|
||||
s.executables = ["restclient"]
|
||||
s.extra_rdoc_files = [
|
||||
"README.rdoc"
|
||||
"README.rdoc", "history.md"
|
||||
]
|
||||
s.files = [
|
||||
"README.rdoc",
|
||||
|
@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|||
"lib/rest_client.rb",
|
||||
"lib/restclient.rb",
|
||||
"lib/restclient/exceptions.rb",
|
||||
"lib/restclient/mixin/response.rb",
|
||||
"lib/restclient/abstract_response.rb",
|
||||
"lib/restclient/net_http_ext.rb",
|
||||
"lib/restclient/payload.rb",
|
||||
"lib/restclient/raw_response.rb",
|
||||
|
@ -30,8 +30,9 @@ Gem::Specification.new do |s|
|
|||
"lib/restclient/response.rb",
|
||||
"spec/base.rb",
|
||||
"spec/exceptions_spec.rb",
|
||||
"spec/integration_spec.rb",
|
||||
"spec/master_shake.jpg",
|
||||
"spec/mixin/response_spec.rb",
|
||||
"spec/abstract_response_spec.rb",
|
||||
"spec/payload_spec.rb",
|
||||
"spec/raw_response_spec.rb",
|
||||
"spec/request_spec.rb",
|
||||
|
@ -48,7 +49,8 @@ Gem::Specification.new do |s|
|
|||
s.test_files = [
|
||||
"spec/base.rb",
|
||||
"spec/exceptions_spec.rb",
|
||||
"spec/mixin/response_spec.rb",
|
||||
"spec/integration_spec.rb",
|
||||
"spec/abstract_response_spec.rb",
|
||||
"spec/payload_spec.rb",
|
||||
"spec/raw_response_spec.rb",
|
||||
"spec/request_spec.rb",
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
require File.dirname(__FILE__) + '/../base'
|
||||
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
|
||||
describe RestClient::AbstractResponse do
|
||||
before do
|
||||
@net_http_res = mock('net http response')
|
||||
@response = MockResponse.new('abc', @net_http_res)
|
||||
@response = RestClient::AbstractResponse.new(@net_http_res)
|
||||
end
|
||||
|
||||
it "fetches the numeric response code" do
|
|
@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/base'
|
|||
|
||||
describe RestClient::Exception do
|
||||
it "sets the exception message to ErrorMessage" do
|
||||
RestClient::ResourceNotFound.new.message.should == 'Resource not found'
|
||||
RestClient::ResourceNotFound.new.message.should == 'Resource Not Found'
|
||||
end
|
||||
|
||||
it "contains exceptions in RestClient" do
|
||||
|
@ -29,10 +29,8 @@ describe RestClient::RequestFailed do
|
|||
end
|
||||
|
||||
it "http_body convenience method for fetching the body (decoding when necessary)" do
|
||||
@response.stub!(:[]).with('content-encoding').and_return('gzip')
|
||||
@response.stub!(:body).and_return('compressed body')
|
||||
RestClient::Request.should_receive(:decode).with('gzip', 'compressed body').and_return('plain body')
|
||||
RestClient::RequestFailed.new(@response).http_body.should == 'plain body'
|
||||
RestClient::RequestFailed.new(@response).http_code.should == 502
|
||||
RestClient::RequestFailed.new(@response).message.should == 'HTTP status code 502'
|
||||
end
|
||||
|
||||
it "shows the status code in the message" do
|
||||
|
|
38
spec/integration_spec.rb
Normal file
38
spec/integration_spec.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
require File.dirname(__FILE__) + '/base'
|
||||
|
||||
require 'webmock/rspec'
|
||||
include WebMock
|
||||
|
||||
describe RestClient do
|
||||
|
||||
it "a simple request" do
|
||||
body = 'abc'
|
||||
stub_request(:get, "www.example.com").to_return(:body => body, :status => 200)
|
||||
response = RestClient.get "www.example.com"
|
||||
response.code.should == 200
|
||||
response.body.should == body
|
||||
end
|
||||
|
||||
it "a simple request with gzipped content" do
|
||||
stub_request(:get, "www.example.com").with(:headers => { 'Accept-Encoding' => 'gzip, deflate' }).to_return(:body => "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000", :status => 200, :headers => { 'Content-Encoding' => 'gzip' } )
|
||||
response = RestClient.get "www.example.com"
|
||||
response.code.should == 200
|
||||
response.body.should == "i'm gziped\n"
|
||||
end
|
||||
|
||||
it "a 404" do
|
||||
body = "Ho hai ! I'm not here !"
|
||||
stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
|
||||
begin
|
||||
RestClient.get "www.example.com"
|
||||
raise
|
||||
rescue RestClient::ResourceNotFound => e
|
||||
e.http_code.should == 404
|
||||
e.response.code.should == 404
|
||||
e.response.body.should == body
|
||||
e.http_body.should == body
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -48,11 +48,11 @@ describe RestClient::Payload do
|
|||
m = RestClient::Payload::Multipart.new([[:bar, "baz"], [:foo, "bar"]])
|
||||
m.to_s.should == <<-EOS
|
||||
--#{m.boundary}\r
|
||||
Content-Disposition: multipart/form-data; name="bar"\r
|
||||
Content-Disposition: form-data; name="bar"\r
|
||||
\r
|
||||
baz\r
|
||||
--#{m.boundary}\r
|
||||
Content-Disposition: multipart/form-data; name="foo"\r
|
||||
Content-Disposition: form-data; name="foo"\r
|
||||
\r
|
||||
bar\r
|
||||
--#{m.boundary}--\r
|
||||
|
@ -64,7 +64,7 @@ bar\r
|
|||
m = RestClient::Payload::Multipart.new({:foo => f})
|
||||
m.to_s.should == <<-EOS
|
||||
--#{m.boundary}\r
|
||||
Content-Disposition: multipart/form-data; name="foo"; filename="master_shake.jpg"\r
|
||||
Content-Disposition: form-data; name="foo"; filename="master_shake.jpg"\r
|
||||
Content-Type: image/jpeg\r
|
||||
\r
|
||||
#{IO.read(f.path)}\r
|
||||
|
@ -79,7 +79,7 @@ Content-Type: image/jpeg\r
|
|||
m = RestClient::Payload::Multipart.new({:foo => f})
|
||||
m.to_s.should == <<-EOS
|
||||
--#{m.boundary}\r
|
||||
Content-Disposition: multipart/form-data; name="foo"; filename="foo.txt"\r
|
||||
Content-Disposition: form-data; name="foo"; filename="foo.txt"\r
|
||||
Content-Type: text/plain\r
|
||||
\r
|
||||
#{IO.read(f.path)}\r
|
||||
|
@ -91,7 +91,7 @@ Content-Type: text/plain\r
|
|||
m = RestClient::Payload::Multipart.new({:bar => {:baz => "foo"}})
|
||||
m.to_s.should == <<-EOS
|
||||
--#{m.boundary}\r
|
||||
Content-Disposition: multipart/form-data; name="bar[baz]"\r
|
||||
Content-Disposition: form-data; name="bar[baz]"\r
|
||||
\r
|
||||
foo\r
|
||||
--#{m.boundary}--\r
|
||||
|
@ -103,7 +103,7 @@ foo\r
|
|||
m = RestClient::Payload::Multipart.new({:foo => {:bar => f}})
|
||||
m.to_s.should == <<-EOS
|
||||
--#{m.boundary}\r
|
||||
Content-Disposition: multipart/form-data; name="foo[bar]"; filename="foo.txt"\r
|
||||
Content-Disposition: form-data; name="foo[bar]"; filename="foo.txt"\r
|
||||
Content-Type: text/plain\r
|
||||
\r
|
||||
#{IO.read(f.path)}\r
|
||||
|
|
|
@ -15,27 +15,29 @@ describe RestClient::Request do
|
|||
@net.stub!(:start).and_yield(@http)
|
||||
@net.stub!(:use_ssl=)
|
||||
@net.stub!(:verify_mode=)
|
||||
RestClient.log = 'test.log'
|
||||
RestClient.log = nil
|
||||
end
|
||||
|
||||
it "accept */* mimetype, preferring xml" do
|
||||
@request.default_headers[:accept].should == '*/*; q=0.5, application/xml'
|
||||
end
|
||||
|
||||
it "decodes an uncompressed result body by passing it straight through" do
|
||||
RestClient::Request.decode(nil, 'xyz').should == 'xyz'
|
||||
end
|
||||
describe "compression" do
|
||||
it "decodes an uncompressed result body by passing it straight through" do
|
||||
RestClient::Request.decode(nil, 'xyz').should == 'xyz'
|
||||
end
|
||||
|
||||
it "decodes a gzip body" do
|
||||
RestClient::Request.decode('gzip', "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000").should == "i'm gziped\n"
|
||||
end
|
||||
it "decodes a gzip body" do
|
||||
RestClient::Request.decode('gzip', "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000").should == "i'm gziped\n"
|
||||
end
|
||||
|
||||
it "ingores gzip for empty bodies" do
|
||||
RestClient::Request.decode('gzip', '').should be_empty
|
||||
end
|
||||
it "ingores gzip for empty bodies" do
|
||||
RestClient::Request.decode('gzip', '').should be_empty
|
||||
end
|
||||
|
||||
it "decodes a deflated body" do
|
||||
RestClient::Request.decode('deflate', "x\234+\316\317MUHIM\313I,IMQ(I\255(\001\000A\223\006\363").should == "some deflated text"
|
||||
it "decodes a deflated body" do
|
||||
RestClient::Request.decode('deflate', "x\234+\316\317MUHIM\313I,IMQ(I\255(\001\000A\223\006\363").should == "some deflated text"
|
||||
end
|
||||
end
|
||||
|
||||
it "processes a successful result" do
|
||||
|
@ -43,7 +45,8 @@ describe RestClient::Request do
|
|||
res.stub!(:code).and_return("200")
|
||||
res.stub!(:body).and_return('body')
|
||||
res.stub!(:[]).with('content-encoding').and_return(nil)
|
||||
@request.process_result(res).should == 'body'
|
||||
@request.process_result(res).body.should == 'body'
|
||||
@request.process_result(res).to_s.should == 'body'
|
||||
end
|
||||
|
||||
it "doesn't classify successful requests as failed" do
|
||||
|
@ -66,76 +69,83 @@ describe RestClient::Request do
|
|||
@request.parse_url('example.com/resource')
|
||||
end
|
||||
|
||||
it "extracts the username and password when parsing http://user:password@example.com/" do
|
||||
URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
|
||||
@request.parse_url_with_auth('http://joe:pass1@example.com/resource')
|
||||
@request.user.should == 'joe'
|
||||
@request.password.should == 'pass1'
|
||||
end
|
||||
describe "user - password" do
|
||||
it "extracts the username and password when parsing http://user:password@example.com/" do
|
||||
URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
|
||||
@request.parse_url_with_auth('http://joe:pass1@example.com/resource')
|
||||
@request.user.should == 'joe'
|
||||
@request.password.should == 'pass1'
|
||||
end
|
||||
|
||||
it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
|
||||
URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
|
||||
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
|
||||
@request.parse_url_with_auth('http://example.com/resource')
|
||||
@request.user.should == 'beth'
|
||||
@request.password.should == 'pass2'
|
||||
it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
|
||||
URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
|
||||
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
|
||||
@request.parse_url_with_auth('http://example.com/resource')
|
||||
@request.user.should == 'beth'
|
||||
@request.password.should == 'pass2'
|
||||
end
|
||||
end
|
||||
|
||||
it "correctly formats cookies provided to the constructor" do
|
||||
URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
|
||||
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :cookies => {:session_id => '1' })
|
||||
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :cookies => {:session_id => '1', :user_id => "someone" })
|
||||
@request.should_receive(:default_headers).and_return({'foo' => 'bar'})
|
||||
headers = @request.make_headers({}).should == { 'Foo' => 'bar', 'Cookie' => 'session_id=1'}
|
||||
headers = @request.make_headers({}).should == { 'Foo' => 'bar', 'Cookie' => 'session_id=1,user_id=someone'}
|
||||
end
|
||||
|
||||
it "determines the Net::HTTP class to instantiate by the method name" do
|
||||
@request.net_http_request_class(:put).should == Net::HTTP::Put
|
||||
end
|
||||
|
||||
it "merges user headers with the default headers" do
|
||||
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
||||
headers = @request.make_headers('3' => '4')
|
||||
headers.should have_key('1')
|
||||
headers['1'].should == '2'
|
||||
headers.should have_key('3')
|
||||
headers['3'].should == '4'
|
||||
describe "user headers" do
|
||||
it "merges user headers with the default headers" do
|
||||
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
||||
headers = @request.make_headers('3' => '4')
|
||||
headers.should have_key('1')
|
||||
headers['1'].should == '2'
|
||||
headers.should have_key('3')
|
||||
headers['3'].should == '4'
|
||||
end
|
||||
|
||||
it "prefers the user header when the same header exists in the defaults" do
|
||||
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
||||
headers = @request.make_headers('1' => '3')
|
||||
headers.should have_key('1')
|
||||
headers['1'].should == '3'
|
||||
end
|
||||
end
|
||||
|
||||
it "prefers the user header when the same header exists in the defaults" do
|
||||
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
||||
headers = @request.make_headers('1' => '3')
|
||||
headers.should have_key('1')
|
||||
headers['1'].should == '3'
|
||||
end
|
||||
describe "header symbols" do
|
||||
|
||||
it "converts header symbols from :content_type to 'Content-type'" do
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:content_type => 'abc')
|
||||
headers.should have_key('Content-type')
|
||||
headers['Content-type'].should == 'abc'
|
||||
end
|
||||
it "converts header symbols from :content_type to 'Content-type'" do
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:content_type => 'abc')
|
||||
headers.should have_key('Content-type')
|
||||
headers['Content-type'].should == 'abc'
|
||||
end
|
||||
|
||||
it "converts content-type from extension to real content-type" do
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:content_type => 'json')
|
||||
headers.should have_key('Content-type')
|
||||
headers['Content-type'].should == 'application/json'
|
||||
end
|
||||
it "converts content-type from extension to real content-type" do
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:content_type => 'json')
|
||||
headers.should have_key('Content-type')
|
||||
headers['Content-type'].should == 'application/json'
|
||||
end
|
||||
|
||||
it "converts accept from extension(s) to real content-type(s)" do
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:accept => 'json, mp3')
|
||||
headers.should have_key('Accept')
|
||||
headers['Accept'].should == 'application/json, audio/mpeg'
|
||||
it "converts accept from extension(s) to real content-type(s)" do
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:accept => 'json, mp3')
|
||||
headers.should have_key('Accept')
|
||||
headers['Accept'].should == 'application/json, audio/mpeg'
|
||||
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:accept => :json)
|
||||
headers.should have_key('Accept')
|
||||
headers['Accept'].should == 'application/json'
|
||||
end
|
||||
@request.should_receive(:default_headers).and_return({})
|
||||
headers = @request.make_headers(:accept => :json)
|
||||
headers.should have_key('Accept')
|
||||
headers['Accept'].should == 'application/json'
|
||||
end
|
||||
|
||||
it "converts header values to strings" do
|
||||
@request.make_headers('A' => 1)['A'].should == '1'
|
||||
it "converts header values to strings" do
|
||||
@request.make_headers('A' => 1)['A'].should == '1'
|
||||
end
|
||||
end
|
||||
|
||||
it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
|
||||
|
@ -150,39 +160,31 @@ describe RestClient::Request do
|
|||
it "transmits the request with Net::HTTP" do
|
||||
@http.should_receive(:request).with('req', 'payload')
|
||||
@request.should_receive(:process_result)
|
||||
@request.should_receive(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "uses SSL when the URI refers to a https address" do
|
||||
@uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
|
||||
@net.should_receive(:use_ssl=).with(true)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
describe "payload" do
|
||||
it "sends nil payloads" do
|
||||
@http.should_receive(:request).with('req', nil)
|
||||
@request.should_receive(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
|
||||
it "sends nil payloads" do
|
||||
@http.should_receive(:request).with('req', nil)
|
||||
@request.should_receive(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
it "passes non-hash payloads straight through" do
|
||||
@request.process_payload("x").should == "x"
|
||||
end
|
||||
|
||||
it "passes non-hash payloads straight through" do
|
||||
@request.process_payload("x").should == "x"
|
||||
end
|
||||
it "converts a hash payload to urlencoded data" do
|
||||
@request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
|
||||
end
|
||||
|
||||
it "converts a hash payload to urlencoded data" do
|
||||
@request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
|
||||
end
|
||||
|
||||
it "accepts nested hashes in payload" do
|
||||
payload = @request.process_payload(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }})
|
||||
payload.should include('user[name]=joe')
|
||||
payload.should include('user[location][country]=USA')
|
||||
payload.should include('user[location][state]=CA')
|
||||
it "accepts nested hashes in payload" do
|
||||
payload = @request.process_payload(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }})
|
||||
payload.should include('user[name]=joe')
|
||||
payload.should include('user[location][country]=USA')
|
||||
payload.should include('user[location][state]=CA')
|
||||
end
|
||||
end
|
||||
|
||||
it "set urlencoded content_type header on hash payloads" do
|
||||
|
@ -190,31 +192,33 @@ describe RestClient::Request do
|
|||
@request.headers[:content_type].should == 'application/x-www-form-urlencoded'
|
||||
end
|
||||
|
||||
it "sets up the credentials prior to the request" do
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
describe "credentials" do
|
||||
it "sets up the credentials prior to the request" do
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
|
||||
@request.stub!(:user).and_return('joe')
|
||||
@request.stub!(:password).and_return('mypass')
|
||||
@request.should_receive(:setup_credentials).with('req')
|
||||
@request.stub!(:user).and_return('joe')
|
||||
@request.stub!(:password).and_return('mypass')
|
||||
@request.should_receive(:setup_credentials).with('req')
|
||||
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
|
||||
it "does not attempt to send any credentials if user is nil" do
|
||||
@request.stub!(:user).and_return(nil)
|
||||
req = mock("request")
|
||||
req.should_not_receive(:basic_auth)
|
||||
@request.setup_credentials(req)
|
||||
end
|
||||
it "does not attempt to send any credentials if user is nil" do
|
||||
@request.stub!(:user).and_return(nil)
|
||||
req = mock("request")
|
||||
req.should_not_receive(:basic_auth)
|
||||
@request.setup_credentials(req)
|
||||
end
|
||||
|
||||
it "setup credentials when there's a user" do
|
||||
@request.stub!(:user).and_return('joe')
|
||||
@request.stub!(:password).and_return('mypass')
|
||||
req = mock("request")
|
||||
req.should_receive(:basic_auth).with('joe', 'mypass')
|
||||
@request.setup_credentials(req)
|
||||
it "setup credentials when there's a user" do
|
||||
@request.stub!(:user).and_return('joe')
|
||||
@request.stub!(:password).and_return('mypass')
|
||||
req = mock("request")
|
||||
req.should_receive(:basic_auth).with('joe', 'mypass')
|
||||
@request.setup_credentials(req)
|
||||
end
|
||||
end
|
||||
|
||||
it "catches EOFError and shows the more informative ServerBrokeConnection" do
|
||||
|
@ -234,275 +238,297 @@ describe RestClient::Request do
|
|||
RestClient::Request.execute(1 => 2)
|
||||
end
|
||||
|
||||
it "raises a Redirect with the new location when the response is in the 30x range" do
|
||||
res = mock('response', :code => '301', :header => { 'Location' => 'http://new/resource' })
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://new/resource'}
|
||||
end
|
||||
|
||||
it "handles redirects with relative paths" do
|
||||
res = mock('response', :code => '301', :header => { 'Location' => 'index' })
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
||||
end
|
||||
|
||||
it "handles redirects with absolute paths" do
|
||||
@request.instance_variable_set('@url', 'http://some/place/else')
|
||||
res = mock('response', :code => '301', :header => { 'Location' => '/index' })
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
||||
end
|
||||
|
||||
it "uses GET and clears payload when following 30x redirects" do
|
||||
url = "http://example.com/redirected"
|
||||
|
||||
@request.should_receive(:execute_inner).once.ordered.and_raise(RestClient::Redirect.new(url))
|
||||
|
||||
@request.should_receive(:execute_inner).once.ordered do
|
||||
@request.url.should == url
|
||||
@request.method.should == :get
|
||||
@request.payload.should be_nil
|
||||
describe "redirection" do
|
||||
it "raises a Redirect with the new location when the response is in the 30x range" do
|
||||
res = mock('response', :code => '301', :header => { 'Location' => 'http://new/resource'}, :[] => ['content-encoding' => ''], :body => '' )
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://new/resource'}
|
||||
end
|
||||
|
||||
@request.execute
|
||||
it "handles redirects with relative paths" do
|
||||
res = mock('response', :code => '301', :header => { 'Location' => 'index' }, :[] => ['content-encoding' => ''], :body => '' )
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
||||
end
|
||||
|
||||
it "handles redirects with absolute paths" do
|
||||
@request.instance_variable_set('@url', 'http://some/place/else')
|
||||
res = mock('response', :code => '301', :header => { 'Location' => '/index' }, :[] => ['content-encoding' => ''], :body => '' )
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
||||
end
|
||||
|
||||
it "uses GET and clears payload and removes possible harmful headers when following 30x redirects" do
|
||||
url = "http://example.com/redirected"
|
||||
|
||||
@request.should_receive(:execute_inner).once.ordered.and_raise(RestClient::Redirect.new(url))
|
||||
|
||||
@request.should_receive(:execute_inner).once.ordered do
|
||||
@request.processed_headers.should_not have_key("Content-Length")
|
||||
@request.url.should == url
|
||||
@request.method.should == :get
|
||||
@request.payload.should be_nil
|
||||
end
|
||||
|
||||
@request.execute
|
||||
end
|
||||
end
|
||||
|
||||
it "raises Unauthorized when the response is 401" do
|
||||
res = mock('response', :code => '401')
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
|
||||
describe "exception" do
|
||||
it "raises Unauthorized when the response is 401" do
|
||||
res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
|
||||
end
|
||||
|
||||
it "raises ResourceNotFound when the response is 404" do
|
||||
res = mock('response', :code => '404', :[] => ['content-encoding' => ''], :body => '' )
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
|
||||
end
|
||||
|
||||
it "raises RequestFailed otherwise" do
|
||||
res = mock('response', :code => '500', :[] => ['content-encoding' => ''], :body => '' )
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::InternalServerError)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises ResourceNotFound when the response is 404" do
|
||||
res = mock('response', :code => '404')
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
|
||||
describe "block usage" do
|
||||
it "returns what asked to" do
|
||||
res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
|
||||
@request.process_result(res){|response| "foo"}.should == "foo"
|
||||
end
|
||||
end
|
||||
|
||||
it "raises RequestFailed otherwise" do
|
||||
res = mock('response', :code => '500')
|
||||
lambda { @request.process_result(res) }.should raise_error(RestClient::RequestFailed)
|
||||
describe "proxy" do
|
||||
it "creates a proxy class if a proxy url is given" do
|
||||
RestClient.stub!(:proxy).and_return("http://example.com/")
|
||||
@request.net_http_class.should include(Net::HTTP::ProxyDelta)
|
||||
end
|
||||
|
||||
it "creates a non-proxy class if a proxy url is not given" do
|
||||
@request.net_http_class.should_not include(Net::HTTP::ProxyDelta)
|
||||
end
|
||||
end
|
||||
|
||||
it "creates a proxy class if a proxy url is given" do
|
||||
RestClient.stub!(:proxy).and_return("http://example.com/")
|
||||
@request.net_http_class.should include(Net::HTTP::ProxyDelta)
|
||||
end
|
||||
|
||||
it "creates a non-proxy class if a proxy url is not given" do
|
||||
@request.net_http_class.should_not include(Net::HTTP::ProxyDelta)
|
||||
end
|
||||
describe "logging" do
|
||||
it "logs a get request" do
|
||||
log = RestClient.log = []
|
||||
RestClient::Request.new(:method => :get, :url => 'http://url').log_request
|
||||
['RestClient.get "http://url", "Accept-encoding"=>"gzip, deflate", "Accept"=>"*/*; q=0.5, application/xml"' + "\n",
|
||||
'RestClient.get "http://url", "Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate"' + "\n"].should include(log[0])
|
||||
end
|
||||
|
||||
it "logs a get request" do
|
||||
['RestClient.get "http://url", headers: {"Accept-encoding"=>"gzip, deflate", "Accept"=>"*/*; q=0.5, application/xml"}',
|
||||
'RestClient.get "http://url", headers: {"Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate}'].should include
|
||||
RestClient::Request.new(:method => :get, :url => 'http://url').request_log
|
||||
end
|
||||
it "logs a post request with a small payload" do
|
||||
log = RestClient.log = []
|
||||
RestClient::Request.new(:method => :post, :url => 'http://url', :payload => 'foo').log_request
|
||||
['RestClient.post "http://url", "foo", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"3", "Accept"=>"*/*; q=0.5, application/xml"' + "\n",
|
||||
'RestClient.post "http://url", "foo", "Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"3"' + "\n"].should include(log[0])
|
||||
end
|
||||
|
||||
it "logs a post request with a small payload" do
|
||||
['RestClient.post "http://url", headers: {"Accept-encoding"=>"gzip, deflate", "Content-Length"=>"3", "Accept"=>"*/*; q=0.5, application/xml"}, paylod: "foo"',
|
||||
'RestClient.post "http://url", headers: {"Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"3"}, paylod: "foo"'].should include
|
||||
RestClient::Request.new(:method => :post, :url => 'http://url', :payload => 'foo').request_log
|
||||
end
|
||||
it "logs a post request with a large payload" do
|
||||
log = RestClient.log = []
|
||||
RestClient::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000)).log_request
|
||||
['RestClient.post "http://url", 1000 byte(s) length, "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000", "Accept"=>"*/*; q=0.5, application/xml"' + "\n",
|
||||
'RestClient.post "http://url", 1000 byte(s) length, "Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000"' + "\n"].should include(log[0])
|
||||
end
|
||||
|
||||
it "logs a post request with a large payload" do
|
||||
['RestClient.post "http://url", headers: {"Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000", "Accept"=>"*/*; q=0.5, application/xml"}, paylod: 1000 byte length',
|
||||
'RestClient.post "http://url", headers: {"Accept"=>"*/*; q=0.5, application/xml", "Accept-encoding"=>"gzip, deflate", "Content-Length"=>"1000"}, paylod: 1000 byte length'].should include
|
||||
RestClient::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000)).request_log
|
||||
end
|
||||
it "logs input headers as a hash" do
|
||||
log = RestClient.log = []
|
||||
RestClient::Request.new(:method => :get, :url => 'http://url', :headers => { :accept => 'text/plain' }).log_request
|
||||
['RestClient.get "http://url", "Accept-encoding"=>"gzip, deflate", "Accept"=>"text/plain"' + "\n",
|
||||
'RestClient.get "http://url", "Accept"=>"text/plain", "Accept-encoding"=>"gzip, deflate"' + "\n"].should include(log[0])
|
||||
end
|
||||
|
||||
it "logs input headers as a hash" do
|
||||
['RestClient.get "http://url", headers: {"Accept-encoding"=>"gzip, deflate", "Accept"=>"text/plain"}',
|
||||
'RestClient.get "http://url", headers: {"Accept"=>"text/plain", "Accept-encoding"=>"gzip, deflate"}'].should include
|
||||
RestClient::Request.new(:method => :get, :url => 'http://url', :headers => { :accept => 'text/plain' })
|
||||
end
|
||||
it "logs a response including the status code, content type, and result body size in bytes" do
|
||||
log = RestClient.log = []
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
|
||||
res.stub!(:[]).with('Content-type').and_return('text/html')
|
||||
@request.log_response res
|
||||
log[0].should == "# => 200 OK | text/html 4 bytes\n"
|
||||
end
|
||||
|
||||
it "logs a response including the status code, content type, and result body size in bytes" do
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
|
||||
res.stub!(:[]).with('Content-type').and_return('text/html')
|
||||
@request.response_log(res).should == "# => 200 OK | text/html 4 bytes"
|
||||
end
|
||||
it "logs a response with a nil Content-type" do
|
||||
log = RestClient.log = []
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
|
||||
res.stub!(:[]).with('Content-type').and_return(nil)
|
||||
@request.log_response res
|
||||
log[0].should == "# => 200 OK | 4 bytes\n"
|
||||
end
|
||||
|
||||
it "logs a response with a nil Content-type" do
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
|
||||
res.stub!(:[]).with('Content-type').and_return(nil)
|
||||
@request.response_log(res).should == "# => 200 OK | 4 bytes"
|
||||
end
|
||||
|
||||
it "logs a response with a nil body" do
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => nil)
|
||||
res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
|
||||
@request.response_log(res).should == "# => 200 OK | text/html 0 bytes"
|
||||
it "logs a response with a nil body" do
|
||||
log = RestClient.log = []
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => nil)
|
||||
res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
|
||||
@request.log_response res
|
||||
log[0].should == "# => 200 OK | text/html 0 bytes\n"
|
||||
end
|
||||
end
|
||||
|
||||
it "strips the charset from the response content type" do
|
||||
log = RestClient.log = []
|
||||
res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
|
||||
res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
|
||||
@request.response_log(res).should == "# => 200 OK | text/html 4 bytes"
|
||||
@request.log_response res
|
||||
log[0].should == "# => 200 OK | text/html 4 bytes\n"
|
||||
end
|
||||
|
||||
it "displays the log to stdout" do
|
||||
RestClient.stub!(:log).and_return('stdout')
|
||||
STDOUT.should_receive(:puts).with('xyz')
|
||||
@request.display_log('xyz')
|
||||
describe "timeout" do
|
||||
it "set read_timeout" do
|
||||
@request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
|
||||
@net.should_receive(:read_timeout=).with(123)
|
||||
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
|
||||
it "set open_timeout" do
|
||||
@request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
|
||||
@net.should_receive(:open_timeout=).with(123)
|
||||
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
end
|
||||
|
||||
it "displays the log to stderr" do
|
||||
RestClient.stub!(:log).and_return('stderr')
|
||||
STDERR.should_receive(:puts).with('xyz')
|
||||
@request.display_log('xyz')
|
||||
end
|
||||
describe "ssl" do
|
||||
it "uses SSL when the URI refers to a https address" do
|
||||
@uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
|
||||
@net.should_receive(:use_ssl=).with(true)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "append the log to the requested filename" do
|
||||
RestClient.stub!(:log).and_return('/tmp/restclient.log')
|
||||
f = mock('file handle')
|
||||
File.should_receive(:open).with('/tmp/restclient.log', 'a').and_yield(f)
|
||||
f.should_receive(:puts).with('xyz')
|
||||
@request.display_log('xyz')
|
||||
end
|
||||
it "should default to not verifying ssl certificates" do
|
||||
@request.verify_ssl.should == false
|
||||
end
|
||||
|
||||
it "set read_timeout" do
|
||||
@request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
|
||||
@net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
@net.should_receive(:read_timeout=).with(123)
|
||||
it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
|
||||
@request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
|
||||
@net.should_not_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
|
||||
mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
||||
@request = RestClient::Request.new( :method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:verify_ssl => mode )
|
||||
@net.should_receive(:verify_mode=).with(mode)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "set open_timeout" do
|
||||
@request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
it "should default to not having an ssl_client_cert" do
|
||||
@request.ssl_client_cert.should be(nil)
|
||||
end
|
||||
|
||||
@net.should_receive(:open_timeout=).with(123)
|
||||
it "should set the ssl_client_cert if provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:ssl_client_cert => "whatsupdoc!"
|
||||
)
|
||||
@net.should_receive(:cert=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
@request.transmit(@uri, 'req', nil)
|
||||
end
|
||||
it "should not set the ssl_client_cert if it is not provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload'
|
||||
)
|
||||
@net.should_not_receive(:cert=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should default to not verifying ssl certificates" do
|
||||
@request.verify_ssl.should == false
|
||||
end
|
||||
it "should default to not having an ssl_client_key" do
|
||||
@request.ssl_client_key.should be(nil)
|
||||
end
|
||||
|
||||
it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
|
||||
@net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
it "should set the ssl_client_key if provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:ssl_client_key => "whatsupdoc!"
|
||||
)
|
||||
@net.should_receive(:key=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
|
||||
@request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
|
||||
@net.should_not_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
it "should not set the ssl_client_key if it is not provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload'
|
||||
)
|
||||
@net.should_not_receive(:key=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
|
||||
mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
||||
@request = RestClient::Request.new( :method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:verify_ssl => mode )
|
||||
@net.should_receive(:verify_mode=).with(mode)
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
it "should default to not having an ssl_ca_file" do
|
||||
@request.ssl_ca_file.should be(nil)
|
||||
end
|
||||
|
||||
it "should default to not having an ssl_client_cert" do
|
||||
@request.ssl_client_cert.should be(nil)
|
||||
end
|
||||
it "should set the ssl_ca_file if provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:ssl_ca_file => "Certificate Authority File"
|
||||
)
|
||||
@net.should_receive(:ca_file=).with("Certificate Authority File")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should set the ssl_client_cert if provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:ssl_client_cert => "whatsupdoc!"
|
||||
)
|
||||
@net.should_receive(:cert=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should not set the ssl_client_cert if it is not provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload'
|
||||
)
|
||||
@net.should_not_receive(:cert=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should default to not having an ssl_client_key" do
|
||||
@request.ssl_client_key.should be(nil)
|
||||
end
|
||||
|
||||
it "should set the ssl_client_key if provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:ssl_client_key => "whatsupdoc!"
|
||||
)
|
||||
@net.should_receive(:key=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should not set the ssl_client_key if it is not provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload'
|
||||
)
|
||||
@net.should_not_receive(:key=).with("whatsupdoc!")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should default to not having an ssl_ca_file" do
|
||||
@request.ssl_ca_file.should be(nil)
|
||||
end
|
||||
|
||||
it "should set the ssl_ca_file if provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload',
|
||||
:ssl_ca_file => "Certificate Authority File"
|
||||
)
|
||||
@net.should_receive(:ca_file=).with("Certificate Authority File")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
|
||||
it "should not set the ssl_ca_file if it is not provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload'
|
||||
)
|
||||
@net.should_not_receive(:ca_file=).with("Certificate Authority File")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
it "should not set the ssl_ca_file if it is not provided" do
|
||||
@request = RestClient::Request.new(
|
||||
:method => :put,
|
||||
:url => 'https://some/resource',
|
||||
:payload => 'payload'
|
||||
)
|
||||
@net.should_not_receive(:ca_file=).with("Certificate Authority File")
|
||||
@http.stub!(:request)
|
||||
@request.stub!(:process_result)
|
||||
@request.stub!(:response_log)
|
||||
@request.transmit(@uri, 'req', 'payload')
|
||||
end
|
||||
end
|
||||
|
||||
it "should still return a response object for 204 No Content responses" do
|
||||
|
@ -512,7 +538,7 @@ describe RestClient::Request do
|
|||
:payload => 'payload'
|
||||
)
|
||||
net_http_res = Net::HTTPNoContent.new("", "204", "No Content")
|
||||
net_http_res.stub(:read_body).and_return(nil)
|
||||
net_http_res.stub!(:read_body).and_return(nil)
|
||||
@http.should_receive(:request).and_return(@request.fetch_body(net_http_res))
|
||||
response = @request.transmit(@uri, 'req', 'payload')
|
||||
response.should_not be_nil
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
require File.dirname(__FILE__) + '/base'
|
||||
|
||||
require 'webmock/rspec'
|
||||
include WebMock
|
||||
|
||||
describe RestClient::Resource do
|
||||
before do
|
||||
@resource = RestClient::Resource.new('http://some/resource', :user => 'jane', :password => 'mypass', :headers => { 'X-Something' => '1'})
|
||||
|
@ -72,4 +75,25 @@ describe RestClient::Resource do
|
|||
it "prints its url with to_s" do
|
||||
RestClient::Resource.new('x').to_s.should == 'x'
|
||||
end
|
||||
|
||||
describe 'block' do
|
||||
it 'can use block when creating the resource' do
|
||||
stub_request(:get, 'www.example.com').to_return(:body => '', :status => 404)
|
||||
resource = RestClient::Resource.new('www.example.com'){|response| 'foo'}
|
||||
resource.get.should == 'foo'
|
||||
end
|
||||
|
||||
it 'can use block when executing the resource' do
|
||||
stub_request(:get, 'www.example.com').to_return(:body => '', :status => 404)
|
||||
resource = RestClient::Resource.new('www.example.com')
|
||||
resource.get{|response| 'foo'}.should == 'foo'
|
||||
end
|
||||
|
||||
it 'execution block override resource block' do
|
||||
stub_request(:get, 'www.example.com').to_return(:body => '', :status => 404)
|
||||
resource = RestClient::Resource.new('www.example.com'){|response| 'foo'}
|
||||
resource.get{|response| 'bar'}.should == 'bar'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,15 +7,68 @@ describe RestClient::Response do
|
|||
end
|
||||
|
||||
it "behaves like string" do
|
||||
@response.should == 'abc'
|
||||
@response.should.to_s == 'abc'
|
||||
end
|
||||
|
||||
it "accepts nil strings and sets it to empty for the case of HEAD" do
|
||||
RestClient::Response.new(nil, @net_http_res).should == ""
|
||||
RestClient::Response.new(nil, @net_http_res).should.to_s == ""
|
||||
end
|
||||
|
||||
it "test headers and raw headers" do
|
||||
@response.raw_headers["Status"][0].should == "200 OK"
|
||||
@response.headers[:status].should == "200 OK"
|
||||
end
|
||||
|
||||
describe "cookie processing" do
|
||||
it "should correctly deal with one Set-Cookie header with one cookie inside" do
|
||||
net_http_res = mock('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT"]})
|
||||
response = RestClient::Response.new('abc', net_http_res)
|
||||
response.headers[:set_cookie].should == ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT"]
|
||||
response.cookies.should == { "main_page" => "main_page_no_rewrite" }
|
||||
end
|
||||
|
||||
it "should correctly deal with multiple cookies [multiple Set-Cookie headers]" do
|
||||
net_http_res = mock('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT", "remember_me=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "user=somebody; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"]})
|
||||
response = RestClient::Response.new('abc', net_http_res)
|
||||
response.headers[:set_cookie].should == ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT", "remember_me=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "user=somebody; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"]
|
||||
response.cookies.should == {
|
||||
"main_page" => "main_page_no_rewrite",
|
||||
"remember_me" => "",
|
||||
"user" => "somebody"
|
||||
}
|
||||
end
|
||||
|
||||
it "should correctly deal with multiple cookies [one Set-Cookie header with multiple cookies]" do
|
||||
net_http_res = mock('net http response', :to_hash => {"etag" => ["\"e1ac1a2df945942ef4cac8116366baad\""], "set-cookie" => ["main_page=main_page_no_rewrite; path=/; expires=Tue, 20-Jan-2015 15:03:14 GMT, remember_me=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT, user=somebody; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"]})
|
||||
response = RestClient::Response.new('abc', net_http_res)
|
||||
response.cookies.should == {
|
||||
"main_page" => "main_page_no_rewrite",
|
||||
"remember_me" => "",
|
||||
"user" => "somebody"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "exceptions processing" do
|
||||
it "should return itself for normal codes" do
|
||||
(200..206).each do |code|
|
||||
net_http_res = mock('net http response', :code => '200')
|
||||
response = RestClient::Response.new('abc', net_http_res)
|
||||
response.return!
|
||||
end
|
||||
end
|
||||
|
||||
it "should throw an exception for other codes" do
|
||||
RestClient::Exceptions::EXCEPTIONS_MAP.each_key do |code|
|
||||
unless (200..206).include? code
|
||||
net_http_res = mock('net http response', :code => code.to_i)
|
||||
response = RestClient::Response.new('abc', net_http_res)
|
||||
lambda { response.return!}.should raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
@ -33,21 +33,31 @@ describe RestClient do
|
|||
RestClient.log = nil
|
||||
end
|
||||
|
||||
it "gets the log source from the RESTCLIENT_LOG environment variable" do
|
||||
ENV.stub!(:[]).with('RESTCLIENT_LOG').and_return('from env')
|
||||
RestClient.log = 'from class method'
|
||||
RestClient.log.should == 'from env'
|
||||
it "uses << if the log is not a string" do
|
||||
log = RestClient.log = []
|
||||
log.should_receive(:<<).with('xyz')
|
||||
RestClient.log << 'xyz'
|
||||
end
|
||||
|
||||
it "sets a destination for log output, used if no environment variable is set" do
|
||||
ENV.stub!(:[]).with('RESTCLIENT_LOG').and_return(nil)
|
||||
RestClient.log = 'from class method'
|
||||
RestClient.log.should == 'from class method'
|
||||
it "displays the log to stdout" do
|
||||
RestClient.log = 'stdout'
|
||||
STDOUT.should_receive(:puts).with('xyz')
|
||||
RestClient.log << 'xyz'
|
||||
end
|
||||
|
||||
it "returns nil (no logging) if neither are set (default)" do
|
||||
ENV.stub!(:[]).with('RESTCLIENT_LOG').and_return(nil)
|
||||
RestClient.log.should == nil
|
||||
it "displays the log to stderr" do
|
||||
RestClient.log = 'stderr'
|
||||
STDERR.should_receive(:puts).with('xyz')
|
||||
RestClient.log << 'xyz'
|
||||
end
|
||||
|
||||
it "append the log to the requested filename" do
|
||||
RestClient.log = '/tmp/restclient.log'
|
||||
f = mock('file handle')
|
||||
File.should_receive(:open).with('/tmp/restclient.log', 'a').and_yield(f)
|
||||
f.should_receive(:puts).with('xyz')
|
||||
RestClient.log << 'xyz'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue