KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > matuschek > util > ChunkedInputStream


1 package net.matuschek.util;
2
3 import java.io.FilterInputStream JavaDoc;
4 import java.io.IOException JavaDoc;
5 import java.io.InputStream JavaDoc;
6 import java.util.Enumeration JavaDoc;
7 import java.util.Vector JavaDoc;
8 // ChunkedInputStream - an InputStream that implements HTTP/1.1 chunking
9
//
10
// Copyright (C) 1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
11
//
12
// Redistribution and use in source and binary forms, with or without
13
// modification, are permitted provided that the following conditions
14
// are met:
15
// 1. Redistributions of source code must retain the above copyright
16
// notice, this list of conditions and the following disclaimer.
17
// 2. Redistributions in binary form must reproduce the above copyright
18
// notice, this list of conditions and the following disclaimer in the
19
// documentation and/or other materials provided with the distribution.
20
//
21
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
// SUCH DAMAGE.
32
//
33
// Visit the ACME Labs Java page for up-to-date versions of this and other
34
// fine Java utilities: http://www.acme.com/java/
35

36 /**
37  * Modifications done by Daniel Matuschek (daniel@matuschek.net)
38  * - modified JavaDoc documentation
39  * - adapted to Java 1.2, removed deprecated DataInputStream.readLine() method
40  * - replaced DataInputStream by InputStream (there was no need for a
41  * DatainputStream, not idea why this was used in the original version)
42  * - fixed a bug (there is an CRLF after every the data block)
43  */

44
45
46 /**
47  * An InputStream that implements HTTP/1.1 chunking.
48  * <P>
49  * This class lets a Servlet read its request data as an HTTP/1.1 chunked
50  * stream. Chunked streams are a way to send arbitrary-length data without
51  * having to know beforehand how much you're going to send. They are
52  * introduced by a "Transfer-Encoding: chunked" header, so if such a header
53  * appears in an HTTP request you should use this class to read any data.
54  * <P>
55  * Sample usage:
56  * <BLOCKQUOTE><PRE><CODE>
57  * InputStream in = req.getInputStream();
58  * if ( "chunked".equals( req.getHeader( "Transfer-Encoding" ) ) )
59  * in = new ChunkedInputStream( in );
60  * </CODE></PRE></BLOCKQUOTE>
61  * <P>
62  * Because it would be impolite to make the authors of every Servlet include
63  * the above code, this is general done at the server level so that it
64  * happens automatically. Servlet authors will generally not create
65  * ChunkedInputStreams. This is in contrast with ChunkedOutputStream,
66  * which Servlets have to call themselves if they want to use it.
67  * <P>
68  * <A HREF="../../../../resources/classes/Acme/Serve/servlet/http/ChunkedInputStream.java">Fetch the software.</A><BR>
69  * <A HREF="../../../../resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
70  *
71  * @author Jef Poskanzer
72  * @author Daniel Matuschek
73  * @version $Id: ChunkedInputStream.java,v 1.6 2002/05/31 14:45:56 matuschd Exp $
74  */

75 public class ChunkedInputStream extends FilterInputStream JavaDoc
76 {
77   
78   private int contentLength;
79   private byte[] b1 = new byte[1];
80
81   /** number of bytes available in the current chunk */
82   private int chunkCount = 0;
83
84   private Vector JavaDoc<String JavaDoc> footerNames = null;
85   private Vector JavaDoc<String JavaDoc> footerValues = null;
86   
87   /**
88    * Make a ChunkedInputStream.
89    */

90   public ChunkedInputStream( InputStream JavaDoc in )
91   {
92     super(in);
93     contentLength = 0;
94   }
95
96
97   /**
98    * The FilterInputStream implementation of the single-byte read()
99    * method just reads directly from the underlying stream. We want
100    * to go through our own read-block method, so we have to override.
101    * Seems like FilterInputStream really ought to do this itself.
102    */

103   public int read() throws IOException JavaDoc
104   {
105     if (read(b1,0,1) == -1 ) {
106       return -1;
107     }
108
109
110     return b1[0];
111   }
112
113
114   /**
115    * Reads into an array of bytes.
116    * @param b the buffer into which the data is read
117    * @param off the start offset of the data
118    * @param len the maximum number of bytes read
119    * @return the actual number of bytes read, or -1 on EOF
120    * @exception IOException if an I/O error has occurred
121    */

122   public int read( byte[] b, int off, int len ) throws IOException JavaDoc
123   {
124     if (chunkCount == 0) {
125       startChunk();
126       if (chunkCount == 0) {
127     return -1;
128       }
129     }
130     int toRead = Math.min( chunkCount, len );
131     int r = in.read( b, off, toRead );
132
133     if ( r != -1 ) {
134       chunkCount -= r;
135     }
136     return r;
137   }
138   
139
140   /**
141    * Reads the start of a chunk.
142    */

143   private void startChunk() throws IOException JavaDoc
144   {
145     String JavaDoc line = readLine();
146     if (line.equals("")) {
147       line=readLine();
148     }
149
150     try {
151       chunkCount = Integer.parseInt(line.trim(),16);
152     } catch (NumberFormatException JavaDoc e) {
153       throw new IOException JavaDoc("malformed chunk ("+line+")");
154     }
155     contentLength += chunkCount;
156     if ( chunkCount == 0 ) {
157       readFooters();
158     }
159
160   }
161   
162
163   /**
164    * Reads any footers.
165    */

166   private void readFooters() throws IOException JavaDoc
167   {
168     footerNames = new Vector JavaDoc<String JavaDoc>();
169     footerValues = new Vector JavaDoc<String JavaDoc>();
170     String JavaDoc line;
171     while ( true ) {
172       line = readLine();
173       if ( line.length() == 0 )
174     break;
175       int colon = line.indexOf( ':' );
176       if ( colon != -1 )
177     {
178       String JavaDoc name = line.substring( 0, colon ).toLowerCase();
179       String JavaDoc value = line.substring( colon + 1 ).trim();
180       footerNames.addElement( name.toLowerCase() );
181       footerValues.addElement( value );
182     }
183     }
184   }
185   
186   
187   /**
188    * Returns the value of a footer field, or null if not known.
189    * Footers come at the end of a chunked stream, so trying to
190    * retrieve them before the stream has given an EOF will return
191    * only nulls.
192    * @param name the footer field name
193    */

194   public String JavaDoc getFooter( String JavaDoc name )
195   {
196     if ( ! isDone() )
197       return null;
198     int i = footerNames.indexOf( name.toLowerCase() );
199     if ( i == -1 )
200       return null;
201     return (String JavaDoc) footerValues.elementAt( i );
202   }
203   
204
205   /**
206    * Returns an Enumeration of the footer names.
207    */

208   public Enumeration JavaDoc getFooters()
209   {
210     if ( ! isDone() )
211       return null;
212     return footerNames.elements();
213   }
214   
215
216   /**
217    * Returns the size of the request entity data, or -1 if not known.
218    */

219   public int getContentLength()
220   {
221     if (! isDone()) {
222       return -1;
223     }
224     return contentLength;
225   }
226   
227   
228   /**
229    * Tells whether the stream has gotten to its end yet. Remembering
230    * whether you've gotten an EOF works fine too, but this is a convenient
231    * predicate. java.io.InputStream should probably have its own isEof()
232    * predicate.
233    */

234   public boolean isDone()
235   {
236     return footerNames != null;
237   }
238
239
240   /**
241    * ChunkedInputStream used DataInputStream.readLine() before. This method
242    * is deprecated, therefore we will it replace by our own method.
243    * Because the chunk lines only use 7bit ASCII, we can use the
244    * system default encoding
245    * The data lines itself will not be read using this readLine method
246    * but by a block read
247    */

248   protected String JavaDoc readLine()
249     throws IOException JavaDoc
250   {
251     final byte CR=13;
252     final byte LF=10;
253
254     ByteBuffer buff = new ByteBuffer();
255     byte b=0;
256
257     int i=0;
258     do {
259       b = (byte)this.in.read();
260       if (b != LF) {
261         buff.append(b);
262       }
263       i++;
264     } while ((b != LF));
265
266     // according to the RFC there must be a CR before the LF, but some
267
// web servers don't do this :-(
268
byte[] byteBuff = buff.getContent();
269
270     if (byteBuff.length == 0) {
271       return "";
272     }
273
274     if (byteBuff[byteBuff.length-1] != CR) {
275       return new String JavaDoc(byteBuff);
276     } else {
277       return new String JavaDoc(byteBuff,0,byteBuff.length-1);
278     }
279   }
280   
281 }
282
Popular Tags