lib/httpx/request.rb in httpx-1.0.2 vs lib/httpx/request.rb in httpx-1.1.0
- old
+ new
@@ -2,24 +2,54 @@
require "delegate"
require "forwardable"
module HTTPX
+ # 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.
class Request
extend Forwardable
include Callbacks
using URIExtensions
+ # default value used for "user-agent" header, when not overridden.
USER_AGENT = "httpx.rb/#{VERSION}"
- attr_reader :verb, :uri, :headers, :body, :state, :options, :response
+ # the upcased string HTTP verb for this request.
+ attr_reader :verb
- # Exception raised during enumerable body writes
+ # the absolute URI object for this request.
+ attr_reader :uri
+
+ # an HTTPX::Headers object containing the request HTTP headers.
+ attr_reader :headers
+
+ # an HTTPX::Request::Body object containing the request body payload (or +nil+, whenn there is none).
+ attr_reader :body
+
+ # a symbol describing which frame is currently being flushed.
+ attr_reader :state
+
+ # an HTTPX::Options object containing request options.
+ attr_reader :options
+
+ # the corresponding HTTPX::Response object, when there is one.
+ attr_reader :response
+
+ # Exception raised during enumerable body writes.
attr_reader :drain_error
+ # The IP address from the peer server.
+ attr_accessor :peer_address
+
+ attr_writer :persistent
+
+ # will be +true+ when request body has been completely flushed.
def_delegator :@body, :empty?
+ # initializes the instance with the given +verb+, an absolute or relative +uri+, and the
+ # request options.
def initialize(verb, uri, options = {})
@verb = verb.to_s.upcase
@options = Options.new(options)
@uri = Utils.to_uri(uri)
if @uri.relative?
@@ -35,46 +65,59 @@
@headers["user-agent"] ||= USER_AGENT
@headers["accept"] ||= "*/*"
@body = @options.request_body_class.new(@headers, @options)
@state = :idle
+ @response = nil
+ @peer_address = nil
+ @persistent = @options.persistent
end
+ # the read timeout defied for this requet.
def read_timeout
@options.timeout[:read_timeout]
end
+ # the write timeout defied for this requet.
def write_timeout
@options.timeout[:write_timeout]
end
+ # the request timeout defied for this requet.
def request_timeout
@options.timeout[:request_timeout]
end
+ def persistent?
+ @persistent
+ end
+
def trailers?
defined?(@trailers)
end
def trailers
@trailers ||= @options.headers_class.new
end
+ # returns +:r+ or +:w+, depending on whether the request is waiting for a response or flushing.
def interests
return :r if @state == :done || @state == :expect
:w
end
def merge_headers(h)
@headers = @headers.merge(h)
end
+ # the URI scheme of the request +uri+.
def scheme
@uri.scheme
end
+ # sets the +response+ on this request.
def response=(response)
return unless response
if response.is_a?(Response) && response.status == 100 && @headers.key?("expect")
@informational_status = response.status
@@ -83,28 +126,41 @@
@response = response
emit(:response_started, response)
end
+ # returnns the URI path of the request +uri+.
def path
path = uri.path.dup
path = +"" if path.nil?
path << "/" if path.empty?
path << "?#{query}" unless query.empty?
path
end
- # https://bugs.ruby-lang.org/issues/15278
+ # 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"
def authority
@uri.authority
end
- # https://bugs.ruby-lang.org/issues/15278
+ # 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"
def origin
@uri.origin
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"
def query
return @query if defined?(@query)
query = []
if (q = @options.params)
@@ -112,10 +168,11 @@
end
query << @uri.query if @uri.query
@query = query.join("&")
end
+ # consumes and returns the next available chunk of request body that can be sent
def drain_body
return nil if @body.nil?
@drainer ||= @body.each
chunk = @drainer.next.dup
@@ -137,10 +194,11 @@
"@headers=#{@headers} " \
"@body=#{@body}>"
end
# :nocov:
+ # moves on to the +nextstate+ of the request state machine (when all preconditions are met)
def transition(nextstate)
case nextstate
when :idle
@body.rewind
@response = nil
@@ -171,9 +229,10 @@
@state = nextstate
emit(@state, self)
nil
end
+ # whether the request supports the 100-continue handshake and already processed the 100 response.
def expects?
@headers["expect"] == "100-continue" && @informational_status == 100 && !@response
end
end
end