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 = 109 @informational_status = @on_response_arrived = nil 110 @ping = @started = 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 240 def authority 241 @uri.authority 242 end
# File lib/httpx/request.rb 178 def can_buffer? 179 @state != :done 180 end
# File lib/httpx/request.rb 123 def complete!(response = @response) 124 emit(:complete, response) 125 end
consumes and returns the next available chunk of request body that can be sent
# File lib/httpx/request.rb 270 def drain_body 271 return nil if @body.nil? 272 273 @drainer ||= @body.each 274 chunk = @drainer.next.dup 275 276 emit(:body_chunk, chunk) 277 chunk 278 rescue StopIteration 279 nil 280 rescue StandardError => e 281 @drain_error = e 282 nil 283 end
# File lib/httpx/request.rb 359 def emit_response(response) 360 emit(:response, response) 361 362 return unless @on_response_arrived 363 364 @on_response_arrived.call 365 end
whether the request supports the 100-continue handshake and already processed the 100 response.
# File lib/httpx/request.rb 336 def expects? 337 @headers["expect"] == "100-continue" && @informational_status == 100 && !@response 338 end
# File lib/httpx/request.rb 349 def handle_error(error) 350 if (connection = @connection) 351 connection.on_error(error, self) 352 else 353 response = ErrorResponse.new(self, error) 354 self.response = response 355 emit_response(response) 356 end 357 end
dupped initialization
# 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
:nocov:
# File lib/httpx/request.rb 286 def inspect 287 "#<#{self.class}:#{object_id} " \ 288 "#{@verb} " \ 289 "#{uri} " \ 290 "@headers=#{@headers} " \ 291 "@body=#{@body}>" 292 end
returns :r or :w, depending on whether the request is waiting for a response or flushing.
# File lib/httpx/request.rb 172 def interests 173 return :r if @state == :done || @state == :expect 174 175 :w 176 end
merges h into the instance of HTTPX::Headers of the request.
# 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
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 248 def origin 249 @uri.origin 250 end
returnns the URI path of the request uri.
# 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
marks the request as having been buffered with a ping
# File lib/httpx/request.rb 133 def ping! 134 @ping = true 135 end
whether request has been buffered with a ping
# File lib/httpx/request.rb 128 def ping? 129 @ping 130 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 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
the read timeout defined for this request.
# File lib/httpx/request.rb 138 def read_timeout 139 @options.timeout[:read_timeout] 140 end
the request timeout defined for this request.
# File lib/httpx/request.rb 148 def request_timeout 149 @options.timeout[:request_timeout] 150 end
sets the response on this request.
# 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
the URI scheme of the request uri.
# File lib/httpx/request.rb 195 def scheme 196 @uri.scheme 197 end
# File lib/httpx/request.rb 340 def set_timeout_callback(event, &callback) 341 clb = once(event, &callback) 342 343 # reset timeout callbacks when requests get rerouted to a different connection 344 once(:idle) do 345 callbacks(event).delete(clb) 346 end 347 end
the total request timeout defined for this request.
# File lib/httpx/request.rb 153 def total_request_timeout 154 @options.timeout[:total_request_timeout] 155 end
returns an instance of HTTPX::Headers containing the trailer headers
# File lib/httpx/request.rb 167 def trailers 168 @trailers ||= @options.headers_class.new 169 end
if the request contains trailer headers
# File lib/httpx/request.rb 162 def trailers? 163 defined?(@trailers) 164 end
moves on to the nextstate of the request state machine (when all preconditions are met)
# File lib/httpx/request.rb 296 def transition(nextstate) 297 case nextstate 298 when :idle 299 @body.rewind 300 @ping = false 301 @response = @drainer = nil 302 @active_timeouts.clear 303 when :headers 304 return unless @state == :idle 305 306 @started = true 307 when :body 308 return unless @state == :headers || 309 @state == :expect 310 311 if @headers.key?("expect") 312 if @informational_status && @informational_status == 100 313 # check for 100 Continue response, and deallocate the var 314 # if @informational_status == 100 315 # @response = nil 316 # end 317 else 318 return if @state == :expect # do not re-set it 319 320 nextstate = :expect 321 end 322 end 323 when :trailers 324 return unless @state == :body 325 when :done 326 return if @state == :expect 327 328 end 329 log(level: 3) { "#{@state}] -> #{nextstate}" } 330 @state = nextstate 331 emit(@state, self) 332 nil 333 end
the write timeout defined for this request.
# File lib/httpx/request.rb 143 def write_timeout 144 @options.timeout[:write_timeout] 145 end