Common interface for Ruby’s HTTP libraries


HTTPI is available through Rubygems and can be installed via:

$ gem install httpi


HTTPI provides a common interface for Ruby’s most popular HTTP clients:

Due to the fact that Rubygems does not allow optional dependencies, HTTPI does not specify any of these libraries as direct dependencies. Therefore if you want to use anything other than Net::HTTP, you need to make sure it’s available in your load path.

When you’re executing any HTTP request for the first time without having specified an adapter to use, HTTPI tries to load and use the "best" library for you. It follows a specific load order with Net::HTTP at the end of the chain:

[:httpclient, :curb, :em_http, :excon, :net_http, :net_http_persistent]

You can also manually specify which adapter you would like to use:

HTTPI.adapter = :curb

Adding new adapters

Also as of version 2, you can extend HTTPI to support your very own adapter by creating a class that inherits from HTTPI::Adapter::Base and implements a simple interface.

# [1] inherit from the base class
class MyAdapter < HTTPI::Adapter::Base

  # [2] register your adapter and a list of dependencies
  register :my_adapter, deps: %w(some_http_client)

  # [3] your adapter receives the request on initialize
  def initialize(request)
    @request = request
    @client =

  # [4] make the underlying client available to others
  attr_reader :client

  # [5] execute an arbitary HTTP request
  def request(method)
    response = @client.request(method, @request.url, @request.body)

    # [6] always return a response object, response.header, response.content)



In order to provide a common interface, HTTPI exposes the HTTPI::Request (request) object for you to configure your requests. Here’s a very simple example of how you can use this object to execute a GET request:

request ="")

And here’s an example of a POST request with a payload using the Curb adapter:

request =
request.url = ""
request.body = "bangarang", :curb)

The previous example only specifies a URL and a request body. For simple use cases like this, HTTPI allows you to omit the request object:"", "bangarang", :curb)

As you can see, the HTTPI module provides access to common HTTP request methods. All of them either accept a request object or a certain set of convenience arguments. Along these arguments, you can optionally specify the adapter to use per request.

HTTPI.get(request, adapter = nil)
HTTPI.get(url, adapter = nil)
POST:, adapter = nil), body, adapter = nil)
HTTPI.head(request, adapter = nil)
HTTPI.head(url, adapter = nil)
HTTPI.put(request, adapter = nil)
HTTPI.put(url, body, adapter = nil)
HTTPI.delete(request, adapter = nil)
HTTPI.delete(url, adapter = nil)
HTTPI.request(method, request, adapter = nil)

The request method is special. You can use it to dynamically specify the HTTP request method to use.

http_method = :get
request ="")

HTTPI.request(http_method, request)

It can also be used for custom HTTP methods. Currently this is only supported by HTTPClient and EM-HTTP-Request.

HTTPI.request(:custom, request)


The HTTPI::Request object should support every option you might need to specify. It can be created with a request URL, an options Hash or no arguments at all."") "", open_timeout: 15)

Of course, every Hash option also has its own accessor method.

request.url = ""
request.url # => #<URI::HTTP:0x101c1ab18 URL:>
request.url = ""
request.query = "q=query"
# or
request.query = {:q => "query"}
request.url.to_s # => ""
request.proxy = ""
request.proxy # => #<URI::HTTP:0x101c1ab18 URL:>
request.headers["Accept-Charset"] = "utf-8"
request.headers = { "Accept-Charset" => "utf-8" }

request.headers # => { "Accept-Charset" => "utf-8" }
request.body = "scary monsters and nice sprites"
request.body # => "scary monsters and nice sprites"

request.body = { user_id: 123, active: false }
request.body # => "user_id=123&active=false"
request.open_timeout = 30 # seconds
request.read_timeout = 30 # seconds
Chunked Responses
response_body = ""
request.on_body do |body|
  response_body << body
response_body # complete body


HTTPI::Request supports HTTP basic and digest authentication.

request.auth.basic("username", "password")
request.auth.digest("username", "password")

The :curb adapter provides support for HTTP Negotiate/SPNEGO (aka Kerberos) authentication.


For NTLM authentication, HTTPI ships with a solution build on top of the :net_http adapter and the Ruby/NTLM library. The configuration method accepts an optional third parameter to specify a domain. If the domain is omitted we assume that you want to authenticate to the local server.

request.auth.ntlm("username", "password")
request.auth.ntlm("username", "password", "domain")

In case you're using SSL client authentication, HTTPI has you covered as well.

request.auth.ssl.cert_key_file     = "client_key.pem"   # the private key file to use
request.auth.ssl.cert_key_password = "C3rtP@ssw0rd"     # the key file's password
request.auth.ssl.cert_file         = "client_cert.pem"  # the certificate file to use
request.auth.ssl.ca_cert_file      = "ca_cert.pem"      # the ca certificate file to use
request.auth.ssl.verify_mode       = :none              # or one of [:peer, :fail_if_no_peer_cert, :client_once]
request.auth.ssl.ssl_version       = :TLSv1             # or one of [:SSLv2, :SSLv3]


Every request returns an HTTPI::Response object which contains various details like the response code, headers and response body.

response = HTTPI.get(request)
response.body # => "<!DOCTYPE HTML PUBLIC ...>"
response.code # => 200
response.headers # => { "Content-Encoding" => "gzip" }
response.body # => "<!DOCTYPE HTML PUBLIC ...>"

This method automatically handles gzipped and DIME encoded responses. You can still access the raw response body though:

response.raw_body # => "x��Qt�w�pU���Qu��tV��ӳ�[��"
response.code   # => 404
response.error? # => true

A response is considered successful when the response code lies between 200 and 299.


HTTPI by default logs each HTTP request to $stdout using a log level of :debug.

HTTPI.log       = false     # disable logging
HTTPI.logger    = MyLogger  # change the logger
HTTPI.log_level = :info     # change the log level