1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.apache.commons.httpclient;
33
34 import java.io.IOException;
35 import java.io.InputStream;
36
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.commons.logging.Log;
39
40 /***
41 * <p>{@link InputStream} wrapper supporting the chunked transfer encoding.</p>
42 *
43 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
44 * @author Sean C. Sullivan
45 * @author <a href="mailto:dion@apache.org">dIon Gillard</a>
46 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
47 * @version $Revision: 1.23.2.1 $ $Date: 2004/02/22 18:21:13 $
48 *
49 * @deprecated Use new ChunkedInputStream(HttpConnecion#getResponseInputStream());
50 *
51 */
52 public class ResponseInputStream extends InputStream {
53
54
55
56 /*** Log object for this class. */
57 public static final Log LOG = LogFactory.getLog(ResponseInputStream.class);
58
59
60
61 /***
62 *
63 * @param stream Must be non-null.
64 * @param chunked <code>true</code> if the input stream is chunked
65 * @param contentLength content length
66 *
67 * @deprecated Use ChunkedInputStream;
68 */
69 public ResponseInputStream(InputStream stream, boolean chunked, int contentLength) {
70 LOG.trace("enter ResponseInputStream(InputStream, boolean, int)");
71
72 if (null == stream) {
73 throw new NullPointerException("InputStream parameter is null");
74 }
75 closed = false;
76 count = 0;
77 this.chunk = chunked;
78 this.contentLength = contentLength;
79 this.stream = stream;
80 }
81
82 /***
83 * Construct a servlet input stream associated with the specified Request.
84 *
85 * @param stream Must be non-null.
86 * @param method Must be non-null.
87 *
88 * @deprecated Use ChunkedInputStream;
89 *
90 */
91 public ResponseInputStream(InputStream stream, HttpMethod method) {
92 super();
93 LOG.trace("enter ResponseInputStream(InputStream, HttpMethod)");
94
95 if (null == stream) {
96 throw new NullPointerException("InputStream parameter is null");
97 }
98
99 if (null == method) {
100 throw new NullPointerException("HttpMethod parameter is null");
101 }
102
103 closed = false;
104 count = 0;
105
106
107 Header transferEncoding = method.getResponseHeader("transfer-encoding");
108 if ((null != transferEncoding) && (transferEncoding.getValue().
109 toLowerCase().indexOf("chunked") != -1)) {
110 chunk = true;
111 }
112
113
114 Header contentLengthHeader = method.getResponseHeader("content-length");
115 if (null != contentLengthHeader) {
116 try {
117 this.contentLength =
118 Integer.parseInt(contentLengthHeader.getValue());
119 } catch (NumberFormatException ignored) {
120
121 }
122 }
123
124 this.stream = stream;
125 }
126
127
128
129
130 /***
131 * Has this stream been closed?
132 */
133 private boolean closed = false;
134
135 /***
136 * Use chunking ?
137 */
138 private boolean chunk = false;
139
140 /***
141 * True if the final chunk was found.
142 */
143 private boolean endChunk = false;
144
145 /***
146 * Chunk buffer.
147 */
148 private byte[] buffer = null;
149
150 /***
151 * Current chunk length.
152 */
153 private int length = 0;
154
155 /***
156 * Current chunk buffer position.
157 */
158 private int pos = 0;
159
160 /***
161 * The number of bytes which have already been returned by this stream.
162 */
163 private int count = 0;
164
165 /***
166 * The content length past which we will not read, or -1 if there is
167 * no defined content length.
168 */
169 private int contentLength = -1;
170
171 /***
172 * The underlying input stream from which we should read data.
173 */
174 private InputStream stream = null;
175
176
177
178 /***
179 * Close this input stream. No physical level I-O is performed, but
180 * any further attempt to read from this stream will throw an IOException.
181 * If a content length has been set but not all of the bytes have yet been
182 * consumed, the remaining bytes will be swallowed.
183 *
184 * @throws IOException If an IO problem occurs.
185 *
186 * @deprecated Use ChunkedInputStream;
187 */
188 public void close() throws IOException {
189 LOG.trace("enter ResponseInputStream.close()");
190
191
192
193
194
195
196
197
198 if (!closed) {
199 try {
200 if (chunk) {
201 while (!endChunk) {
202 int b = read();
203 if (b < 0) {
204 break;
205 }
206 }
207 } else {
208 if (length > 0) {
209 while (count < length) {
210 int b = read();
211 if (b < 0) {
212 break;
213 }
214 }
215 }
216 }
217 } catch (java.io.IOException ex) {
218 throw ex;
219 } finally {
220 closed = true;
221 }
222 }
223 }
224
225
226 /***
227 * Read up to <code>len</code> bytes of data from the input stream
228 * into an array of bytes. An attempt is made to read as many as
229 * <code>len</code> bytes, but a smaller number may be read,
230 * possibly zero. The number of bytes actually read is returned as
231 * an integer. This method blocks until input data is available,
232 * end of file is detected, or an exception is thrown.
233 *
234 * @param b The buffer into which the data is read
235 * @param off The start offset into array <code>b</code> at which
236 * the data is written
237 * @param len The maximum number of bytes to read
238 * @return The number of bytes that were read.
239 *
240 * @exception IOException if an input/output error occurs
241 *
242 * @deprecated Use ChunkedInputStream;
243 */
244 public int read(byte b[], int off, int len)
245 throws IOException {
246 LOG.trace("enter ResponseInputStream.read(byte, int, int)");
247
248 int avail = length - pos;
249 if ((avail == 0) && (!fillBuffer())) {
250 return (-1);
251 }
252
253 avail = length - pos;
254 if (avail == 0) {
255 return (-1);
256 }
257
258 int toCopy = avail;
259
260 if (toCopy < 0) {
261 return (-1);
262 }
263
264 if (avail > len) {
265 toCopy = len;
266 }
267 System.arraycopy(buffer, pos, b, off, toCopy);
268 pos += toCopy;
269 return toCopy;
270 }
271
272 /***
273 * Read and return a single byte from this input stream, or -1 if end of
274 * file has been encountered.
275 *
276 * @return The next byte in the stream or -1.
277 * @exception IOException if an input/output error occurs
278 *
279 * @deprecated Use ChunkedInputStream;
280 */
281 public int read() throws IOException {
282 LOG.trace("enter ResponseInputStream.read()");
283
284 if (pos == length) {
285 if (!fillBuffer()) {
286 return (-1);
287 }
288 }
289
290 return (buffer[pos++] & 0xff);
291
292 }
293
294
295
296
297 /***
298 * Fill the chunk buffer.
299 * @return true If successful
300 * @throws IOException If an IO problem occurs.
301 *
302 * @deprecated Use ChunkedInputStream;
303 */
304 private boolean fillBuffer() throws IOException {
305 LOG.trace("enter ResponseInputStream.fillBuffer()");
306
307
308 if (closed) {
309 return false;
310 }
311
312
313 if (endChunk) {
314 return false;
315 }
316
317
318 if ((contentLength >= 0) && (count >= contentLength)) {
319 return false;
320 }
321
322 pos = 0;
323
324 if (chunk) {
325
326 try {
327 String numberValue = readLineFromStream();
328 if (numberValue == null) {
329 throw new NumberFormatException("unable to find chunk length");
330 }
331
332 length = Integer.parseInt(numberValue.trim(), 16);
333 } catch (NumberFormatException e) {
334
335 length = -1;
336 chunk = false;
337 endChunk = true;
338 closed = true;
339 return false;
340 }
341
342 if (length == 0) {
343
344
345 String trailingLine = readLineFromStream();
346 while (!trailingLine.equals("")) {
347 trailingLine = readLineFromStream();
348 }
349 endChunk = true;
350 return false;
351
352 } else {
353
354 if ((buffer == null) || (length > buffer.length)) {
355 buffer = new byte[length];
356 }
357
358
359
360 int nbRead = 0;
361 int currentRead = 0;
362
363 while (nbRead < length) {
364 try {
365 currentRead = stream.read(buffer, nbRead,
366 length - nbRead);
367 } catch (Throwable t) {
368 LOG.debug("Exception thrown reading chunk from response", t);
369 throw new IOException();
370 }
371 if (currentRead < 0) {
372 throw new IOException("Not enough bytes read");
373 }
374 nbRead += currentRead;
375 }
376
377
378 readLineFromStream();
379
380 }
381
382 } else {
383
384 try {
385 if (buffer == null) {
386 buffer = new byte[4096];
387 }
388 length = stream.read(buffer);
389 count += length;
390 } catch (Throwable t) {
391 LOG.debug("Exception thrown reading from response", t);
392 throw new IOException(t.getMessage());
393 }
394
395 }
396
397 return true;
398
399 }
400
401 /***
402 * Reads the input stream, one line at a time. Reads bytes into an array,
403 * until it reads a certain number of bytes or reaches a newline character,
404 * which it reads into the array as well.
405 *
406 * @return The line that was read, or <code>null</code> if end-of-file
407 * was encountered
408 * @exception IOException if an input or output exception has occurred
409 *
410 * @deprecated Use ChunkedInputStream;
411 */
412 private String readLineFromStream()
413 throws IOException {
414 LOG.trace("enter ResponseInputStream.ReadLineFromStream()");
415
416 StringBuffer sb = new StringBuffer();
417 while (true) {
418 int ch = stream.read();
419 if (ch < 0) {
420 if (sb.length() == 0) {
421 return (null);
422 } else {
423 break;
424 }
425 } else if (ch == '\r') {
426 continue;
427 } else if (ch == '\n') {
428 break;
429 }
430 sb.append((char) ch);
431 }
432 return (sb.toString());
433 }
434 }