class Akita::HarLogger::HttpRequest

Public Class Methods

new(env) click to toggle source

Produces an HttpRequest from a request's HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 9
def initialize(env)
  req = Rack::Request.new env

  @self = {
    method: getMethod(env),
    url: HarUtils.fixEncoding(req.url),
    httpVersion: getHttpVersion(env),
    cookies: getCookies(env),
    headers: getHeaders(env),
    queryString: getQueryString(env),
    headersSize: getHeadersSize(env),
    bodySize: getBodySize(env),
  }

  # Augment with post data if we have any.
  postData = getPostData(env)
  if postData != nil then
    @self[:postData] = postData
  end
end

Public Instance Methods

getBodySize(env) click to toggle source

Obtains the size of the request body from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 172
def getBodySize(env)
  # Assume no content if Content-Length header was not provided.
  env.key?('CONTENT_LENGTH') ? env['CONTENT_LENGTH'].to_i : 0
end
getCookies(env) click to toggle source

Builds a list of cookie objects from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 50
def getCookies(env)
  req = Rack::Request::new env
  HarUtils.hashToList req.cookies
end
getHeaders(env) click to toggle source

Builds a list of headers from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 56
def getHeaders(env)
  # HTTP headers in the environment can be identified with the "HTTP_"
  # prefix. Filter for these. In the resulting map, rewrite keys of the
  # form "HTTP_FOO_BAR_BAZ" into "Foo-Bar-Baz", and convert into an
  # array.
  HarUtils.hashToList (
    env.select { |k,v| k.start_with? 'HTTP_' }.
      transform_keys { |k|
        k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
      }
  )
end
getHeadersSize(env) click to toggle source
# File lib/akita/har_logger/http_request.rb, line 150
def getHeadersSize(env)
  # XXX This seems to under-count, compared to a HAR produced by Firefox.

  # Count the number of bytes needed to produce the first line of the
  # request (HTTP method, full path, HTTP version, CRLF). For example,
  #
  #   GET /index.html?foo=bar&baz=qux HTTP/1.1<CR><LF>
  req = Rack::Request::new env
  line_length =
    getMethod(env).length + 1
      + req.fullpath.length + 1
      + getHttpVersion(env).length + 2

  # Add the size of the headers. Add 2 to the starting value to account
  # for the CRLF on the blank line.
  getHeaders(env).reduce(line_length + 2) { |accum, entry|
    # Header-Name: header value<CR><LF>
    accum + entry[:name].length + 2 + entry[:value].length + 2
  }
end
getHttpVersion(env) click to toggle source

Obtains the client-requested HTTP version from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 40
def getHttpVersion(env)
  # The environment doesn't have HTTP_VERSION when running with `rspec`;
  # assume HTTP/1.1 when this happens. We don't return nil, so we can
  # calculate the size of the headers.
  env.key?('HTTP_VERSION') ?
    HarUtils.fixEncoding(env['HTTP_VERSION']) :
    'HTTP/1.1'
end
getMethod(env) click to toggle source

Obtains the client's request method from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 35
def getMethod(env)
  HarUtils.fixEncoding (Rack::Request.new env).request_method
end
getPostData(env) click to toggle source

Obtains the posted data from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 92
def getPostData(env)
  if env.key?('CONTENT_TYPE') && env['CONTENT_TYPE'] then
    result = { mimeType: env['CONTENT_TYPE'] }

    # Populate 'params' if we have URL-encoded parameters. Otherwise,
    # populate 'text'.
    req = Rack::Request.new env
    if env['CONTENT_TYPE'] == 'application/x-www-form-urlencoded' then
      # Decoded parameters can be found as a map in req.params.
      #
      # Requests originating from specs can be malformed: the values in
      # req.params are not necessarily strings. Encode all of req.params
      # in JSON and pretend the content type was "application/json".
      if HarUtils.allValuesAreStrings req.params then
        # Convert req.params into an array.
        #
        # XXX Spec has space for files, but are file uploads ever
        # URL-encoded?
        result[:params] = HarUtils.hashToList req.params
      else
        result[:mimeType] = 'application/json'
        result[:text] = req.params.to_json
      end
    else
      # Rack has been observed to use ASCII-8BIT encoding for the request
      # body when the request specifies UTF-8. Reinterpret the content
      # body according to what the request says it is, and re-encode into
      # UTF-8.
      #
      # Gracefully handle any characters that are invalid in the source
      # encoding and characters that have no UTF-8 representation by
      # replacing with '?'. Log a warning when this happens.
      sourceCharset = getPostDataCharSet(env)
      source = String.new(req.body.read).force_encoding(sourceCharset)
      utf8EncodingSuccessful = false
      if source.valid_encoding? then
        begin
          result[:text] = source.encode(Encoding::UTF_8)
          utf8EncodingSuccessful = true
        rescue Encoding::UndefinedConversionError
          Rails.logger.warn "[#{caller_locations(0, 1)}] Unable to losslessly convert request body from #{source.encoding} to UTF-8. Characters undefined in UTF-8 will be replaced with '?'."
        end
      else
        Rails.logger.warn "[#{caller_locations(0, 1)}] Request body is not valid #{source.encoding}. Invalid characters and characters undefined in UTF-8 will be replaced with '?'."
      end

      if !utf8EncodingSuccessful then
        result[:text] = source.encode(Encoding::UTF_8,
            invalid: :replace, undef: :replace, replace: '?')
      end
    end

    result
  else
    nil
  end
end
getPostDataCharSet(env) click to toggle source

Obtains the character set of the posted data from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 77
def getPostDataCharSet(env)
  req = Rack::Request.new env
  if req.content_charset != nil then
    return req.content_charset
  end

  # RFC 2616 says that "text/*" defaults to ISO-8859-1.
  if env['CONTENT_TYPE'].start_with?('text/') then
    return Encoding::ISO_8859_1
  end

  Encoding::UTF_8
end
getQueryString(env) click to toggle source

Builds a list of query parameters from an HTTP environment.

# File lib/akita/har_logger/http_request.rb, line 70
def getQueryString(env)
  req = Rack::Request::new env
  paramMap = Rack::Utils.parse_nested_query req.query_string
  HarUtils.hashToList paramMap
end
to_json(*args) click to toggle source
# File lib/akita/har_logger/http_request.rb, line 30
def to_json(*args)
  @self.to_json(*args)
end