132
|
1 /*******************************************************************************
|
|
2
|
|
3 copyright: Copyright (c) Nov 2007 Kris Bell. All rights reserved
|
|
4
|
|
5 license: BSD style: $(LICENSE)
|
|
6
|
|
7 version: Nov 2007: Initial release
|
|
8
|
|
9 author: Kris
|
|
10
|
|
11 Support for HTTP chunked I/O.
|
|
12
|
|
13 See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
|
|
14
|
|
15 *******************************************************************************/
|
|
16
|
|
17 module tango.net.http.ChunkStream;
|
|
18
|
|
19 private import tango.io.Buffer,
|
|
20 tango.io.Conduit;
|
|
21
|
|
22 private import tango.text.stream.LineIterator;
|
|
23
|
|
24 private import Integer = tango.text.convert.Integer;
|
|
25
|
|
26 /*******************************************************************************
|
|
27
|
|
28 Prefix each block of data with its length (in hex digits) and add
|
|
29 appropriate \r\n sequences. To write trailing headers you'll need
|
|
30 to step around this stream (otherwise those headers will be chunk
|
|
31 stamped also: use this.host or this.buffer to obtain the upstream
|
|
32 sibling)
|
|
33
|
|
34 *******************************************************************************/
|
|
35
|
|
36 class ChunkOutput : OutputFilter, Buffered
|
|
37 {
|
|
38 private IBuffer output;
|
|
39
|
|
40 /***********************************************************************
|
|
41
|
|
42 Use a buffer belonging to our sibling, if one is available
|
|
43
|
|
44 ***********************************************************************/
|
|
45
|
|
46 this (OutputStream stream)
|
|
47 {
|
|
48 super (output = Buffer.share(stream));
|
|
49 }
|
|
50
|
|
51 /***********************************************************************
|
|
52
|
|
53 Buffered interface
|
|
54
|
|
55 ***********************************************************************/
|
|
56
|
|
57 IBuffer buffer ()
|
|
58 {
|
|
59 return output;
|
|
60 }
|
|
61
|
|
62 /***********************************************************************
|
|
63
|
|
64 Write a chunk to the output, prefixed and postfixed in a
|
|
65 manner consistent with the HTTP chunked transfer coding
|
|
66
|
|
67 ***********************************************************************/
|
|
68
|
|
69 final override uint write (void[] src)
|
|
70 {
|
|
71 char[8] tmp = void;
|
|
72
|
|
73 output.append (Integer.format (tmp, src.length, Integer.Style.Hex))
|
|
74 .append ("\r\n")
|
|
75 .append (src)
|
|
76 .append ("\r\n");
|
|
77 return src.length;
|
|
78 }
|
|
79 }
|
|
80
|
|
81
|
|
82 /*******************************************************************************
|
|
83
|
|
84 Parse hex digits, and use the resultant size to modulate requests
|
|
85 for incoming data. A chunk size of 0 terminates the stream, so to
|
|
86 read any trailing headers you'll need to reach into the upstream
|
|
87 sibling instead (this.host or this.buffer, for example).
|
|
88
|
|
89 *******************************************************************************/
|
|
90
|
|
91 class ChunkInput : LineIterator!(char)
|
|
92 {
|
|
93 private uint available;
|
|
94
|
|
95 /***********************************************************************
|
|
96
|
|
97 Prime the available chunk size by reading and parsing the
|
|
98 first available line
|
|
99
|
|
100 ***********************************************************************/
|
|
101
|
|
102 this (InputStream stream)
|
|
103 {
|
|
104 super (stream);
|
|
105 available = nextChunk;
|
|
106 }
|
|
107
|
|
108 /***********************************************************************
|
|
109
|
|
110 Read and parse another chunk size
|
|
111
|
|
112 ***********************************************************************/
|
|
113
|
|
114 private final uint nextChunk ()
|
|
115 {
|
|
116 char[] tmp;
|
|
117
|
|
118 if ((tmp = super.next).ptr)
|
|
119 return cast(uint) Integer.parse (tmp, 16);
|
|
120 return 0;
|
|
121 }
|
|
122
|
|
123 /***********************************************************************
|
|
124
|
|
125 Read content based on a previously parsed chunk size
|
|
126
|
|
127 ***********************************************************************/
|
|
128
|
|
129 final override uint read (void[] dst)
|
|
130 {
|
|
131 if (available is 0)
|
|
132 return IConduit.Eof;
|
|
133
|
|
134 auto size = dst.length > available ? available : dst.length;
|
|
135 auto read = super.read (dst [0 .. size]);
|
|
136
|
|
137 // check for next chunk header
|
|
138 if (read != IConduit.Eof && (available -= read) is 0)
|
|
139 {
|
|
140 // consume trailing \r\n
|
|
141 super.buffer.skip (2);
|
|
142 available = nextChunk ();
|
|
143 }
|
|
144
|
|
145 return read;
|
|
146 }
|
|
147 }
|
|
148
|
|
149
|
|
150 /*******************************************************************************
|
|
151
|
|
152 *******************************************************************************/
|
|
153
|
|
154 debug (ChunkStream)
|
|
155 {
|
|
156 import tango.io.Console;
|
|
157
|
|
158 void main()
|
|
159 {
|
|
160 auto buf = new Buffer(20);
|
|
161 auto chunk = new ChunkOutput (buf);
|
|
162 chunk.write ("hello world");
|
|
163 auto input = new ChunkInput (buf);
|
|
164 Cout.stream.copy (input);
|
|
165 }
|
|
166 }
|