package com.koushikdutta.async.http;
import com.koushikdutta.async.*;
import com.koushikdutta.async.LineEmitter.StringCallback;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.callback.WritableCallback;
import com.koushikdutta.async.http.filter.ChunkedOutputFilter;
import com.koushikdutta.async.http.libcore.RawHeaders;
import com.koushikdutta.async.http.libcore.ResponseHeaders;
import java.nio.ByteBuffer;
abstract class AsyncHttpResponseImpl extends FilteredDataEmitter implements AsyncHttpResponse {
private AsyncHttpRequestBody mWriter;
public AsyncSocket getSocket() {
return mSocket;
}
@Override
public AsyncHttpRequest getRequest() {
return mRequest;
}
void setSocket(AsyncSocket exchange) {
mSocket = exchange;
if (mSocket == null)
return;
mWriter = mRequest.getBody();
if (mWriter != null) {
mRequest.getHeaders().setContentType(mWriter.getContentType());
if (mWriter.length() != -1) {
mRequest.getHeaders().setContentLength(mWriter.length());
mSink = mSocket;
}
else {
mRequest.getHeaders().getHeaders().set("Transfer-Encoding", "Chunked");
mSink = new ChunkedOutputFilter(mSocket);
}
}
else {
mSink = mSocket;
}
mSocket.setEndCallback(mReporter);
mSocket.setClosedCallback(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
// TODO: do we care? throw if socket is still writing or something?
}
});
String rs = mRequest.getRequestString();
com.koushikdutta.async.Util.writeAll(exchange, rs.getBytes(), new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
if (mWriter != null) {
mWriter.write(mRequest, AsyncHttpResponseImpl.this, new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
onRequestCompleted(ex);
}
});
}
else {
onRequestCompleted(null);
}
}
});
LineEmitter liner = new LineEmitter();
exchange.setDataCallback(liner);
liner.setLineCallback(mHeaderCallback);
}
protected void onRequestCompleted(Exception ex) {
}
private CompletedCallback mReporter = new CompletedCallback() {
@Override
public void onCompleted(Exception error) {
if (error != null && !mCompleted) {
report(new Exception("connection closed before response completed."));
}
else {
report(error);
}
}
};
protected abstract void onHeadersReceived();
StringCallback mHeaderCallback = new StringCallback() {
private RawHeaders mRawHeaders = new RawHeaders();
@Override
public void onStringAvailable(String s) {
try {
if (mRawHeaders.getStatusLine() == null) {
mRawHeaders.setStatusLine(s);
}
else if (!"\r".equals(s)) {
mRawHeaders.addLine(s);
}
else {
mHeaders = new ResponseHeaders(mRequest.getUri(), mRawHeaders);
onHeadersReceived();
// socket may get detached after headers (websocket)
if (mSocket == null)
return;
DataEmitter emitter = HttpUtil.getBodyDecoder(mSocket, mRawHeaders, false);
setDataEmitter(emitter);
}
}
catch (Exception ex) {
report(ex);
}
}
};
@Override
protected void report(Exception e) {
super.report(e);
// DISCONNECT. EVERYTHING.
// should not get any data after this point...
// if so, eat it and disconnect.
mSocket.setDataCallback(new NullDataCallback() {
@Override
public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
super.onDataAvailable(emitter, bb);
mSocket.close();
}
});
mSocket.setWriteableCallback(null);
mSocket.setClosedCallback(null);
mSocket.setEndCallback(null);
mCompleted = true;
}
private AsyncHttpRequest mRequest;
private AsyncSocket mSocket;
ResponseHeaders mHeaders;
public AsyncHttpResponseImpl(AsyncHttpRequest request) {
mRequest = request;
}
boolean mCompleted = false;
@Override
public ResponseHeaders getHeaders() {
return mHeaders;
}
private boolean mFirstWrite = true;
private void assertContent() {
if (!mFirstWrite)
return;
mFirstWrite = false;
assert null != mRequest.getHeaders().getHeaders().get("Content-Type");
assert mRequest.getHeaders().getHeaders().get("Transfer-Encoding") != null || mRequest.getHeaders().getContentLength() != -1;
}
DataSink mSink;
@Override
public void write(ByteBuffer bb) {
assertContent();
mSink.write(bb);
}
@Override
public void write(ByteBufferList bb) {
assertContent();
mSink.write(bb);
}
@Override
public void end() {
write(ByteBuffer.wrap(new byte[0]));
}
@Override
public void setWriteableCallback(WritableCallback handler) {
mSink.setWriteableCallback(handler);
}
@Override
public WritableCallback getWriteableCallback() {
return mSink.getWriteableCallback();
}
@Override
public boolean isOpen() {
return mSink.isOpen();
}
@Override
public void close() {
mSink.close();
}
@Override
public void setClosedCallback(CompletedCallback handler) {
mSink.setClosedCallback(handler);
}
@Override
public CompletedCallback getClosedCallback() {
return mSink.getClosedCallback();
}
@Override
public AsyncServer getServer() {
return mSocket.getServer();
}
}