KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > http11 > filters > ChunkedInputFilter


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.coyote.http11.filters;
19
20 import java.io.IOException JavaDoc;
21
22 import org.apache.tomcat.util.buf.ByteChunk;
23 import org.apache.tomcat.util.buf.HexUtils;
24
25 import org.apache.coyote.InputBuffer;
26 import org.apache.coyote.Request;
27 import org.apache.coyote.http11.Constants;
28 import org.apache.coyote.http11.InputFilter;
29
30 /**
31  * Chunked input filter. Parses chunked data according to
32  * <a HREF="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1</a><br>
33  *
34  * @author Remy Maucherat
35  * @author Filip Hanik
36  */

37 public class ChunkedInputFilter implements InputFilter {
38
39
40     // -------------------------------------------------------------- Constants
41

42
43     protected static final String JavaDoc ENCODING_NAME = "chunked";
44     protected static final ByteChunk ENCODING = new ByteChunk();
45
46
47     // ----------------------------------------------------- Static Initializer
48

49
50     static {
51         ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
52     }
53
54
55     // ----------------------------------------------------- Instance Variables
56

57
58     /**
59      * Next buffer in the pipeline.
60      */

61     protected InputBuffer buffer;
62
63
64     /**
65      * Number of bytes remaining in the current chunk.
66      */

67     protected int remaining = 0;
68
69
70     /**
71      * Position in the buffer.
72      */

73     protected int pos = 0;
74
75
76     /**
77      * Last valid byte in the buffer.
78      */

79     protected int lastValid = 0;
80
81
82     /**
83      * Read bytes buffer.
84      */

85     protected byte[] buf = null;
86
87
88     /**
89      * Byte chunk used to read bytes.
90      */

91     protected ByteChunk readChunk = new ByteChunk();
92
93
94     /**
95      * Flag set to true when the end chunk has been read.
96      */

97     protected boolean endChunk = false;
98
99     /**
100      * Flag set to true if the next call to doRead() must parse a CRLF pair
101      * before doing anything else.
102      */

103     protected boolean needCRLFParse = false;
104
105     // ------------------------------------------------------------- Properties
106

107
108     // ---------------------------------------------------- InputBuffer Methods
109

110
111     /**
112      * Read bytes.
113      *
114      * @return If the filter does request length control, this value is
115      * significant; it should be the number of bytes consumed from the buffer,
116      * up until the end of the current request body, or the buffer length,
117      * whichever is greater. If the filter does not do request body length
118      * control, the returned value should be -1.
119      */

120     public int doRead(ByteChunk chunk, Request req)
121         throws IOException JavaDoc {
122
123         if (endChunk)
124             return -1;
125
126         if(needCRLFParse) {
127             needCRLFParse = false;
128             parseCRLF();
129         }
130
131         if (remaining <= 0) {
132             if (!parseChunkHeader()) {
133                 throw new IOException JavaDoc("Invalid chunk header");
134             }
135             if (endChunk) {
136                 parseEndChunk();
137                 return -1;
138             }
139         }
140
141         int result = 0;
142
143         if (pos >= lastValid) {
144             readBytes();
145         }
146
147         if (remaining > (lastValid - pos)) {
148             result = lastValid - pos;
149             remaining = remaining - result;
150             chunk.setBytes(buf, pos, result);
151             pos = lastValid;
152         } else {
153             result = remaining;
154             chunk.setBytes(buf, pos, remaining);
155             pos = pos + remaining;
156             remaining = 0;
157             needCRLFParse = true;
158         }
159
160         return result;
161
162     }
163
164
165     // ---------------------------------------------------- InputFilter Methods
166

167
168     /**
169      * Read the content length from the request.
170      */

171     public void setRequest(Request request) {
172     }
173
174
175     /**
176      * End the current request.
177      */

178     public long end()
179         throws IOException JavaDoc {
180
181         // Consume extra bytes : parse the stream until the end chunk is found
182
while (doRead(readChunk, null) >= 0) {
183         }
184
185         // Return the number of extra bytes which were consumed
186
return (lastValid - pos);
187
188     }
189
190
191     /**
192      * Set the next buffer in the filter pipeline.
193      */

194     public void setBuffer(InputBuffer buffer) {
195         this.buffer = buffer;
196     }
197
198
199     /**
200      * Make the filter ready to process the next request.
201      */

202     public void recycle() {
203         remaining = 0;
204         pos = 0;
205         lastValid = 0;
206         endChunk = false;
207     }
208
209
210     /**
211      * Return the name of the associated encoding; Here, the value is
212      * "identity".
213      */

214     public ByteChunk getEncodingName() {
215         return ENCODING;
216     }
217
218
219     // ------------------------------------------------------ Protected Methods
220

221
222     /**
223      * Read bytes from the previous buffer.
224      */

225     protected int readBytes()
226         throws IOException JavaDoc {
227
228         int nRead = buffer.doRead(readChunk, null);
229         pos = readChunk.getStart();
230         lastValid = pos + nRead;
231         buf = readChunk.getBytes();
232
233         return nRead;
234
235     }
236
237
238     /**
239      * Parse the header of a chunk.
240      * A chunk header can look like
241      * A10CRLF
242      * F23;chunk-extension to be ignoredCRLF
243      * The letters before CRLF but after the trailer mark, must be valid hex digits,
244      * we should not parse F23IAMGONNAMESSTHISUP34CRLF as a valid header
245      * according to spec
246      */

247     protected boolean parseChunkHeader()
248         throws IOException JavaDoc {
249
250         int result = 0;
251         boolean eol = false;
252         boolean readDigit = false;
253         boolean trailer = false;
254
255         while (!eol) {
256
257             if (pos >= lastValid) {
258                 if (readBytes() <= 0)
259                     return false;
260             }
261
262             if (buf[pos] == Constants.CR) {
263             } else if (buf[pos] == Constants.LF) {
264                 eol = true;
265             } else if (buf[pos] == Constants.SEMI_COLON) {
266                 trailer = true;
267             } else if (!trailer) {
268                 //don't read data after the trailer
269
if (HexUtils.DEC[buf[pos]] != -1) {
270                     readDigit = true;
271                     result *= 16;
272                     result += HexUtils.DEC[buf[pos]];
273                 } else {
274                     //we shouldn't allow invalid, non hex characters
275
//in the chunked header
276
return false;
277                 }
278             }
279
280             pos++;
281
282         }
283
284         if (!readDigit)
285             return false;
286
287         if (result == 0)
288             endChunk = true;
289
290         remaining = result;
291         if (remaining < 0)
292             return false;
293
294         return true;
295
296     }
297
298
299     /**
300      * Parse CRLF at end of chunk.
301      */

302     protected boolean parseCRLF()
303         throws IOException JavaDoc {
304
305         boolean eol = false;
306
307         while (!eol) {
308
309             if (pos >= lastValid) {
310                 if (readBytes() <= 0)
311                     throw new IOException JavaDoc("Invalid CRLF");
312             }
313
314             if (buf[pos] == Constants.CR) {
315             } else if (buf[pos] == Constants.LF) {
316                 eol = true;
317             } else {
318                 throw new IOException JavaDoc("Invalid CRLF");
319             }
320
321             pos++;
322
323         }
324
325         return true;
326
327     }
328
329
330     /**
331      * Parse end chunk data.
332      * FIXME: Handle trailers
333      */

334     protected boolean parseEndChunk()
335         throws IOException JavaDoc {
336
337         return parseCRLF(); // FIXME
338

339     }
340
341
342 }
343
Popular Tags