/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.communication.protocol.http;
import java.io.IOException;
import java.io.OutputStream;
import org.ws4d.java.constants.HTTPConstants;
import org.ws4d.java.constants.Specialchars;
/**
* A nice chunked HTTP output stream. Creates chunks from the incoming data.
* WARNING: The stream MUST be finished in order to write the last chunk header!
*/
public class ChunkedOutputStream extends OutputStream {
private static final int CHUNK_SIZE = 8192;
private OutputStream out = null;
private byte[] buffer = null;
private int i = 0;
private boolean trailer = false;
private long totalLength = 0;
private boolean last = false;
public ChunkedOutputStream(OutputStream out, int chunkSize, boolean trailer) {
this.out = out;
this.trailer = trailer;
buffer = new byte[chunkSize];
}
public ChunkedOutputStream(OutputStream out, boolean trailer) {
this(out, CHUNK_SIZE, trailer);
}
/*
* (non-Javadoc)
* @see java.io.OutputStream#write(int)
*/
public void write(int arg0) throws IOException {
if (last) return;
buffer[i++] = (byte) arg0;
if (i == buffer.length) {
flushBuffer();
}
}
/*
* (non-Javadoc)
* @see java.io.OutputStream#write(byte[], int, int)
*/
public void write(byte[] b, int off, int len) throws IOException {
if (last) return;
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
int numberOfBytes = i + len;
if (numberOfBytes >= buffer.length) {
HTTPChunkHeader chunk = new HTTPChunkHeader(numberOfBytes, null, null);
chunk.toStream(out);
out.write(buffer, 0, i);
i = 0;
out.write(b, off, len);
totalLength += numberOfBytes;
out.write(Specialchars.CR);
out.write(Specialchars.LF);
} else {
System.arraycopy(b, off, buffer, i, len);
i += len;
}
}
/**
* NECESSARY! Writes the last chunk to stream.
*
* @throws IOException
*/
public void flush() throws IOException {
flushBuffer();
out.flush();
}
public void close() throws IOException {
last();
out.close();
}
public static void writeLastChunk(ChunkedOutputStream out) throws IOException {
out.last();
}
private void flushBuffer() throws IOException {
// flush current buffer contents
if (i > 0) {
HTTPChunkHeader chunk = new HTTPChunkHeader(i, null, null);
chunk.toStream(out);
out.write(buffer, 0, i);
totalLength += i;
i = 0;
out.write(Specialchars.CR);
out.write(Specialchars.LF);
}
}
/**
* Writes the last chunk to the stream.
*
* @throws IOException
*/
private void last() throws IOException {
if (last) return;
flushBuffer();
buffer = null;
// last chunk!
HTTPChunkHeader chunk = new HTTPChunkHeader(0, null, null);
chunk.toStream(out);
// RFC 2614 Sec. 14.10
if (trailer) {
out.write((HTTPConstants.HTTP_HEADER_CONTENT_LENGTH + ": " + String.valueOf(totalLength)).toString().getBytes());
out.write(Specialchars.CR);
out.write(Specialchars.LF);
}
out.write(Specialchars.CR);
out.write(Specialchars.LF);
last = true;
out.flush();
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((out == null) ? 0 : out.hashCode());
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ChunkedOutputStream other = (ChunkedOutputStream) obj;
if (out == null) {
if (other.out != null) return false;
} else if (!out.equals(other.out)) return false;
return true;
}
}