class HTTPX::Request

  1. lib/httpx/request.rb
  2. lib/httpx/request/body.rb
  3. show all
Superclass: Object

Defines how an HTTP request is handled internally, both in terms of making attributes accessible, as well as maintaining the state machine which manages streaming the request onto the wire.

Included modules

  1. Loggable
  2. Callbacks

Classes and Modules

  1. HTTPX::Request::Body

Constants

ALLOWED_URI_SCHEMES = %w[https http].freeze  

Attributes

active_timeouts [R]
body [R]

an HTTPX::Request::Body object containing the request body payload (or nil, whenn there is none).

connection [W]

the connection the request is currently being sent to (none if before or after transaction)

drain_error [R]

Exception raised during enumerable body writes.

headers [R]

an HTTPX::Headers object containing the request HTTP headers.

http2_stream_options [R]

when this request is sent via HTTP/2, it’ll use this hash of options to set the priority of the respective HTTP/2 frame.

on_response_arrived [W]

callback triggered when a response (which may not be the final response) was assigned to the request.

options [R]

an HTTPX::Options object containing request options.

peer_address [RW]

The IP address from the peer server.

persistent [W]
response [R]

the corresponding HTTPX::Response object, when there is one.

state [R]

a symbol describing which frame is currently being flushed.

uri [R]

the absolute URI object for this request.

verb [R]

the upcased string HTTP verb for this request.

Public Class methods

new(verb, uri, options, params = EMPTY_HASH)

initializes the instance with the given verb (an upppercase String, ex. ‘GEt’), an absolute or relative uri (either as String or URI::HTTP object), the request options (instance of HTTPX::Options) and an optional Hash of params.

Besides any of the options documented in HTTPX::Options (which would override or merge with what options sets), it accepts also the following:

:params

hash or array of key-values which will be encoded and set in the query string of request uris.

:body

to be encoded in the request body payload. can be a String, an IO object (i.e. a File), or an Enumerable.

:form

hash of array of key-values which will be form-urlencoded- or multipart-encoded in requests body payload.

:json

hash of array of key-values which will be JSON-encoded in requests body payload.

:xml

Nokogiri XML nodes which will be encoded in requests body payload.

:http2_stream_options

hash of options to be used to set the HTTP/2 priority by sending an initial PRIORITY frame.

:body, :form, :json and :xml are all mutually exclusive, i.e. only one of them gets picked up.

[show source]
    # File lib/httpx/request.rb
 80 def initialize(verb, uri, options, params = EMPTY_HASH)
 81   @verb    = verb.to_s.upcase
 82   @uri     = Utils.to_uri(uri)
 83 
 84   @headers = options.headers.dup
 85   merge_headers(params.delete(:headers)) if params.key?(:headers)
 86 
 87   @query_params = params.delete(:params) if params.key?(:params)
 88 
 89   @http2_stream_options = params.key?(:http2_stream_options) ? params.delete(:http2_stream_options) : EMPTY_HASH
 90 
 91   @body = options.request_body_class.new(@headers, options, **params)
 92 
 93   @options = @body.options
 94 
 95   if @uri.relative? || @uri.host.nil?
 96     origin = @options.origin
 97     raise(Error, "invalid URI: #{@uri}") unless origin
 98 
 99     base_path = @options.base_path
100 
101     @uri = origin.merge("#{base_path}#{@uri}")
102   end
103 
104   raise UnsupportedSchemeError, "#{@uri}: #{@uri.scheme}: unsupported URI scheme" unless ALLOWED_URI_SCHEMES.include?(@uri.scheme)
105 
106   @state = :idle
107   @connection = @response =
108     @drainer = @peer_address =
109       @informational_status = @on_response_arrived = nil
110   @ping = @started = false
111   @persistent = @options.persistent
112   @active_timeouts = []
113 end

Public Instance methods

authority()

returs the URI authority of the request.

session.build_request("GET", "https://google.com/query").authority #=> "google.com"
session.build_request("GET", "http://internal:3182/a").authority #=> "internal:3182"
[show source]
    # File lib/httpx/request.rb
240 def authority
241   @uri.authority
242 end
can_buffer?()
[show source]
    # File lib/httpx/request.rb
178 def can_buffer?
179   @state != :done
180 end
complete!(response = @response)
[show source]
    # File lib/httpx/request.rb
123 def complete!(response = @response)
124   emit(:complete, response)
125 end
drain_body()

consumes and returns the next available chunk of request body that can be sent

[show source]
    # File lib/httpx/request.rb
270 def drain_body
271   return if @body.nil?
272 
273   @drainer ||= @body.each
274   @drainer.next.dup
275 rescue StopIteration
276   nil
277 rescue StandardError => e
278   # in case an error occurs while emitting body chunks
279   @drain_error = e
280   nil
281 end
emit_response(response)
[show source]
    # File lib/httpx/request.rb
357 def emit_response(response)
358   emit(:response, response)
359 
360   return unless @on_response_arrived
361 
362   @on_response_arrived.call
363 end
expects?()

whether the request supports the 100-continue handshake and already processed the 100 response.

[show source]
    # File lib/httpx/request.rb
334 def expects?
335   @headers["expect"] == "100-continue" && @informational_status == 100 && !@response
336 end
handle_error(error)
[show source]
    # File lib/httpx/request.rb
347 def handle_error(error)
348   if (connection = @connection)
349     connection.on_error(error, self)
350   else
351     response = ErrorResponse.new(self, error)
352     self.response = response
353     emit_response(response)
354   end
355 end
initialize_dup(orig)

dupped initialization

[show source]
    # File lib/httpx/request.rb
116 def initialize_dup(orig)
117   super
118   @uri = orig.instance_variable_get(:@uri).dup
119   @headers = orig.instance_variable_get(:@headers).dup
120   @body = orig.instance_variable_get(:@body).dup
121 end
inspect()

simplecov:disable

[show source]
    # File lib/httpx/request.rb
284 def inspect
285   "#<#{self.class}:#{object_id} " \
286     "#{@verb} " \
287     "#{uri} " \
288     "@headers=#{@headers} " \
289     "@body=#{@body}>"
290 end
interests()

returns :r or :w, depending on whether the request is waiting for a response or flushing.

[show source]
    # File lib/httpx/request.rb
172 def interests
173   return :r if @state == :done || @state == :expect
174 
175   :w
176 end
merge_headers(h)

merges h into the instance of HTTPX::Headers of the request.

[show source]
    # File lib/httpx/request.rb
187 def merge_headers(h)
188   @headers = @headers.merge(h)
189   return unless @headers.key?("range")
190 
191   @headers.delete("accept-encoding")
192 end
origin()

returs the URI origin of the request.

session.build_request("GET", "https://google.com/query").authority #=> "https://google.com"
session.build_request("GET", "http://internal:3182/a").authority #=> "http://internal:3182"
[show source]
    # File lib/httpx/request.rb
248 def origin
249   @uri.origin
250 end
path()

returnns the URI path of the request uri.

[show source]
    # File lib/httpx/request.rb
228 def path
229   path = uri.path.dup
230   path =  +"" if path.nil?
231   path << "/" if path.empty?
232   path << "?#{query}" unless query.empty?
233   path
234 end
persistent?()
[show source]
    # File lib/httpx/request.rb
157 def persistent?
158   @persistent
159 end
ping!()

marks the request as having been buffered with a ping

[show source]
    # File lib/httpx/request.rb
133 def ping!
134   @ping = true
135 end
ping?()

whether request has been buffered with a ping

[show source]
    # File lib/httpx/request.rb
128 def ping?
129   @ping
130 end
query()

returs the URI query string of the request (when available).

session.build_request("GET", "https://search.com").query #=> ""
session.build_request("GET", "https://search.com?q=a").query #=> "q=a"
session.build_request("GET", "https://search.com", params: { q: "a"}).query #=> "q=a"
session.build_request("GET", "https://search.com?q=a", params: { foo: "bar"}).query #=> "q=a&foo&bar"
[show source]
    # File lib/httpx/request.rb
258 def query
259   return @query if defined?(@query)
260 
261   query = []
262   if (q = @query_params) && !q.empty?
263     query << Transcoder::Form.encode(q)
264   end
265   query << @uri.query if @uri.query
266   @query = query.join("&")
267 end
read_timeout()

the read timeout defined for this request.

[show source]
    # File lib/httpx/request.rb
138 def read_timeout
139   @options.timeout[:read_timeout]
140 end
request_timeout()

the request timeout defined for this request.

[show source]
    # File lib/httpx/request.rb
148 def request_timeout
149   @options.timeout[:request_timeout]
150 end
response=(response)

sets the response on this request.

[show source]
    # File lib/httpx/request.rb
200 def response=(response)
201   return unless response
202 
203   case response
204   when Response
205     if response.status < 200
206       # deal with informational responses
207 
208       if response.status == 100 && @headers.key?("expect")
209         @informational_status = response.status
210         return
211       end
212 
213       # 103 Early Hints advertises resources in document to browsers.
214       # not very relevant for an HTTP client, discard.
215       return if response.status >= 103
216 
217     end
218   when ErrorResponse
219     response.error.connection = nil if response.error.respond_to?(:connection=)
220   end
221 
222   @response = response
223 
224   emit(:response_started, response)
225 end
scheme()

the URI scheme of the request uri.

[show source]
    # File lib/httpx/request.rb
195 def scheme
196   @uri.scheme
197 end
set_timeout_callback(event, &callback)
[show source]
    # File lib/httpx/request.rb
338 def set_timeout_callback(event, &callback)
339   clb = once(event, &callback)
340 
341   # reset timeout callbacks when requests get rerouted to a different connection
342   once(:idle) do
343     callbacks(event).delete(clb)
344   end
345 end
started?()
[show source]
    # File lib/httpx/request.rb
182 def started?
183   @started
184 end
total_request_timeout()

the total request timeout defined for this request.

[show source]
    # File lib/httpx/request.rb
153 def total_request_timeout
154   @options.timeout[:total_request_timeout]
155 end
trailers()

returns an instance of HTTPX::Headers containing the trailer headers

[show source]
    # File lib/httpx/request.rb
167 def trailers
168   @trailers ||= @options.headers_class.new
169 end
trailers?()

if the request contains trailer headers

[show source]
    # File lib/httpx/request.rb
162 def trailers?
163   defined?(@trailers)
164 end
transition(nextstate)

moves on to the nextstate of the request state machine (when all preconditions are met)

[show source]
    # File lib/httpx/request.rb
294 def transition(nextstate)
295   case nextstate
296   when :idle
297     @body.rewind
298     @ping = false
299     @response = @drainer = nil
300     @active_timeouts.clear
301   when :headers
302     return unless @state == :idle
303 
304     @started = true
305   when :body
306     return unless @state == :headers ||
307                   @state == :expect
308 
309     if @headers.key?("expect")
310       if @informational_status && @informational_status == 100
311         # check for 100 Continue response, and deallocate the var
312         # if @informational_status == 100
313         #   @response = nil
314         # end
315       else
316         return if @state == :expect # do not re-set it
317 
318         nextstate = :expect
319       end
320     end
321   when :trailers
322     return unless @state == :body
323   when :done
324     return if @state == :expect
325 
326   end
327   log(level: 3) { "#{@state} -> #{nextstate}" }
328   @state = nextstate
329   emit(@state, self)
330   nil
331 end
write_timeout()

the write timeout defined for this request.

[show source]
    # File lib/httpx/request.rb
143 def write_timeout
144   @options.timeout[:write_timeout]
145 end