This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
module Typhoeus
|
||||
class Response
|
||||
module Cacheable
|
||||
|
||||
# Set the cache status, if we got response from cache
|
||||
# it will have cached? == true
|
||||
attr_writer :cached
|
||||
|
||||
def cached?
|
||||
defined?(@cached) ? !!@cached : false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,105 @@
|
||||
require 'delegate'
|
||||
|
||||
module Typhoeus
|
||||
class Response
|
||||
|
||||
# This class represents the response header.
|
||||
# It can be accessed like a hash.
|
||||
# Values can be strings (normal case) or arrays of strings (for duplicates headers)
|
||||
#
|
||||
# @api private
|
||||
class Header < DelegateClass(Hash)
|
||||
|
||||
# Create a new header.
|
||||
#
|
||||
# @example Create new header.
|
||||
# Header.new(raw)
|
||||
#
|
||||
# @param [ String ] raw The raw header.
|
||||
def initialize(raw)
|
||||
super({})
|
||||
@raw = raw
|
||||
@sanitized = {}
|
||||
parse
|
||||
end
|
||||
|
||||
def [](key)
|
||||
fetch(key) { @sanitized[key.to_s.downcase] }
|
||||
end
|
||||
|
||||
# Parses the raw header.
|
||||
#
|
||||
# @example Parse header.
|
||||
# header.parse
|
||||
def parse
|
||||
case @raw
|
||||
when Hash
|
||||
raw.each do |k, v|
|
||||
process_pair(k, v)
|
||||
end
|
||||
when String
|
||||
raw.split(/\r?\n(?!\s)/).each do |header|
|
||||
header.strip!
|
||||
next if header.empty? || header.start_with?( 'HTTP/' )
|
||||
process_line(header)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Processes line and saves the result.
|
||||
#
|
||||
# @return [ void ]
|
||||
def process_line(header)
|
||||
key, value = header.split(':', 2)
|
||||
process_pair(key.strip, (value ? value.strip.gsub(/\r?\n\s*/, ' ') : ''))
|
||||
end
|
||||
|
||||
# Sets key value pair for self and @sanitized.
|
||||
#
|
||||
# @return [ void ]
|
||||
def process_pair(key, value)
|
||||
set_value(key, value, self)
|
||||
@sanitized[key.downcase] = self[key]
|
||||
end
|
||||
|
||||
# Sets value for key in specified hash
|
||||
#
|
||||
# @return [ void ]
|
||||
def set_value(key, value, hash)
|
||||
current_value = hash[key]
|
||||
if current_value
|
||||
if current_value.is_a? Array
|
||||
current_value << value
|
||||
else
|
||||
hash[key] = [current_value, value]
|
||||
end
|
||||
else
|
||||
hash[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the raw header or empty string.
|
||||
#
|
||||
# @example Return raw header.
|
||||
# header.raw
|
||||
#
|
||||
# @return [ String ] The raw header.
|
||||
def raw
|
||||
@raw || ''
|
||||
end
|
||||
|
||||
# Sets the default proc for the specified hash independent of the Ruby version.
|
||||
#
|
||||
# @return [ void ]
|
||||
def set_default_proc_on(hash, default_proc)
|
||||
if hash.respond_to?(:default_proc=)
|
||||
hash.default_proc = default_proc
|
||||
else
|
||||
hash.replace(Hash.new(&default_proc).merge(hash))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,305 @@
|
||||
module Typhoeus
|
||||
class Response
|
||||
|
||||
# This module contains logic about informations
|
||||
# on a response.
|
||||
module Informations
|
||||
|
||||
# Return libcurls return value.
|
||||
#
|
||||
# @example Get return_code.
|
||||
# response.return_code
|
||||
#
|
||||
# @return [ Symbol ] The return_code.
|
||||
def return_code
|
||||
options[:return_code]
|
||||
end
|
||||
|
||||
# Returns a string describing the return.
|
||||
#
|
||||
# @example Get return_message.
|
||||
# response.return_message
|
||||
#
|
||||
# @return [ String ] The return_message.
|
||||
#
|
||||
# @since 0.6.2
|
||||
def return_message
|
||||
Ethon::Curl.easy_strerror(return_code) if return_code
|
||||
end
|
||||
|
||||
# Return the http response body.
|
||||
#
|
||||
# @example Get response_body.
|
||||
# response.response_body
|
||||
#
|
||||
# @return [ String ] The response_body.
|
||||
def response_body
|
||||
options[:response_body] || options[:body]
|
||||
end
|
||||
alias :body :response_body
|
||||
|
||||
# Return the http response headers.
|
||||
#
|
||||
# @example Get response_headers.
|
||||
# response.response_headers
|
||||
#
|
||||
# @return [ String ] The response_headers.
|
||||
def response_headers
|
||||
return options[:response_headers] if options[:response_headers]
|
||||
if mock? && h = options[:headers]
|
||||
status_code = return_code || "200"
|
||||
reason_phrase = status_code == "200" ? "OK" : "Mock Reason Phrase"
|
||||
status_line = "HTTP/1.1 #{status_code} #{reason_phrase}"
|
||||
actual_headers = h.map{ |k,v| [k, v.respond_to?(:join) ? v.join(',') : v] }.
|
||||
map{ |e| "#{e.first}: #{e.last}" }
|
||||
|
||||
[status_line, *actual_headers].join("\r\n")
|
||||
end
|
||||
end
|
||||
|
||||
# Return the last received HTTP, FTP or SMTP response code.
|
||||
# The value will be zero if no server response code has
|
||||
# been received. Note that a proxy's CONNECT response should
|
||||
# be read with http_connect_code and not this.
|
||||
#
|
||||
# @example Get response_code.
|
||||
# response.response_code
|
||||
#
|
||||
# @return [ Integer ] The response_code.
|
||||
def response_code
|
||||
(options[:response_code] || options[:code]).to_i
|
||||
end
|
||||
alias :code :response_code
|
||||
|
||||
# Return the available http auth methods.
|
||||
# Bitmask indicating the authentication method(s)
|
||||
# available.
|
||||
#
|
||||
# @example Get httpauth_avail.
|
||||
# response.httpauth_avail
|
||||
#
|
||||
# @return [ Integer ] The bitmask.
|
||||
def httpauth_avail
|
||||
options[:httpauth_avail]
|
||||
end
|
||||
|
||||
|
||||
# Return the total time in seconds for the previous
|
||||
# transfer, including name resolving, TCP connect etc.
|
||||
#
|
||||
# @example Get total_time.
|
||||
# response.total_time
|
||||
#
|
||||
# @return [ Float ] The total_time.
|
||||
def total_time
|
||||
options[:total_time] || options[:time]
|
||||
end
|
||||
alias :time :total_time
|
||||
|
||||
# Return the time, in seconds, it took from the start
|
||||
# until the first byte is received by libcurl. This
|
||||
# includes pretransfer time and also the time the
|
||||
# server needs to calculate the result.
|
||||
#
|
||||
# @example Get starttransfer_time.
|
||||
# response.starttransfer_time
|
||||
#
|
||||
# @return [ Float ] The starttransfer_time.
|
||||
def starttransfer_time
|
||||
options[:starttransfer_time] || options[:start_transfer_time]
|
||||
end
|
||||
alias :start_transfer_time :starttransfer_time
|
||||
|
||||
# Return the time, in seconds, it took from the start
|
||||
# until the SSL/SSH connect/handshake to the remote
|
||||
# host was completed. This time is most often very near
|
||||
# to the pre transfer time, except for cases such as HTTP
|
||||
# pipelining where the pretransfer time can be delayed
|
||||
# due to waits in line for the pipeline and more.
|
||||
#
|
||||
# @example Get appconnect_time.
|
||||
# response.appconnect_time
|
||||
#
|
||||
# @return [ Float ] The appconnect_time.
|
||||
def appconnect_time
|
||||
options[:appconnect_time] || options[:app_connect_time]
|
||||
end
|
||||
alias :app_connect_time :appconnect_time
|
||||
|
||||
# Return the time, in seconds, it took from the start
|
||||
# until the file transfer is just about to begin. This
|
||||
# includes all pre-transfer commands and negotiations
|
||||
# that are specific to the particular protocol(s) involved.
|
||||
# It does not involve the sending of the protocol-
|
||||
# specific request that triggers a transfer.
|
||||
#
|
||||
# @example Get pretransfer_time.
|
||||
# response.pretransfer_time
|
||||
#
|
||||
# @return [ Float ] The pretransfer_time.
|
||||
def pretransfer_time
|
||||
options[:pretransfer_time]
|
||||
end
|
||||
|
||||
# Return the time, in seconds, it took from the start
|
||||
# until the connect to the remote host (or proxy) was completed.
|
||||
#
|
||||
# @example Get connect_time.
|
||||
# response.connect_time
|
||||
#
|
||||
# @return [ Float ] The connect_time.
|
||||
def connect_time
|
||||
options[:connect_time]
|
||||
end
|
||||
|
||||
# Return the time, in seconds, it took from the
|
||||
# start until the name resolving was completed.
|
||||
#
|
||||
# @example Get namelookup_time.
|
||||
# response.namelookup_time
|
||||
#
|
||||
# @return [ Float ] The namelookup_time.
|
||||
def namelookup_time
|
||||
options[:namelookup_time] || options[:name_lookup_time]
|
||||
end
|
||||
alias :name_lookup_time :namelookup_time
|
||||
|
||||
# Return the time, in seconds, it took for all redirection steps
|
||||
# include name lookup, connect, pretransfer and transfer before the
|
||||
# final transaction was started. time_redirect shows the complete
|
||||
# execution time for multiple redirections.
|
||||
#
|
||||
# @example Get redirect_time.
|
||||
# response.redirect_time
|
||||
#
|
||||
# @return [ Float ] The redirect_time.
|
||||
def redirect_time
|
||||
options[:redirect_time]
|
||||
end
|
||||
|
||||
# Return the last used effective url.
|
||||
#
|
||||
# @example Get effective_url.
|
||||
# response.effective_url
|
||||
#
|
||||
# @return [ String ] The effective_url.
|
||||
def effective_url
|
||||
options[:effective_url]
|
||||
end
|
||||
|
||||
# Return the string holding the IP address of the most recent
|
||||
# connection done with this curl handle. This string
|
||||
# may be IPv6 if that's enabled.
|
||||
#
|
||||
# @example Get primary_ip.
|
||||
# response.primary_ip
|
||||
#
|
||||
# @return [ String ] The primary_ip.
|
||||
def primary_ip
|
||||
options[:primary_ip]
|
||||
end
|
||||
|
||||
# Return the total number of redirections that were
|
||||
# actually followed
|
||||
#
|
||||
# @example Get redirect_count.
|
||||
# response.redirect_count
|
||||
#
|
||||
# @return [ Integer ] The redirect_count.
|
||||
def redirect_count
|
||||
options[:redirect_count]
|
||||
end
|
||||
|
||||
# Return the URL a redirect would take you to, had you enabled redirects.
|
||||
#
|
||||
# @example Get redirect_url.
|
||||
# response.redirect_url
|
||||
#
|
||||
# @return [ String ] The redirect_url.
|
||||
def redirect_url
|
||||
options[:redirect_url]
|
||||
end
|
||||
|
||||
def request_size
|
||||
options[:request_size]
|
||||
end
|
||||
|
||||
# Return the bytes, the total amount of bytes that were uploaded
|
||||
#
|
||||
# @example Get size_upload.
|
||||
# response.size_upload
|
||||
#
|
||||
# @return [ Float ] The size_upload.
|
||||
def size_upload
|
||||
options[:size_upload]
|
||||
end
|
||||
|
||||
|
||||
# Return the bytes, the total amount of bytes that were downloaded.
|
||||
# The amount is only for the latest transfer and will be reset again
|
||||
# for each new transfer. This counts actual payload data, what's
|
||||
# also commonly called body. All meta and header data are excluded
|
||||
# and will not be counted in this number.
|
||||
#
|
||||
# @example Get size_download
|
||||
# response.size_download
|
||||
#
|
||||
# @return [ Float ] The size_download.
|
||||
def size_download
|
||||
options[:size_download]
|
||||
end
|
||||
|
||||
# Return the bytes/second, the average upload speed that curl
|
||||
# measured for the complete upload
|
||||
#
|
||||
# @example Get speed_upload.
|
||||
# response.speed_upload
|
||||
#
|
||||
# @return [ Float ] The speed_upload.
|
||||
def speed_upload
|
||||
options[:speed_upload]
|
||||
end
|
||||
|
||||
# Return the bytes/second, the average download speed that curl
|
||||
# measured for the complete download
|
||||
#
|
||||
# @example Get speed_download.
|
||||
# response.speed_download
|
||||
#
|
||||
# @return [ Float ] The speed_download.
|
||||
def speed_download
|
||||
options[:speed_download]
|
||||
end
|
||||
|
||||
def debug_info
|
||||
options[:debug_info]
|
||||
end
|
||||
|
||||
# Returns the response header.
|
||||
#
|
||||
# @example Return headers.
|
||||
# response.headers
|
||||
#
|
||||
# @return [ Typhoeus::Header ] The response header.
|
||||
def headers
|
||||
return Header.new(options[:headers]) if mock? && options[:headers]
|
||||
return nil if response_headers.nil? && !defined?(@headers)
|
||||
@headers ||= Header.new(response_headers.split("\r\n\r\n").last)
|
||||
end
|
||||
alias :headers_hash :headers
|
||||
|
||||
# Return all redirections in between as multiple
|
||||
# responses with header.
|
||||
#
|
||||
# @example Return redirections.
|
||||
# response.redirections
|
||||
#
|
||||
# @return [ Array<Typhoeus::Response> ] The redirections
|
||||
def redirections
|
||||
return [] unless response_headers
|
||||
response_headers.split("\r\n\r\n")[0..-2].map{ |h| Response.new(:response_headers => h) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
module Typhoeus
|
||||
class Response
|
||||
|
||||
# This module contains logic about the http
|
||||
# status.
|
||||
module Status
|
||||
|
||||
# Return the status message if present.
|
||||
#
|
||||
# @example Return status message.
|
||||
# reesponse.status_message
|
||||
#
|
||||
# @return [ String ] The message.
|
||||
def status_message
|
||||
return @status_message if defined?(@status_message) && @status_message
|
||||
return options[:status_message] unless options[:status_message].nil?
|
||||
|
||||
# HTTP servers can choose not to include the explanation to HTTP codes. The RFC
|
||||
# states this (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4):
|
||||
# Except when responding to a HEAD request, the server SHOULD include an entity containing
|
||||
# an explanation of the error situation [...]
|
||||
# This means 'HTTP/1.1 404' is as valid as 'HTTP/1.1 404 Not Found' and we have to handle it.
|
||||
#
|
||||
# Regexp doc: http://rubular.com/r/eAr1oVYsVa
|
||||
if first_header_line != nil and first_header_line[/\d{3} (.*)$/, 1] != nil
|
||||
@status_message = first_header_line[/\d{3} (.*)$/, 1].chomp
|
||||
else
|
||||
@status_message = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Return the http version.
|
||||
#
|
||||
# @example Return http version.
|
||||
# response.http_version
|
||||
#
|
||||
# @return [ String ] The http version.
|
||||
def http_version
|
||||
@http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
|
||||
end
|
||||
|
||||
# Return whether the response is a success.
|
||||
#
|
||||
# @example Return if the response was successful.
|
||||
# response.success?
|
||||
#
|
||||
# @return [ Boolean ] Return true if successful, false else.
|
||||
def success?
|
||||
(mock || return_code == :ok) && response_code && has_good_response_code?
|
||||
end
|
||||
|
||||
# Return whether the response is a failure.
|
||||
#
|
||||
# @example Return if the response was failed.
|
||||
# response.failure?
|
||||
#
|
||||
# @return [ Boolean ] Return true if failure, false else.
|
||||
def failure?
|
||||
(mock || return_code == :internal_server_error) && response_code && has_bad_response_code?
|
||||
end
|
||||
|
||||
# Return wether the response is modified.
|
||||
#
|
||||
# @example Return if the response was modified.
|
||||
# response.modified?
|
||||
#
|
||||
# @return [ Boolean ] Return true if modified, false else.
|
||||
def modified?
|
||||
(mock || return_code == :ok) && response_code && response_code != 304
|
||||
end
|
||||
|
||||
# Return whether the response is timed out.
|
||||
#
|
||||
# @example Return if the response timed out.
|
||||
# response.timed_out?
|
||||
#
|
||||
# @return [ Boolean ] Return true if timed out, false else.
|
||||
def timed_out?
|
||||
return_code == :operation_timedout
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# :nodoc:
|
||||
def first_header_line
|
||||
@first_header_line ||= begin
|
||||
if response_headers.to_s.include?("\r\n\r\n")
|
||||
response_headers.to_s.split("\r\n\r\n").last.split("\r\n").first
|
||||
else
|
||||
response_headers.to_s.split("\r\n").first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def has_good_response_code?
|
||||
response_code >= 200 && response_code < 300
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def has_bad_response_code?
|
||||
!has_good_response_code?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user