1 /*
2 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ResponseInputStream.java,v 1.23 2003/02/12 13:21:27 olegk Exp $
3 * $Revision: 1.23 $
4 * $Date: 2003/02/12 13:21:27 $
5 *
6 * ====================================================================
7 *
8 * The Apache Software License, Version 1.1
9 *
10 * Copyright (c) 1999-2003 The Apache Software Foundation. All rights
11 * reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 *
25 * 3. The end-user documentation included with the redistribution, if
26 * any, must include the following acknowlegement:
27 * "This product includes software developed by the
28 * Apache Software Foundation (http://www.apache.org/)."
29 * Alternately, this acknowlegement may appear in the software itself,
30 * if and wherever such third-party acknowlegements normally appear.
31 *
32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33 * Foundation" must not be used to endorse or promote products derived
34 * from this software without prior written permission. For written
35 * permission, please contact apache@apache.org.
36 *
37 * 5. Products derived from this software may not be called "Apache"
38 * nor may "Apache" appear in their names without prior written
39 * permission of the Apache Group.
40 *
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 * ====================================================================
54 *
55 * This software consists of voluntary contributions made by many
56 * individuals on behalf of the Apache Software Foundation. For more
57 * information on the Apache Software Foundation, please see
58 * <http://www.apache.org/>.
59 *
60 * [Additional notices, if required by prior licensing conditions]
61 *
62 */
63
64 package org.apache.commons.httpclient;
65
66 import java.io.IOException;
67 import java.io.InputStream;
68
69 import org.apache.commons.logging.LogFactory;
70 import org.apache.commons.logging.Log;
71
72 /***
73 * <p>{@link InputStream} wrapper supporting the chunked transfer encoding.</p>
74 *
75 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
76 * @author Sean C. Sullivan
77 * @author <a href="mailto:dion@apache.org">dIon Gillard</a>
78 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
79 * @version $Revision: 1.23 $ $Date: 2003/02/12 13:21:27 $
80 *
81 * @deprecated Use new ChunkedInputStream(HttpConnecion#getResponseInputStream());
82 *
83 */
84 public class ResponseInputStream extends InputStream {
85
86 // -------------------------------------------------------- Class Variables
87
88 /*** Log object for this class. */
89 public static final Log LOG = LogFactory.getLog(ResponseInputStream.class);
90
91 // ----------------------------------------------------------- Constructors
92
93 /***
94 *
95 * @param stream Must be non-null.
96 * @param chunked <code>true</code> if the input stream is chunked
97 * @param contentLength content length
98 *
99 * @deprecated Use ChunkedInputStream;
100 */
101 public ResponseInputStream(InputStream stream, boolean chunked, int contentLength) {
102 LOG.trace("enter ResponseInputStream(InputStream, boolean, int)");
103
104 if (null == stream) {
105 throw new NullPointerException("InputStream parameter is null");
106 }
107 closed = false;
108 count = 0;
109 this.chunk = chunked;
110 this.contentLength = contentLength;
111 this.stream = stream;
112 }
113
114 /***
115 * Construct a servlet input stream associated with the specified Request.
116 *
117 * @param stream Must be non-null.
118 * @param method Must be non-null.
119 *
120 * @deprecated Use ChunkedInputStream;
121 *
122 */
123 public ResponseInputStream(InputStream stream, HttpMethod method) {
124 super();
125 LOG.trace("enter ResponseInputStream(InputStream, HttpMethod)");
126
127 if (null == stream) {
128 throw new NullPointerException("InputStream parameter is null");
129 }
130
131 if (null == method) {
132 throw new NullPointerException("HttpMethod parameter is null");
133 }
134
135 closed = false;
136 count = 0;
137
138 // Retrieving transfer encoding header
139 Header transferEncoding = method.getResponseHeader("transfer-encoding");
140 if ((null != transferEncoding) && (transferEncoding.getValue().
141 toLowerCase().indexOf("chunked") != -1)) {
142 chunk = true;
143 }
144
145 // Retrieving content length header
146 Header contentLengthHeader = method.getResponseHeader("content-length");
147 if (null != contentLengthHeader) {
148 try {
149 this.contentLength =
150 Integer.parseInt(contentLengthHeader.getValue());
151 } catch (NumberFormatException ignored) {
152 // we are intentionally ignoring the NumberFormatException
153 }
154 }
155
156 this.stream = stream;
157 }
158
159
160 // ----------------------------------------------------- Instance Variables
161
162 /***
163 * Has this stream been closed?
164 */
165 private boolean closed = false;
166
167 /***
168 * Use chunking ?
169 */
170 private boolean chunk = false;
171
172 /***
173 * True if the final chunk was found.
174 */
175 private boolean endChunk = false;
176
177 /***
178 * Chunk buffer.
179 */
180 private byte[] buffer = null;
181
182 /***
183 * Current chunk length.
184 */
185 private int length = 0;
186
187 /***
188 * Current chunk buffer position.
189 */
190 private int pos = 0;
191
192 /***
193 * The number of bytes which have already been returned by this stream.
194 */
195 private int count = 0;
196
197 /***
198 * The content length past which we will not read, or -1 if there is
199 * no defined content length.
200 */
201 private int contentLength = -1;
202
203 /***
204 * The underlying input stream from which we should read data.
205 */
206 private InputStream stream = null;
207
208 // --------------------------------------------------------- Public Methods
209
210 /***
211 * Close this input stream. No physical level I-O is performed, but
212 * any further attempt to read from this stream will throw an IOException.
213 * If a content length has been set but not all of the bytes have yet been
214 * consumed, the remaining bytes will be swallowed.
215 *
216 * @throws IOException If an IO problem occurs.
217 *
218 * @deprecated Use ChunkedInputStream;
219 */
220 public void close() throws IOException {
221 LOG.trace("enter ResponseInputStream.close()");
222 /*
223 // Xerces appears to doubly-close the input stream...
224 if(closed) {
225 throw new IOException("Stream is already closed");
226 }
227 */
228
229 //TODO: This close code is not very robust
230 if (!closed) {
231 try {
232 if (chunk) {
233 while (!endChunk) {
234 int b = read();
235 if (b < 0) {
236 break;
237 }
238 }
239 } else {
240 if (length > 0) {
241 while (count < length) {
242 int b = read();
243 if (b < 0) {
244 break;
245 }
246 }
247 }
248 }
249 } catch (java.io.IOException ex) {
250 throw ex;
251 } finally {
252 closed = true;
253 }
254 }
255 }
256
257
258 /***
259 * Read up to <code>len</code> bytes of data from the input stream
260 * into an array of bytes. An attempt is made to read as many as
261 * <code>len</code> bytes, but a smaller number may be read,
262 * possibly zero. The number of bytes actually read is returned as
263 * an integer. This method blocks until input data is available,
264 * end of file is detected, or an exception is thrown.
265 *
266 * @param b The buffer into which the data is read
267 * @param off The start offset into array <code>b</code> at which
268 * the data is written
269 * @param len The maximum number of bytes to read
270 * @return The number of bytes that were read.
271 *
272 * @exception IOException if an input/output error occurs
273 *
274 * @deprecated Use ChunkedInputStream;
275 */
276 public int read(byte b[], int off, int len)
277 throws IOException {
278 LOG.trace("enter ResponseInputStream.read(byte, int, int)");
279
280 int avail = length - pos;
281 if ((avail == 0) && (!fillBuffer())) {
282 return (-1);
283 }
284
285 avail = length - pos;
286 if (avail == 0) {
287 return (-1);
288 }
289
290 int toCopy = avail;
291
292 if (toCopy < 0) {
293 return (-1);
294 }
295
296 if (avail > len) {
297 toCopy = len;
298 }
299 System.arraycopy(buffer, pos, b, off, toCopy);
300 pos += toCopy;
301 return toCopy;
302 }
303
304 /***
305 * Read and return a single byte from this input stream, or -1 if end of
306 * file has been encountered.
307 *
308 * @return The next byte in the stream or -1.
309 * @exception IOException if an input/output error occurs
310 *
311 * @deprecated Use ChunkedInputStream;
312 */
313 public int read() throws IOException {
314 LOG.trace("enter ResponseInputStream.read()");
315
316 if (pos == length) {
317 if (!fillBuffer()) {
318 return (-1);
319 }
320 }
321
322 return (buffer[pos++] & 0xff);
323
324 }
325
326 // -------------------------------------------------------- Private Methods
327
328
329 /***
330 * Fill the chunk buffer.
331 * @return true If successful
332 * @throws IOException If an IO problem occurs.
333 *
334 * @deprecated Use ChunkedInputStream;
335 */
336 private boolean fillBuffer() throws IOException {
337 LOG.trace("enter ResponseInputStream.fillBuffer()");
338
339 // Has this stream been closed?
340 if (closed) {
341 return false;
342 }
343 //throw new IOException("Stream is closed");
344
345 if (endChunk) {
346 return false;
347 }
348
349 // Have we read the specified content length already?
350 if ((contentLength >= 0) && (count >= contentLength)) {
351 return false; // End of file indicator
352 }
353
354 pos = 0;
355
356 if (chunk) {
357
358 try {
359 String numberValue = readLineFromStream();
360 if (numberValue == null) {
361 throw new NumberFormatException("unable to find chunk length");
362 }
363
364 length = Integer.parseInt(numberValue.trim(), 16);
365 } catch (NumberFormatException e) {
366 // Critical error, unable to parse the chunk length
367 length = -1;
368 chunk = false;
369 endChunk = true;
370 closed = true;
371 return false;
372 }
373
374 if (length == 0) {
375
376 // Skipping trailing headers, if any
377 String trailingLine = readLineFromStream();
378 while (!trailingLine.equals("")) {
379 trailingLine = readLineFromStream();
380 }
381 endChunk = true;
382 return false;
383
384 } else {
385
386 if ((buffer == null) || (length > buffer.length)) {
387 buffer = new byte[length];
388 }
389
390 // Now read the whole chunk into the buffer
391
392 int nbRead = 0;
393 int currentRead = 0;
394
395 while (nbRead < length) {
396 try {
397 currentRead = stream.read(buffer, nbRead,
398 length - nbRead);
399 } catch (Throwable t) {
400 LOG.debug("Exception thrown reading chunk from response", t);
401 throw new IOException();
402 }
403 if (currentRead < 0) {
404 throw new IOException("Not enough bytes read");
405 }
406 nbRead += currentRead;
407 }
408
409 // Skipping the CRLF
410 readLineFromStream();
411
412 }
413
414 } else { //not using chunking
415
416 try {
417 if (buffer == null) {
418 buffer = new byte[4096];
419 }
420 length = stream.read(buffer);
421 count += length;
422 } catch (Throwable t) {
423 LOG.debug("Exception thrown reading from response", t);
424 throw new IOException(t.getMessage());
425 }
426
427 }
428
429 return true;
430
431 }
432
433 /***
434 * Reads the input stream, one line at a time. Reads bytes into an array,
435 * until it reads a certain number of bytes or reaches a newline character,
436 * which it reads into the array as well.
437 *
438 * @return The line that was read, or <code>null</code> if end-of-file
439 * was encountered
440 * @exception IOException if an input or output exception has occurred
441 *
442 * @deprecated Use ChunkedInputStream;
443 */
444 private String readLineFromStream()
445 throws IOException {
446 LOG.trace("enter ResponseInputStream.ReadLineFromStream()");
447
448 StringBuffer sb = new StringBuffer();
449 while (true) {
450 int ch = stream.read();
451 if (ch < 0) {
452 if (sb.length() == 0) {
453 return (null);
454 } else {
455 break;
456 }
457 } else if (ch == '\r') {
458 continue;
459 } else if (ch == '\n') {
460 break;
461 }
462 sb.append((char) ch);
463 }
464 return (sb.toString());
465 }
466 }
This page was automatically generated by Maven