package ddth.dasp.servlet.rp;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ddth.dasp.common.rp.IRequestParser;
import ddth.dasp.common.rp.MalformedRequestException;
import ddth.dasp.common.rp.RequestParsingInteruptedException;
/**
* Abstract implementation of {@link IRequestParser}.
*
* @author NBThanh <btnguyen2k@gmail.com>
*/
public abstract class AbstractRequestParser implements IRequestParser {
private final static Logger LOGGER = LoggerFactory.getLogger(AbstractRequestParser.class);
private long maxContentLength = DEFAULT_MAX_POST_SIZE;
private int timeout = DEFAULT_TIMEOUT;
private String requestContent;
private boolean isParsed = false;
private boolean isInterrupted = false;
private boolean isMalformed = false;
private boolean isDirty = true;
private ByteArrayOutputStream buffer = new ByteArrayOutputStream(4096);
private long startParsingTimestamp = -1;
/**
* {@inheritDoc}
*/
@Override
public void reset() {
this.isParsed = false;
this.requestContent = null;
this.isInterrupted = false;
this.isMalformed = false;
this.isDirty = true;
this.requestContent = null;
this.buffer = new ByteArrayOutputStream(4096);
this.startParsingTimestamp = -1;
}
/**
* Gets timestamp when the parsing started.
*
* @return
*/
public long getStartParsingTimestamp() {
return startParsingTimestamp;
}
/**
* {@inheritDoc}
*/
public long getMaxContentLength() {
return this.maxContentLength;
}
/**
* {@inheritDoc}
*/
public void setMaxContentLength(long maxContentLength) {
this.maxContentLength = maxContentLength < 1 ? DEFAULT_MAX_POST_SIZE : maxContentLength;
// if (this.maxContentLength < 1) {
// this.maxContentLength = DEFAULT_MAX_CONTENT_LENGTH;
// }
}
/**
* {@inheritDoc}
*/
public int getTimeout() {
return this.timeout;
}
/**
* {@inheritDoc}
*/
public void setTimeout(int timeout) {
this.timeout = timeout < 1 ? DEFAULT_TIMEOUT : timeout;
if (this.timeout < 1) {
this.timeout = DEFAULT_TIMEOUT;
}
}
/**
* {@inheritDoc}
*
* Note: This method returns the data from its internal buffer.
*/
@Override
public byte[] getRawRequestContent() {
return this.buffer.size() == 0 ? null : this.buffer.toByteArray();
}
/**
* {@inheritDoc}
*/
@Override
public String getRequestContent() {
if (this.requestContent == null || this.isDirty) {
try {
this.requestContent = this.buffer.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
this.requestContent = null;
}
this.isDirty = false;
}
return this.requestContent;
}
/**
* {@inheritDoc}
*
* @throws UnsupportedEncodingException
*/
@Override
public String getRequestContent(String charset) throws UnsupportedEncodingException {
return this.buffer.toString(charset);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isInterrupted() {
return this.isInterrupted;
}
/**
* {@inheritDoc}
*
* Note: this method sets its internal "is interrupted" flag to
* <code>true</code>.
*/
@Override
public void interrupt() {
this.isInterrupted = true;
String logMessage = "Request parsing timed out [" + getTimeout() + "]!";
LOGGER.warn(logMessage);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isMalformed() {
return this.isMalformed;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isParsed() {
return this.isParsed;
}
/**
* Marks the request as malformed.
*/
protected void markMalformedRequest() {
this.isMalformed = true;
}
/**
* Writes data to the internal buffer.
*
* @param data
* byte[]
* @throws MalformedRequestException
* thrown when the buffer is overload
*/
protected void write(byte[] data) throws MalformedRequestException {
if (data != null) {
write(data, 0, data.length);
}
}
/**
* Writes data to the internal buffer.
*
* @param data
* byte[]
* @param offset
* int
* @param length
* int
* @throws MalformedRequestException
* thrown when the buffer is overload
*/
protected void write(byte[] data, int offset, int length) throws MalformedRequestException {
if (data != null) {
this.isDirty = true;
this.buffer.write(data, offset, length);
if (this.buffer.size() > getMaxContentLength()) {
String logMessage = "Request input exceeds maximum length limit ["
+ getMaxContentLength() + "]!";
LOGGER.error(logMessage);
markMalformedRequest();
throw new MalformedRequestException();
}
}
}
/**
* Writes data to the internal buffer.
*
* @param data
* String
* @throws MalformedRequestException
* thrown when the buffer is overload
*/
protected void write(String data) throws MalformedRequestException {
if (data != null) {
try {
write(data.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
// IGNORE! It should never happen!
}
}
}
/**
* {@inheritDoc}
*
* Note: this method creates a "safeguard" thread and calls
* {@link #internalParseRequest()}. The safeguard thread monitors and
* notifies the parser if timeout has occurred.
*/
@Override
public void parseRequest() throws RequestParsingInteruptedException, MalformedRequestException {
if (this.isParsed) {
return;
}
this.isParsed = true;
// TimerTask task = new SafeguardTask(this);
// DaspGlobal.getContextTimer().schedule(task, getTimeout());
try {
startParsingTimestamp = System.currentTimeMillis();
internalPreParseRequest();
internalParseRequest();
internalPostParseRequest();
} finally {
// ((SafeguardTask) task).finish();
}
}
// static class SafeguardTask extends TimerTask {
//
// private boolean finish = false;
// private IRequestParser requestParser;
//
// public SafeguardTask(IRequestParser requestParser) {
// this.requestParser = requestParser;
// }
//
// public void finish() {
// this.finish = true;
// }
//
// @Override
// public void run() {
// if (!finish) {
// requestParser.interrupt();
// }
// }
// }
/**
* This method is called before {@link #internalParseRequest()}. Sub-class
* may override it to perform pre-parsing work.
*/
protected void internalPreParseRequest() {
// empty
}
/**
* Sub-class overrides this method to implement its own business.
*
* Note: sub-class should check for {@link #isInterrupted()} flag and act
* accordingly.
*
* @throws RequestParsingInteruptedException
* @throws MalformedRequestException
*/
protected abstract void internalParseRequest() throws RequestParsingInteruptedException,
MalformedRequestException;
/**
* This method is called after {@link #internalParseRequest()}. Sub-class
* may override it to perform post-parsing work.
*/
protected void internalPostParseRequest() {
// empty
}
}