class Akita::HarLogger::HttpResponse

Public Class Methods

new(env, status, headers, body) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 8
def initialize(env, status, headers, body)
  @self = {
    status: status,
    statusText: getStatusText(status),
    httpVersion: getHttpVersion(env),
    cookies: getCookies(headers),
    headers: (HarUtils.hashToList headers),
    content: getContent(headers, body),
    redirectURL: getRedirectUrl(headers),
    headersSize: getHeadersSize(env, status, headers),
    bodySize: getBodySize(body),
  }
end

Public Instance Methods

getBodySize(body) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 160
def getBodySize(body)
  length = 0
  # Convert each body part into a string in case we're dealing with
  # non-Rack-compliant components.
  body.each { |part| length += part.to_s.bytesize }
  length
end
getContent(headers, body) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 76
def getContent(headers, body)
  # XXX Handle compression
  # XXX Figure out how to properly join together multi-part bodies.

  # Try to convert the body into UTF-8. If this fails, assume the body is
  # binary data.
  # XXX TODO Take charset part of Content-Type header into account.
  text = +""
  haveBinaryData = false
  body.each { |part|
    partStr = part.to_s

    if partStr.encoding == Encoding::ASCII_8BIT then
      # Have 8-bit ASCII data. Try to interpret as UTF-8. If this fails,
      # treat as binary data.
      forced = String.new(partStr).force_encoding(Encoding::UTF_8)
      if forced.valid_encoding? then
        text << forced
        next
      end

      haveBinaryData = true
      break
    end

    if !partStr.valid_encoding? then
      # Source encoding is not valid. Treat as binary data.
      haveBinaryData = true
      break
    end

    # Try to re-encode as UTF-8. If this fails, treat as binary data.
    begin
      text << partStr.encode(Encoding::UTF_8)
    rescue Encoding::UndefinedConversionError
      haveBinaryData = true
      break
    end
  }

  if haveBinaryData then
    # TODO Encode binary body data with base64.
    # XXX Omit for now.
    text = ""
  end

  {
    size: getBodySize(body),

    # XXX What to use when no Content-Type is given?
    mimeType: HarUtils.fixEncoding(headers['Content-Type']),

    text: text,
  }
end
getCookies(headers) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 44
def getCookies(headers)
  result = []
  headers.each { |k, v|
    if "Set-Cookie".casecmp(k) != 0 then next end

    # Couldn't find a great library for parsing Set-Cookie headers, so
    # let's roll our own...
    #
    # According to RFC 6265, the value of Set-Cookie has the format
    # "cookieName=cookieValue", optionally followed by a semicolon and
    # attribute-value pairs. The cookieValue can be optionally enclosed
    # in double quotes. Neither cookieName nor cookieValue can contain
    # double quotes, semicolons, or equal signs.
    match = /^([^=]*)=([^;]*)(|;.*)$/.match(v)
    if !match then next end

    cookie_name = match[1]
    cookie_value = match[2]

    # Strip quotation marks from the value if they are present.
    match = /^"(.*)"$/.match(cookie_value)
    if match then cookie_value = match[1] end

    result << {
      name: HarUtils.fixEncoding(cookie_name),
      value: HarUtils.fixEncoding(cookie_value),
    }
  }

  result
end
getHeadersSize(env, status, headers) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 140
def getHeadersSize(env, status, headers)
  # 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
  # response (HTTP version, status code, status text, CRLF). For example,
  #
  #   HTTP/1.1 404 Not Found<CR><LF>
  status_length =
    getHttpVersion(env).length + 1
      + status.to_s.length + 1
      + getStatusText(status).length + 2

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

Obtains the HTTP version in the response.

# File lib/akita/har_logger/http_response.rb, line 32
def getHttpVersion(env)
  # XXX Assume the server replies with the same HTTP version as the
  # XXX request. This seems to hold true empirically.

  # 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
getRedirectUrl(headers) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 132
def getRedirectUrl(headers)
  # Use the "Location" header if it exists. Otherwise, based on some HAR
  # examples found online, it looks like an empty string is used.
  headers.key?('Location') ?
    HarUtils.fixEncoding(headers['Location']) :
    ''
end
getStatusText(status) click to toggle source

Obtains the status text corresponding to a status code.

# File lib/akita/har_logger/http_response.rb, line 27
def getStatusText(status)
  HarUtils.fixEncoding(Rack::Utils::HTTP_STATUS_CODES[status])
end
to_json(*args) click to toggle source
# File lib/akita/har_logger/http_response.rb, line 22
def to_json(*args)
  @self.to_json(*args)
end