001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012:
013: package org.tmatesoft.svn.core.internal.io.dav.http;
014:
015: import java.io.ByteArrayOutputStream;
016: import java.io.IOException;
017: import java.io.InputStream;
018:
019: /**
020: * @version 1.1.1
021: * @author TMate Software Ltd.
022: */
023: class ChunkedInputStream extends InputStream {
024:
025: private String myCharset;
026: private InputStream myInputStream;
027: private int myChunkSize;
028: private int myPosition;
029: private boolean myIsBOF = true;
030: private boolean myIsEOF = false;
031: private boolean myIsClosed = false;
032:
033: public ChunkedInputStream(final InputStream in, String charset) {
034: myInputStream = in;
035: myPosition = 0;
036: myCharset = charset;
037: }
038:
039: public int read() throws IOException {
040: if (myIsClosed) {
041: throw new IOException("Attempted read from closed stream.");
042: }
043: if (myIsEOF) {
044: return -1;
045: }
046: if (myPosition >= myChunkSize) {
047: nextChunk();
048: if (myIsEOF) {
049: return -1;
050: }
051: }
052: myPosition++;
053: return myInputStream.read();
054: }
055:
056: public int read(byte[] b, int off, int len) throws IOException {
057: if (myIsClosed) {
058: throw new IOException("Attempted read from closed stream.");
059: }
060: if (myIsEOF) {
061: return -1;
062: }
063: if (myPosition >= myChunkSize) {
064: nextChunk();
065: if (myIsEOF) {
066: return -1;
067: }
068: }
069: len = Math.min(len, myChunkSize - myPosition);
070: int count = myInputStream.read(b, off, len);
071: myPosition += count;
072: return count;
073: }
074:
075: public int read(byte[] b) throws IOException {
076: return read(b, 0, b.length);
077: }
078:
079: private void readCRLF() throws IOException {
080: int cr = myInputStream.read();
081: int lf = myInputStream.read();
082: if ((cr != '\r') || (lf != '\n')) {
083: throw new IOException("CRLF expected at end of chunk: "
084: + cr + "/" + lf);
085: }
086: }
087:
088: private void nextChunk() throws IOException {
089: if (!myIsBOF) {
090: readCRLF();
091: }
092: myChunkSize = getChunkSizeFromInputStream(myInputStream,
093: myCharset);
094: myIsBOF = false;
095: myPosition = 0;
096: if (myChunkSize == 0) {
097: myIsEOF = true;
098: }
099: }
100:
101: private static int getChunkSizeFromInputStream(
102: final InputStream in, String charset) throws IOException {
103: ByteArrayOutputStream baos = new ByteArrayOutputStream();
104: // States: 0=normal, 1=\r was scanned, 2=inside quoted string,
105: // -1=end
106: int state = 0;
107: while (state != -1) {
108: int b = in.read();
109: if (b == -1) {
110: throw new IOException(
111: "chunked stream ended unexpectedly");
112: }
113: switch (state) {
114: case 0:
115: switch (b) {
116: case '\r':
117: state = 1;
118: break;
119: case '\"':
120: state = 2;
121: /* fall through */
122: default:
123: baos.write(b);
124: }
125: break;
126:
127: case 1:
128: if (b == '\n') {
129: state = -1;
130: } else {
131: // this was not CRLF
132: throw new IOException(
133: "Protocol violation: Unexpected"
134: + " single newline character in chunk size");
135: }
136: break;
137:
138: case 2:
139: switch (b) {
140: case '\\':
141: b = in.read();
142: baos.write(b);
143: break;
144: case '\"':
145: state = 0;
146: /* fall through */
147: default:
148: baos.write(b);
149: }
150: break;
151: default:
152: throw new RuntimeException("assertion failed");
153: }
154: }
155:
156: // parse data
157: String dataString = new String(baos.toByteArray(), charset);
158: int separator = dataString.indexOf(';');
159: dataString = (separator > 0) ? dataString.substring(0,
160: separator).trim() : dataString.trim();
161:
162: int result;
163: try {
164: result = Integer.parseInt(dataString.trim(), 16);
165: } catch (NumberFormatException e) {
166: throw new IOException("Bad chunk size: " + dataString);
167: }
168: return result;
169: }
170:
171: public void close() throws IOException {
172: if (!myIsClosed) {
173: try {
174: if (!myIsEOF) {
175: FixedSizeInputStream.consumeRemaining(this );
176: }
177: } finally {
178: myIsEOF = true;
179: myIsClosed = true;
180: }
181: }
182: }
183: }
|