lib/rev/http_client.rb in rev-0.1.1 vs lib/rev/http_client.rb in rev-0.1.2

- old
+ new

@@ -144,11 +144,11 @@ @parser = HttpClientParser.new @parser_nbytes = 0 @state = :response_header - @data = '' + @data = Rev::Buffer.new @response_header = HttpResponseHeader.new @chunk_header = HttpChunkHeader.new end @@ -222,13 +222,12 @@ @connected = true send_request if @method and @uri end def on_read(data) - until @state == :finished or @state == :invalid or data.empty? - @state, data = dispatch_data(@state, data) - end + @data << data + dispatch end # # Request sending # @@ -271,149 +270,147 @@ # # Response processing # - def dispatch_data(state, data) - case state - when :response_header - parse_response_header(data) - when :chunk_header - parse_chunk_header(data) - when :chunk_body - process_chunk_body(data) - when :chunk_footer - process_chunk_footer(data) - when :response_footer - process_response_footer(data) - when :body - process_body(data) - else raise RuntimeError, "invalid state: #{@state}" + def dispatch + while case @state + when :response_header + parse_response_header + when :chunk_header + parse_chunk_header + when :chunk_body + process_chunk_body + when :chunk_footer + process_chunk_footer + when :response_footer + process_response_footer + when :body + process_body + when :finished, :invalid + break + else raise RuntimeError, "invalid state: #{@state}" + end end end - def parse_header(header, data) - @data << data - @parser_nbytes = @parser.execute(header, @data, @parser_nbytes) - return unless @parser.finished? + def parse_header(header) + return false if @data.empty? + + @parser_nbytes = @parser.execute(header, @data.to_str, @parser_nbytes) + return false unless @parser.finished? - remainder = @data.slice(@parser_nbytes, @data.size) - @data = '' + # Clear parsed data from the buffer + @data.read(@parser_nbytes) @parser.reset @parser_nbytes = 0 - - remainder + + true end - def parse_response_header(data) - data = parse_header(@response_header, data) - return :response_header, '' if data.nil? + def parse_response_header + return false unless parse_header(@response_header) unless @response_header.http_status and @response_header.http_reason on_error "no HTTP response" - return :invalid + @state = :invalid + return false end on_response_header(@response_header) if @response_header.chunked_encoding? - return :chunk_header, data + @state = :chunk_header else + @state = :body @bytes_remaining = @response_header.content_length - return :body, data end + + true end - def parse_chunk_header(data) - data = parse_header(@chunk_header, data) - return :chunk_header, '' if data.nil? + def parse_chunk_header + return false unless parse_header(@chunk_header) @bytes_remaining = @chunk_header.chunk_size @chunk_header = HttpChunkHeader.new - if @bytes_remaining > 0 - return :chunk_body, data - else - @bytes_remaining = 2 - return :response_footer, data - end + @state = @bytes_remaining > 0 ? :chunk_body : :response_footer + true end - def process_chunk_body(data) - if data.size < @bytes_remaining - @bytes_remaining -= data.size - on_body_data data - return :chunk_body, '' + def process_chunk_body + if @data.size < @bytes_remaining + @bytes_remaining -= @data.size + on_body_data @data.read + return false end - on_body_data data.slice!(0, @bytes_remaining) - @bytes_remaining = 2 - return :chunk_footer, data + on_body_data @data.read(@bytes_remaining) + @bytes_remaining = 0 + + @state = :chunk_footer + true end - def process_crlf(data) - @data << data.slice!(0, @bytes_remaining) - @bytes_remaining = 2 - @data.size - return unless @bytes_remaining == 0 + def process_chunk_footer + return false if @data.size < 2 - matches_crlf = (@data == CRLF) - @data = '' - - return matches_crlf, data - end - - def process_chunk_footer(data) - result, data = process_crlf(data) - return :chunk_footer, '' if result.nil? - - if result - return :chunk_header, data + if @data.read(2) == CRLF + @state = :chunk_header else on_error "non-CRLF chunk footer" - return :invalid + @state = :invalid end + + true end - def process_response_footer(data) - result, data = process_crlf(data) - return :response_footer, '' if result.nil? - if result - unless data.empty? + def process_response_footer + return false if @data.size < 2 + + if @data.read(2) == CRLF + if @data.empty? + on_request_complete + @state = :finished + else on_error "garbage at end of chunked response" - return :invalid + @state = :invalid end - - on_request_complete - return :finished else on_error "non-CRLF response footer" - return :invalid + @state = :invalid end + + false end - def process_body(data) + def process_body # FIXME the proper thing to do here is probably to keep reading until # the socket closes, then assume that's the end of the body, provided # the server has specified Connection: close if @bytes_remaining.nil? on_error "no content length specified" - return :invalid + @state = :invalid end - if data.size < @bytes_remaining - @bytes_remaining -= data.size - on_body_data data - return :body, '' + if @data.size < @bytes_remaining + @bytes_remaining -= @data.size + on_body_data @data.read + return false end - on_body_data data.slice!(0, @bytes_remaining) + on_body_data @data.read(@bytes_remaining) + @bytes_remaining = 0 - unless data.empty? + if @data.empty? + on_request_complete + @state = :finished + else on_error "garbage at end of body" - return :invalid + @state = :invalid end - on_request_complete - return :finished + false end end -end +end \ No newline at end of file