/**
* (c) 2012 tkuri
*/
package tkuri.jxy.arch;
import java.nio.channels.WritableByteChannel;
import tkuri.jxy.Util;
import tkuri.uty.Bs;
/**
*
*/
public class MessageBodyHandler {
public static enum MbhState {
CHUNK_SIZE,
CHUNK_BODY,
CHUNK_CRLF,
CHUNK_TRAILER,
CONTENT,
UNTIL_CLOSE,
RECV_END,
RECV_END_CLOSED,
RECV_END_ERR, // 途中で切断された
SEND_END,
SEND_END_CLOSED,
}
private MbhState state_ = MbhState.CONTENT;
private ChannelReader reader_;
private boolean multipart_ = false;
private int pointer_ = 0;
public boolean isInputFine = true;
public boolean isOutputFine = true;
/**
*
* @param aReader
* @param aInfo
*/
public MessageBodyHandler(ChannelReader aReader, GeneralHeaderInfo aInfo) {
reader_ = aReader;
if (! aInfo.hasMessageBody) {
//Util.say("no message body");
_setState(MbhState.RECV_END);
// nop
} else if (aInfo.multipartFinalBorder != null) {
// TODO
multipart_ = true;
} else if (aInfo.chunked) {
_setState(MbhState.CHUNK_SIZE);
} else if (aInfo.connectionClose) {
_setState(MbhState.UNTIL_CLOSE);
pointer_ = Integer.MAX_VALUE;
} else {
_setState(MbhState.CONTENT);
pointer_ = (int) aInfo.contentLength;
}
}
private void _setState(MbhState aS) {
//Util.say("bodyhandler: set state: ", aS);
state_ = aS;
}
public void receive() {
if (state_.ordinal() >= MbhState.RECV_END.ordinal()) {
return;
}
if (multipart_) {
// TODO
throw new RuntimeException("not implemented");
}
while (state_ == MbhState.CHUNK_SIZE
|| state_ == MbhState.CHUNK_CRLF
|| state_ == MbhState.CHUNK_TRAILER) {
Bs line = _findNextLF();
if (line == null) {
break; // 次のバッファを待つ
}
pointer_ += line.length();
if (state_ == MbhState.CHUNK_SIZE) {
int size = (int) line.toLong(16);
pointer_ += size;
_setState( (size == 0)
? MbhState.CHUNK_TRAILER
: MbhState.CHUNK_CRLF);
} else if (state_ == MbhState.CHUNK_CRLF) {
_setState(MbhState.CHUNK_SIZE);
} else { // TRAILER & CRLF
_setState(MbhState.RECV_END);
return; // chunk 正常終了
}
}
if (0 < reader_.available()) {
// go on
} else if (state_ == MbhState.UNTIL_CLOSE) {
state_ = MbhState.RECV_END_CLOSED;
} else {
isInputFine = false;
state_ = MbhState.RECV_END_ERR;
}
}
/**
* rv > 0 ... 処理続行
* rv == 0 ... 処理終了,接続は存続
* rv < 0 ... 処理終了,Connection: close のため接続断
*
* @throws 予期せぬ接続断時
* もしくは aOut.write()から例外発生時
*/
public MbhState transferTo(WritableByteChannel aOut) throws Exception {
if (state_.ordinal() < MbhState.SEND_END.ordinal()) {
if (isOutputFine) {
try {
int wrote = reader_.through(aOut, pointer_);
//Util.say("message body: wrote: ", wrote);
pointer_ -= wrote;
} catch (Exception x) {
Util.say("message body: through: error: ", x);
isOutputFine = false;
}
}
if (! isOutputFine) {
int size = Math.min(reader_.length(), pointer_);
reader_.consume(size);
pointer_ -= size;
}
}
if (pointer_ == 0) {
switch (state_) {
case RECV_END:
_setState(MbhState.SEND_END);
break;
case RECV_END_CLOSED:
_setState(MbhState.SEND_END_CLOSED);
break;
case RECV_END_ERR:
_setState(MbhState.SEND_END_CLOSED);
break;
}
}
return state_; // 処理続行
}
// FIXME ChannelReader が用意しているバッファサイズ以上の長さの行は読めない
private Bs _findNextLF() {
int lf = reader_.indexOf('\n', pointer_);
if (lf < 0) {
return null;
}
int len = lf + 1;
Bs rv = reader_.sub(0, len);
pointer_ += len;
return rv;
}
}