Class: HTTP::Client

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Chainable
Defined in:
lib/http/client.rb,
sig/http.rbs

Overview

Clients make requests and receive responses

Constant Summary

Constants included from Chainable

HTTP::Chainable::PROXY_ARG_MAP

Instance Method Summary collapse

Methods included from Chainable

#accept, #auth, #base_uri, #basic_auth, #branch, #build_proxy_hash, #cookies, #default_options, #default_options=, #digest_auth, #encoding, #follow, #headers, #make_client, #nodelay, #persistent, #resolve_global_only, #resolve_timeout_hash, #retriable, #timeout, #use, #via

Methods included from HTTP::Chainable::Verbs

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

Methods included from Base64

encode64, #self?.encode64

Constructor Details

#initialize(default_options = nil) ⇒ HTTP::Client

Initialize a new HTTP Client

Examples:

client = HTTP::Client.new(headers: {"Accept" => "application/json"})

Parameters:

  • default_options (HTTP::Options, nil) (defaults to: nil)

    existing options instance

  • options (Hash)

    keyword options (see HTTP::Options#initialize)

  • (Object)


30
31
32
33
34
# File 'lib/http/client.rb', line 30

def initialize(default_options = nil, **)
  @default_options = HTTP::Options.new(default_options, **)
  @connection = nil
  @state = :clean
end

Instance Method Details

#around_request(request, options) {|arg0| ... } ⇒ HTTP::Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Compose around_request chains from all features

Parameters:

Yields:

Yield Parameters:

Yield Returns:

Returns:



191
192
193
194
195
# File 'lib/http/client.rb', line 191

def around_request(request, options, &block)
  options.features.values.reverse.reduce(block) do |inner, feature|
    ->(req) { feature.around_request(req) { |r| inner.call(r) } }
  end.call(request)
end

#build_response(req, options) ⇒ HTTP::Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Build a response from the current connection

Parameters:

Returns:



200
201
202
203
204
205
206
207
208
209
210
# File 'lib/http/client.rb', line 200

def build_response(req, options)
  Response.new(
    status:        @connection.status_code,
    version:       @connection.http_version,
    headers:       @connection.headers,
    proxy_headers: @connection.proxy_response_headers,
    connection:    @connection,
    encoding:      options.encoding,
    request:       req
  ).tap { |res| @connection.pending_response = res }
end

#build_wrapped_response(req, options) ⇒ HTTP::Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Build response and apply feature wrapping

Parameters:

Returns:



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

def build_wrapped_response(req, options)
  res = build_response(req, options)

  options.features.values.reverse.inject(res) do |response, feature|
    feature.wrap_response(response)
  end
end

#closevoid

This method returns an undefined value.

Close the connection and reset state

Examples:

client.close


103
104
105
106
107
# File 'lib/http/client.rb', line 103

def close
  @connection&.close
  @connection = nil
  @state = :clean
end

#notify_features(req, options) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Notify features of an upcoming request attempt

Parameters:



172
173
174
# File 'lib/http/client.rb', line 172

def notify_features(req, options)
  options.features.each_value { |feature| feature.on_request(req) }
end

#perform(req, options) ⇒ HTTP::Response

Perform a single (no follow) HTTP request

Examples:

client.perform(request, options)

Parameters:

Returns:



88
89
90
91
92
93
94
# File 'lib/http/client.rb', line 88

def perform(req, options)
  if options.retriable
    perform_with_retry(req, options)
  else
    perform_once(req, options)
  end
end

#perform_exchange(req, options) ⇒ HTTP::Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Execute the HTTP exchange wrapped by feature around_request hooks

Parameters:

Returns:



179
180
181
182
183
184
185
186
# File 'lib/http/client.rb', line 179

def perform_exchange(req, options)
  around_request(req, options) do |request|
    verify_connection!(request.uri)
    @state = :dirty
    send_request(request, options)
    build_wrapped_response(request, options)
  end
end

#perform_once(req, options) ⇒ HTTP::Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Execute a single HTTP request without retry logic

Parameters:

Returns:



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/http/client.rb', line 117

def perform_once(req, options)
  res = perform_exchange(req, options)

  @connection.finish_response if res.request.verb == :head
  @state = :clean

  res
rescue
  close
  raise
end

#perform_with_retry(req, options) ⇒ HTTP::Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Execute a request with retry logic

Parameters:

Returns:



135
136
137
138
139
# File 'lib/http/client.rb', line 135

def perform_with_retry(req, options)
  Retriable::Performer.new(**options.retriable).perform(self, req) do
    perform_once(req, options)
  end
end

#persistent?Boolean

Indicate whether the client has persistent connections

Examples:

client.persistent?

Returns:

  • (Boolean)

    whenever client is persistent

See Also:



77
# File 'lib/http/client.rb', line 77

def_delegator :default_options, :persistent?

#request(verb, uri, headers: nil, params: nil, form: nil, json: nil, body: nil, response: nil, encoding: nil, follow: nil, ssl: nil, ssl_context: nil, proxy: nil, nodelay: nil, features: nil, retriable: nil, socket_class: nil, ssl_socket_class: nil, timeout_class: nil, timeout_options: nil, keep_alive_timeout: nil, base_uri: nil, persistent: nil) ⇒ HTTP::Response

Make an HTTP request

Examples:

client.request(:get, "https://example.com")

Parameters:

  • verb (Symbol)

    the HTTP method

  • uri (#to_s)

    the URI to request

Returns:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/http/client.rb', line 45

def request(verb, uri,
            headers: nil, params: nil, form: nil, json: nil, body: nil,
            response: nil, encoding: nil, follow: nil, ssl: nil, ssl_context: nil,
            proxy: nil, nodelay: nil, features: nil, retriable: nil,
            socket_class: nil, ssl_socket_class: nil, timeout_class: nil,
            timeout_options: nil, keep_alive_timeout: nil, base_uri: nil, persistent: nil)
  opts = { headers: headers, params: params, form: form, json: json, body: body,
           response: response, encoding: encoding, follow: follow, ssl: ssl,
           ssl_context: ssl_context, proxy: proxy, nodelay: nodelay, features: features,
           retriable: retriable, socket_class: socket_class, ssl_socket_class: ssl_socket_class,
           timeout_class: timeout_class, timeout_options: timeout_options,
           keep_alive_timeout: keep_alive_timeout, base_uri: base_uri, persistent: persistent }.compact
  opts = @default_options.merge(opts)
  builder = Request::Builder.new(opts)
  req     = builder.build(verb, uri)
  res     = perform(req, opts)
  return res unless opts.follow

  Redirector.new(**opts.follow).perform(req, res) do |request|
    perform(builder.wrap(request), opts)
  end
end

#send_request(req, options) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Send request over the connection, handling proxy and errors

Parameters:



144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/http/client.rb', line 144

def send_request(req, options)
  notify_features(req, options)

  @connection ||= HTTP::Connection.new(req, options)

  unless @connection.failed_proxy_connect?
    @connection.send_request(req)
    @connection.read_headers!
  end
rescue Error => e
  options.features.each_value { |feature| feature.on_error(req, e) }
  raise
end

#verify_connection!(uri) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Verify our request isn't going to be made against another URI

Parameters:



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/http/client.rb', line 216

def verify_connection!(uri)
  if default_options.persistent? && uri.origin != default_options.persistent
    raise StateError, "Persistence is enabled for #{default_options.persistent}, but we got #{uri.origin}"
  end

  # We re-create the connection object because we want to let prior requests
  # lazily load the body as long as possible, and this mimics prior functionality.
  return close if @connection && (!@connection.keep_alive? || @connection.expired?)

  # If we get into a bad state (eg, Timeout.timeout ensure being killed)
  # close the connection to prevent potential for mixed responses.
  close if @state == :dirty
end