lib/protocol/http/body/inflate.rb



# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2019-2024, by Samuel Williams.

require "zlib"

require_relative "deflate"

module Protocol
	module HTTP
		module Body
			# A body which decompresses the contents using the DEFLATE or GZIP algorithm.
			class Inflate < ZStream
				# Create a new body which decompresses the given body using the GZIP algorithm by default.
				#
				# @parameter body [Readable] the body to wrap.
				# @parameter window_size [Integer] the window size to use for decompression.
				def self.for(body, window_size = GZIP)
					self.new(body, Zlib::Inflate.new(window_size))
				end
				
				# Read from the underlying stream and inflate it.
				#
				# @returns [String | Nil] the inflated data, or nil if the stream is finished.
				def read
					if stream = @stream
						# Read from the underlying stream and inflate it:
						while chunk = super
							@input_length += chunk.bytesize
							
							# It's possible this triggers the stream to finish.
							chunk = stream.inflate(chunk)
							
							break unless chunk&.empty?
						end
					
						if chunk
							@output_length += chunk.bytesize
						elsif !stream.closed?
							chunk = stream.finish
							@output_length += chunk.bytesize
						end
						
						# If the stream is finished, we need to close it and potentially return nil:
						if stream.finished?
							@stream = nil
							stream.close
							
							while super
								# There is data left in the stream, so we need to keep reading until it's all consumed.
							end
							
							if chunk.empty?
								return nil
							end
						end
						
						return chunk
					end
				end
			end
		end
	end
end