lib/httpx/transcoder/chunker.rb in httpx-0.2.1 vs lib/httpx/transcoder/chunker.rb in httpx-0.3.0

- old
+ new

@@ -2,19 +2,18 @@ require "forwardable" module HTTPX::Transcoder module Chunker - module_function + Error = Class.new(HTTPX::Error) + CRLF = "\r\n".b class Encoder extend Forwardable def_delegator :@raw, :readpartial - CRLF = "\r\n" - def initialize(body) @raw = body end def each @@ -27,9 +26,83 @@ def respond_to_missing?(meth, *args) @raw.respond_to?(meth, *args) || super end end + + class Decoder + extend Forwardable + + def_delegator :@buffer, :empty? + + def_delegator :@buffer, :<< + + def_delegator :@buffer, :clear + + def initialize(buffer, trailers = false) + @buffer = buffer + @chunk_length = nil + @chunk_buffer = "".b + @finished = false + @state = :length + @trailers = trailers + end + + def to_s + @buffer + end + + def each + loop do + case @state + when :length + index = @buffer.index(CRLF) + return unless index && index.positive? + # Read hex-length + hexlen = @buffer.slice!(0, index) + hexlen[/\h/] || raise(Error, "wrong chunk size line: #{hexlen}") + @chunk_length = hexlen.hex + # check if is last chunk + @finished = @chunk_length.zero? + nextstate(:crlf) + when :crlf + crlf_size = @finished && !@trailers ? 4 : 2 + # consume CRLF + return if @buffer.bytesize < crlf_size + raise Error, "wrong chunked encoding format" unless @buffer.start_with?(CRLF * (crlf_size / 2)) + @buffer.slice!(0, crlf_size) + if @chunk_length.nil? + nextstate(:length) + else + return if @finished + nextstate(:data) + end + when :data + @chunk_buffer << (slice = @buffer.slice!(0, @chunk_length)) + @chunk_length -= slice.bytesize + if @chunk_length.zero? + yield @chunk_buffer unless @chunk_buffer.empty? + @chunk_buffer.clear + @chunk_length = nil + nextstate(:crlf) + end + end + break if @buffer.empty? + end + end + + def finished? + @finished + end + + private + + def nextstate(state) + @state = state + end + end + + module_function def encode(chunks) Encoder.new(chunks) end end