/** * Copyright (c) 2009 - 2011 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.utils.net * * This software is licensed under the Artistic License 2.0, * see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php * for details */ package org.appwork.utils.net; import java.io.IOException; import java.io.OutputStream; /** * @author daniel,ChunkedOutputStream, see rfc2616#section-3.6 * */ public class ChunkedOutputStream extends OutputStream { private final OutputStream os; private final byte[] buffer; private int bufUsed = 0; boolean closed = false; public ChunkedOutputStream(final OutputStream os) { this(os, 4096); } public ChunkedOutputStream(final OutputStream os, final byte[] buffer) { this.os = os; this.buffer = buffer; } public ChunkedOutputStream(final OutputStream os, final int bufSize) { this.os = os; this.buffer = new byte[bufSize]; } private void _flush(final boolean emptyFlush) throws IOException { if (this.closed == false) { if (this.bufUsed > 0 || emptyFlush) { final byte[] bytes = Integer.toHexString(this.bufUsed).getBytes(); /* send chunk size */ this.os.write(bytes); this.os.write((byte) '\r'); this.os.write((byte) '\n'); /* send chunk data */ if (this.bufUsed > 0 || emptyFlush) { /* flush buffered data if any available */ if (this.bufUsed > 0) { this.os.write(this.buffer, 0, this.bufUsed); } this.os.write((byte) '\r'); this.os.write((byte) '\n'); } this.bufUsed = 0; } } } @Override public synchronized void close() throws IOException { if (this.closed == false) { this.sendEOF(); this.closed = true; } this.os.close(); } @Override public synchronized void flush() throws IOException { this._flush(false); this.os.flush(); } public synchronized void sendEOF() throws IOException { /* flush rest available chunk data */ this._flush(false); /* send empty chunk = EOF */ this._flush(true); this.os.flush(); this.closed = true; } @Override public synchronized void write(final byte b[], final int off, final int len) throws IOException { if (len == 0) { return; } if (this.bufUsed + len < this.buffer.length) { /* buffer has enough space for len bytes */ /* fill buffer */ System.arraycopy(b, off, this.buffer, this.bufUsed, len); this.bufUsed += len; } else { /* buffer has not enough space for len bytes, send as chunk */ final byte[] bytes = Integer.toHexString(this.bufUsed + len).getBytes(); /* send chunk size */ this.os.write(bytes); this.os.write((byte) '\r'); this.os.write((byte) '\n'); /* send chunk data */ if (this.bufUsed > 0) { /* flush buffered data if any available */ this.os.write(this.buffer, 0, this.bufUsed); this.bufUsed = 0; } this.os.write(b, off, len); this.os.write((byte) '\r'); this.os.write((byte) '\n'); } } @Override public synchronized void write(final int b) throws IOException { if (this.bufUsed == this.buffer.length) { /* buffer full,send as chunked data */ final byte[] bytes = Integer.toHexString(this.buffer.length).getBytes(); /* send chunk size */ this.os.write(bytes); this.os.write((byte) '\r'); this.os.write((byte) '\n'); /* send buffer as chunk data */ this.os.write(this.buffer); this.os.write((byte) '\r'); this.os.write((byte) '\n'); this.bufUsed = 0; } /* fill buffer */ this.buffer[this.bufUsed++] = (byte) b; } }