ferencd@0: require 'net/http/server/stream' ferencd@0: ferencd@0: require 'net/protocol' ferencd@0: require 'stringio' ferencd@0: ferencd@0: module Net ferencd@0: class HTTP < Protocol ferencd@0: module Server ferencd@0: # ferencd@0: # Handles reading and writing to Chunked Transfer-Encoded streams. ferencd@0: # ferencd@0: # @since 0.2.0 ferencd@0: # ferencd@0: class ChunkedStream < Stream ferencd@0: ferencd@0: # ferencd@0: # Initializes the chuked stream. ferencd@0: # ferencd@0: # @param [#read, #write, #flush] socket ferencd@0: # The socket to read from and write to. ferencd@0: # ferencd@0: def initialize(socket) ferencd@0: super(socket) ferencd@0: ferencd@0: @buffer = '' ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Reads a chunk from the stream. ferencd@0: # ferencd@0: # @param [Integer] length ferencd@0: # ferencd@0: # @param [#<<] buffer ferencd@0: # The optional buffer to append the data to. ferencd@0: # ferencd@0: # @return [String, nil] ferencd@0: # A chunk from the stream. ferencd@0: # ferencd@0: # @raise [ArgumentError] ferencd@0: # The buffer did not respond to `#<<`. ferencd@0: # ferencd@0: # @since 0.2.0 ferencd@0: # ferencd@0: def read(length=4096,buffer='') ferencd@0: unless buffer.respond_to?(:<<) ferencd@0: raise(ArgumentError,"buffer must respond to #<<") ferencd@0: end ferencd@0: ferencd@0: until @buffer.length >= length ferencd@0: length_line = @socket.readline("\r\n").chomp ferencd@0: chunk_length = length_line.split(';',2).first.to_i(16) ferencd@0: ferencd@0: # read the chunk ferencd@0: @buffer << @socket.read(chunk_length) ferencd@0: ferencd@0: # chomp the terminating CRLF ferencd@0: @socket.read(2) ferencd@0: ferencd@0: # end-of-stream ferencd@0: break if chunk_length == 0 ferencd@0: end ferencd@0: ferencd@0: # clear the buffer before appending ferencd@0: buffer.replace('') ferencd@0: ferencd@0: unless @buffer.empty? ferencd@0: # empty a slice of the buffer ferencd@0: buffer << @buffer.slice!(0,length) ferencd@0: return buffer ferencd@0: end ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Writes data to the chunked stream. ferencd@0: # ferencd@0: # @param [String] data ferencd@0: # The data to write to the stream. ferencd@0: # ferencd@0: # @return [Integer] ferencd@0: # The length of the data written. ferencd@0: # ferencd@0: # @since 0.2.0 ferencd@0: # ferencd@0: def write(data) ferencd@0: length = data.length ferencd@0: ferencd@0: # do not write empty chunks ferencd@0: unless length == 0 ferencd@0: # write the chunk length ferencd@0: @socket.write("%X\r\n" % length) ferencd@0: ferencd@0: # write the data ferencd@0: @socket.write(data) ferencd@0: @socket.write("\r\n") ferencd@0: @socket.flush ferencd@0: end ferencd@0: ferencd@0: return length ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Closes the chunked stream. ferencd@0: # ferencd@0: # @since 0.2.0 ferencd@0: # ferencd@0: def close ferencd@0: # last chunk ferencd@0: @socket.write("0\r\n\r\n") ferencd@0: @socket.flush ferencd@0: end ferencd@0: ferencd@0: end ferencd@0: end ferencd@0: end ferencd@0: end