001: // ChunkedOutputStream.java
002: // $Id: ChunkedOutputStream.java,v 1.6 2002/07/03 15:55:40 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.DataOutputStream;
009: import java.io.FilterOutputStream;
010: import java.io.IOException;
011: import java.io.OutputStream;
012:
013: public class ChunkedOutputStream extends DataOutputStream {
014:
015: private final static byte hexaTable[] = { (byte) '0', (byte) '1',
016: (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
017: (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B',
018: (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };
019: private static final byte crlf[] = { (byte) 13, (byte) 10 };
020: private static final byte zeroChunk[] = { (byte) 48, (byte) 13,
021: (byte) 10 };
022: private static final int DEFAULT_CHUNK_SIZE = 512;
023:
024: /**
025: * The chunking buffer.
026: */
027: protected byte buffer[] = null;
028: /**
029: * Where to put next piece of data (current chunk size).
030: */
031: protected int bufptr = 0;
032: /**
033: * The chunk size to use (defaults to buffer size).
034: */
035: protected int chunksize = -1;
036: /**
037: * Internal buffer to hold chunk size.
038: */
039: protected byte bheader[] = new byte[32];
040:
041: /**
042: * Send the close chunk.
043: * @Exception IOException If writing fails.
044: */
045:
046: protected void sendClose() throws IOException {
047: out.write(zeroChunk, 0, 3);
048: out.write(crlf, 0, 2);
049: out.flush();
050: }
051:
052: /**
053: * Send given buffer as a full chunk.
054: * @param b The buffer that contains the data to emit.
055: * @param off Offset of chunk in above buffer.
056: * @param len Length of chunk.
057: * @exception IOException If writing fails.
058: */
059:
060: protected void sendChunk(byte b[], int off, int len)
061: throws IOException {
062: // Anything to send ?
063: if (len == 0)
064: return;
065: // Send one chunk:
066: int size = len;
067: int blen = 3;
068: // we dump the hexa size of the header backward
069: bheader[30] = ((byte) 13); // \r
070: bheader[31] = ((byte) 10); // \n
071: while (size > 15) {
072: bheader[32 - blen] = hexaTable[size % 16];
073: size >>= 4;
074: blen++;
075: }
076: bheader[32 - blen] = hexaTable[size];
077: out.write(bheader, 32 - blen, blen);
078: out.write(b, off, len);
079: out.write(crlf, 0, 2);
080: out.flush();
081: }
082:
083: /**
084: * Send current chunk of data.
085: * @exception IOException If writing fails.
086: */
087:
088: protected void sendChunk() throws IOException {
089: if (bufptr == 0)
090: return;
091: sendChunk(buffer, 0, bufptr);
092: bufptr = 0;
093: }
094:
095: /**
096: * Append one byte to pending chunk.
097: * @param v The byte to append.
098: * @exception IOException If writing fails.
099: */
100:
101: protected final void append(int v) throws IOException {
102: if (bufptr + 1 >= chunksize)
103: sendChunk();
104: buffer[bufptr++] = (byte) (v & 0xff);
105: }
106:
107: /**
108: * Append a bunch of bytes to current pending chunk.
109: * @param b The chunk of bytes to add.
110: * @param off Offset of chunk within above buffer.
111: * @param len Length of chunk.
112: * @exception IOException If writing fails.
113: */
114:
115: protected final void append(byte b[], int off, int len)
116: throws IOException {
117: if (bufptr + len >= chunksize)
118: sendChunk();
119: if (len < buffer.length) {
120: System.arraycopy(b, off, buffer, bufptr, len);
121: bufptr += len;
122: } else {
123: sendChunk(b, off, len);
124: }
125: }
126:
127: /**
128: * Close that encoding stream.
129: * @exception IOException If writing fails.
130: */
131:
132: public void close() throws IOException {
133: close(true);
134: }
135:
136: /**
137: * Close that encoding stream.
138: * @exception IOException If writing fails.
139: */
140: public void close(boolean really) throws IOException {
141: if (really) {
142: sendChunk();
143: sendClose();
144: super .close();
145: } else {
146: sendChunk();
147: sendClose();
148: }
149: }
150:
151: /**
152: * Flush pending output.
153: * @exception IOException If writing fails.
154: */
155:
156: public void flush() throws IOException {
157: sendChunk();
158: super .flush();
159: }
160:
161: /**
162: * Write one byte of output.
163: * @param v The byte to write.
164: * @exception IOException If writing fails.
165: */
166:
167: public void write(int b) throws IOException {
168: append(b);
169: }
170:
171: /**
172: * Write an array of bytes.
173: * @param b The data to write.
174: * @param off Offfset within above buffer.
175: * @param len Length of data to write.
176: * @exception IOException If writing fails.
177: */
178:
179: public void write(byte b[], int off, int len) throws IOException {
180: append(b, off, len);
181: }
182:
183: /**
184: * Create a chunk encoder, using the provided buffer.
185: * @param buffer The buffer to use (determines the default chunk size).
186: * @param put The DataOutputStream to write encoded data to.
187: */
188:
189: public ChunkedOutputStream(byte buffer[], DataOutputStream out) {
190: super (out);
191: this .buffer = buffer;
192: this .chunksize = buffer.length;
193: }
194:
195: /**
196: * Create a chunk encoder.
197: * @param out The DataOutputStream to write to.
198: */
199:
200: public ChunkedOutputStream(DataOutputStream out) {
201: this (new byte[DEFAULT_CHUNK_SIZE], out);
202: }
203:
204: }
|