package org.voovan.network.messagesplitter;
import org.voovan.http.websocket.WebSocketTools;
import org.voovan.network.IoSession;
import org.voovan.network.MessageSplitter;
import org.voovan.tools.TByteBuffer;
import org.voovan.tools.TString;
import org.voovan.tools.log.Logger;
import java.nio.ByteBuffer;
/**
* Http 消息分割类
*
* @author helyho
*
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class HttpMessageSplitter implements MessageSplitter {
private static final String BODY_TAG = "\r\n\r\n";
private int result = -1;
private int bodyTagIndex= -1;
private int contentLength = -1;
boolean isChunked = false;
@Override
public int canSplite(IoSession session, ByteBuffer byteBuffer) {
if(byteBuffer.limit()==0){
return -1;
}
result = isHttpFrame(byteBuffer);
if (result==-1 && "WebSocket".equals(session.getAttribute("Type")) ) {
result = WebSocketTools.isWebSocketFrame(byteBuffer);
}
if(result!=-1){
bodyTagIndex = -1;
}
return result;
}
private void getBodyTagIndex(ByteBuffer byteBuffer){
byte[] buffer = TByteBuffer.toArray(byteBuffer);
StringBuilder stringBuilder = new StringBuilder();
String httpHead = null;
for(int x=0;x<buffer.length-3;x++){
if(buffer[x] == '\r' && buffer[x+1] == '\n' && buffer[x+2] == '\r' && buffer[x+3] == '\n'){
bodyTagIndex = x + 3;
httpHead = stringBuilder.toString();
break;
}else{
stringBuilder.append((char)buffer[x]);
}
}
if(httpHead !=null && isHttpHead(httpHead)) {
String[] contentLengthLines = TString.searchByRegex(httpHead, "Content-Length: \\d+");
if (contentLengthLines.length > 1) {
contentLength = Integer.parseInt(contentLengthLines[0].split(" ")[1].trim());
}
isChunked = httpHead.contains("chunked");
}else{
bodyTagIndex = -1;
}
}
private boolean isHttpHead(String str){
//判断是否是 HTTP 头
int firstLineIndex = str.indexOf("\r\n");
if(firstLineIndex != -1) {
String firstLine = str.substring(0, firstLineIndex);
if (TString.regexMatch(firstLine, "HTTP\\/\\d\\.\\d\\s\\d{3}\\s.*") <= 0) {
if (TString.regexMatch(firstLine, "^[A-Z]*\\s.*\\sHTTP\\/\\d\\.\\d") <= 0) {
return false;
}
}
}
return true;
}
public int isHttpFrame(ByteBuffer byteBuffer) {
try{
if(bodyTagIndex==-1) {
getBodyTagIndex(byteBuffer);
}
if(bodyTagIndex != -1) {
// // 1.包含 content Length 的则通过获取 contentLenght 来计算报文的总长度,长度相等时,返回成功
// if (contentLength != -1) {
// int totalLength = bodyTagIndex + contentLength + 1; //
// if (byteBuffer.limit() >= totalLength) {
// return 0;
// }
// }
//
// // 2. 如果是 HTTP 响应报文 chunk
// // 则trim 后判断最后一个字符是否是 0
// if (isChunked) {
// byteBuffer.position(byteBuffer.limit() - 7);
// byte[] tailBytes = new byte[7];
// byteBuffer.get(tailBytes);
// byteBuffer.position(0);
// String tailStr = new String(tailBytes, "UTF-8");
// if("\r\n0\r\n\r\n".equals(tailStr) || tailStr.endsWith("\r\n0")) {
// return 0;
// }
// }
//
// // 3.是否是无报文体的简单请求报文(1.Header 中没有 ContentLength / 2.非 Chunked 报文形式)
// if (contentLength == -1 && !isChunked) {
// return 0;
// }
return 0;
}
}catch(Exception e){
Logger.error(e);
}
return -1;
}
}