class RaptorIO::Protocol::HTTP::Request

HTTP Request.

@author Tasos Laskos <tasos_laskos@rapid7.com>

Constants

CALLBACK_TYPES

Acceptable response callback types.

Attributes

callbacks[RW]
client_address[RW]

@return [String] IP address – populated by {Server}.

continue[R]

@note Defaults to ‘true`. @return [Bool]

Whether or not to automatically continue on responses with status 100
http_method[R]

@return [Symbol] HTTP method.

parameters[R]

@return [Hash] Request parameters.

parsed_url[R]

@return [URI] Parsed version of {#url}.

raw[RW]

@note Defaults to ‘false`. @return [Bool]

Whether or not encode any of the given data for HTTP transmission.
root_redirect_id[RW]

@private

timeout[RW]

@return [Integer, Float] Timeout in seconds.

url[R]

@return [String] URL of the targeted resource.

Public Class Methods

new( options = {} ) click to toggle source

@note This class’ options are in addition to {Message#initialize}.

@param [Hash] options Request options. @option options [String] :version (‘1.1’) HTTP version to use. @option options [Symbol, String] :http_method (:get) HTTP method to use. @option options [Hash] :parameters ({})

Parameters to send. If performing a GET request and the URL has parameters
of its own they will be merged and overwritten.

@option options [Integer] :timeout

Max time to wait for a response in seconds.

@option options [Bool] :continue

Whether or not to automatically continue on responses with status 100.
Only applicable when the 'Expect' header has been set to '100-continue'.

@option options [Bool] :raw (false)

`true` to not encode any of the given data for HTTP transmission, `false`
otherwise.

@see Message#initialize @see parameters= @see http_method=

Calls superclass method RaptorIO::Protocol::HTTP::Message::new
# File lib/raptor-io/protocol/http/request.rb, line 81
def initialize( options = {} )
  super( options )

  clear_callbacks

  fail ArgumentError, "Missing ':url' option." if !@url

  @parameters  ||= {}
  @http_method ||= :get
  @continue    = true  if @continue.nil?
  @raw         = false if @raw.nil?
end
parse( request ) click to toggle source

@param [String] request HTTP request message to parse. @return [Request]

# File lib/raptor-io/protocol/http/request.rb, line 282
def self.parse( request )
  data = {}
  first_line, headers_and_body = request.split( CRLF_PATTERN, 2 )
  data[:http_method], data[:url], data[:version] = first_line.scan( /([A-Z]+)\s+(.*)\s+HTTP\/([0-9\.]+)/ ).flatten
  headers, data[:body] = headers_and_body.split( HEADER_SEPARATOR_PATTERN, 2 )

  # Use Host to fill in the parsed_uri stuff.
  data[:headers] = Headers.parse( headers.to_s )

  new data
end

Public Instance Methods

clear_callbacks() click to toggle source

Clears all callbacks.

# File lib/raptor-io/protocol/http/request.rb, line 95
def clear_callbacks
  @callbacks = CALLBACK_TYPES.inject( {} ) { |h, type| h[type] = []; h }
  nil
end
connection_id() click to toggle source

@return [Integer] Identification for the remote host:port.

# File lib/raptor-io/protocol/http/request.rb, line 121
def connection_id
  "#{parsed_url.host}:#{parsed_url.port}".hash
end
continue?() click to toggle source

@return [Bool]

Whether or not to automatically continue on responses with status 100
# File lib/raptor-io/protocol/http/request.rb, line 108
def continue?
  !!@continue
end
dup() click to toggle source

@return [Request] Duplicate of ‘self`.

# File lib/raptor-io/protocol/http/request.rb, line 272
def dup
  r = self.class.new( url: url )
  instance_variables.each do |iv|
    r.instance_variable_set iv, instance_variable_get( iv )
  end
  r
end
effective_body() click to toggle source

@return [String] Response body to use.

# File lib/raptor-io/protocol/http/request.rb, line 168
def effective_body
  return '' if headers['Expect'] == '100-continue'
  return encode_if_not_raw(body.to_s)

  body_params = if !body.to_s.empty?
                  body.split('&').inject({}) do |h, pair|
                    k, v = pair.split('=', 2)
                    h.merge( decode_if_not_raw(k) => decode_if_not_raw(v) )
                  end
                else
                  {}
                end

  return '' if body_params.empty? && parameters.empty?

  body_params.merge( parameters ).map do |k, v|
    "#{encode_if_not_raw(k)}=#{encode_if_not_raw(v)}"
  end.join('&')
end
effective_url() click to toggle source

@return [URI] Location of the resource to request.

# File lib/raptor-io/protocol/http/request.rb, line 158
def effective_url
  cparsed_url = parsed_url.dup
  cparsed_url.query = query_parameters.map do |k, v|
    "#{encode_if_not_raw(k)}=#{encode_if_not_raw(v)}"
  end.join('&') if query_parameters.any?

  cparsed_url.normalize
end
handle_response( response ) click to toggle source

Handles the ‘response` to `self` by passing to the appropriate callbacks.

@param [Response] response

@private

# File lib/raptor-io/protocol/http/request.rb, line 261
def handle_response( response )
  response.request = self

  type = (response.code.to_i == 0) ? :on_failure : :on_success

  @callbacks[type].each { |block| block.call response }
  @callbacks[:on_complete].each { |block| block.call response }
  true
end
http_method=( http_verb ) click to toggle source

@note Method will be normalized to a lower-case symbol.

Sets the request HTTP method.

@param [#to_s] http_verb HTTP method.

@return [Symbol] HTTP method.

# File lib/raptor-io/protocol/http/request.rb, line 197
def http_method=( http_verb )
  @http_method = http_verb.to_s.downcase.to_sym
end
idempotent?() click to toggle source

@return [Bool] ‘true` if the request if idempotent, `false` otherwise.

# File lib/raptor-io/protocol/http/request.rb, line 202
def idempotent?
  http_method != :post
end
parameters=( params ) click to toggle source

@note All keys and values will be recursively converted to strings.

Sets request parameters.

@param [Hash] params

Parameters to assign to this request.
If performing a GET request and the URL has parameters of its own they
will be merged and overwritten.

@return [Hash] Normalized parameters.

# File lib/raptor-io/protocol/http/request.rb, line 137
def parameters=( params )
  @parameters = params.stringify
end
query_parameters() click to toggle source

@return [Hash] Parameters to be used for the query part of the resource.

# File lib/raptor-io/protocol/http/request.rb, line 142
def query_parameters
  query = parsed_url.query
  if !query
    return http_method == :get ? parameters : {}
  end

  qparams = query.split('&').inject({}) do |h, pair|
    k, v = pair.split('=', 2)
    h.merge( decode_if_not_raw(k) => decode_if_not_raw(v) )
  end
  return qparams if http_method != :get

  qparams.merge( parameters )
end
raw?() click to toggle source

@return [Bool]

Whether or not encode any of the given data for HTTP transmission.
# File lib/raptor-io/protocol/http/request.rb, line 102
def raw?
  !!@raw
end
resource() click to toggle source

@return [String] Server-side resource to request.

# File lib/raptor-io/protocol/http/request.rb, line 207
def resource
  req_resource  = "#{effective_url.path}"
  req_resource << "?#{effective_url.query}" if effective_url.query
  req_resource
end
to_s() click to toggle source

@return [String]

String representation of the request, ready for HTTP transmission.
# File lib/raptor-io/protocol/http/request.rb, line 215
def to_s
  final_body = effective_body

  computed_headers = Headers.new( 'Host' => "#{effective_url.host}:#{effective_url.port}" )
  computed_headers['Content-Length'] = final_body.size.to_s if !final_body.to_s.empty?

  request = "#{http_method.to_s.upcase} #{resource} HTTP/#{version}#{CRLF}"
  request << computed_headers.merge(headers).to_s
  request << HEADER_SEPARATOR

  return request if final_body.to_s.empty?

  request << final_body.to_s
end
url=( uri ) click to toggle source

@param [String] uri Request URL. @return [String] ‘uri`

# File lib/raptor-io/protocol/http/request.rb, line 114
def url=( uri )
  @url = uri
  @parsed_url= URI(@url)
  @url
end

Private Instance Methods

decode_if_not_raw( str ) click to toggle source
# File lib/raptor-io/protocol/http/request.rb, line 300
def decode_if_not_raw( str )
  raw? ? str : CGI.unescape( str )
end
encode_if_not_raw( str ) click to toggle source
# File lib/raptor-io/protocol/http/request.rb, line 296
def encode_if_not_raw( str )
  raw? ? str : CGI.escape( str )
end