package org.jboss.pitbull.internal.nio.http;
import org.jboss.pitbull.ContentOutputStream;
import org.jboss.pitbull.OrderedHeaders;
import org.jboss.pitbull.RequestHeader;
import org.jboss.pitbull.StatusCode;
import org.jboss.pitbull.internal.logging.Logger;
import org.jboss.pitbull.internal.nio.socket.ManagedChannel;
import org.jboss.pitbull.internal.util.OrderedHeadersImpl;
import org.jboss.pitbull.server.handlers.stream.StreamedResponse;
import java.nio.ByteBuffer;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class NioStreamedResponse implements StreamedResponse
{
protected StatusCode status = StatusCode.INTERNAL_SERVER_ERROR;
protected OrderedHeaders headers = new OrderedHeadersImpl();
protected ManagedChannel channel;
protected ContentOutputStream stream;
protected RequestHeader requestHeader;
protected ContentInputStream is;
protected boolean ended;
protected boolean detached;
protected static final Logger log = Logger.getLogger(NioStreamedResponse.class);
public NioStreamedResponse(ManagedChannel channel, RequestHeader requestHeader, ContentInputStream is)
{
this.channel = channel;
this.requestHeader = requestHeader;
this.is = is;
}
@Override
public boolean isDetached()
{
return detached;
}
@Override
public void detach()
{
this.detached = true;
}
@Override
public StatusCode getStatusCode()
{
return status;
}
@Override
public void setStatus(StatusCode status)
{
this.status = status;
}
@Override
public OrderedHeaders getHeaders()
{
return headers;
}
@Override
public boolean isCommitted()
{
if (stream == null) return false;
return stream.isCommitted();
}
@Override
public boolean isEnded()
{
return ended;
}
@Override
public void reset()
{
if (isEnded() || isCommitted()) throw new IllegalStateException("Response is committed");
headers.clear();
if (stream != null)
{
stream.reset();
}
}
@Override
public ContentOutputStream getOutputStream()
{
if (stream == null) stream = new ServerContentOutputStream(channel, requestHeader, this);
return stream;
}
protected void resumeWorker()
{
// todo keep-alive logic, right now everything kept alive
try
{
channel.resumeReads();
}
catch (Exception ex)
{
log.error("Failed to resume worker", ex);
closeChannel();
return;
}
}
protected void closeChannel()
{
try
{ channel.close(); }
catch (Throwable t)
{}
}
@Override
public void end()
{
if (ended)
{
log.error("StreamResponseWriter.end() called twice");
return;
}
ended = true;
boolean mustClose = false;
try
{
is.eat(); // eat the input stream
}
catch (Throwable e)
{
log.warn("Exception while eating", e);
mustClose = true;
}
if (stream == null)
{
HttpResponse response = new HttpResponse(this);
try
{
response.prepareEmptyBody(requestHeader);
byte[] bytes = response.responseBytes();
channel.writeBlocking(ByteBuffer.wrap(bytes));
}
catch (Throwable e)
{
log.error("Failed writing response", e);
mustClose = true;
}
}
else
{
// stream was open
try
{
stream.close();
}
catch (Throwable e)
{
log.error("Failed writing response", e);
mustClose = true;
}
}
if (mustClose)
{
closeChannel();
}
else
{
resumeWorker();
}
}
}