class HTTP::Client::Request

Constants

DEFAULT_HEADERS
REDIRECT_WITH_GET
REDIRECT_WITH_ORIGINAL
VALID_PARAMETERS
VALID_REDIRECT_CODES
VALID_SSL_VERIFICATIONS
VALID_VERBS

Public Class Methods

new(verb, uri, args = {}) click to toggle source

Create a new HTTP Client Request.

@param [Symbol] verb HTTP verb, one of :get, :head, :put, :post, :delete, :options, :trace. @param [String or URI] uri Remote URI. @param [Hash] args Options, see below. @option args [Hash] headers Net::HTTP headers, in key-value pairs. @option args [Hash] query Net::HTTP query-string in key-value pairs. @option args [Hash] files Multi-part file uploads, in key-value pairs of name & path or name & File object. @option args [String] body Request body. @option args [Hash] auth Basic-Auth hash. Requires :username and :password. @option args [Integer] timeout Fixed timeout for connection, read and ssl handshake in seconds. @option args [Integer] open_timeout Connection timeout in seconds. @option args [Integer] read_timeout Read timeout in seconds. @option args [Integer] ssl_timeout SSL handshake timeout in seconds. @option args [Integer] max_redirects Max redirect follow, default: 0 @option args [Integer] ssl_verify OpenSSL verification, SSL_VERIFY_PEER (default) or SSL_VERIFY_NONE. @option args [HTTP::CookieJar] jar Optional cookie jar to use. Relies on HTTP::CookieJar from http-cookie gem.

@return [HTTP::Client::Request]

@example Retrieve a page using GET.

request  = HTTP::Client::Request.new(:get, "http://www.example.org/", query: {q: "search something"})
response = request.execute

@example Handle redirects.

request  = HTTP::Client::Request.new(:get, "http://www.example.org/", max_redirects: 3)
response = request.execute

@example Perform request and return result in one go.

response = HTTP::Client.get("http://www.example.org/", max_redirects: 3)

@example Upload a few files in a POST request.

request  = HTTP::Client::Request.new(
  :post, "http://www.example.org/",
  files: {"cats" => "cats.jpg", "dogs" => "dogs.jpg"},
  query: {title: "cute pics"}
)
response = request.execute

@example Pass in an external cookie jar.

jar  = HTTP::CookieJar.new
jar.load("mycookies.cky")
response = HTTP::Client.get("http://www.example.org/", jar: jar)
# File lib/http/client.rb, line 84
def initialize verb, uri, args = {}
  args.each do |k, v|
    raise Error::Argument, "unknown argument #{k}" unless VALID_PARAMETERS.include?(k.to_s)
  end

  uri       = parse_uri!(uri)
  @delegate = create_request_delegate(verb, uri, args)

  if body = args[:body]
    raise Error::Argument, "#{verb} cannot have body" unless @delegate.class.const_get(:REQUEST_HAS_BODY)
    @delegate.body = body
  end

  # if auth is passed as arguments.
  if auth = args[:auth]
    @delegate.basic_auth(auth.fetch(:username), auth.fetch(:password))
  end

  # if auth is passed in uri.
  if uri.user && uri.password
    @delegate.basic_auth(uri.user, uri.password)
  end

  @open_timeout = HTTP::Client.open_timeout
  @read_timeout = HTTP::Client.read_timeout
  @ssl_timeout  = HTTP::Client.ssl_timeout

  # generic timeout
  if timeout = args[:timeout]
    @open_timeout = timeout
    @read_timeout = timeout
    @ssl_timeout  = timeout
  end

  # overrides
  @open_timeout = args[:open_timeout] if args[:open_timeout]
  @read_timeout = args[:read_timeout] if args[:read_timeout]
  @ssl_timeout  = args[:ssl_timeout]  if args[:ssl_timeout]

  @max_redirects = args.fetch(:max_redirects, 0)
  @ssl_verify    = args.fetch(:ssl_verify, SSL_VERIFY_PEER)
  @jar           = args.fetch(:jar, HTTP::CookieJar.new)
end

Public Instance Methods

execute() click to toggle source

Executes a request.

@return [HTTP::Client::Response]

# File lib/http/client.rb, line 132
def execute
  last_effective_uri = uri

  cookie = HTTP::Cookie.cookie_value(@jar.cookies(uri))
  if cookie && !cookie.empty?
    @delegate.add_field('Cookie', cookie)
  end

  response = request!(uri, @delegate)
  @jar.parse(response['set-cookie'].to_s, uri)

  redirects = 0
  while redirects < @max_redirects && VALID_REDIRECT_CODES.include?(response.code.to_i)
    redirects         += 1
    last_effective_uri = parse_uri! response['location']
    redirect_delegate  = redirect_to(last_effective_uri, response.code.to_i)

    cookie = HTTP::Cookie.cookie_value(@jar.cookies(last_effective_uri))
    if cookie && !cookie.empty?
      redirect_delegate.add_field('Cookie', cookie)
    end

    response = request!(last_effective_uri, redirect_delegate)
    @jar.parse(response['set-cookie'].to_s, last_effective_uri)
  end

  Response.new(response, last_effective_uri)
end

Private Instance Methods

create_request_delegate(verb, uri, args) click to toggle source
# File lib/http/client.rb, line 185
def create_request_delegate verb, uri, args
  klass   = find_delegate_class(verb)
  headers = DEFAULT_HEADERS.merge(args.fetch(:headers, {}))

  files    = args[:files]
  qs       = args[:query]
  uri      = uri.dup
  delegate = nil

  if files
    raise Error::Argument, "#{verb} cannot have body" unless klass.const_get(:REQUEST_HAS_BODY)
    multipart             = Multipart.new(files, qs)
    delegate              = klass.new(uri, headers)
    delegate.content_type = multipart.content_type
    delegate.body         = multipart.body
  elsif qs
    if klass.const_get(:REQUEST_HAS_BODY)
      delegate = klass.new(uri, headers)
      delegate.set_form_data(qs)
    else
      uri.query = URI.encode_www_form(qs)
      delegate  = klass.new(uri, headers)
    end
  else
    delegate = klass.new(uri, headers)
  end

  delegate
end
find_delegate_class(verb) click to toggle source
# File lib/http/client.rb, line 263
def find_delegate_class verb
  if VALID_VERBS.include?(verb)
    verb
  else
    find_verb_class(verb.to_s)
  end
end
find_verb_class(string) click to toggle source
# File lib/http/client.rb, line 271
def find_verb_class string
  case string
    when /^get$/i     then GET
    when /^head$/i    then HEAD
    when /^put$/i     then PUT
    when /^post$/i    then POST
    when /^delete$/i  then DELETE
    else
      raise Error::Argument, "Invalid verb #{string}"
  end
end
parse_uri!(uri) click to toggle source
# File lib/http/client.rb, line 166
def parse_uri! uri
  uri = uri.kind_of?(URI) ? uri : URI.parse(uri)
  case uri
    when URI::HTTP, URI::HTTPS
      raise Error::URI, "Invalid URI #{uri}" if uri.host.nil?
      uri
    when URI::Generic
      if @delegate && @delegate.uri
        @delegate.uri.dup.tap {|s| s += uri }
      else
        raise Error::URI, "Invalid URI #{uri}"
      end
    else
      raise Error::URI, "Invalid URI #{uri}"
  end
rescue URI::InvalidURIError => e
  raise Error::URI, "Invalid URI #{uri}"
end
redirect_to(uri, code) click to toggle source
# File lib/http/client.rb, line 239
def redirect_to uri, code
  # NOTE request-uri with query string is not preserved.
  case code
    when *REDIRECT_WITH_GET
      GET.new(uri, {}).tap do |r|
        @delegate.each_header do |field, value|
          next if field.downcase == 'host'
          r[field] = value
        end
      end
    when *REDIRECT_WITH_ORIGINAL
      @delegate.class.new(uri, {}).tap do |r|
        @delegate.each_header do |field, value|
          next if field.downcase == 'host'
          r[field] = value
        end

        r.body = @delegate.body
      end
    else
      raise Error, "response #{code} should not result in redirection."
  end
end
request!(uri, delegate) click to toggle source
# File lib/http/client.rb, line 215
def request! uri, delegate
  http = Net::HTTP.new(uri.host, uri.port, :ENV)
  if uri.scheme == 'https'
    http.use_ssl     = true
    http.verify_mode = @ssl_verify
  end

  http.open_timeout = @open_timeout if @open_timeout
  http.read_timeout = @read_timeout if @read_timeout
  http.ssl_timeout  = @ssl_timeout  if @ssl_timeout

  response = http.request(delegate)
  http.finish if http.started?
  response
rescue URI::Error => e
  raise Error::URI.new(e.message, e)
rescue Zlib::Error => e
  raise Error::Zlib.new(e.message, e)
rescue Timeout::Error => e
  raise Error::Timeout.new(e.message, e)
rescue Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
  raise Error::Transport.new(e.message, e)
end
uri() click to toggle source
# File lib/http/client.rb, line 162
def uri
  @delegate.uri
end