# frozen_string_literal: true# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com># # Permission is hereby granted, free of charge, to any person obtaining a copy# of this software and associated documentation files (the "Software"), to deal# in the Software without restriction, including without limitation the rights# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell# copies of the Software, and to permit persons to whom the Software is# furnished to do so, subject to the following conditions:# # The above copyright notice and this permission notice shall be included in# all copies or substantial portions of the Software.# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN# THE SOFTWARE.require'protocol/http/body/readable'require'protocol/http/body/file'moduleFalconmoduleAdapters# Wraps the rack response body.## The `rack` body must respond to `each` and must only yield `String` values. If the body responds to `close`, it will be called after iteration. If the body is replaced by a middleware after action, the original body must be closed first, if it responds to `close`. If the body responds to `to_path`, it must return a String identifying the location of a file whose contents are identical to that produced by calling `each`; this may be used by the server as an alternative, possibly more efficient way to transport the response. The body commonly is an `Array` of strings, the application instance itself, or a `File`-like object.classOutput<::Protocol::HTTP::Body::ReadableCONTENT_LENGTH='content-length'.freeze# Wraps an array into a buffered body.# @parameter status [Integer] The response status.# @parameter headers [Protocol::HTTP::Headers] The response headers.# @parameter body [Object] The `rack` response body.defself.wrap(status,headers,body,request=nil)# In no circumstance do we want this header propagating out:iflength=headers.delete(CONTENT_LENGTH)# We don't really trust the user to provide the right length to the transport.length=Integer(length)end# If we have an Async::HTTP body, we return it directly:ifbody.is_a?(::Protocol::HTTP::Body::Readable)# warn "Returning #{body.class} as body is falcon-specific and may be removed in the future!"returnbodyend# Otherwise, we have a more typical response body:ifstatus==200andbody.respond_to?(:to_path)begin# Don't mangle partial responses (206)return::Protocol::HTTP::Body::File.open(body.to_path).tapdobody.closeifbody.respond_to?(:close)# Close the original body.endrescueErrno::ENOENT# If the file is not available, ignore.endend# If we have a streaming body, we hijack the connection:unlessbody.respond_to?(:each)returnAsync::HTTP::Body::Hijack.new(body,request&.body)endifbody.is_a?(Array)length||=body.sum(&:bytesize)returnself.new(body,length)elsereturnself.new(body,length)endend# Initialize the output wrapper.# @parameter body [Object] The rack response body.# @parameter length [Integer] The rack response length.definitialize(body,length)@length=length@body=body@chunks=nilend# The rack response body.attr:body# The content length of the rack response body.attr:length# Whether the body is empty.defempty?@length==0or(@body.respond_to?(:empty?)and@body.empty?)end# Whether the body can be read immediately.defready?body.is_a?(Array)orbody.respond_to?(:to_ary)end# Close the response body.defclose(error=nil)if@bodyand@body.respond_to?(:close)@body.closeend@body=nil@chunks=nilsuperend# Enumerate the response body.# @yields {|chunk| ...}# @parameter chunk [String]defeach(&block)@body.each(&block)ensureself.close($!)end# Read the next chunk from the response body.# @returns [String | Nil]defread@chunks||=@body.to_enum(:each)return@chunks.nextrescueStopIterationreturnnilenddefinspect"\#<#{self.class} length=#{@length.inspect} body=#{@body.class}>"endendendend