test_upload: extra CRLF is needed
[unicorn.git] / lib / unicorn / chunked_reader.rb
blobb813da61e1df3055eca550b8fcd0d6333d048ff9
1 # Copyright (c) 2009 Eric Wong
2 # You can redistribute it and/or modify it under the same terms as Ruby.
4 require 'unicorn'
5 require 'unicorn_http'
7 module Unicorn
8   class ChunkedReader
10     def initialize(env, input, buf)
11       @env, @input, @buf = env, input, buf
12       @chunk_left = 0
13       parse_chunk_header
14     end
16     def readpartial(max, buf = Z.dup)
17       while @input && @chunk_left <= 0 && ! parse_chunk_header
18         @buf << @input.readpartial(Const::CHUNK_SIZE, buf)
19       end
21       if @input
22         begin
23           @buf << @input.read_nonblock(Const::CHUNK_SIZE, buf)
24         rescue Errno::EAGAIN, Errno::EINTR
25         end
26       end
28       max = @chunk_left if max > @chunk_left
29       buf.replace(last_block(max) || Z)
30       @chunk_left -= buf.size
31       (0 == buf.size && @input.nil?) and raise EOFError
32       buf
33     end
35   private
37     def last_block(max = nil)
38       rv = @buf
39       if max && rv && max < rv.size
40         @buf = rv[max - rv.size, rv.size - max]
41         return rv[0, max]
42       end
43       @buf = Z.dup
44       rv
45     end
47     def parse_chunk_header
48       buf = @buf
49       # ignoring chunk-extension info for now, I haven't seen any use for it
50       # (or any users, and TE:chunked sent by clients is rare already)
51       # if there was not enough data in buffer to parse length of the chunk
52       # then just return
53       if buf.sub!(/\A(?:\r\n)?([a-fA-F0-9]{1,8})[^\r]*?\r\n/, Z)
54         @chunk_left = $1.to_i(16)
55         if 0 == @chunk_left # EOF
56           buf.sub!(/\A\r\n(?:\r\n)?/, Z) # cleanup for future requests
57           if trailer = @env[Const::HTTP_TRAILER]
58             tp = TrailerParser.new(trailer)
59             while ! tp.execute!(@env, buf)
60               buf << @input.readpartial(Const::CHUNK_SIZE)
61             end
62           end
63           @input = nil
64         end
65         return @chunk_left
66       end
68       buf.size > 256 and
69           raise HttpParserError,
70                 "malformed chunk, chunk-length not found in buffer: " \
71                 "#{buf.inspect}"
72       nil
73     end
75   end
77 end