OSDN Git Service

libjava/ChangeLog:
[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 gnu.java.lang.CPStringBuilder;
42
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.net.ProtocolException;
46
47
48 //
49 // Note that we rely on the implemtation of skip() in the super class
50 // (InputStream) calling our read methods to account for chunk headers
51 // while skipping.
52 //
53
54
55 /**
56  * Input stream wrapper for the "chunked" transfer-coding.
57  *
58  * @author Chris Burdess (dog@gnu.org)
59  */
60 public class ChunkedInputStream
61   extends InputStream
62 {
63   Headers headers;
64
65   /** The underlying stream. */
66   private InputStream in;
67
68   /** Size of the chunk we're reading.  */
69   int size;
70   /** Number of bytes we've read in this chunk.  */
71   int count;
72   /**
73    * True when we should read meta-information, false when we should
74    * read data.
75    */
76   boolean meta;
77   /** True when we've hit EOF.  */
78   boolean eof;
79
80   /**
81    * Constructor.
82    * @param in the response socket input stream
83    * @param headers the headers to receive additional header lines
84    */
85   public ChunkedInputStream(InputStream in, Headers headers)
86   {
87     this.in = in;
88     this.headers = headers;
89     size = -1;
90     count = 0;
91     meta = true;
92   }
93
94   public int read()
95     throws IOException
96   {
97     byte[] buf = new byte[1];
98     int len = read(buf, 0, 1);
99     if (len == -1)
100       {
101         return -1;
102       }
103     return 0xff & buf[0];
104   }
105
106   public synchronized int read(byte[] buffer, int offset, int length)
107     throws IOException
108   {
109     if (eof)
110       {
111         return -1;
112       }
113     if (meta)
114       {
115         // Read chunk header
116         int c, last = 0;
117         boolean seenSemi = false;
118         CPStringBuilder buf = new CPStringBuilder();
119         do
120           {
121             c = in.read();
122             if (c == 0x3b) // ;
123               {
124                 seenSemi = true;
125               }
126             else if (c == 0x0a && last == 0x0d) // CRLF
127               {
128                 try
129                   {
130                     size = Integer.parseInt(buf.toString(), 16);
131                   }
132                 catch (NumberFormatException nfe)
133                   {
134                     IOException ioe = new IOException("Bad chunk header");
135                     ioe.initCause(nfe);
136                     // Unrecoverable.  Don't try to read more.
137                     in.close();
138                     throw ioe;
139                   }
140                 break;
141               }
142             else if (!seenSemi && c >= 0x30)
143               {
144                 buf.append ((char) c);
145               }
146             last = c;
147           }
148         while(c != -1);
149         count = 0;
150         meta = false;
151       }
152     if (size == 0)
153       {
154         // Read trailer
155         headers.parse(in);
156         eof = true;
157         return -1;
158       }
159     else
160       {
161         int canRead = Math.min(size - count, length);
162         int len = in.read(buffer, offset, canRead);
163         if (len == -1)
164           {
165             // This is an error condition but it isn't clear what we
166             // should do with it.
167             eof = true;
168             return -1;
169           }
170         count += len;
171         if (count == size)
172           {
173             // Read CRLF
174             int c1 = in.read();
175             int c2 = in.read();
176             if (c1 == -1 || c2 == -1)
177               {
178                 // EOF before CRLF: bad, but ignore
179                 eof = true;
180                 return -1;
181               }
182             if (c1 != 0x0d || c2 != 0x0a)
183               {
184                 throw new ProtocolException("expecting CRLF: " + c1 + "," + c2);
185               }
186             meta = true;
187           }
188         return len;
189       }
190   }
191
192   /**
193    * This method returns the number of bytes that can be read from
194    * this stream before a read might block.  Even if the underlying
195    * InputStream has data available past the end of the current chunk,
196    * we have no way of knowing how large the next chunk header will
197    * be. So we cannot report available data past the current chunk.
198    *
199    * @return The number of bytes that can be read before a read might
200    * block
201    *
202    * @exception IOException If an error occurs
203    */
204   public int available() throws IOException
205   {
206     if (meta)
207       return 0;
208     
209     return Math.min(in.available(), size - count);
210   }
211
212   /**
213    * This method closes the ChunkedInputStream by closing the underlying
214    * InputStream.
215    * 
216    * @exception IOException If an error occurs
217    */
218   public void close() throws IOException
219   {
220     in.close();
221   }
222   
223 }
224