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.
Methods
Public Class
Public Instance
- active_timeouts
- authority
- body
- can_buffer?
- complete!
- connection
- drain_body
- drain_error
- emit_response
- expects?
- handle_error
- headers
- http2_stream_options
- initialize_dup
- inspect
- interests
- merge_headers
- on_response_arrived
- options
- origin
- path
- peer_address
- persistent
- persistent?
- ping!
- ping?
- query
- read_timeout
- request_timeout
- response
- response=
- scheme
- set_timeout_callback
- started?
- state
- total_request_timeout
- trailers
- trailers?
- transition
- uri
- verb
- write_timeout
Classes and Modules
Constants
| ALLOWED_URI_SCHEMES | = | %w[https http].freeze |
Attributes
| active_timeouts | [R] | |
| body | [R] |
an |
| 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 |
| 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 |
| peer_address | [RW] |
The IP address from the peer server. |
| persistent | [W] | |
| response | [R] |
the corresponding |
| 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
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.
# 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 = @callbacks = 109 @informational_status = @on_response_arrived = nil 110 @ping = @started = @complete = false 111 @persistent = @options.persistent 112 @active_timeouts = [] 113 end
Public Instance methods
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"
# File lib/httpx/request.rb 244 def authority 245 @uri.authority 246 end
# File lib/httpx/request.rb 182 def can_buffer? 183 @state != :done 184 end
# File lib/httpx/request.rb 123 def complete!(response = @response) 124 return false if @complete 125 126 emit(:complete, response) 127 reset_timers(true) 128 @complete = true 129 end
consumes and returns the next available chunk of request body that can be sent
# File lib/httpx/request.rb 274 def drain_body 275 return if @body.nil? 276 277 @drainer ||= @body.each 278 @drainer.next.dup 279 rescue StopIteration 280 nil 281 rescue StandardError => e 282 # in case an error occurs while emitting body chunks 283 @drain_error = e 284 nil 285 end
# File lib/httpx/request.rb 364 def emit_response(response) 365 emit(:response, response) 366 367 return unless @on_response_arrived 368 369 @on_response_arrived.call 370 end
whether the request supports the 100-continue handshake and already processed the 100 response.
# File lib/httpx/request.rb 341 def expects? 342 @headers["expect"] == "100-continue" && @informational_status == 100 && !@response 343 end
# File lib/httpx/request.rb 354 def handle_error(error) 355 if (connection = @connection) 356 connection.on_error(error, self) 357 else 358 response = ErrorResponse.new(self, error) 359 self.response = response 360 emit_response(response) 361 end 362 end
dupped initialization
# File lib/httpx/request.rb 116 def initialize_dup(orig) 117 super 118 @headers = orig.instance_variable_get(:@headers).dup 119 @body = orig.instance_variable_get(:@body).dup 120 @active_timeouts = orig.instance_variable_get(:@active_timeouts).dup 121 end
simplecov:disable
# File lib/httpx/request.rb 288 def inspect 289 "#<#{self.class}:#{object_id} " \ 290 "#{@verb} " \ 291 "#{uri} " \ 292 "@headers=#{@headers} " \ 293 "@body=#{@body}>" 294 end
returns :r or :w, depending on whether the request is waiting for a response or flushing.
# File lib/httpx/request.rb 176 def interests 177 return :r if @state == :done || @state == :expect 178 179 :w 180 end
merges h into the instance of HTTPX::Headers of the request.
# File lib/httpx/request.rb 191 def merge_headers(h) 192 @headers = @headers.merge(h) 193 return unless @headers.key?("range") 194 195 @headers.delete("accept-encoding") 196 end
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"
# File lib/httpx/request.rb 252 def origin 253 @uri.origin 254 end
returnns the URI path of the request uri.
# File lib/httpx/request.rb 232 def path 233 path = uri.path.dup 234 path = +"" if path.nil? 235 path << "/" if path.empty? 236 path << "?#{query}" unless query.empty? 237 path 238 end
marks the request as having been buffered with a ping
# File lib/httpx/request.rb 137 def ping! 138 @ping = true 139 end
whether request has been buffered with a ping
# File lib/httpx/request.rb 132 def ping? 133 @ping 134 end
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"
# File lib/httpx/request.rb 262 def query 263 return @query if defined?(@query) 264 265 query = [] 266 if (q = @query_params) && !q.empty? 267 query << Transcoder::Form.encode(q) 268 end 269 query << @uri.query if @uri.query 270 @query = query.join("&") 271 end
the read timeout defined for this request.
# File lib/httpx/request.rb 142 def read_timeout 143 @options.timeout[:read_timeout] 144 end
the request timeout defined for this request.
# File lib/httpx/request.rb 152 def request_timeout 153 @options.timeout[:request_timeout] 154 end
sets the response on this request.
# File lib/httpx/request.rb 204 def response=(response) 205 return unless response 206 207 case response 208 when Response 209 if response.status < 200 210 # deal with informational responses 211 212 if response.status == 100 && @headers.key?("expect") 213 @informational_status = response.status 214 return 215 end 216 217 # 103 Early Hints advertises resources in document to browsers. 218 # not very relevant for an HTTP client, discard. 219 return if response.status >= 103 220 221 end 222 when ErrorResponse 223 response.error.connection = nil if response.error.respond_to?(:connection=) 224 end 225 226 @response = response 227 228 emit(:response_started, response) 229 end
the URI scheme of the request uri.
# File lib/httpx/request.rb 199 def scheme 200 @uri.scheme 201 end
# File lib/httpx/request.rb 345 def set_timeout_callback(event, &callback) 346 clb = once(event, &callback) 347 348 # reset timeout callbacks when requests get rerouted to a different connection 349 once(:idle) do 350 callbacks(event).delete(clb) 351 end 352 end
the total request timeout defined for this request.
# File lib/httpx/request.rb 157 def total_request_timeout 158 @options.timeout[:total_request_timeout] 159 end
returns an instance of HTTPX::Headers containing the trailer headers
# File lib/httpx/request.rb 171 def trailers 172 @trailers ||= @options.headers_class.new 173 end
if the request contains trailer headers
# File lib/httpx/request.rb 166 def trailers? 167 defined?(@trailers) 168 end
moves on to the nextstate of the request state machine (when all preconditions are met)
# File lib/httpx/request.rb 298 def transition(nextstate) 299 case nextstate 300 when :idle 301 @body.rewind 302 @ping = @complete = false 303 @response = @drainer = nil 304 305 # request may be sent to a different connection and will be 306 # reassigned a new set of timers. 307 reset_timers(false) 308 when :headers 309 return unless @state == :idle 310 311 @started = true 312 when :body 313 return unless @state == :headers || 314 @state == :expect 315 316 if @headers.key?("expect") 317 if @informational_status && @informational_status == 100 318 # check for 100 Continue response, and deallocate the var 319 # if @informational_status == 100 320 # @response = nil 321 # end 322 else 323 return if @state == :expect # do not re-set it 324 325 nextstate = :expect 326 end 327 end 328 when :trailers 329 return unless @state == :body 330 when :done 331 return if @state == :expect 332 333 end 334 log(level: 3) { "#{@state} -> #{nextstate}" } 335 @state = nextstate 336 emit(@state, self) 337 nil 338 end
the write timeout defined for this request.
# File lib/httpx/request.rb 147 def write_timeout 148 @options.timeout[:write_timeout] 149 end