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