/*******************************************************************************
* 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.InputStream;
import org.ws4d.java.communication.ProtocolException;
import org.ws4d.java.constants.HTTPConstants;
import org.ws4d.java.io.buffered.BufferedInputStream;
import org.ws4d.java.util.Sync;
/**
* HTTP input stream wrapper. This class wraps the input stream and controls the
* length of data read.
*/
public class HTTPInputStream extends InputStream {
private InputStream in = null;
private int size = 0;
private int read = 0;
private String encoding = null;
private boolean end = false;
private InputStream wrapped = null;
protected HTTPChunkHeader chunkedheader = null;
protected boolean chunked = false;
private Sync notify = null;
/**
* Creates a HTTP input stream.
*/
public HTTPInputStream(InputStream in, String encoding, int size) {
this(in, encoding, size, null);
}
/**
* Creates a HTTP input stream with synchronization.
*/
public HTTPInputStream(InputStream in, String encoding, int size, Sync notify) {
this.in = in;
this.encoding = encoding;
if (HTTPConstants.HTTP_HEADERVALUE_TRANSFERCODING_CHUNKED.equals(encoding)) {
chunked = true;
}
if (size < 0) {
this.size = 0;
} else {
this.size = size;
}
this.notify = notify;
}
/*
* (non-Javadoc)
* @see java.io.InputStream#close()
*/
public void close() throws IOException {
try {
if (in == null) {
throw new IOException("InputStream not available");
}
if (notify != null) {
synchronized (notify) {
notify.notifyNow();
}
}
if (wrapped != null) {
wrapped.close();
}
in.close();
} catch (IOException e) {
if (notify != null) {
synchronized (notify) {
notify.notifyNow(e);
}
}
throw e;
}
}
/*
* (non-Javadoc)
* @see java.io.InputStream#read()
*/
public synchronized int read() throws IOException {
try {
if (in == null) {
throw new IOException("InputStream not available");
}
if (end) {
if (notify != null) {
synchronized (notify) {
notify.notifyNow();
}
}
return -1;
}
int k = -1;
if (!chunked) {
k = readNonChunked();
} else {
k = readChunked();
}
if (notify != null && k == -1) {
synchronized (notify) {
notify.notifyNow();
}
}
return k;
} catch (IOException e) {
if (notify != null) {
synchronized (notify) {
notify.notifyNow(e);
}
}
throw e;
}
}
private int readNonChunked() throws IOException {
/*
* HTTP body not chunked
*/
if (size > 0) {
if (wrapped == null) {
wrapped = wrap(size);
}
read++;
return wrapped.read();
}
/*
* The size MUST NOT be < 0.
*/
return -1;
}
private int readChunked() throws IOException {
/*
* HTTP body chunked
*/
if (chunkedheader == null) {
readChunkHeader();
}
int chunksize = chunkedheader.getSize();
if (chunksize == 0) {
end = true;
return -1;
}
if (wrapped == null) {
wrapped = wrap(chunksize);
}
int k = wrapped.read();
/*
* Chunk done ...
*/
if (k == -1) {
chunkedheader = null;
wrapped = null;
read = 0;
/*
* next chunk ...
*/
HTTPUtil.readRequestLine(in);
return readChunked();
}
read++;
return k;
}
private void readChunkHeader() throws IOException {
try {
chunkedheader = HTTPUtil.readChunkHeader(in);
} catch (ProtocolException e) {
chunkedheader = null;
throw new IOException("Cannot read HTTP chunk header. " + e.getMessage());
}
}
private InputStream wrap(int size) {
// if (CommunicationConstants.BUFFERED_INPUT) {
// return new BufferedInputStream(in, size);
// } else {
// return new WrappedInputStream(in, size);
// }
return new WrappedInputStream(in, size);
}
/*
* (non-Javadoc)
* @see java.io.InputStream#available()
*/
public int available() throws IOException {
try {
if (end) return 0;
if (!chunked) {
return size - read;
}
if (chunkedheader == null) {
// read first chunk header
readChunkHeader();
}
return (chunkedheader.getSize() - read);
} catch (IOException e) {
if (notify != null) {
synchronized (notify) {
notify.notifyNow(e);
}
}
throw e;
}
}
/**
* Returns the encoding for this stream.
*
* @return the stream encoding.
*/
public String getEncoding() {
return encoding;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((in == null) ? 0 : in.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;
HTTPInputStream other = (HTTPInputStream) obj;
if (in == null) {
if (other.in != null) return false;
} else if (!in.equals(other.in)) return false;
return true;
}
/**
* This input stream handles chunks if no {@link BufferedInputStream} is
* used.
*/
private class WrappedInputStream extends InputStream {
private int size = 0;
private int read = 0;
private InputStream in = null;
WrappedInputStream(InputStream in, int size) {
this.in = in;
this.size = size;
}
/*
* (non-Javadoc)
* @see java.io.InputStream#available()
*/
public int available() throws IOException {
return read - size;
}
public int read() throws IOException {
if (read == size) return -1;
read++;
return in.read();
}
private HTTPInputStream getOuterType() {
return HTTPInputStream.this;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + ((in == null) ? 0 : in.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;
WrappedInputStream other = (WrappedInputStream) obj;
if (!getOuterType().equals(other.getOuterType())) return false;
if (in == null) {
if (other.in != null) return false;
} else if (!in.equals(other.in)) return false;
return true;
}
}
}