diff --git a/lib/http/request.rb b/lib/http/request.rb index 9d74c86d..66e11b8d 100644 --- a/lib/http/request.rb +++ b/lib/http/request.rb @@ -86,7 +86,8 @@ def initialize(opts) raise(UnsupportedSchemeError, "unknown scheme: #{scheme}") unless SCHEMES.include?(@scheme) @proxy = opts[:proxy] || {} - @body = request_body(opts[:body], opts) + @body = Request::Body.new(opts[:body]) + @deflate = opts[:auto_deflate] @version = opts[:version] || "1.1" @headers = HTTP::Headers.coerce(opts[:headers] || {}) @@ -100,18 +101,20 @@ def redirect(uri, verb = @verb) headers.delete(Headers::HOST) self.class.new( - :verb => verb, - :uri => @uri.join(uri), - :headers => headers, - :proxy => proxy, - :body => body, - :version => version + :verb => verb, + :uri => @uri.join(uri), + :headers => headers, + :proxy => proxy, + :body => body.source, + :auto_deflate => @deflate, + :version => version ) end # Stream the request to a socket def stream(socket) include_proxy_headers if using_proxy? && !@uri.https? + body = @deflate ? @deflate.deflated_body(self.body) : self.body Request::Writer.new(socket, body, headers, headline).stream end @@ -186,13 +189,6 @@ def socket_port private - # Transforms body to an object suitable for streaming. - def request_body(body, opts) - body = Request::Body.new(body) unless body.is_a?(Request::Body) - body = opts[:auto_deflate].deflated_body(body) if opts[:auto_deflate] - body - end - # @!attribute [r] host # @return [String] def_delegator :@uri, :host diff --git a/lib/http/request/body.rb b/lib/http/request/body.rb index cef79b5d..4c5379de 100644 --- a/lib/http/request/body.rb +++ b/lib/http/request/body.rb @@ -3,25 +3,27 @@ module HTTP class Request class Body - def initialize(body) - @body = body + attr_reader :source - validate_body_type! + def initialize(source) + @source = source + + validate_source_type! end # Returns size which should be used for the "Content-Length" header. # # @return [Integer] def size - if @body.is_a?(String) - @body.bytesize - elsif @body.respond_to?(:read) - raise RequestError, "IO object must respond to #size" unless @body.respond_to?(:size) - @body.size - elsif @body.nil? + if @source.is_a?(String) + @source.bytesize + elsif @source.respond_to?(:read) + raise RequestError, "IO object must respond to #size" unless @source.respond_to?(:size) + @source.size + elsif @source.nil? 0 else - raise RequestError, "cannot determine size of body: #{@body.inspect}" + raise RequestError, "cannot determine size of body: #{@source.inspect}" end end @@ -29,24 +31,29 @@ def size # # @yieldparam [String] def each(&block) - if @body.is_a?(String) - yield @body - elsif @body.respond_to?(:read) - IO.copy_stream(@body, ProcIO.new(block)) - elsif @body.is_a?(Enumerable) - @body.each(&block) + if @source.is_a?(String) + yield @source + elsif @source.respond_to?(:read) + IO.copy_stream(@source, ProcIO.new(block)) + elsif @source.is_a?(Enumerable) + @source.each(&block) end end + # Request bodies are equivalent when they have the same source. + def ==(other) + self.class == other.class && self.source == other.source # rubocop:disable Style/RedundantSelf + end + private - def validate_body_type! - return if @body.is_a?(String) - return if @body.respond_to?(:read) - return if @body.is_a?(Enumerable) - return if @body.nil? + def validate_source_type! + return if @source.is_a?(String) + return if @source.respond_to?(:read) + return if @source.is_a?(Enumerable) + return if @source.nil? - raise RequestError, "body of wrong type: #{@body.class}" + raise RequestError, "body of wrong type: #{@source.class}" end # This class provides a "writable IO" wrapper around a proc object, with diff --git a/spec/lib/http/request/body_spec.rb b/spec/lib/http/request/body_spec.rb index 020e34b4..3616ba29 100644 --- a/spec/lib/http/request/body_spec.rb +++ b/spec/lib/http/request/body_spec.rb @@ -46,6 +46,12 @@ end end + describe "#source" do + it "returns the original object" do + expect(subject.source).to eq "" + end + end + describe "#size" do context "when body is nil" do let(:body) { nil } @@ -135,4 +141,33 @@ end end end + + describe "#==" do + context "when sources are equivalent" do + let(:body1) { HTTP::Request::Body.new("content") } + let(:body2) { HTTP::Request::Body.new("content") } + + it "returns true" do + expect(body1).to eq body2 + end + end + + context "when sources are not equivalent" do + let(:body1) { HTTP::Request::Body.new("content") } + let(:body2) { HTTP::Request::Body.new(nil) } + + it "returns false" do + expect(body1).not_to eq body2 + end + end + + context "when objects are not of the same class" do + let(:body1) { HTTP::Request::Body.new("content") } + let(:body2) { "content" } + + it "returns false" do + expect(body1).not_to eq body2 + end + end + end end