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 package org.apache.commons.httpclient;
32
33 import java.io.IOException;
34 import java.io.InputStream;
35
36 /***
37 * Cuts the wrapped InputStream off after a specified number of bytes.
38 *
39 * <p>Implementation note: Choices abound. One approach would pass
40 * through the {@link InputStream#mark} and {@link InputStream#reset} calls to
41 * the underlying stream. That's tricky, though, because you then have to
42 * start duplicating the work of keeping track of how much a reset rewinds.
43 * Further, you have to watch out for the "readLimit", and since the semantics
44 * for the readLimit leave room for differing implementations, you might get
45 * into a lot of trouble.</p>
46 *
47 * <p>Alternatively, you could make this class extend {@link java.io.BufferedInputStream}
48 * and then use the protected members of that class to avoid duplicated effort.
49 * That solution has the side effect of adding yet another possible layer of
50 * buffering.</p>
51 *
52 * <p>Then, there is the simple choice, which this takes - simply don't
53 * support {@link InputStream#mark} and {@link InputStream#reset}. That choice
54 * has the added benefit of keeping this class very simple.</p>
55 *
56 * @author Ortwin Glueck
57 * @author Eric Johnson
58 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
59 * @since 2.0
60 */
61 public class ContentLengthInputStream extends InputStream {
62
63 /***
64 * The maximum number of bytes that can be read from the stream. Subsequent
65 * read operations will return -1.
66 */
67 private long contentLength;
68
69 /*** The current position */
70 private long pos = 0;
71
72 /*** True if the stream is closed. */
73 private boolean closed = false;
74
75 /***
76 * Wrapped input stream that all calls are delegated to.
77 */
78 private InputStream wrappedStream = null;
79
80 /***
81 * @deprecated use {@link #ContentLengthInputStream(InputStream, long)}
82 *
83 * Creates a new length limited stream
84 *
85 * @param in The stream to wrap
86 * @param contentLength The maximum number of bytes that can be read from
87 * the stream. Subsequent read operations will return -1.
88 */
89 public ContentLengthInputStream(InputStream in, int contentLength) {
90 this(in, (long)contentLength);
91 }
92
93 /***
94 * Creates a new length limited stream
95 *
96 * @param in The stream to wrap
97 * @param contentLength The maximum number of bytes that can be read from
98 * the stream. Subsequent read operations will return -1.
99 *
100 * @since 3.0
101 */
102 public ContentLengthInputStream(InputStream in, long contentLength) {
103 super();
104 this.wrappedStream = in;
105 this.contentLength = contentLength;
106 }
107
108 /***
109 * <p>Reads until the end of the known length of content.</p>
110 *
111 * <p>Does not close the underlying socket input, but instead leaves it
112 * primed to parse the next response.</p>
113 * @throws IOException If an IO problem occurs.
114 */
115 public void close() throws IOException {
116 if (!closed) {
117 try {
118 ChunkedInputStream.exhaustInputStream(this);
119 } finally {
120
121
122 closed = true;
123 }
124 }
125 }
126
127
128 /***
129 * Read the next byte from the stream
130 * @return The next byte or -1 if the end of stream has been reached.
131 * @throws IOException If an IO problem occurs
132 * @see java.io.InputStream#read()
133 */
134 public int read() throws IOException {
135 if (closed) {
136 throw new IOException("Attempted read from closed stream.");
137 }
138
139 if (pos >= contentLength) {
140 return -1;
141 }
142 pos++;
143 return this.wrappedStream.read();
144 }
145
146 /***
147 * Does standard {@link InputStream#read(byte[], int, int)} behavior, but
148 * also notifies the watcher when the contents have been consumed.
149 *
150 * @param b The byte array to fill.
151 * @param off Start filling at this position.
152 * @param len The number of bytes to attempt to read.
153 * @return The number of bytes read, or -1 if the end of content has been
154 * reached.
155 *
156 * @throws java.io.IOException Should an error occur on the wrapped stream.
157 */
158 public int read (byte[] b, int off, int len) throws java.io.IOException {
159 if (closed) {
160 throw new IOException("Attempted read from closed stream.");
161 }
162
163 if (pos >= contentLength) {
164 return -1;
165 }
166
167 if (pos + len > contentLength) {
168 len = (int) (contentLength - pos);
169 }
170 int count = this.wrappedStream.read(b, off, len);
171 pos += count;
172 return count;
173 }
174
175
176 /***
177 * Read more bytes from the stream.
178 * @param b The byte array to put the new data in.
179 * @return The number of bytes read into the buffer.
180 * @throws IOException If an IO problem occurs
181 * @see java.io.InputStream#read(byte[])
182 */
183 public int read(byte[] b) throws IOException {
184 return read(b, 0, b.length);
185 }
186
187 /***
188 * Skips and discards a number of bytes from the input stream.
189 * @param n The number of bytes to skip.
190 * @return The actual number of bytes skipped. <= 0 if no bytes
191 * are skipped.
192 * @throws IOException If an error occurs while skipping bytes.
193 * @see InputStream#skip(long)
194 */
195 public long skip(long n) throws IOException {
196
197
198 long length = Math.min(n, contentLength - pos);
199
200 length = this.wrappedStream.skip(length);
201
202
203 if (length > 0) {
204 pos += length;
205 }
206 return length;
207 }
208
209 public int available() throws IOException {
210 if (this.closed) {
211 return 0;
212 }
213 int avail = this.wrappedStream.available();
214 if (this.pos + avail > this.contentLength ) {
215 avail = (int)(this.contentLength - this.pos);
216 }
217 return avail;
218 }
219
220 }