/**
* (c) 2012 tkuri
*/
package tkuri.jxy.server;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import tkuri.jxy.Const;
import tkuri.jxy.Util;
import tkuri.jxy.arch.ChannelReader;
import tkuri.jxy.arch.MessageBodyHandler;
import tkuri.jxy.arch.MessageBodyHandler.MbhState;
import tkuri.uty.Bs;
/**
*
*/
public class TaskHttpTransfer implements ITransfer, IHasMessageBodyState {
private ClientPeer client_ = null;
//private ClientRequest request_ = null;
private ByteBuffer requestHeaderBuf_;
private OriginPeer origin_ = null;
private OriginResponse response_ = null;
private ByteBuffer responseHeaderBuf_ = null;
private MessageBodyHandler msgBodyHandler_ = null;
private Msg ascentMsg_ = null;
private Msg descent_ = null;
// private TaskHttpDescent descent_ = null;
private String stringified_ = "";
/**
*
* @param aClientPeer
* @param aRequest
*/
public TaskHttpTransfer(ClientPeer aClientPeer) {
client_ = aClientPeer;
ascentMsg_ = new Msg();
ascentMsg_.reader = client_.reader;
ascentMsg_.headerBuffer = TaskUtil.makeRequestHeaderBuffer(client_.request);
// requestHeaderBuf_ = TaskUtil.makeRequestHeaderBuffer(aRequest);
// msgBodyHandler_ = new MessageBodyHandler(client_.reader, request_.headerInfo);
stringified_ = client_.request.toString();
}
@Override
public void onOriginConnect(OriginPeer aOrigin, boolean aConnectFinished) {
if (origin_ != null) {
throw new RuntimeException("wtf?");
}
origin_ = aOrigin;
if (aConnectFinished) {
// ok
} else if (TaskUtil.finishConnection(origin_.socket)) {
// ok
} else {
Util.say("httptask: no connect. client.invalidate()");
client_.invalidate();
origin_.invalidate(true);
}
}
@Override
public void onClientReadable() {
ascentMsg_.reader.fill();
_receive(ascentMsg_);
}
@Override
public void onOriginWritable() {
//Util.say("httptask: onOriginW");
try {
_transfer(ascentMsg_, origin_.socket);
} catch (Exception x) {
client_.invalidate();
origin_.invalidate(true);
}
}
@Override
public void onOriginReadable() {
//Util.say("httptask: onOriginR");
if (response_ == null) {
boolean isRequestHead = Const.S_HEAD.equals(client_.request.method);
response_ = new OriginResponse(origin_, isRequestHead, client_.request.toString());
client_.notifyResponseStart();
}
if (responseHeaderBuf_ == null) {
if (! response_.getReady()) {
Util.say("no response yeth");
if (origin_.reader.available() < 0) {
Util.say("httptask: origin.reader.avail < 0");
client_.notifyResponseEnd();
origin_.notifyResponseEnd();
}
return;
}
responseHeaderBuf_ = TaskUtil.makeResponseHeaderBuffer(response_, client_.request.headerInfo.connectionClose);
//Util.say(new String(responseHeaderBuf_.array()));
msgBodyHandler_ = new MessageBodyHandler(origin_.reader, response_.headerInfo);
}
if (! client_.isValid()) { // クライアントの接続が切れた.独自に消化しなければ
Util.say("httptask: onOriginRead: fake transfer.");
_transferResponse();
}
}
@Override
public void onClientWritable() {
//Util.say("httptask: onClientW");
if (response_ == null) {
Util.say("httptask: no resp obj.");
if (! origin_.isValid()) {
Util.say("httptask: onClientW: invalid origin. go next");
client_.notifyResponseEnd();
}
return;
}
_transferResponse();
}
private void _transferResponse() {
try {
if (responseHeaderBuf_.hasRemaining()) {
if (client_.isValid()) {
client_.socket.write(responseHeaderBuf_);
} else {
responseHeaderBuf_.position(responseHeaderBuf_.limit());
}
if (responseHeaderBuf_.hasRemaining()) {
return;
}
//Util.say("httptask: res header end.");
}
int rv = msgBodyHandler_.transferTo(
client_.isValid() ? client_.socket : null);
Util.say("httptask: response body rv = ", rv);
if (rv <= 0) {
Util.say("httptask: res body end.");
if (client_.isValid()) {
if (client_.request.headerInfo.connectionClose) {
Util.say("httptask: client req conn close");
client_.invalidate(); // Connection: close の場合は,こちらから切断してあげないとクライアントは分からないからね
} else {
client_.notifyResponseEnd();
}
}
origin_.notifyResponseEnd();
}
} catch (Exception x) {
Util.say(x);
Util.say("httptask: go fake transfer mode.");
client_.invalidate(); // おそらくクライアントが勝手に接続を切った.
// 後は onOriginReadable 時に独自に消化しなきゃ
}
}
public ITaskAscent ascent() {
return null;//ascent_;
}
public ITaskDescent descent() {
return null;//descent_;
}
@Override
public String toString() {
return stringified_;
}
private void _receive(Msg aMsgData) {
while (aMsgData.state == MSG_CHUNK_SIZE
|| aMsgData.state == MSG_CHUNK_CRLF
|| aMsgData.state == MSG_CHUNK_TRAILER) {
Bs line = _findNextLF(aMsgData);
if (line == null) {
break; // 次のバッファを待つ
}
aMsgData.rest += line.length();
if (aMsgData.state == MSG_CHUNK_SIZE) {
int size = (int) line.toLong(16);
aMsgData.rest += size;
aMsgData.state = (size == 0)
? MSG_CHUNK_TRAILER
: MSG_CHUNK_CRLF;
} else if (aMsgData.state == MSG_CHUNK_CRLF) {
aMsgData.state = MSG_CHUNK_SIZE;
} else { // TRAILER & CRLF
aMsgData.state = MSG_SIZE_FIXED;
return; // chunk 正常終了
}
}
if (aMsgData.reader.isClosed()) {
if (aMsgData.state == MSG_UNTIL_CLOSED) {
aMsgData.rest = aMsgData.reader.length();
aMsgData.state = MSG_SIZE_FIXED;
} else {
aMsgData.state = MSG_RECV_ERR;
}
}
}
private Bs _findNextLF(Msg aMsgData) {
int lf = aMsgData.reader.indexOf('\n', aMsgData.rest);
if (lf < 0) {
return null;
}
int len = lf + 1;
Bs rv = aMsgData.reader.sub(0, len);
aMsgData.rest += len;
return rv;
}
void _transfer(Msg aMsg, WritableByteChannel aOut) throws Exception {
try {
if (aMsg.headerBuffer == null) {
return;
}
if (aMsg.headerBuffer.hasRemaining()) {
aOut.write(aMsg.headerBuffer);
if (aMsg.headerBuffer.hasRemaining()) {
return;
}
}
if (0 < aMsg.rest) {
int wrote = aMsg.reader.through(aOut, aMsg.rest);
aMsg.rest -= wrote;
}
if (aMsg.rest == 0 && aMsg.state == MSG_SIZE_FIXED) {
aMsg.state = MSG_TRANS_END;
}
} catch (Exception x) {
}
}
class Msg {
ChannelReader reader;
ByteBuffer headerBuffer;
int rest = 0;
int state = 0;
}
}
interface IHasMessageBodyState {
static final int MSG_CHUNK_SIZE = 1;
static final int MSG_CHUNK_CRLF = 2;
static final int MSG_CHUNK_TRAILER = 3;
static final int MSG_UNTIL_CLOSED = 4;
static final int MSG_SIZE_FIXED = 5;
static final int MSG_RECV_ERR = 6;
static final int MSG_TRANS_END = 7;
}