|
ferencd@0
|
1 require 'net/http/server/stream'
|
|
ferencd@0
|
2
|
|
ferencd@0
|
3 require 'net/protocol'
|
|
ferencd@0
|
4 require 'stringio'
|
|
ferencd@0
|
5
|
|
ferencd@0
|
6 module Net
|
|
ferencd@0
|
7 class HTTP < Protocol
|
|
ferencd@0
|
8 module Server
|
|
ferencd@0
|
9 #
|
|
ferencd@0
|
10 # Handles reading and writing to Chunked Transfer-Encoded streams.
|
|
ferencd@0
|
11 #
|
|
ferencd@0
|
12 # @since 0.2.0
|
|
ferencd@0
|
13 #
|
|
ferencd@0
|
14 class ChunkedStream < Stream
|
|
ferencd@0
|
15
|
|
ferencd@0
|
16 #
|
|
ferencd@0
|
17 # Initializes the chuked stream.
|
|
ferencd@0
|
18 #
|
|
ferencd@0
|
19 # @param [#read, #write, #flush] socket
|
|
ferencd@0
|
20 # The socket to read from and write to.
|
|
ferencd@0
|
21 #
|
|
ferencd@0
|
22 def initialize(socket)
|
|
ferencd@0
|
23 super(socket)
|
|
ferencd@0
|
24
|
|
ferencd@0
|
25 @buffer = ''
|
|
ferencd@0
|
26 end
|
|
ferencd@0
|
27
|
|
ferencd@0
|
28 #
|
|
ferencd@0
|
29 # Reads a chunk from the stream.
|
|
ferencd@0
|
30 #
|
|
ferencd@0
|
31 # @param [Integer] length
|
|
ferencd@0
|
32 #
|
|
ferencd@0
|
33 # @param [#<<] buffer
|
|
ferencd@0
|
34 # The optional buffer to append the data to.
|
|
ferencd@0
|
35 #
|
|
ferencd@0
|
36 # @return [String, nil]
|
|
ferencd@0
|
37 # A chunk from the stream.
|
|
ferencd@0
|
38 #
|
|
ferencd@0
|
39 # @raise [ArgumentError]
|
|
ferencd@0
|
40 # The buffer did not respond to `#<<`.
|
|
ferencd@0
|
41 #
|
|
ferencd@0
|
42 # @since 0.2.0
|
|
ferencd@0
|
43 #
|
|
ferencd@0
|
44 def read(length=4096,buffer='')
|
|
ferencd@0
|
45 unless buffer.respond_to?(:<<)
|
|
ferencd@0
|
46 raise(ArgumentError,"buffer must respond to #<<")
|
|
ferencd@0
|
47 end
|
|
ferencd@0
|
48
|
|
ferencd@0
|
49 until @buffer.length >= length
|
|
ferencd@0
|
50 length_line = @socket.readline("\r\n").chomp
|
|
ferencd@0
|
51 chunk_length = length_line.split(';',2).first.to_i(16)
|
|
ferencd@0
|
52
|
|
ferencd@0
|
53 # read the chunk
|
|
ferencd@0
|
54 @buffer << @socket.read(chunk_length)
|
|
ferencd@0
|
55
|
|
ferencd@0
|
56 # chomp the terminating CRLF
|
|
ferencd@0
|
57 @socket.read(2)
|
|
ferencd@0
|
58
|
|
ferencd@0
|
59 # end-of-stream
|
|
ferencd@0
|
60 break if chunk_length == 0
|
|
ferencd@0
|
61 end
|
|
ferencd@0
|
62
|
|
ferencd@0
|
63 # clear the buffer before appending
|
|
ferencd@0
|
64 buffer.replace('')
|
|
ferencd@0
|
65
|
|
ferencd@0
|
66 unless @buffer.empty?
|
|
ferencd@0
|
67 # empty a slice of the buffer
|
|
ferencd@0
|
68 buffer << @buffer.slice!(0,length)
|
|
ferencd@0
|
69 return buffer
|
|
ferencd@0
|
70 end
|
|
ferencd@0
|
71 end
|
|
ferencd@0
|
72
|
|
ferencd@0
|
73 #
|
|
ferencd@0
|
74 # Writes data to the chunked stream.
|
|
ferencd@0
|
75 #
|
|
ferencd@0
|
76 # @param [String] data
|
|
ferencd@0
|
77 # The data to write to the stream.
|
|
ferencd@0
|
78 #
|
|
ferencd@0
|
79 # @return [Integer]
|
|
ferencd@0
|
80 # The length of the data written.
|
|
ferencd@0
|
81 #
|
|
ferencd@0
|
82 # @since 0.2.0
|
|
ferencd@0
|
83 #
|
|
ferencd@0
|
84 def write(data)
|
|
ferencd@0
|
85 length = data.length
|
|
ferencd@0
|
86
|
|
ferencd@0
|
87 # do not write empty chunks
|
|
ferencd@0
|
88 unless length == 0
|
|
ferencd@0
|
89 # write the chunk length
|
|
ferencd@0
|
90 @socket.write("%X\r\n" % length)
|
|
ferencd@0
|
91
|
|
ferencd@0
|
92 # write the data
|
|
ferencd@0
|
93 @socket.write(data)
|
|
ferencd@0
|
94 @socket.write("\r\n")
|
|
ferencd@0
|
95 @socket.flush
|
|
ferencd@0
|
96 end
|
|
ferencd@0
|
97
|
|
ferencd@0
|
98 return length
|
|
ferencd@0
|
99 end
|
|
ferencd@0
|
100
|
|
ferencd@0
|
101 #
|
|
ferencd@0
|
102 # Closes the chunked stream.
|
|
ferencd@0
|
103 #
|
|
ferencd@0
|
104 # @since 0.2.0
|
|
ferencd@0
|
105 #
|
|
ferencd@0
|
106 def close
|
|
ferencd@0
|
107 # last chunk
|
|
ferencd@0
|
108 @socket.write("0\r\n\r\n")
|
|
ferencd@0
|
109 @socket.flush
|
|
ferencd@0
|
110 end
|
|
ferencd@0
|
111
|
|
ferencd@0
|
112 end
|
|
ferencd@0
|
113 end
|
|
ferencd@0
|
114 end
|
|
ferencd@0
|
115 end
|