diff --git a/jim-common/src/main/java/org/jim/common/ImConfig.java b/jim-common/src/main/java/org/jim/common/ImConfig.java index f09b255..fd1d36d 100644 --- a/jim-common/src/main/java/org/jim/common/ImConfig.java +++ b/jim-common/src/main/java/org/jim/common/ImConfig.java @@ -43,6 +43,10 @@ public class ImConfig { * 是否开启持久化; */ public static String isStore; + /** + * 默认的接收数据的buffer size + */ + private long readBufferSize = 1024 * 1024; public ImConfig(String bindIp,Integer bindPort){ @@ -85,4 +89,11 @@ public class ImConfig { public static void setMessageHelper(IMesssageHelper helper) { messageHelper = helper; } + public long getReadBufferSize() { + return readBufferSize; + } + public void setReadBufferSize(long readBufferSize) { + this.readBufferSize = readBufferSize; + } + } diff --git a/jim-common/src/main/java/org/jim/common/http/HttpRequest.java b/jim-common/src/main/java/org/jim/common/http/HttpRequest.java index 93db627..688ed6b 100644 --- a/jim-common/src/main/java/org/jim/common/http/HttpRequest.java +++ b/jim-common/src/main/java/org/jim/common/http/HttpRequest.java @@ -38,7 +38,7 @@ public class HttpRequest extends HttpPacket { /** * 请求参数 */ - private Map params = null; + private Map params = new HashMap<>();; private List cookies = null; private Map cookieMap = null; private int contentLength; diff --git a/jim-common/src/main/java/org/jim/common/http/HttpRequestDecoder.java b/jim-common/src/main/java/org/jim/common/http/HttpRequestDecoder.java index 1d6d2d5..b53f15a 100644 --- a/jim-common/src/main/java/org/jim/common/http/HttpRequestDecoder.java +++ b/jim-common/src/main/java/org/jim/common/http/HttpRequestDecoder.java @@ -3,21 +3,21 @@ package org.jim.common.http; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.ByteBuffer; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.jim.common.http.HttpConst.RequestBodyFormat; +import org.jim.common.utils.HttpParseUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.core.ChannelContext; import org.tio.core.exception.AioDecodeException; import org.tio.core.exception.LengthOverflowException; import org.tio.core.utils.ByteBufferUtils; -import org.jim.common.http.HttpConst.RequestBodyFormat; -import org.jim.common.utils.HttpParseUtils; import cn.hutool.core.util.StrUtil; + /** * * @author WChao @@ -31,17 +31,18 @@ public class HttpRequestDecoder { private static Logger log = LoggerFactory.getLogger(HttpRequestDecoder.class); /** - * 头部最多有多少字节 + * 头部,最多有多少字节 */ - public static final int MAX_HEADER_LENGTH = 20480; + public static final int MAX_LENGTH_OF_HEADER = 20480; /** * 头部,每行最大的字节数 */ - public static final int MAX_LENGTH_OF_LINE = 1024; + public static final int MAX_LENGTH_OF_HEADERLINE = 2048; - public static HttpRequest decode(ByteBuffer buffer, ChannelContext channelContext) throws AioDecodeException { + public static HttpRequest decode(ByteBuffer buffer, ChannelContext channelContext,boolean isBody) throws AioDecodeException { int initPosition = buffer.position(); + int readableLength = buffer.limit() - initPosition; // int count = 0; Step step = Step.firstline; // StringBuilder currLine = new StringBuilder(); @@ -54,14 +55,14 @@ public class HttpRequestDecoder { while (buffer.hasRemaining()) { String line; try { - line = ByteBufferUtils.readLine(buffer, null, MAX_LENGTH_OF_LINE); + line = ByteBufferUtils.readLine(buffer, null, MAX_LENGTH_OF_HEADERLINE); } catch (LengthOverflowException e) { throw new AioDecodeException(e); } int newPosition = buffer.position(); - if (newPosition - initPosition > MAX_HEADER_LENGTH) { - throw new AioDecodeException("max http header length " + MAX_HEADER_LENGTH); + if (newPosition - initPosition > MAX_LENGTH_OF_HEADER) { + throw new AioDecodeException("max http header length " + MAX_LENGTH_OF_HEADER); } if (line == null) { @@ -69,7 +70,7 @@ public class HttpRequestDecoder { } headerSb.append(line).append("\r\n"); - if ("".equals(line)) {//头部解析完成了 + if ("".equals(line) && isBody) {//头部解析完成了 String contentLengthStr = headers.get(HttpConst.RequestHeaderKey.Content_Length); if (StringUtils.isBlank(contentLengthStr)) { contentLength = 0; @@ -77,11 +78,14 @@ public class HttpRequestDecoder { contentLength = Integer.parseInt(contentLengthStr); } - int readableLength = buffer.limit() - buffer.position(); - if (readableLength >= contentLength) { +// int readableLength = buffer.limit() - buffer.position(); + int headerLength = (buffer.position() - initPosition); + int allNeedLength = headerLength + contentLength; //这个packet所需要的字节长度(含头部和体部) + if (readableLength >= allNeedLength) { step = Step.body; break; } else { + channelContext.setPacketNeededLength(allNeedLength); return null; } } else { @@ -89,6 +93,8 @@ public class HttpRequestDecoder { firstLine = parseRequestLine(line, channelContext); step = Step.header; } else if (step == Step.header) { + if("".equals(line) && !isBody)//不解析包体的话,结束(换句话说就是只解析请求行与请求头) + break; KeyValue keyValue = parseHeaderLine(line); headers.put(keyValue.getKey(), keyValue.getValue()); } @@ -96,7 +102,7 @@ public class HttpRequestDecoder { } } - if (step != Step.body) { + if (step != Step.body && isBody) { return null; } @@ -112,11 +118,12 @@ public class HttpRequestDecoder { httpRequest.setHeaders(headers); httpRequest.setContentLength(contentLength); + parseQueryString(httpRequest, firstLine, channelContext); + if (contentLength == 0) { - if (StringUtils.isNotBlank(firstLine.getQuery())) { - Map params = decodeParams(firstLine.getQuery(), httpRequest.getCharset(), channelContext); - httpRequest.setParams(params); - } + // if (StringUtils.isNotBlank(firstLine.getQuery())) { + // decodeParams(httpRequest.getParams(), firstLine.getQuery(), httpRequest.getCharset(), channelContext); + // } } else { bodyBytes = new byte[contentLength]; buffer.get(bodyBytes); @@ -148,9 +155,9 @@ public class HttpRequestDecoder { } - public static Map decodeParams(String paramsStr, String charset, ChannelContext channelContext) { + public static void decodeParams(Map params, String paramsStr, String charset, ChannelContext channelContext) { if (StrUtil.isBlank(paramsStr)) { - return Collections.emptyMap(); + return; } // // 去掉Path部分 @@ -158,7 +165,7 @@ public class HttpRequestDecoder { // if (pathEndPos > 0) { // paramsStr = StrUtil.subSuf(paramsStr, pathEndPos + 1); // } - Map ret = new HashMap<>(); + // Map ret = new HashMap<>(); String[] keyvalues = StringUtils.split(paramsStr, "&"); for (String keyvalue : keyvalues) { String[] keyvalueArr = StringUtils.split(keyvalue, "="); @@ -174,24 +181,24 @@ public class HttpRequestDecoder { log.error(channelContext.toString(), e); } - Object[] existValue = ret.get(key); + Object[] existValue = params.get(key); if (existValue != null) { String[] newExistValue = new String[existValue.length + 1]; System.arraycopy(existValue, 0, newExistValue, 0, existValue.length); newExistValue[newExistValue.length - 1] = value; - ret.put(key, newExistValue); + params.put(key, newExistValue); } else { String[] newExistValue = new String[] { value }; - ret.put(key, newExistValue); + params.put(key, newExistValue); } } - return ret; + return; } /** * @param args * - * @author wchao + * @author WChao * 2017年2月22日 下午4:06:42 * */ @@ -206,7 +213,7 @@ public class HttpRequestDecoder { * @param bodyBytes * @param channelContext * @throws AioDecodeException - * @author wchao + * @author WChao */ private static void parseBody(HttpRequest httpRequest, RequestLine firstLine, byte[] bodyBytes, ChannelContext channelContext) throws AioDecodeException { parseBodyFormat(httpRequest, httpRequest.getHeaders()); @@ -218,20 +225,20 @@ public class HttpRequestDecoder { if (log.isInfoEnabled()) { String bodyString = null; if (bodyBytes != null && bodyBytes.length > 0) { - try { - - bodyString = new String(bodyBytes, httpRequest.getCharset()); - log.info("{} multipart body string\r\n{}", channelContext, bodyString); - } catch (UnsupportedEncodingException e) { - log.error(channelContext.toString(), e); + if (log.isDebugEnabled()) { + try { + bodyString = new String(bodyBytes, httpRequest.getCharset()); + log.debug("{} multipart body string\r\n{}", channelContext, bodyString); + } catch (UnsupportedEncodingException e) { + log.error(channelContext.toString(), e); + } } } - } //【multipart/form-data; boundary=----WebKitFormBoundaryuwYcfA2AIgxqIxA0】 String initboundary = HttpParseUtils.getPerprotyEqualValue(httpRequest.getHeaders(), HttpConst.RequestHeaderKey.Content_Type, "boundary"); - log.info("{}, initboundary:{}", channelContext, initboundary); + log.debug("{}, initboundary:{}", channelContext, initboundary); HttpMultiBodyDecoder.decode(httpRequest, firstLine, bodyBytes, initboundary, channelContext); } else { String bodyString = null; @@ -239,7 +246,9 @@ public class HttpRequestDecoder { try { bodyString = new String(bodyBytes, httpRequest.getCharset()); httpRequest.setBodyString(bodyString); - log.info("{} body string\r\n{}", channelContext, bodyString); + if (log.isInfoEnabled()) { + log.info("{} body string\r\n{}", channelContext, bodyString); + } } catch (UnsupportedEncodingException e) { log.error(channelContext.toString(), e); } @@ -276,7 +285,7 @@ public class HttpRequestDecoder { * Content-Type : application/x-www-form-urlencoded; charset=UTF-8 * @param httpRequest * @param headers - * @author wchao + * @author WChao */ public static void parseBodyFormat(HttpRequest httpRequest, Map headers) { String Content_Type = StringUtils.lowerCase(headers.get(HttpConst.RequestHeaderKey.Content_Type)); @@ -290,9 +299,9 @@ public class HttpRequestDecoder { } httpRequest.setBodyFormat(bodyFormat); - if (StringUtils.isNoneBlank(Content_Type)) { + if (StringUtils.isNotBlank(Content_Type)) { String charset = HttpParseUtils.getPerprotyEqualValue(headers, HttpConst.RequestHeaderKey.Content_Type, "charset"); - if (StringUtils.isNoneBlank(charset)) { + if (StringUtils.isNotBlank(charset)) { httpRequest.setCharset(charset); } } @@ -303,7 +312,7 @@ public class HttpRequestDecoder { * @param line * @return * - * @author wchao + * @author WChao * 2017年2月23日 下午1:37:58 * */ @@ -330,7 +339,7 @@ public class HttpRequestDecoder { * @param channelContext * @return * - * @author wchao + * @author WChao * 2017年2月23日 下午1:37:51 * */ @@ -360,6 +369,7 @@ public class HttpRequestDecoder { RequestLine requestLine = new RequestLine(); requestLine.setMethod(method); requestLine.setPath(path); + requestLine.setInitPath(path); requestLine.setPathAndQuery(pathAndQuerystr); requestLine.setQuery(queryStr); requestLine.setVersion(version); @@ -367,7 +377,7 @@ public class HttpRequestDecoder { requestLine.setLine(line); return requestLine; - } catch (Exception e) { + } catch (Throwable e) { log.error(channelContext.toString(), e); throw new AioDecodeException(e); } @@ -376,32 +386,42 @@ public class HttpRequestDecoder { /** * 解析URLENCODED格式的消息体 * 形如: 【Content-Type : application/x-www-form-urlencoded; charset=UTF-8】 - * @author wchao + * @author WChao */ private static void parseUrlencoded(HttpRequest httpRequest, RequestLine firstLine, byte[] bodyBytes, String bodyString, ChannelContext channelContext) { - String paramStr = ""; - if (StringUtils.isNotBlank(firstLine.getQuery())) { - paramStr += firstLine.getQuery(); - } - if (bodyString != null) { - if (paramStr != null) { - paramStr += "&"; - } - paramStr += bodyString; - } + // String paramStr = ""; + // if (StringUtils.isNotBlank(firstLine.getQuery())) { + // paramStr += firstLine.getQuery(); + // } + // if (bodyString != null) { + // if (paramStr != null) { + // paramStr += "&"; + // } + // paramStr += bodyString; + // } - if (paramStr != null) { - Map params = decodeParams(paramStr, httpRequest.getCharset(), channelContext); - httpRequest.setParams(params); - // log.error("paramStr:{}", paramStr); - // log.error("param:{}", Json.toJson(params)); + if (StringUtils.isNotBlank(bodyString)) { + decodeParams(httpRequest.getParams(), bodyString, httpRequest.getCharset(), channelContext); + } + } + + /** + * 解析查询 + * @param httpRequest + * @param firstLine + * @param channelContext + */ + private static void parseQueryString(HttpRequest httpRequest, RequestLine firstLine, ChannelContext channelContext) { + String paramStr = firstLine.getQuery(); + if (StringUtils.isNotBlank(paramStr)) { + decodeParams(httpRequest.getParams(), paramStr, httpRequest.getCharset(), channelContext); } } /** * * - * @author wchao + * @author WChao * 2017年2月22日 下午4:06:42 * */ diff --git a/jim-common/src/main/java/org/jim/common/http/RequestLine.java b/jim-common/src/main/java/org/jim/common/http/RequestLine.java index b0f38ee..df3b174 100644 --- a/jim-common/src/main/java/org/jim/common/http/RequestLine.java +++ b/jim-common/src/main/java/org/jim/common/http/RequestLine.java @@ -7,6 +7,7 @@ package org.jim.common.http; public class RequestLine { private Method method; private String path; //譬如http://www.163.com/user/get?name=tan&id=789,那些此值就是/user/get + private String initPath; //同path,只是path可能会被业务端修改,而这个是记录访问者访问的最原始path的 private String query; //譬如http://www.163.com/user/get?name=tan&id=789,那些此值就是name=tan&id=789 private String pathAndQuery; private String protocol; @@ -104,4 +105,13 @@ public class RequestLine { public void setProtocol(String protocol) { this.protocol = protocol; } + + public String getInitPath() { + return initPath; + } + + public void setInitPath(String initPath) { + this.initPath = initPath; + } + } diff --git a/jim-server/src/main/java/org/jim/server/ImServerStarter.java b/jim-server/src/main/java/org/jim/server/ImServerStarter.java index a9adc6d..b8d7d7a 100644 --- a/jim-server/src/main/java/org/jim/server/ImServerStarter.java +++ b/jim-server/src/main/java/org/jim/server/ImServerStarter.java @@ -38,6 +38,7 @@ public class ImServerStarter { } public void init(){ + System.setProperty("tio.default.read.buffer.size", String.valueOf(imConfig.getReadBufferSize())); imAioHandler = new ImServerAioHandler() ; if(imAioListener == null){ imAioListener = new ImServerAioListener(); diff --git a/jim-server/src/main/java/org/jim/server/http/HttpServerHandler.java b/jim-server/src/main/java/org/jim/server/http/HttpServerHandler.java index 943d443..a016339 100644 --- a/jim-server/src/main/java/org/jim/server/http/HttpServerHandler.java +++ b/jim-server/src/main/java/org/jim/server/http/HttpServerHandler.java @@ -83,7 +83,7 @@ public class HttpServerHandler extends AbServerHandler{ @Override public boolean isProtocol(ByteBuffer buffer,ChannelContext channelContext)throws Throwable{ if(buffer != null){ - HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext); + HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext,false); if(request.getHeaders().get(HttpConst.RequestHeaderKey.Sec_WebSocket_Key) == null) { channelContext.setAttribute(new HttpSession().setServerHandler(this)); @@ -109,7 +109,7 @@ public class HttpServerHandler extends AbServerHandler{ @Override public Packet decode(ByteBuffer buffer, ChannelContext channelContext)throws AioDecodeException { - HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext); + HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext,true); channelContext.setAttribute(Const.HTTP_REQUEST,request); return request; } diff --git a/jim-server/src/main/java/org/jim/server/http/api/TestController.java b/jim-server/src/main/java/org/jim/server/http/api/TestController.java index 86e21df..1ab8da5 100644 --- a/jim-server/src/main/java/org/jim/server/http/api/TestController.java +++ b/jim-server/src/main/java/org/jim/server/http/api/TestController.java @@ -126,7 +126,7 @@ public class TestController { public HttpResponse upload(UploadFile uploadFile, String before, String end, HttpRequest request) throws Exception { HttpResponse ret; if (uploadFile != null) { - File file = new File("c:/" + uploadFile.getName()); + File file = new File("D:/" + uploadFile.getName()); FileUtils.writeByteArrayToFile(file, uploadFile.getData()); System.out.println("【" + before + "】"); diff --git a/jim-server/src/main/java/org/jim/server/ws/WsServerHandler.java b/jim-server/src/main/java/org/jim/server/ws/WsServerHandler.java index c51ad49..49277d3 100644 --- a/jim-server/src/main/java/org/jim/server/ws/WsServerHandler.java +++ b/jim-server/src/main/java/org/jim/server/ws/WsServerHandler.java @@ -68,7 +68,7 @@ public class WsServerHandler extends AbServerHandler{ @Override public boolean isProtocol(ByteBuffer buffer,ChannelContext channelContext)throws Throwable{ if(buffer != null){//第一次连接; - HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext); + HttpRequest request = HttpRequestDecoder.decode(buffer, channelContext,false); if(request.getHeaders().get(HttpConst.RequestHeaderKey.Sec_WebSocket_Key) != null) { channelContext.setAttribute(new WsSessionContext().setServerHandler(this)); @@ -113,7 +113,7 @@ public class WsServerHandler extends AbServerHandler{ public ImPacket decode(ByteBuffer buffer, ChannelContext channelContext) throws AioDecodeException { WsSessionContext wsSessionContext = (WsSessionContext)channelContext.getAttribute(); if(!wsSessionContext.isHandshaked()){//握手 - HttpRequest httpRequest = HttpRequestDecoder.decode(buffer,channelContext); + HttpRequest httpRequest = HttpRequestDecoder.decode(buffer,channelContext,true); if(httpRequest == null) return null; //升级到WebSocket协议处理