lib/restclient/request.rb in rest-client-maestro-1.7.3.maestro vs lib/restclient/request.rb in rest-client-maestro-1.7.4.maestro
- old
+ new
@@ -19,11 +19,12 @@
# * :user and :password for basic auth, will be replaced by a user/password available in the :url
# * :block_response call the provided block with the HTTPResponse as parameter
# * :raw_response return a low-level RawResponse instead of a Response
# * :max_redirects maximum number of redirections (default to 10)
# * :verify_ssl enable ssl verification, possible values are constants from OpenSSL::SSL
- # * :timeout and :open_timeout passing in -1 will disable the timeout by setting the corresponding net timeout values to nil
+ # * :timeout and :open_timeout are how long to wait for a response and to
+ # open a connection, in seconds. Pass nil to disable the timeout.
# * :ssl_client_cert, :ssl_client_key, :ssl_ca_file, :ssl_ca_path
# * :ssl_version specifies the SSL version for the underlying Net::HTTP connection (defaults to 'SSLv3')
class Request
attr_reader :method, :url, :headers, :cookies,
@@ -46,12 +47,16 @@
end
@cookies = @headers.delete(:cookies) || args[:cookies] || {}
@payload = Payload.generate(args[:payload])
@user = args[:user]
@password = args[:password]
- @timeout = args[:timeout]
- @open_timeout = args[:open_timeout]
+ if args.include?(:timeout)
+ @timeout = args[:timeout]
+ end
+ if args.include?(:open_timeout)
+ @open_timeout = args[:open_timeout]
+ end
@block_response = args[:block_response]
@raw_response = args[:raw_response] || false
@verify_ssl = args[:verify_ssl] || false
@ssl_client_cert = args[:ssl_client_cert] || nil
@ssl_client_key = args[:ssl_client_key] || nil
@@ -90,17 +95,50 @@
end
end
def make_headers user_headers
unless @cookies.empty?
- user_headers[:cookie] = @cookies.map { |(key, val)| "#{key.to_s}=#{CGI::unescape(val.to_s)}" }.sort.join('; ')
+
+ # Validate that the cookie names and values look sane. If you really
+ # want to pass scary characters, just set the Cookie header directly.
+ # RFC6265 is actually much more restrictive than we are.
+ @cookies.each do |key, val|
+ unless valid_cookie_key?(key)
+ raise ArgumentError.new("Invalid cookie name: #{key.inspect}")
+ end
+ unless valid_cookie_value?(val)
+ raise ArgumentError.new("Invalid cookie value: #{val.inspect}")
+ end
+ end
+
+ user_headers[:cookie] = @cookies.map { |key, val| "#{key}=#{val}" }.sort.join('; ')
end
headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
headers.merge!(@payload.headers) if @payload
headers
end
+ # Do some sanity checks on cookie keys.
+ #
+ # Properly it should be a valid TOKEN per RFC 2616, but lots of servers are
+ # more liberal.
+ #
+ # Disallow the empty string as well as keys containing control characters,
+ # equals sign, semicolon, comma, or space.
+ #
+ def valid_cookie_key?(string)
+ return false if string.empty?
+
+ ! Regexp.new('[\x0-\x1f\x7f=;, ]').match(string)
+ end
+
+ # Validate cookie values. Rather than following RFC 6265, allow anything
+ # but control characters, comma, and semicolon.
+ def valid_cookie_value?(value)
+ ! Regexp.new('[\x0-\x1f\x7f,;]').match(value)
+ end
+
def net_http_class
if RestClient.proxy
proxy_uri = URI.parse(RestClient.proxy)
Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
else
@@ -165,16 +203,25 @@
end
net.cert = @ssl_client_cert if @ssl_client_cert
net.key = @ssl_client_key if @ssl_client_key
net.ca_file = @ssl_ca_file if @ssl_ca_file
net.ca_path = @ssl_ca_path if @ssl_ca_path
- net.read_timeout = @timeout if @timeout
- net.open_timeout = @open_timeout if @open_timeout
- # disable the timeout if the timeout value is -1
- net.read_timeout = nil if @timeout == -1
- net.open_timeout = nil if @open_timeout == -1
+ if defined? @timeout
+ if @timeout == -1
+ warn 'To disable read timeouts, please set timeout to nil instead of -1'
+ @timeout = nil
+ end
+ net.read_timeout = @timeout
+ end
+ if defined? @open_timeout
+ if @open_timeout == -1
+ warn 'To disable open timeouts, please set open_timeout to nil instead of -1'
+ @open_timeout = nil
+ end
+ net.open_timeout = @open_timeout
+ end
RestClient.before_execution_procs.each do |before_proc|
before_proc.call(req, args)
end
@@ -203,11 +250,11 @@
else
raise e
end
rescue EOFError
raise RestClient::ServerBrokeConnection
- rescue Timeout::Error
+ rescue Timeout::Error, Errno::ETIMEDOUT
raise RestClient::RequestTimeout
end
def setup_credentials(req)
req.basic_auth(user, password) if user
@@ -217,9 +264,10 @@
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")
+ @tf.binmode
size, total = 0, http_response.header['Content-Length'].to_i
http_response.read_body do |chunk|
@tf.write chunk
size += chunk.size
if RestClient.log