001: // ChunkedInputStream.java
002: // $Id: ChunkedInputStream.java,v 1.13 2002/07/03 15:54:46 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.http;
007:
008: import java.io.IOException;
009: import java.io.InputStream;
010:
011: /**
012: * A Stream that parses and present chunk encoded data.
013: * This stream should only be used on top of a buffered input stream, it might
014: * be very inefficient otherwise.
015: * Chunk encoding is defined in version 1.1 of the HTTP specification.
016: */
017:
018: public class ChunkedInputStream extends InputStream {
019: protected InputStream in = null;
020: protected HttpBuffer buf = null;
021: protected ParseState ps = null;
022: protected boolean inited = false;
023:
024: protected int clen = -1; // Remaining bytes to read for current chunk
025: protected int ahead = -1; // Any look ahead byte
026: protected boolean isahead = false;
027:
028: protected boolean eof = false;
029:
030: protected HttpStreamObserver observer = null;
031:
032: private final void checkInit() throws IOException {
033: if (!inited) {
034: clen = nextChunk(false);
035: inited = true;
036: }
037: }
038:
039: /**
040: * Read in next chunk description.
041: * Sets the eof flag to <strong>true</strong> when reached.
042: * @return The length of next incomming chunk of data.
043: */
044:
045: protected int nextChunk(boolean skipCRLF) throws IOException {
046: if (eof)
047: return 0;
048: int ch = -1;
049: buf.reset();
050: if (skipCRLF) {
051: ch = in.read(); // '\r'
052: ch = in.read(); // '\n'
053: }
054: loop: while (true) {
055: ch = in.read();
056: switch (ch) {
057: case -1:
058: throw new IOException(
059: "Premature end of chunked stream.");
060: case '\r':
061: if ((ch = in.read()) != '\n') {
062: ahead = ch;
063: isahead = true;
064: }
065: break loop;
066: case '\n':
067: break loop;
068: default:
069: buf.append(ch);
070: }
071: }
072: // Parse the buffer content as an hexa number:
073: ps.ioff = 0;
074: ps.bufend = buf.length();
075: int len = HttpParser.parseInt(buf.getBytes(), 16, ps);
076: eof = (len == 0);
077: return len;
078: }
079:
080: public void close() throws IOException {
081: checkInit();
082: if (observer != null) {
083: observer.notifyClose(this );
084: observer = null;
085: }
086: }
087:
088: public int read() throws IOException {
089: checkInit();
090: if (clen == 0) {
091: if (eof || ((clen = nextChunk(true)) == 0)) {
092: if (observer != null) {
093: observer.notifyEOF(this );
094: observer = null;
095: }
096: return -1;
097: }
098: if (isahead) {
099: clen--;
100: isahead = false;
101: return ahead;
102: }
103: }
104: clen--;
105: return in.read();
106: }
107:
108: public int read(byte b[], int off, int len) throws IOException {
109: checkInit();
110: if (eof) {
111: if (observer != null) {
112: observer.notifyEOF(this );
113: observer = null;
114: }
115: return -1;
116: }
117: if (clen > len) {
118: // More data available then requested:
119: int cnt = in.read(b, off, len);
120: if (cnt == -1) {
121: observer.notifyFailure(this );
122: observer = null;
123: throw new IOException("Chunked stream read aborted ("
124: + clen + " remaining in chunk)");
125: }
126: clen -= cnt;
127: return cnt;
128: } else {
129: int copied = 0;
130: while (len > 0) {
131: // Check for available data:
132: if (clen == 0) {
133: // End of data ?
134: if (eof || ((clen = nextChunk(true)) == 0)) {
135: if (observer != null) {
136: observer.notifyEOF(this );
137: observer = null;
138: }
139: return (copied == 0) ? -1 : copied;
140: } else if (isahead) {
141: b[off++] = (byte) (ahead & 0xff);
142: len--;
143: clen--;
144: isahead = false;
145: }
146: }
147: // Read in available data:
148: int got = in.read(b, off, Math.min(clen, len));
149: if (got == -1) {
150: observer.notifyFailure(this );
151: observer = null;
152: throw new IOException("Chunked stream read aborted");
153: }
154: copied += got;
155: len -= got;
156: clen -= got;
157: off += got;
158: }
159: return copied;
160: }
161: }
162:
163: public int available() throws IOException {
164: checkInit();
165: return eof ? -1 : 1;
166: }
167:
168: public long skip(long n) throws IOException {
169: checkInit();
170: byte buf[] = new byte[512];
171: int cnt = Math.min(buf.length, (int) n);
172: while ((cnt = read(buf, 0, cnt)) > 0) {
173: n -= cnt;
174: cnt = Math.min(buf.length, (int) n);
175: }
176: return n;
177: }
178:
179: /**
180: * Make sure the stream is ultimately closed !
181: */
182:
183: public void finalize() {
184: try {
185: close();
186: } catch (IOException ex) {
187: }
188: }
189:
190: public ChunkedInputStream(InputStream in) throws IOException {
191: this (null, in);
192: }
193:
194: public ChunkedInputStream(HttpStreamObserver observer,
195: InputStream in) throws IOException {
196: this .observer = observer;
197: this .buf = new HttpBuffer();
198: this .ps = new ParseState();
199: this.in = in;
200: }
201: }
|