OSDN Git Service

8abef71d521d6ccc9005f1f338a44642a2b7260a
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / net / protocol / http / ChunkedInputStream.java
1 /* ChunkedInputStream.java --
2    Copyright (C) 2004, 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.java.net.protocol.http;
40
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.net.ProtocolException;
44
45
46 //
47 // Note that we rely on the implemtation of skip() in the super class
48 // (InputStream) calling our read methods to account for chunk headers
49 // while skipping.
50 //
51
52
53 /**
54  * Input stream wrapper for the "chunked" transfer-coding.
55  *
56  * @author Chris Burdess (dog@gnu.org)
57  */
58 public class ChunkedInputStream
59   extends InputStream
60 {
61   Headers headers;
62
63   /** The underlying stream. */
64   private InputStream in;
65
66   /** Size of the chunk we're reading.  */
67   int size;
68   /** Number of bytes we've read in this chunk.  */
69   int count;
70   /**
71    * True when we should read meta-information, false when we should
72    * read data.
73    */
74   boolean meta;
75   /** True when we've hit EOF.  */
76   boolean eof;
77
78   /**
79    * Constructor.
80    * @param in the response socket input stream
81    * @param headers the headers to receive additional header lines
82    */
83   public ChunkedInputStream(InputStream in, Headers headers)
84   {
85     this.in = in;
86     this.headers = headers;
87     size = -1;
88     count = 0;
89     meta = true;
90   }
91
92   public int read()
93     throws IOException
94   {
95     byte[] buf = new byte[1];
96     int len = read(buf, 0, 1);
97     if (len == -1)
98       {
99         return -1;
100       }
101     return 0xff & buf[0];
102   }
103
104   public synchronized int read(byte[] buffer, int offset, int length)
105     throws IOException
106   {
107     if (eof)
108       {
109         return -1;
110       }
111     if (meta)
112       {
113         // Read chunk header
114         int c, last = 0;
115         boolean seenSemi = false;
116         StringBuilder buf = new StringBuilder();
117         do
118           {
119             c = in.read();
120             if (c == 0x3b) // ;
121               {
122                 seenSemi = true;
123               }
124             else if (c == 0x0a && last == 0x0d) // CRLF
125               {
126                 try
127                   {
128                     size = Integer.parseInt(buf.toString(), 16);
129                   }
130                 catch (NumberFormatException nfe)
131                   {
132                     IOException ioe = new IOException("Bad chunk header");
133                     ioe.initCause(nfe);
134                     // Unrecoverable.  Don't try to read more.
135                     in.close();
136                     throw ioe;
137                   }
138                 break;
139               }
140             else if (!seenSemi && c >= 0x30)
141               {
142                 buf.append ((char) c);
143               }
144             last = c;
145           }
146         while(c != -1);
147         count = 0;
148         meta = false;
149       }
150     if (size == 0)
151       {
152         // Read trailer
153         headers.parse(in);
154         eof = true;
155         return -1;
156       }
157     else
158       {
159         int canRead = Math.min(size - count, length);
160         int len = in.read(buffer, offset, canRead);
161         if (len == -1)
162           {
163             // This is an error condition but it isn't clear what we
164             // should do with it.
165             eof = true;
166             return -1;
167           }
168         count += len;
169         if (count == size)
170           {
171             // Read CRLF
172             int c1 = in.read();
173             int c2 = in.read();
174             if (c1 == -1 || c2 == -1)
175               {
176                 // EOF before CRLF: bad, but ignore
177                 eof = true;
178                 return -1;
179               }
180             if (c1 != 0x0d || c2 != 0x0a)
181               {
182                 throw new ProtocolException("expecting CRLF: " + c1 + "," + c2);
183               }
184             meta = true;
185           }
186         return len;
187       }
188   }
189
190   /**
191    * This method returns the number of bytes that can be read from
192    * this stream before a read might block.  Even if the underlying
193    * InputStream has data available past the end of the current chunk,
194    * we have no way of knowing how large the next chunk header will
195    * be. So we cannot report available data past the current chunk.
196    *
197    * @return The number of bytes that can be read before a read might
198    * block
199    *
200    * @exception IOException If an error occurs
201    */
202   public int available() throws IOException
203   {
204     if (meta)
205       return 0;
206     
207     return Math.min(in.available(), size - count);
208   }
209
210   /**
211    * This method closes the ChunkedInputStream by closing the underlying
212    * InputStream.
213    * 
214    * @exception IOException If an error occurs
215    */
216   public void close() throws IOException
217   {
218     in.close();
219   }
220   
221 }
222