Module: HTTP::Chainable

Includes:
Base64, Verbs
Included in:
HTTP, Client, Session
Defined in:
lib/http/chainable.rb,
lib/http/chainable/verbs.rb,
lib/http/chainable/helpers.rb

Overview

HTTP verb methods and client configuration DSL

Defined Under Namespace

Modules: Verbs

Constant Summary collapse

PROXY_ARG_MAP =

Mapping of proxy argument positions to hash keys and expected types

[
  [:proxy_address,  0, String],
  [:proxy_port,     1, Integer],
  [:proxy_username, 2, String],
  [:proxy_password, 3, String],
  [:proxy_headers,  2, Hash],
  [:proxy_headers,  4, Hash]
].freeze

Instance Method Summary collapse

Methods included from Verbs

#connect, #delete, #get, #head, #options, #patch, #post, #put, #trace

Methods included from Base64

encode64

Instance Method Details

#accept(type) ⇒ HTTP::Session

Accept the given MIME type(s)

Examples:

HTTP.accept("application/json").get("http://example.com")

Parameters:

  • type (String, Symbol)

    MIME type to accept

Returns:



238
239
240
# File 'lib/http/chainable.rb', line 238

def accept(type)
  headers Headers::ACCEPT => MimeType.normalize(type)
end

#auth(value) ⇒ HTTP::Session

Make a request with the given Authorization header

Examples:

HTTP.auth("Bearer token123").get("http://example.com")

Parameters:

  • value (#to_s)

    Authorization header value

Returns:



250
251
252
# File 'lib/http/chainable.rb', line 250

def auth(value)
  headers Headers::AUTHORIZATION => value.to_s
end

#base_uri(uri) ⇒ HTTP::Session

Set a base URI for resolving relative request paths

The first call must use an absolute URI that includes a scheme (e.g. “example.com”). Once a base URI is set, subsequent chained calls may use relative paths that are resolved against the existing base.

Examples:

HTTP.base_uri("https://example.com/api/v1").get("users")

Chaining base URIs

HTTP.base_uri("https://example.com").base_uri("api/v1").get("users")

Parameters:

  • uri (String, HTTP::URI)

    the base URI (absolute with scheme when no base is set; may be relative when chaining)

Returns:

Raises:

  • (HTTP::Error)

    if no base URI is set and the given URI has no scheme



87
88
89
# File 'lib/http/chainable.rb', line 87

def base_uri(uri)
  branch default_options.with_base_uri(uri)
end

#basic_auth(user:, pass:) ⇒ HTTP::Session

Make a request with the given Basic authorization header

Examples:

HTTP.basic_auth(user: "user", pass: "pass").get("http://example.com")

Parameters:

  • user (#to_s)
  • pass (#to_s)

Returns:

See Also:



264
265
266
# File 'lib/http/chainable.rb', line 264

def basic_auth(user:, pass:)
  auth("Basic #{encode64("#{user}:#{pass}")}")
end

#cookies(cookies) ⇒ HTTP::Session

Make a request with the given cookies

Examples:

HTTP.cookies(session: "abc123").get("http://example.com")

Parameters:

  • cookies (Hash, Array<HTTP::Cookie>)

    cookies to set

Returns:



205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/http/chainable.rb', line 205

def cookies(cookies)
  value = cookies.map do |entry|
    case entry
    when HTTP::Cookie then entry.cookie_value
    else
      name, val = entry
      HTTP::Cookie.new(name.to_s, val.to_s).cookie_value
    end
  end.join("; ")

  headers(Headers::COOKIE => value)
end

#default_optionsHTTP::Options

Get options for HTTP

Examples:

HTTP.default_options

Returns:



292
293
294
# File 'lib/http/chainable.rb', line 292

def default_options
  @default_options ||= HTTP::Options.new
end

#default_options=(opts) ⇒ HTTP::Options

Set options for HTTP

Examples:

HTTP.default_options = { response: :object }

Parameters:

Returns:



304
305
306
# File 'lib/http/chainable.rb', line 304

def default_options=(opts)
  @default_options = HTTP::Options.new(opts)
end

#digest_auth(user:, pass:) ⇒ HTTP::Session

Enable HTTP Digest authentication

Automatically handles 401 Digest challenges by computing the digest response and retrying the request with proper credentials.

Examples:

HTTP.digest_auth(user: "admin", pass: "secret").get("http://example.com")

Parameters:

  • user (#to_s)
  • pass (#to_s)

Returns:

See Also:



281
282
283
# File 'lib/http/chainable.rb', line 281

def digest_auth(user:, pass:)
  use(digest_auth: { user: user, pass: pass })
end

#encoding(encoding) ⇒ HTTP::Session

Force a specific encoding for response body

Examples:

HTTP.encoding("UTF-8").get("http://example.com")

Parameters:

  • encoding (String, Encoding)

    encoding to use

Returns:



226
227
228
# File 'lib/http/chainable.rb', line 226

def encoding(encoding)
  branch default_options.with_encoding(encoding)
end

#follow(strict: nil, max_hops: nil, on_redirect: nil) ⇒ HTTP::Session

Make client follow redirects

Examples:

HTTP.follow.get("http://example.com")

Parameters:

  • strict (Boolean) (defaults to: nil)

    (true) redirector hops policy

  • max_hops (Integer) (defaults to: nil)

    (5) maximum allowed redirect hops

  • on_redirect (#call, nil) (defaults to: nil)

    optional redirect callback

Returns:

See Also:



180
181
182
183
# File 'lib/http/chainable.rb', line 180

def follow(strict: nil, max_hops: nil, on_redirect: nil)
  opts = { strict: strict, max_hops: max_hops, on_redirect: on_redirect }.compact
  branch default_options.with_follow(opts)
end

#headers(headers) ⇒ HTTP::Session

Make a request with the given headers

Examples:

HTTP.headers("Accept" => "text/plain").get("http://example.com")

Parameters:

  • headers (Hash)

    request headers

Returns:



193
194
195
# File 'lib/http/chainable.rb', line 193

def headers(headers)
  branch default_options.with_headers(headers)
end

#nodelayHTTP::Session

Set TCP_NODELAY on the socket

Examples:

HTTP.nodelay.get("http://example.com")

Returns:



315
316
317
# File 'lib/http/chainable.rb', line 315

def nodelay
  branch default_options.with_nodelay(true)
end

#persistent(host = nil, timeout: 5) ⇒ HTTP::Session #persistent(host = nil, timeout: 5) {|session| ... } ⇒ Object

Open a persistent connection to a host

Returns an Session that pools persistent HTTP::Client instances by origin. This allows connection reuse within the same origin and transparent cross-origin redirect handling.

When no host is given, the origin is derived from the configured base URI.

Examples:

HTTP.persistent("http://example.com").get("/")

Derive host from base URI

HTTP.base_uri("https://example.com/api").persistent.get("users")

Overloads:

  • #persistent(host = nil, timeout: 5) ⇒ HTTP::Session

    Flags as persistent

    Parameters:

    • host (String, nil) (defaults to: nil)

      connection origin (derived from base URI when nil)

    Returns:

    Raises:

    • (ArgumentError)

      if host is nil and no base URI is set

    • (Request::Error)

      if Host is invalid

  • #persistent(host = nil, timeout: 5) {|session| ... } ⇒ Object

    Executes given block with persistent session and automatically closes all connections at the end of execution.

    Examples:

    
    def keys(users)
      HTTP.persistent("https://github.com") do |http|
        users.map { |u| http.get("/#{u}.keys").to_s }
      end
    end
    
    # same as
    
    def keys(users)
      http = HTTP.persistent "https://github.com"
      users.map { |u| http.get("/#{u}.keys").to_s }
    ensure
      http.close if http
    end

    Yield Parameters:

    Returns:

    • (Object)

      result of last expression in the block

Returns:



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/http/chainable.rb', line 138

def persistent(host = nil, timeout: 5)
  host ||= default_options.base_uri&.origin
  raise ArgumentError, "host is required for persistent connections" unless host

  options = default_options.merge(keep_alive_timeout: timeout).with_persistent(host)
  session = branch(options)
  return session unless block_given?

  yield session
ensure
  session&.close if block_given?
end

#request(verb, uri) {|response| ... } ⇒ HTTP::Response, Object

Make an HTTP request with the given verb

Examples:

Without a block

HTTP.request(:get, "http://example.com")

With a block (auto-closes connection)

HTTP.request(:get, "http://example.com") { |res| res.status }

Parameters:

  • verb (Symbol)

    the HTTP method

  • uri (#to_s)

    the URI to request

Yield Parameters:

Returns:



26
27
28
29
30
31
32
33
34
# File 'lib/http/chainable.rb', line 26

def request(verb, uri, **, &block)
  client   = make_client(default_options)
  response = client.request(verb, uri, **)
  return response unless block

  yield response
ensure
  client&.close if block
end

#retriable(tries: nil, delay: nil, exceptions: nil, retry_statuses: nil, on_retry: nil, max_delay: nil, should_retry: nil) ⇒ HTTP::Session

Return a retriable session that retries on failure

Examples:

Usage


# Retry max 5 times with randomly growing delay between retries
HTTP.retriable.get(url)

# Retry max 3 times with randomly growing delay between retries
HTTP.retriable(tries: 3).get(url)

# Retry max 3 times with 1 sec delay between retries
HTTP.retriable(tries: 3, delay: proc { 1 }).get(url)

# Retry max 3 times with geometrically progressed delay between retries
HTTP.retriable(tries: 3, delay: proc { |i| 1 + i*i }).get(url)

Returns:



350
351
352
353
354
355
# File 'lib/http/chainable.rb', line 350

def retriable(tries: nil, delay: nil, exceptions: nil, retry_statuses: nil,
              on_retry: nil, max_delay: nil, should_retry: nil)
  opts = { tries: tries, delay: delay, exceptions: exceptions, retry_statuses: retry_statuses,
           on_retry: on_retry, max_delay: max_delay, should_retry: should_retry }.compact
  branch default_options.with_retriable(opts.empty? || opts)
end

#timeout(options = {}) ⇒ HTTP::Session #timeout(global_timeout) ⇒ HTTP::Session

Set timeout on the request

Examples:

HTTP.timeout(10).get("http://example.com")

Overloads:

  • #timeout(options = {}) ⇒ HTTP::Session

    Adds per operation timeouts to the request

    Parameters:

    • options (Hash) (defaults to: {})

    Options Hash (options):

    • :read (Float)

      Read timeout

    • :write (Float)

      Write timeout

    • :connect (Float)

      Connect timeout

    • :global (Float)

      Global timeout (combines with per-operation)

  • #timeout(global_timeout) ⇒ HTTP::Session

    Adds a global timeout to the full request

    Parameters:

    • global_timeout (Numeric)

Returns:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/http/chainable.rb', line 53

def timeout(options)
  klass, options = case options
                   when Numeric then [HTTP::Timeout::Global, { global_timeout: options }]
                   when Hash    then resolve_timeout_hash(options)
                   when :null   then [HTTP::Timeout::Null, {}]
                   else raise ArgumentError,
                              "Use `.timeout(:null)`, " \
                              "`.timeout(global_timeout_in_seconds)` or " \
                              "`.timeout(connect: x, write: y, read: z)`."
                   end

  branch default_options.merge(
    timeout_class:   klass,
    timeout_options: options
  )
end

#use(*features) ⇒ HTTP::Session

Enable one or more features

Examples:

HTTP.use(:auto_inflate).get("http://example.com")

Parameters:

  • features (Array<Symbol, Hash>)

    features to enable

Returns:



327
328
329
# File 'lib/http/chainable.rb', line 327

def use(*features)
  branch default_options.with_features(features)
end

#via(*proxy) ⇒ HTTP::Session Also known as: through

Make a request through an HTTP proxy

Examples:

HTTP.via("proxy.example.com", 8080).get("http://example.com")

Parameters:

  • proxy (Array)

Returns:

Raises:

  • (Request::Error)

    if HTTP proxy is invalid



160
161
162
163
164
165
166
# File 'lib/http/chainable.rb', line 160

def via(*proxy)
  proxy_hash = build_proxy_hash(proxy)

  raise(RequestError, "invalid HTTP proxy: #{proxy_hash}") unless (2..5).cover?(proxy_hash.keys.size)

  branch default_options.with_proxy(proxy_hash)
end