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!
- context
- current_context?
- drain_body
- drain_error
- expects?
- headers
- inspect
- interests
- merge_headers
- options
- origin
- path
- peer_address
- persistent
- persistent?
- ping!
- ping?
- query
- read_timeout
- request_timeout
- response
- response=
- scheme
- set_context!
- set_timeout_callback
- state
- trailers
- trailers?
- transition
- uri
- verb
- write_timeout
Classes and Modules
Constants
ALLOWED_URI_SCHEMES | = | %w[https http].freeze | ||
USER_AGENT | = | "httpx.rb/#{VERSION}".freeze |
default value used for “user-agent” header, when not overridden. |
Attributes
active_timeouts | [R] | |
body | [R] |
an |
context | [R] |
the execution context (fiber) this request was sent on. |
drain_error | [R] |
Exception raised during enumerable body writes. |
headers | [R] |
an |
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. |
:body, :form, :json and :xml are all mutually exclusive, i.e. only one of them gets picked up.
# File lib/httpx/request.rb 74 def initialize(verb, uri, options, params = EMPTY_HASH) 75 @verb = verb.to_s.upcase 76 @uri = Utils.to_uri(uri) 77 78 @headers = options.headers.dup 79 merge_headers(params.delete(:headers)) if params.key?(:headers) 80 81 @headers["user-agent"] ||= USER_AGENT 82 @headers["accept"] ||= "*/*" 83 84 # forego compression in the Range request case 85 if @headers.key?("range") 86 @headers.delete("accept-encoding") 87 else 88 @headers["accept-encoding"] ||= options.supported_compression_formats 89 end 90 91 @query_params = params.delete(:params) if params.key?(:params) 92 93 @body = options.request_body_class.new(@headers, options, **params) 94 95 @options = @body.options 96 97 if @uri.relative? || @uri.host.nil? 98 origin = @options.origin 99 raise(Error, "invalid URI: #{@uri}") unless origin 100 101 base_path = @options.base_path 102 103 @uri = origin.merge("#{base_path}#{@uri}") 104 end 105 106 raise UnsupportedSchemeError, "#{@uri}: #{@uri.scheme}: unsupported URI scheme" unless ALLOWED_URI_SCHEMES.include?(@uri.scheme) 107 108 @state = :idle 109 @response = @peer_address = @context = nil 110 @ping = 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 225 def authority 226 @uri.authority 227 end
# File lib/httpx/request.rb 176 def can_buffer? 177 @state != :done 178 end
# File lib/httpx/request.rb 125 def complete!(response = @response) 126 @context = nil 127 emit(:complete, response) 128 end
checks whether the current execution context is the one where the request was created.
# File lib/httpx/request.rb 121 def current_context? 122 @context == Fiber.current 123 end
consumes and returns the next available chunk of request body that can be sent
# File lib/httpx/request.rb 255 def drain_body 256 return nil if @body.nil? 257 258 @drainer ||= @body.each 259 chunk = @drainer.next.dup 260 261 emit(:body_chunk, chunk) 262 chunk 263 rescue StopIteration 264 nil 265 rescue StandardError => e 266 @drain_error = e 267 nil 268 end
whether the request supports the 100-continue handshake and already processed the 100 response.
# File lib/httpx/request.rb 321 def expects? 322 @headers["expect"] == "100-continue" && @informational_status == 100 && !@response 323 end
:nocov:
# File lib/httpx/request.rb 271 def inspect 272 "#<#{self.class}:#{object_id} " \ 273 "#{@verb} " \ 274 "#{uri} " \ 275 "@headers=#{@headers} " \ 276 "@body=#{@body}>" 277 end
returns :r
or :w
, depending on whether the request is waiting for a response or flushing.
# File lib/httpx/request.rb 170 def interests 171 return :r if @state == :done || @state == :expect 172 173 :w 174 end
merges h
into the instance of HTTPX::Headers
of the request.
# File lib/httpx/request.rb 181 def merge_headers(h) 182 @headers = @headers.merge(h) 183 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 233 def origin 234 @uri.origin 235 end
returnns the URI path of the request uri
.
# File lib/httpx/request.rb 213 def path 214 path = uri.path.dup 215 path = +"" if path.nil? 216 path << "/" if path.empty? 217 path << "?#{query}" unless query.empty? 218 path 219 end
marks the request as having been buffered with a ping
# File lib/httpx/request.rb 136 def ping! 137 @ping = true 138 end
whether request has been buffered with a ping
# File lib/httpx/request.rb 131 def ping? 132 @ping 133 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 243 def query 244 return @query if defined?(@query) 245 246 query = [] 247 if (q = @query_params) && !q.empty? 248 query << Transcoder::Form.encode(q) 249 end 250 query << @uri.query if @uri.query 251 @query = query.join("&") 252 end
the read timeout defined for this request.
# File lib/httpx/request.rb 141 def read_timeout 142 @options.timeout[:read_timeout] 143 end
the request timeout defined for this request.
# File lib/httpx/request.rb 151 def request_timeout 152 @options.timeout[:request_timeout] 153 end
sets the response
on this request.
# File lib/httpx/request.rb 191 def response=(response) 192 return unless response 193 194 if response.is_a?(Response) && response.status < 200 195 # deal with informational responses 196 197 if response.status == 100 && @headers.key?("expect") 198 @informational_status = response.status 199 return 200 end 201 202 # 103 Early Hints advertises resources in document to browsers. 203 # not very relevant for an HTTP client, discard. 204 return if response.status >= 103 205 end 206 207 @response = response 208 209 emit(:response_started, response) 210 end
the URI scheme of the request uri
.
# File lib/httpx/request.rb 186 def scheme 187 @uri.scheme 188 end
sets the execution context for this request. the default is the current fiber.
# File lib/httpx/request.rb 116 def set_context! 117 @context ||= Fiber.current # rubocop:disable Naming/MemoizedInstanceVariableName 118 end
# File lib/httpx/request.rb 325 def set_timeout_callback(event, &callback) 326 clb = once(event, &callback) 327 328 # reset timeout callbacks when requests get rerouted to a different connection 329 once(:idle) do 330 callbacks(event).delete(clb) 331 end 332 end
returns an instance of HTTPX::Headers
containing the trailer headers
# File lib/httpx/request.rb 165 def trailers 166 @trailers ||= @options.headers_class.new 167 end
if the request contains trailer headers
# File lib/httpx/request.rb 160 def trailers? 161 defined?(@trailers) 162 end
moves on to the nextstate
of the request state machine (when all preconditions are met)
# File lib/httpx/request.rb 281 def transition(nextstate) 282 case nextstate 283 when :idle 284 @body.rewind 285 @ping = false 286 @response = nil 287 @drainer = nil 288 @active_timeouts.clear 289 when :headers 290 return unless @state == :idle 291 292 when :body 293 return unless @state == :headers || 294 @state == :expect 295 296 if @headers.key?("expect") 297 if @informational_status && @informational_status == 100 298 # check for 100 Continue response, and deallocate the var 299 # if @informational_status == 100 300 # @response = nil 301 # end 302 else 303 return if @state == :expect # do not re-set it 304 305 nextstate = :expect 306 end 307 end 308 when :trailers 309 return unless @state == :body 310 when :done 311 return if @state == :expect 312 313 end 314 log(level: 3) { "#{@state}] -> #{nextstate}" } 315 @state = nextstate 316 emit(@state, self) 317 nil 318 end
the write timeout defined for this request.
# File lib/httpx/request.rb 146 def write_timeout 147 @options.timeout[:write_timeout] 148 end