lib/protocol/rack/body.rb



# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2022, by Samuel Williams.

require_relative 'body/streaming'
require_relative 'body/enumerable'
require 'protocol/http/body/completable'

module Protocol
	module Rack
		module Body
			CONTENT_LENGTH = 'content-length'
			
			def self.wrap(env, status, headers, body, input = nil)
				# In no circumstance do we want this header propagating out:
				if length = 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:
				if body.is_a?(::Protocol::HTTP::Body::Readable)
					# Ignore.
				elsif status == 200 and body.respond_to?(:to_path)
					begin
						# Don't mangle partial responses (206)
						body = ::Protocol::HTTP::Body::File.open(body.to_path).tap do
							body.close if body.respond_to?(:close) # Close the original body.
						end
					rescue Errno::ENOENT
						# If the file is not available, ignore.
					end
				elsif body.respond_to?(:each)
					body = Body::Enumerable.wrap(body, length)
				else
					body = Body::Streaming.new(body, input)
				end
				
				if response_finished = env[RACK_RESPONSE_FINISHED] and response_finished.any?
					body = ::Protocol::HTTP::Body::Completable.new(body, completion_callback(response_finished, env, status, headers))
				end
				
				return body
			end
			
			def self.completion_callback(response_finished, env, status, headers)
				proc do |error|
					response_finished.each do |callback|
						callback.call(env, status, headers, error)
					end
				end
			end
		end
	end
end