/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jlibs.nio.http;
import jlibs.core.lang.Util;
import jlibs.nio.Debugger;
import jlibs.nio.Reactor;
import jlibs.nio.filters.BufferInput;
import jlibs.nio.filters.ChunkedInput;
import jlibs.nio.filters.FixedLengthInput;
import jlibs.nio.http.msg.*;
import jlibs.nio.http.msg.parser.HeadersParser;
import jlibs.nio.http.msg.parser.MessageParser;
import jlibs.nio.http.util.Encoding;
import jlibs.nio.listeners.Task;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import static java.nio.channels.SelectionKey.OP_READ;
import static jlibs.nio.Debugger.HTTP;
import static jlibs.nio.Debugger.println;
/**
* @author Santhosh Kumar Tekuri
*/
public class ReadMessage extends Task{
private final long maxHeadSize;
private final MessageParser parser;
protected ReadMessage(long maxHeadSize, MessageParser parser){
super(OP_READ);
this.maxHeadSize = maxHeadSize;
this.parser = parser;
}
@Override
protected boolean process(int readyOp) throws IOException{
while(true){
int read;
try{
read = in.read(buffer);
}catch(IOException ex){
if(consumed+buffer.position()==0 && message instanceof Request)
throw IGNORABLE_EOF_EXCEPTION;
throw ex;
}
if(read==0){
in.addReadInterest();
return false;
}
if(read==-1){
if(consumed==0 && message instanceof Request)
throw IGNORABLE_EOF_EXCEPTION;
throw message.badMessage("Unexpected EOF");
}
buffer.flip();
int pos = buffer.position();
parser.consumed = consumed;
boolean done = parser.parse(buffer, false);
consumed += buffer.position()-pos;
if(maxHeadSize>0 && (done ? consumed>maxHeadSize : consumed>=maxHeadSize)){
if(message instanceof Request)
throw Status.REQUEST_HEADER_FIELDS_TOO_LARGE;
else
throw message.badMessage("Response Header Fields Too Large");
}
if(done)
break;
else
buffer.compact();
}
if(buffer.hasRemaining()){
in = new BufferInput(in, buffer); // unread
buffer = Reactor.current().allocator.allocate();
}
if(HTTP){
println("readMessage(){");
Debugger.println(message);
}
// init payload --------------------------------
keepAlive = message.isKeepAlive();
long contentLength = -1;
List<Encoding> encodings = null;
if(!emptyPayload){
if(message instanceof Request){
Request request = (Request)message;
if(!request.method.requestPayloadAllowed)
emptyPayload = true;
}else{
Response response = (Response)message;
if(response.status.payloadNotAllowed)
emptyPayload = true;
}
}
if(!emptyPayload){
if(message.isChunked()){
HeadersParser trailersParser = new HeadersParser();
trailersParser.resetForTrailers(message);
in = new ChunkedInput(in, trailersParser);
}else{
Header clHeader = message.headers.get(Message.CONTENT_LENGTH);
if(clHeader!=null){
if(clHeader.getValue().length()==0)
throw message.badMessage("Empty Content-Length");
if(clHeader.getValue().charAt(0)=='-')
throw message.badMessage("Negative Content-Length");
contentLength = Util.parseLong(clHeader.getValue());
if(contentLength==0)
emptyPayload = true;
else
in = new FixedLengthInput(in, contentLength);
}else{
encodings = message.getContentEncodings();
if(keepAlive || !(message instanceof Response)){
if(!encodings.isEmpty())
in = encodings.remove(encodings.size()-1).wrap(in);
else if(message instanceof Request)
emptyPayload = true;
else
keepAlive = false;
}
}
}
}
if(!emptyPayload){
if(encodings==null)
encodings = message.getContentEncodings();
message.setPayload(new SocketPayload(contentLength,
message.headers.value(Message.CONTENT_TYPE),
in, encodings));
}
if(HTTP){
if(emptyPayload)
println("payload = empty");
else
println("payload = "+in);
println("}");
}
return true;
}
private Message message;
private ByteBuffer buffer;
private long consumed = 0;
private boolean keepAlive;
private boolean emptyPayload;
public void reset(Message message, boolean emptyPayload){
this.message = message;
if(buffer==null)
buffer = Reactor.current().allocator.allocate();
else
buffer.clear();
consumed = 0;
parser.reset(message);
keepAlive = false;
this.emptyPayload = emptyPayload;
}
public Message getMessage(){
return message;
}
public long consumed(){
return consumed;
}
public boolean keepAlive(){
return keepAlive;
}
public void dispose(){
if(buffer!=null){
Reactor.current().allocator.free(buffer);
buffer = null;
}
}
@Override
public String toString(){
return "ReadMessage";
}
public static final EOFException IGNORABLE_EOF_EXCEPTION = new EOFException(){
@Override
public Throwable fillInStackTrace(){
return this;
}
@Override
public String toString(){
return "IGNORABLE_EOF";
}
};
}