001: /*
002: License $Id: ChunkedOutputStream.java,v 1.6 2003/09/13 04:59:55 hendriks73 Exp $
003:
004: Copyright (c) 2001-2005 tagtraum industries.
005:
006: LGPL
007: ====
008:
009: jo! is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: jo! is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023: For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
024:
025:
026: Sun license
027: ===========
028:
029: This release contains software by Sun Microsystems. Therefore
030: the following conditions have to be met, too. They apply to the
031: files
032:
033: - lib/mail.jar
034: - lib/activation.jar
035: - lib/jsse.jar
036: - lib/jcert.jar
037: - lib/jaxp.jar
038: - lib/crimson.jar
039: - lib/servlet.jar
040: - lib/jnet.jar
041: - lib/jaas.jar
042: - lib/jaasmod.jar
043:
044: contained in this release.
045:
046: a. Licensee may not modify the Java Platform
047: Interface (JPI, identified as classes contained within the javax
048: package or any subpackages of the javax package), by creating additional
049: classes within the JPI or otherwise causing the addition to or modification
050: of the classes in the JPI. In the event that Licensee creates any
051: Java-related API and distribute such API to others for applet or
052: application development, you must promptly publish broadly, an accurate
053: specification for such API for free use by all developers of Java-based
054: software.
055:
056: b. Software is confidential copyrighted information of Sun and
057: title to all copies is retained by Sun and/or its licensors. Licensee
058: shall not modify, decompile, disassemble, decrypt, extract, or otherwise
059: reverse engineer Software. Software may not be leased, assigned, or
060: sublicensed, in whole or in part. Software is not designed or intended
061: for use in on-line control of aircraft, air traffic, aircraft navigation
062: or aircraft communications; or in the design, construction, operation or
063: maintenance of any nuclear facility. Licensee warrants that it will not
064: use or redistribute the Software for such purposes.
065:
066: c. Software is provided "AS IS," without a warranty
067: of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
068: INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
069: PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
070:
071: d. This License is effective until terminated. Licensee may
072: terminate this License at any time by destroying all copies of Software.
073: This License will terminate immediately without notice from Sun if Licensee
074: fails to comply with any provision of this License. Upon such termination,
075: Licensee must destroy all copies of Software.
076:
077: e. Software, including technical data, is subject to U.S.
078: export control laws, including the U.S. Export Administration Act and its
079: associated regulations, and may be subject to export or import regulations
080: in other countries. Licensee agrees to comply strictly with all such
081: regulations and acknowledges that it has the responsibility to obtain
082: licenses to export, re-export, or import Software. Software may not be
083: downloaded, or otherwise exported or re-exported (i) into, or to a national
084: or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
085: country to which the U.S. has embargoed goods; or (ii) to anyone on the
086: U.S. Treasury Department's list of Specially Designated Nations or the U.S.
087: Commerce Department's Table of Denial Orders.
088:
089:
090: Feedback
091: ========
092:
093: We encourage your feedback and suggestions and want to use your feedback to
094: improve the Software. Send all such feedback to:
095: <feedback@tagtraum.com>
096:
097: For more information on tagtraum industries and jo!
098: please see <http://www.tagtraum.com/>.
099:
100:
101: */
102: package com.tagtraum.framework.http;
103:
104: import java.io.FilterOutputStream;
105: import java.io.IOException;
106: import java.io.OutputStream;
107: import java.io.UnsupportedEncodingException;
108:
109: /**
110: * OutputStream that supports chunked encoding as defined in HTTP/1.1.
111: *
112: * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
113: * @version 1.1beta1 $Id: ChunkedOutputStream.java,v 1.6 2003/09/13 04:59:55 hendriks73 Exp $
114: */
115: public class ChunkedOutputStream extends FilterOutputStream {
116:
117: public final static byte C_CRLF[] = { (byte) '\r', (byte) '\n' };
118:
119: /**
120: * Bytes in buffer.
121: */
122: private int pos;
123:
124: /**
125: * Chunkbuffer.
126: */
127: private byte buf[];
128:
129: /**
130: * Chunkextension.
131: */
132: private String chunkExtension;
133:
134: /**
135: * Chunkextension bytes.
136: */
137: private byte[] chunkExtensionBytes;
138:
139: /**
140: * ChunkSize.
141: */
142: private int chunkSize;
143:
144: /**
145: * ChunkSize bytes.
146: */
147: private byte[] chunkSizeBytes;
148:
149: /**
150: * Trailer.
151: */
152: private HttpHeader trailer;
153:
154: /**
155: * Flag finished.
156: */
157: private boolean finished;
158:
159: /**
160: * Writed 8k chunks in the supplied OutputStream.
161: *
162: * @param out the stream to write into
163: */
164: public ChunkedOutputStream(OutputStream out) throws IOException {
165: this (out, 1024 * 8);
166: }
167:
168: /**
169: * Writes chunks of the specified size in the supplied OutputStream.
170: *
171: * @param chunkSize the size of the chunks in bytes.
172: * @param out the stream to write into
173: */
174: public ChunkedOutputStream(OutputStream out, int chunkSize)
175: throws IOException {
176: super (out);
177: trailer = new HttpHeader();
178: if (chunkSize <= 0) {
179: throw new IllegalArgumentException("Chunk size <= 0");
180: }
181: this .chunkSize = chunkSize;
182: chunkSizeBytes = Integer.toHexString(chunkSize).getBytes(
183: "ASCII");
184: buf = new byte[chunkSize];
185: }
186:
187: /**
188: * Writes the specified byte to this output stream. The general
189: * contract for <code>write</code> is that one byte is written
190: * to the output stream. The byte to be written is the eight
191: * low-order bits of the argument <code>b</code>. The 24
192: * high-order bits of <code>b</code> are ignored.
193: * @param b the <code>byte</code>.
194: * @exception IOException if an I/O error occurs. In particular,
195: * an <code>IOException</code> may be thrown if the
196: * output stream has been closed.
197: */
198: public void write(int b) throws IOException {
199: if (pos < buf.length) {
200: buf[pos] = (byte) b;
201: pos++;
202: } else {
203: flush();
204: write(b);
205: }
206: }
207:
208: /**
209: * Indicates whether this stream has already been finished.
210: *
211: * @return <code>true</code> or <code>false</code>
212: */
213: public boolean isFinished() {
214: return finished;
215: }
216:
217: /**
218: * Writes <code>len</code> bytes from the specified byte array
219: * starting at offset <code>off</code> to this output stream.
220: * The general contract for <code>write(b, off, len)</code> is that
221: * some of the bytes in the array <code>b</code> are written to the
222: * output stream in order; element <code>b[off]</code> is the first
223: * byte written and <code>b[off+len-1]</code> is the last byte written
224: * by this operation.
225: * <p>
226: * The <code>write</code> method of <code>OutputStream</code> calls
227: * the write method of one argument on each of the bytes to be
228: * written out. Subclasses are encouraged to override this method and
229: * provide a more efficient implementation.
230: * <p>
231: * If <code>b</code> is <code>null</code>, a
232: * <code>NullPointerException</code> is thrown.
233: * <p>
234: * If <code>off</code> is negative, or <code>len</code> is negative, or
235: * <code>off+len</code> is greater than the length of the array
236: * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
237: *
238: * @param b the data.
239: * @param off the start offset in the data.
240: * @param len the number of bytes to write.
241: * @exception IOException if an I/O error occurs. In particular,
242: * an <code>IOException</code> is thrown if the output
243: * stream is closed.
244: */
245: public void write(byte b[], int off, int len) throws IOException {
246: if (b == null) {
247: throw new NullPointerException();
248: } else if ((off < 0) || (off > b.length) || (len < 0)
249: || ((off + len) > b.length) || ((off + len) < 0)) {
250: throw new IndexOutOfBoundsException();
251: } else if (len == 0) {
252: return;
253: }
254: int available = buf.length - pos;
255: if (len > available) {
256: System.arraycopy(b, off, buf, pos, available);
257: pos += available;
258: flush();
259: write(b, off + available, len - available);
260: } else {
261: System.arraycopy(b, off, buf, pos, len);
262: pos += len;
263: }
264: }
265:
266: /**
267: * Sets the chunk extension.
268: *
269: * @param chunkExtension
270: */
271: public void setChunkExtension(String chunkExtension) {
272: this .chunkExtension = chunkExtension;
273: if (chunkExtension != null) {
274: try {
275: chunkExtensionBytes = chunkExtension.getBytes("ASCII");
276: } catch (UnsupportedEncodingException uee) {
277: // this should not be possible
278: uee.printStackTrace();
279: // fallback
280: chunkExtensionBytes = chunkExtension.getBytes();
281: }
282: } else
283: chunkExtensionBytes = null;
284: }
285:
286: /**
287: * Returns the size of the chunks.
288: */
289: public int getChunkSize() {
290: return chunkSize;
291: }
292:
293: /**
294: * Returns the chunkextension.
295: *
296: * @return chunkextension
297: */
298: public String getChunkExtension() {
299: return chunkExtension;
300: }
301:
302: /**
303: * Returns the trailer.
304: *
305: * @return trailer
306: */
307: public HttpHeader getTrailer() {
308: return trailer;
309: }
310:
311: /**
312: * Sets the trailer.
313: *
314: * @param trailer
315: */
316: public void setTrailer(HttpHeader trailer) {
317: this .trailer = trailer;
318: }
319:
320: /**
321: * Flushes this output stream and forces any buffered output bytes
322: * to be written out. The general contract of <code>flush</code> is
323: * that calling it is an indication that, if any bytes previously
324: * written have been buffered by the implementation of the output
325: * stream, such bytes should immediately be written to their
326: * intended destination.
327: *
328: * @exception IOException if an I/O error occurs.
329: */
330: public void flush() throws IOException {
331: if (pos == 0) {
332: return;
333: }
334: if (pos == chunkSize) {
335: out.write(chunkSizeBytes);
336: } else {
337: out.write(Integer.toHexString(pos).getBytes("ASCII"));
338: }
339: if (chunkExtension != null) {
340: out.write(chunkExtensionBytes);
341: }
342: out.write(C_CRLF);
343: out.write(buf, 0, pos);
344: out.write(C_CRLF);
345: pos = 0;
346: }
347:
348: /**
349: * Writes the trailer and the last chunk, but does not close
350: * the underlying stream.
351: *
352: * @exception IOException if an I/O error occurs.
353: * @see #close();
354: */
355: public void finish() throws IOException {
356: if (finished) {
357: return;
358: }
359: finished = true;
360: flush();
361: out.write('0');
362: if (chunkExtension != null) {
363: out.write(chunkExtensionBytes);
364: }
365: out.write(C_CRLF);
366: trailer.write(out);
367: out.write(C_CRLF);
368: }
369:
370: /**
371: * Calls {@link #finish()} and closes the underlying stream.
372: *
373: * @exception IOException if an I/O error occurs.
374: * @see #finish();
375: */
376: public void close() throws IOException {
377: finish();
378: out.close();
379: }
380:
381: }
|