class FTW::Request

An HTTP Request.

See RFC2616 section 5: <tools.ietf.org/html/rfc2616#section-5>

Attributes

method[RW]

The http method. Like GET, PUT, POST, etc.. RFC2616 5.1.1 - <tools.ietf.org/html/rfc2616#section-5.1.1>

Warning: this accessor obscures the ruby Kernel#method() method. I would like to call this 'verb', but my preference is first to adhere to RFC terminology. Further, ruby's stdlib Net::HTTP calls this 'method' as well (See Net::HTTPGenericRequest).

path[RW]

This is the Request-URI. Many people call this the 'path' of the request. RFC2616 5.1.2 - <tools.ietf.org/html/rfc2616#section-5.1.2>

port[RW]

RFC2616 section 14.23 allows the Host header to include a port, but I have never seen this in practice, and I shudder to think about what poorly-behaving web servers will barf if the Host header includes a port. So, instead of storing the port in the Host header, it is stored here. It is not included in the Request when sent from a client and it is not used on a server.

protocol[RW]

This is not an RFC2616 field. It exists so that the connection handling this request knows what protocol to use. The protocol for this request. Usually 'http' or 'https' or perhaps 'spdy' maybe?

request_uri[RW]

This is the Request-URI. Many people call this the 'path' of the request. RFC2616 5.1.2 - <tools.ietf.org/html/rfc2616#section-5.1.2>

Public Class Methods

new(uri=nil) click to toggle source

Make a new request with a uri if given.

The uri is used to set the address, protocol, Host header, etc.

Calls superclass method FTW::HTTP::Message::new
# File lib/ftw/request.rb, line 54
def initialize(uri=nil)
  super()
  @port = 80
  @protocol = "http"
  @version = 1.1
  use_uri(uri) if !uri.nil?
  @logger = Cabin::Channel.get
end

Public Instance Methods

execute(connection) click to toggle source

Execute this request on a given connection: Writes the request, returns a Response object.

This method will block until the HTTP response header has been completely received. The body will not have been read yet at the time of this method's return.

The 'connection' should be a FTW::Connection instance, but it might work with a normal IO object.

# File lib/ftw/request.rb, line 73
def execute(connection)
  tries = 3
  begin
    connection.write(to_s + CRLF)
    if body?
      write_http_body(body, connection,
                      headers["Transfer-Encoding"] == "chunked") 
    end
  rescue => e
    # TODO(sissel): Rescue specific exceptions, not just anything.
    # Reconnect and retry
    if tries > 0
      tries -= 1
      connection.connect
      retry
    else
      raise e
    end
  end

  response = read_http_message(connection)
  # TODO(sissel): make sure we got a response, not a request, cuz that'd be weird.
  return response
end
method=(method) click to toggle source

Set the method for this request. Usually something like “GET” or “PUT” etc. See <tools.ietf.org/html/rfc2616#section-5.1.1>

# File lib/ftw/request.rb, line 131
def method=(method)
  # RFC2616 5.1.1 doesn't say the method has to be uppercase.
  # It can be any 'token' besides the ones defined in section 5.1.1:
  # The grammar for 'token' is:
  #          token          = 1*<any CHAR except CTLs or separators>
  # TODO(sissel): support section 5.1.1 properly. Don't upcase, but
  # maybe upcase things that are defined in 5.1.1 like GET, etc.
  @method = method.upcase
end
request_line() click to toggle source

Get the request line (first line of the http request) From the RFC: Request-Line = Method SP Request-URI SP HTTP-Version CRLF

Note: I skip the trailing CRLF. See the to_s method where it is provided.

# File lib/ftw/request.rb, line 145
def request_line
  return "#{method} #{request_uri} HTTP/#{version}"
end
Also aliased as: start_line
start_line()

Define the Message's start_line as request_line

Alias for: request_line
use_uri(uri) click to toggle source

Use a URI to help fill in parts of this Request.

# File lib/ftw/request.rb, line 99
def use_uri(uri)
  # Convert URI objects to Addressable::URI
  case uri
    when URI, String
      uri = Addressable::URI.parse(uri.to_s)
  end

  # TODO(sissel): Use uri.password and uri.user to set Authorization basic
  # stuff.
  if uri.password || uri.user
    encoded = Base64.strict_encode64("#{uri.user}:#{uri.password}")
    @headers.set("Authorization", "Basic #{encoded}")
  end
  # uri.password
  # uri.user
  @request_uri = uri.path
  # Include the query string, too.
  @request_uri += "?#{uri.query}" if !uri.query.nil?

  @headers.set("Host", uri.host)
  @protocol = uri.scheme
  if uri.port.nil?
    # default to port 80
    uri.port = { "http" => 80, "https" => 443 }.fetch(uri.scheme, 80)
  end
  @port = uri.port
  
  # TODO(sissel): support authentication
end