class Arachni::HTTP::Response
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Constants
- HTML_CONTENT_TYPES
- HTML_IDENTIFIERS
- HTML_IDENTIFIER_REGEXPS
Attributes
app_time[RW]
@return [Float]
Approximate time the web application took to process the {#request}.
code[RW]
@return [Integer]
HTTP response status code.
headers_string[RW]
@return [String]
Raw headers.
ip_address[RW]
@return [String]
IP address of the server.
message[RW]
@return [String]
HTTP response status message.
redirections[RW]
@return [Array<Response>]
Automatically followed redirections that eventually led to this response.
request[RW]
@return [Request]
HTTP {Request} which triggered this {Response}.
return_code[RW]
@return [Symbol]
`libcurl` return code.
return_message[RW]
@return [String]
`libcurl` return code.
time[RW]
@return [Float]
Time, in seconds, it took from the start until the full response was received.
total_time[RW]
@return [Float]
Total time in seconds for the transfer, including name resolving, TCP connect etc.
Public Class Methods
from_rpc_data( data )
click to toggle source
@param [Hash] data {#to_rpc_data} @return [Request]
# File lib/arachni/http/response.rb, line 241 def self.from_rpc_data( data ) data['request'] = Request.from_rpc_data( data['request'] ) data['return_code'] = data['return_code'].to_sym if data['return_code'] new data end
from_typhoeus( response, options = {} )
click to toggle source
# File lib/arachni/http/response.rb, line 285 def self.from_typhoeus( response, options = {} ) redirections = response.redirections.map do |redirect| rurl = URI.to_absolute( redirect.headers['Location'], response.effective_url ) rurl ||= URI.normalize( response.effective_url ) # Broken redirection, skip it... next if !rurl new( options.merge( url: rurl, code: redirect.code, headers: redirect.headers )) end.compact return_code = response.return_code return_message = response.return_message # A write error in this case will be because body reading was aborted # during our own callback in Request#set_body_reader. # # So, this is here just for consistency. if response.return_code == :write_error return_code = :filesize_exceeded return_message = 'Maximum file size exceeded' end new( options.merge( url: response.effective_url, code: response.code, ip_address: response.primary_ip, headers: response.headers, headers_string: response.response_headers, body: response.body, redirections: redirections, time: response.time, app_time: (response.timed_out? ? response.time : response.start_transfer_time - response.pretransfer_time).to_f, total_time: response.total_time.to_f, return_code: return_code, return_message: return_message )) end
new( options = {} )
click to toggle source
Calls superclass method
Arachni::HTTP::Message::new
# File lib/arachni/http/response.rb, line 70 def initialize( options = {} ) super( options ) @body ||= '' @code ||= 0 # Holds the redirection responses that eventually led to this one. @redirections ||= [] @time ||= 0.0 end
Public Instance Methods
==( other )
click to toggle source
# File lib/arachni/http/response.rb, line 247 def ==( other ) hash == other.hash end
body=( body )
click to toggle source
# File lib/arachni/http/response.rb, line 193 def body=( body ) @body = body.to_s text_check = text? @body.recode! if text_check.nil? || text_check @body end
hash()
click to toggle source
# File lib/arachni/http/response.rb, line 251 def hash to_h.hash end
headers_string=( string )
click to toggle source
# File lib/arachni/http/response.rb, line 124 def headers_string=( string ) @headers_string = string.to_s.recode.freeze end
html?()
click to toggle source
# File lib/arachni/http/response.rb, line 175 def html? # IF we've got a Content-Type that's all we need to know. if (ct = headers.content_type) ct = ct.split( ';' ).first ct.strip! return HTML_CONTENT_TYPES.include?( ct.downcase ) end # Server insists we should only only use the content-type. respect it. return false if headers['X-Content-Type-Options'].to_s.downcase.include?( 'nosniff' ) # If there's a doctype then we're good to go. return true if body.start_with?( '<!DOCTYPE html' ) # Last resort, sniff the content-type from several HTML tags. HTML_IDENTIFIER_REGEXPS.find { |regexp| body =~ regexp } end
modified?()
click to toggle source
@note Depends on the response code.
@return [Boolean]
`true` if the remote resource has been modified since the date given in the `If-Modified-Since` request header field, `false` otherwise.
# File lib/arachni/http/response.rb, line 133 def modified? code != 304 end
ok?()
click to toggle source
@return [Boolean]
`true` if the request was performed successfully and the response was received in full, `false` otherwise.
# File lib/arachni/http/response.rb, line 140 def ok? !return_code || return_code == :ok end
parse()
click to toggle source
# File lib/arachni/http/response.rb, line 207 def parse Parser.new self end
partial?()
click to toggle source
@return [Boolean]
`true` if the client could not read the entire response, `false` otherwise.
# File lib/arachni/http/response.rb, line 88 def partial? # Streamed response which was aborted before completing. return_code == :partial_file || return_code == :recv_error || # Normal response with some data written, but without reaching # content-length. (code != 0 && timed_out?) end
platforms()
click to toggle source
@return [Platform]
Applicable platforms for the page.
# File lib/arachni/http/response.rb, line 99 def platforms Platform::Manager[url] end
redirect?()
click to toggle source
@return [Boolean]
`true` if the response is a `3xx` redirect **and** there is a `Location` header field.
# File lib/arachni/http/response.rb, line 119 def redirect? code >= 300 && code <= 399 && !!headers.location end
Also aliased as: redirection?
status_line()
click to toggle source
@return [String]
First line of the response.
# File lib/arachni/http/response.rb, line 105 def status_line return if !headers_string @status_line ||= headers_string.lines.first.to_s.chomp.freeze end
text?()
click to toggle source
@return [Bool]
`true` if the response body is textual in nature, `false` if binary, `nil` if could not be determined.
# File lib/arachni/http/response.rb, line 147 def text? return nil if !@body return nil if @is_text == :inconclusive return @is_text if !@is_text.nil? if (type = headers.content_type) return @is_text = true if type.start_with?( 'text/' ) # Non "text/" nor "application/" content types will surely not be # text-based so bail out early. return @is_text = false if !type.start_with?( 'application/' ) end # Last resort, more resource intensive binary detection. begin @is_text = !@body.binary? rescue ArgumentError @is_text = :inconclusive nil end end
time=( t )
click to toggle source
# File lib/arachni/http/response.rb, line 82 def time=( t ) @time = t.to_f end
timed_out?()
click to toggle source
@return [Boolean]
`true` if timed out, `false` otherwise.
# File lib/arachni/http/response.rb, line 171 def timed_out? return_code == :operation_timedout end
to_h()
click to toggle source
@return [Hash]
# File lib/arachni/http/response.rb, line 212 def to_h hash = {} instance_variables.each do |var| hash[var.to_s.gsub( /@/, '' ).to_sym] = instance_variable_get( var ) end hash[:headers] = {}.merge( hash[:headers] ) hash.delete( :normalize_url ) hash.delete( :is_text ) hash.delete( :scope ) hash.delete( :parsed_url ) hash.delete( :redirections ) hash.delete( :request ) hash.delete( :scope ) hash end
to_page()
click to toggle source
@return [Arachni::Page]
# File lib/arachni/http/response.rb, line 203 def to_page Page.from_response self end
to_rpc_data()
click to toggle source
@return [Hash]
Data representing this instance that are suitable the RPC transmission.
# File lib/arachni/http/response.rb, line 233 def to_rpc_data data = to_h data[:request] = request.to_rpc_data data.my_stringify_keys(false) end
to_s()
click to toggle source
@return [String]
HTTP response string.
# File lib/arachni/http/response.rb, line 112 def to_s "#{headers_string}#{body}" end
update_from_typhoeus( response, options = {} )
click to toggle source
# File lib/arachni/http/response.rb, line 255 def update_from_typhoeus( response, options = {} ) return_code = response.return_code return_message = response.return_message # A write error in this case will be because body reading was aborted # during our own callback in Request#set_body_reader. # # So, this is here just for consistency. if response.return_code == :write_error return_code = :filesize_exceeded return_message = 'Maximum file size exceeded' end update( options.merge( url: response.effective_url, code: response.code, ip_address: response.primary_ip, headers: response.headers, headers_string: response.response_headers, body: response.body, redirections: redirections, time: response.time, app_time: (response.timed_out? ? response.time : response.start_transfer_time - response.pretransfer_time).to_f, total_time: response.total_time.to_f, return_code: return_code, return_message: return_message )) end