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