61 private MessageHeader responses;
62
63 /**
64 * The size, in bytes, of the chunk that is currently being read.
65 * This size is only valid if the current position in the underlying
66 * input stream is inside a chunk (ie: state == STATE_READING_CHUNK).
67 */
68 private int chunkSize;
69
70 /**
71 * The number of bytes read from the underlying stream for the current
72 * chunk. This value is always in the range <code>0</code> through to
73 * <code>chunkSize</code>
74 */
75 private int chunkRead;
76
77 /**
78 * The internal buffer array where chunk data is available for the
79 * application to read.
80 */
81 private byte chunkData[] = new byte[4096];
82
83 /**
84 * The current position in the buffer. It contains the index
85 * of the next byte to read from <code>chunkData</code>
86 */
87 private int chunkPos;
88
89 /**
90 * The index one greater than the index of the last valid byte in the
91 * buffer. This value is always in the range <code>0</code> through
92 * <code>chunkData.length</code>.
93 */
94 private int chunkCount;
95
96 /**
97 * The internal buffer where bytes from the underlying stream can be
98 * read. It may contain bytes representing chunk-size, chunk-data, or
99 * trailer fields.
100 */
101 private byte rawData[] = new byte[32];
102
103 /**
104 * The current position in the buffer. It contains the index
105 * of the next byte to read from <code>rawData</code>
106 */
107 private int rawPos;
108
109 /**
110 * The index one greater than the index of the last valid byte in the
111 * buffer. This value is always in the range <code>0</code> through
112 * <code>rawData.length</code>.
113 */
114 private int rawCount;
115
116 /**
117 * Indicates if an error was encountered when processing the chunked
118 * stream.
119 */
120 private boolean error;
121
169 /**
170 * Check to make sure that this stream has not been closed.
171 */
172 private void ensureOpen() throws IOException {
173 if (closed) {
174 throw new IOException("stream is closed");
175 }
176 }
177
178
179 /**
180 * Ensures there is <code>size</code> bytes available in
181 * <code>rawData</code>. This requires that we either
182 * shift the bytes in use to the begining of the buffer
183 * or allocate a large buffer with sufficient space available.
184 */
185 private void ensureRawAvailable(int size) {
186 if (rawCount + size > rawData.length) {
187 int used = rawCount - rawPos;
188 if (used + size > rawData.length) {
189 byte tmp[] = new byte[used + size];
190 if (used > 0) {
191 System.arraycopy(rawData, rawPos, tmp, 0, used);
192 }
193 rawData = tmp;
194 } else {
195 if (used > 0) {
196 System.arraycopy(rawData, rawPos, rawData, 0, used);
197 }
198 }
199 rawCount = used;
200 rawPos = 0;
201 }
202 }
203
204
205 /**
206 * Close the underlying input stream by either returning it to the
207 * keep alive cache or closing the stream.
208 * <p>
209 * As a chunked stream is inheritly persistent (see HTTP 1.1 RFC) the
343 * total read from the underlying stream to date.
344 */
345 case STATE_READING_CHUNK :
346 /* no data available yet */
347 if (rawPos >= rawCount) {
348 return;
349 }
350
351 /*
352 * Compute the number of bytes of chunk data available in the
353 * raw buffer.
354 */
355 int copyLen = Math.min( chunkSize-chunkRead, rawCount-rawPos );
356
357 /*
358 * Expand or compact chunkData if needed.
359 */
360 if (chunkData.length < chunkCount + copyLen) {
361 int cnt = chunkCount - chunkPos;
362 if (chunkData.length < cnt + copyLen) {
363 byte tmp[] = new byte[cnt + copyLen];
364 System.arraycopy(chunkData, chunkPos, tmp, 0, cnt);
365 chunkData = tmp;
366 } else {
367 System.arraycopy(chunkData, chunkPos, chunkData, 0, cnt);
368 }
369 chunkPos = 0;
370 chunkCount = cnt;
371 }
372
373 /*
374 * Copy the chunk data into chunkData so that it's available
375 * to the read methods.
376 */
377 System.arraycopy(rawData, rawPos, chunkData, chunkCount, copyLen);
378 rawPos += copyLen;
379 chunkCount += copyLen;
380 chunkRead += copyLen;
381
382 /*
383 * If all the chunk has been copied into chunkData then the next
650 if (chunkPos >= chunkCount) {
651 if (readAhead(true) <= 0) {
652 return -1;
653 }
654 }
655 return chunkData[chunkPos++] & 0xff;
656 }
657
658
659 /**
660 * Reads bytes from this stream into the specified byte array, starting at
661 * the given offset.
662 *
663 * @param b destination buffer.
664 * @param off offset at which to start storing bytes.
665 * @param len maximum number of bytes to read.
666 * @return the number of bytes read, or <code>-1</code> if the end of
667 * the stream has been reached.
668 * @exception IOException if an I/O error occurs.
669 */
670 public synchronized int read(byte b[], int off, int len)
671 throws IOException
672 {
673 ensureOpen();
674 if ((off < 0) || (off > b.length) || (len < 0) ||
675 ((off + len) > b.length) || ((off + len) < 0)) {
676 throw new IndexOutOfBoundsException();
677 } else if (len == 0) {
678 return 0;
679 }
680
681 int avail = chunkCount - chunkPos;
682 if (avail <= 0) {
683 /*
684 * Optimization: if we're in the middle of the chunk read
685 * directly from the underlying stream into the caller's
686 * buffer
687 */
688 if (state == STATE_READING_CHUNK) {
689 return fastRead( b, off, len );
690 }
|
61 private MessageHeader responses;
62
63 /**
64 * The size, in bytes, of the chunk that is currently being read.
65 * This size is only valid if the current position in the underlying
66 * input stream is inside a chunk (ie: state == STATE_READING_CHUNK).
67 */
68 private int chunkSize;
69
70 /**
71 * The number of bytes read from the underlying stream for the current
72 * chunk. This value is always in the range <code>0</code> through to
73 * <code>chunkSize</code>
74 */
75 private int chunkRead;
76
77 /**
78 * The internal buffer array where chunk data is available for the
79 * application to read.
80 */
81 private byte[] chunkData = new byte[4096];
82
83 /**
84 * The current position in the buffer. It contains the index
85 * of the next byte to read from <code>chunkData</code>
86 */
87 private int chunkPos;
88
89 /**
90 * The index one greater than the index of the last valid byte in the
91 * buffer. This value is always in the range <code>0</code> through
92 * <code>chunkData.length</code>.
93 */
94 private int chunkCount;
95
96 /**
97 * The internal buffer where bytes from the underlying stream can be
98 * read. It may contain bytes representing chunk-size, chunk-data, or
99 * trailer fields.
100 */
101 private byte[] rawData = new byte[32];
102
103 /**
104 * The current position in the buffer. It contains the index
105 * of the next byte to read from <code>rawData</code>
106 */
107 private int rawPos;
108
109 /**
110 * The index one greater than the index of the last valid byte in the
111 * buffer. This value is always in the range <code>0</code> through
112 * <code>rawData.length</code>.
113 */
114 private int rawCount;
115
116 /**
117 * Indicates if an error was encountered when processing the chunked
118 * stream.
119 */
120 private boolean error;
121
169 /**
170 * Check to make sure that this stream has not been closed.
171 */
172 private void ensureOpen() throws IOException {
173 if (closed) {
174 throw new IOException("stream is closed");
175 }
176 }
177
178
179 /**
180 * Ensures there is <code>size</code> bytes available in
181 * <code>rawData</code>. This requires that we either
182 * shift the bytes in use to the begining of the buffer
183 * or allocate a large buffer with sufficient space available.
184 */
185 private void ensureRawAvailable(int size) {
186 if (rawCount + size > rawData.length) {
187 int used = rawCount - rawPos;
188 if (used + size > rawData.length) {
189 byte[] tmp = new byte[used + size];
190 if (used > 0) {
191 System.arraycopy(rawData, rawPos, tmp, 0, used);
192 }
193 rawData = tmp;
194 } else {
195 if (used > 0) {
196 System.arraycopy(rawData, rawPos, rawData, 0, used);
197 }
198 }
199 rawCount = used;
200 rawPos = 0;
201 }
202 }
203
204
205 /**
206 * Close the underlying input stream by either returning it to the
207 * keep alive cache or closing the stream.
208 * <p>
209 * As a chunked stream is inheritly persistent (see HTTP 1.1 RFC) the
343 * total read from the underlying stream to date.
344 */
345 case STATE_READING_CHUNK :
346 /* no data available yet */
347 if (rawPos >= rawCount) {
348 return;
349 }
350
351 /*
352 * Compute the number of bytes of chunk data available in the
353 * raw buffer.
354 */
355 int copyLen = Math.min( chunkSize-chunkRead, rawCount-rawPos );
356
357 /*
358 * Expand or compact chunkData if needed.
359 */
360 if (chunkData.length < chunkCount + copyLen) {
361 int cnt = chunkCount - chunkPos;
362 if (chunkData.length < cnt + copyLen) {
363 byte[] tmp = new byte[cnt + copyLen];
364 System.arraycopy(chunkData, chunkPos, tmp, 0, cnt);
365 chunkData = tmp;
366 } else {
367 System.arraycopy(chunkData, chunkPos, chunkData, 0, cnt);
368 }
369 chunkPos = 0;
370 chunkCount = cnt;
371 }
372
373 /*
374 * Copy the chunk data into chunkData so that it's available
375 * to the read methods.
376 */
377 System.arraycopy(rawData, rawPos, chunkData, chunkCount, copyLen);
378 rawPos += copyLen;
379 chunkCount += copyLen;
380 chunkRead += copyLen;
381
382 /*
383 * If all the chunk has been copied into chunkData then the next
650 if (chunkPos >= chunkCount) {
651 if (readAhead(true) <= 0) {
652 return -1;
653 }
654 }
655 return chunkData[chunkPos++] & 0xff;
656 }
657
658
659 /**
660 * Reads bytes from this stream into the specified byte array, starting at
661 * the given offset.
662 *
663 * @param b destination buffer.
664 * @param off offset at which to start storing bytes.
665 * @param len maximum number of bytes to read.
666 * @return the number of bytes read, or <code>-1</code> if the end of
667 * the stream has been reached.
668 * @exception IOException if an I/O error occurs.
669 */
670 public synchronized int read(byte[] b, int off, int len)
671 throws IOException
672 {
673 ensureOpen();
674 if ((off < 0) || (off > b.length) || (len < 0) ||
675 ((off + len) > b.length) || ((off + len) < 0)) {
676 throw new IndexOutOfBoundsException();
677 } else if (len == 0) {
678 return 0;
679 }
680
681 int avail = chunkCount - chunkPos;
682 if (avail <= 0) {
683 /*
684 * Optimization: if we're in the middle of the chunk read
685 * directly from the underlying stream into the caller's
686 * buffer
687 */
688 if (state == STATE_READING_CHUNK) {
689 return fastRead( b, off, len );
690 }
|