diff --git a/src/main/java/org/redkale/net/http/HttpRequest.java b/src/main/java/org/redkale/net/http/HttpRequest.java index 14d4c72de..c860a2f52 100644 --- a/src/main/java/org/redkale/net/http/HttpRequest.java +++ b/src/main/java/org/redkale/net/http/HttpRequest.java @@ -182,7 +182,7 @@ public class HttpRequest extends Request { private final ByteArray bodyBytes; - private byte[] headerBytes; + private final ByteArray headerBytes; private boolean headerParsed = false; @@ -203,11 +203,12 @@ public class HttpRequest extends Request { HttpServlet.ActionEntry actionEntry; public HttpRequest(HttpContext context) { - this(context, new ByteArray()); + this(context, new ByteArray(), new ByteArray()); } - protected HttpRequest(HttpContext context, ByteArray bodyBytes) { + protected HttpRequest(HttpContext context, ByteArray headerBytes, ByteArray bodyBytes) { super(context); + this.headerBytes = headerBytes; this.bodyBytes = bodyBytes; this.remoteAddrHeader = context.remoteAddrHeader; this.remoteAddrHeaders = context.remoteAddrHeaders; @@ -219,6 +220,7 @@ public class HttpRequest extends Request { @SuppressWarnings("OverridableMethodCallInConstructor") protected HttpRequest(HttpContext context, WebRequest req) { super(context); + this.headerBytes = new ByteArray(); this.bodyBytes = new ByteArray(); this.remoteAddrHeader = null; this.remoteAddrHeaders = null; @@ -333,27 +335,30 @@ public class HttpRequest extends Request { @Override protected int readHeader(final ByteBuffer buf, final int pipelineHeaderLength) { final ByteBuffer buffer = buf; - ByteArray bytes = bodyBytes; if (this.readState == READ_STATE_ROUTE) { int rs = readMethodUriLine(buffer); if (rs != 0) { return rs; } + this.headerBytes.clear(); + this.headerLength = 0; + this.headerHalfLen = 0; this.readState = READ_STATE_HEADER; } if (this.readState == READ_STATE_HEADER) { if (pipelineHeaderLength > 0) { + ByteArray hbytes = this.headerBytes; int bufremain = buffer.remaining(); int remainHalf = pipelineHeaderLength - this.headerHalfLen; if (remainHalf > bufremain) { - bytes.put(buffer); + hbytes.put(buffer); this.headerHalfLen += bufremain; buffer.clear(); return 1; } - bytes.put(buffer, remainHalf); - this.headerBytes = bytes.getBytes(); - this.headerLength = this.headerBytes.length; + hbytes.put(buffer, remainHalf); + this.headerLength = this.headerBytes.length(); + this.headerHalfLen = this.headerLength; this.headerParsed = false; } else if (context.lazyHeader && getmethod) { // 非GET必须要读header,会有Content-Length int rs = loadHeaderBytes(buffer); @@ -372,7 +377,8 @@ public class HttpRequest extends Request { this.headerParsed = false; } else { int startpos = buffer.position(); - int rs = readHeaderLines(buffer, bytes); + ByteArray hbytes = this.headerBytes; + int rs = readHeaderLines(buffer, hbytes); this.headerLength = buffer.position() - startpos + this.headerHalfLen; if (rs >= 0 && this.headerLength > context.getMaxHeader()) { context.getLogger() @@ -383,14 +389,13 @@ public class HttpRequest extends Request { return -1; } if (rs != 0) { - this.headerHalfLen = bytes.length(); + this.headerHalfLen = hbytes.length(); buffer.clear(); return rs; } this.headerParsed = true; this.headerHalfLen = this.headerLength; } - bytes.clear(); if (this.contentType != null && this.contentType.contains("boundary=")) { this.boundary = true; } @@ -400,12 +405,14 @@ public class HttpRequest extends Request { } // completed=true时ProtocolCodec会继续读下一个request this.completed = !this.boundary && !maybews; + this.bodyBytes.clear(); this.readState = READ_STATE_BODY; } if (this.readState == READ_STATE_BODY) { if (this.chunked) { return readChunkedBody(buffer); } + ByteArray bbytes = this.bodyBytes; if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) { if (this.contentLength > context.getMaxBody()) { context.getLogger() @@ -415,12 +422,12 @@ public class HttpRequest extends Request { + this.contentLength + ", path: " + requestPath); return -1; } - bytes.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); - int lr = (int) this.contentLength - bytes.length(); + bbytes.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); + int lr = (int) this.contentLength - bbytes.length(); if (lr == 0) { this.readState = READ_STATE_END; - if (bytes.isEmpty()) { - this.bodyParsed = true; // no bodyBytes data + if (bbytes.isEmpty()) { + this.bodyParsed = true; // no body data } } else { buffer.clear(); @@ -429,11 +436,11 @@ public class HttpRequest extends Request { } // 文件上传、HTTP1.0或Connection:close if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) { - bytes.put(buffer, buffer.remaining()); + bbytes.put(buffer, buffer.remaining()); } this.readState = READ_STATE_END; - if (bytes.isEmpty()) { - this.bodyParsed = true; // no bodyBytes data + if (bbytes.isEmpty()) { + this.bodyParsed = true; // no body data } else if (!getmethod && this.contentLength < 0 && keepAlive) { // keep-alive=true: Content-Length和chunk必然是二选一。 // keep-alive=false: Content-Length可有可无. @@ -567,29 +574,29 @@ public class HttpRequest extends Request { private int loadHeaderBytes(final ByteBuffer buf) { final ByteBuffer buffer = buf; - ByteArray bytes = bodyBytes; // body当temp buffer使用 + ByteArray hbytes = this.headerBytes; int remain = buffer.remaining(); byte b1, b2, b3, b4; for (; ; ) { if (remain-- < 4) { // bytes不存放\r\n\r\n这4个字节 - bytes.put(buffer); + hbytes.put(buffer); buffer.clear(); - if (bytes.length() > 0) { + if (hbytes.length() > 0) { byte rn1 = 0, rn2 = 0, rn3 = 0; - byte b = bytes.getLastByte(); + byte b = hbytes.getLastByte(); if (b == '\r' || b == '\n') { rn3 = b; - bytes.backCount(); - if (bytes.length() > 0) { - b = bytes.getLastByte(); + hbytes.backCount(); + if (hbytes.length() > 0) { + b = hbytes.getLastByte(); if (b == '\r' || b == '\n') { rn2 = b; - bytes.backCount(); - if (bytes.length() > 0) { - b = bytes.getLastByte(); + hbytes.backCount(); + if (hbytes.length() > 0) { + b = hbytes.getLastByte(); if (b == '\r' || b == '\n') { rn1 = b; - bytes.backCount(); + hbytes.backCount(); } } } @@ -605,30 +612,31 @@ public class HttpRequest extends Request { buffer.put(rn3); } } + this.headerHalfLen = hbytes.length(); return 1; } b1 = buffer.get(); - bytes.put(b1); + hbytes.put(b1); if (b1 == '\r') { remain--; b2 = buffer.get(); - bytes.put(b2); if (b2 == '\n') { remain--; b3 = buffer.get(); - bytes.put(b3); if (b3 == '\r') { remain--; b4 = buffer.get(); - bytes.put(b4); + hbytes.put(b2, b3, b4); if (b4 == '\n') { - this.headerBytes = Utility.append(this.headerBytes, bytes.content(), 0, bytes.length()); - this.headerLength = this.headerBytes.length; + this.headerLength = hbytes.length(); this.headerHalfLen = this.headerLength; - bytes.clear(); return 0; } + } else { + hbytes.put(b2, b3); } + } else { + hbytes.put(b2); } } } @@ -1088,10 +1096,10 @@ public class HttpRequest extends Request { return; } if (bodyBytes.isEmpty()) { // body当temp buffer使用 - readHeaderLines(ByteBuffer.wrap(headerBytes), bodyBytes); + readHeaderLines(this.headerBytes.wrapByteBuffer(), bodyBytes); bodyBytes.clear(); } else { // 存有body数据 - readHeaderLines(ByteBuffer.wrap(headerBytes), new ByteArray()); + readHeaderLines(this.headerBytes.wrapByteBuffer(), array().clear()); } } @@ -1299,7 +1307,7 @@ public class HttpRequest extends Request { // header this.headerLength = 0; this.headerHalfLen = 0; - this.headerBytes = null; + this.headerBytes.clear(); this.headerParsed = false; this.contentType = null; this.contentLength = -1; diff --git a/src/test/java/org/redkale/test/httpparser/HttpRequestTest.java b/src/test/java/org/redkale/test/httpparser/HttpRequestTest.java new file mode 100644 index 000000000..f6f146c12 --- /dev/null +++ b/src/test/java/org/redkale/test/httpparser/HttpRequestTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016-2116 Redkale + * All rights reserved. + */ +package org.redkale.test.httpparser; + +import java.nio.ByteBuffer; +import org.junit.jupiter.api.*; +import org.redkale.net.http.HttpContext; +import org.redkale.net.http.HttpRequest; + +/** + * + * @author zhangjx + */ +public class HttpRequestTest { + + private static final String REQ_TEXT = + "GET /test/getinfo HTTP/1.1\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: 0\r\n" + "\r\n"; + + public static void main(String[] args) throws Throwable { + HttpRequestTest test = new HttpRequestTest(); + test.run1(); + test.run2(); + test.run3(); + } + + @Test + public void run1() throws Exception { + HttpContext.HttpContextConfig httpConfig = new HttpContext.HttpContextConfig(); + httpConfig.maxHeader = 16 * 1024; + httpConfig.maxBody = 64 * 1024; + HttpContext context = new HttpContext(httpConfig); + HttpRequestX req = new HttpRequestX(context); + Assertions.assertEquals(0, req.readHeader(ByteBuffer.wrap(REQ_TEXT.getBytes()), -1)); + + req = new HttpRequestX(context); + int sublen = "GET /test/getinfo HTTP/1.1\r\n".length() + 3; + String text1 = REQ_TEXT.substring(0, sublen); + int r1 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), -1); + Assertions.assertEquals(1, r1); + Assertions.assertEquals(3, req.headerHalfLen()); + System.out.println(req.headerHalfLen()); + + text1 = REQ_TEXT.substring(sublen, sublen + 7); + int r2 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), -1); + Assertions.assertEquals(1, r2); + Assertions.assertEquals(7, req.headerHalfLen()); + + text1 = REQ_TEXT.substring(sublen + 7); + int r3 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), -1); + Assertions.assertEquals(0, r3); + } + + @Test + public void run2() throws Exception { + HttpContext.HttpContextConfig httpConfig = new HttpContext.HttpContextConfig(); + httpConfig.lazyHeader = true; + httpConfig.maxHeader = 16 * 1024; + httpConfig.maxBody = 64 * 1024; + HttpContext context = new HttpContext(httpConfig); + HttpRequestX req = new HttpRequestX(context); + Assertions.assertEquals(0, req.readHeader(ByteBuffer.wrap(REQ_TEXT.getBytes()), -1)); + + req = new HttpRequestX(context); + int sublen = "GET /test/getinfo HTTP/1.1\r\n".length() + 3; + String text1 = REQ_TEXT.substring(0, sublen); + int r1 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), -1); + Assertions.assertEquals(1, r1); + Assertions.assertEquals(3, req.headerHalfLen()); + System.out.println(req.headerHalfLen()); + + text1 = REQ_TEXT.substring(sublen, sublen + 7); + int r2 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), -1); + Assertions.assertEquals(1, r2); + Assertions.assertEquals(10, req.headerHalfLen()); + + text1 = REQ_TEXT.substring(sublen + 7); + int r3 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), -1); + Assertions.assertEquals(0, r3); + } + + @Test + public void run3() throws Exception { + HttpContext.HttpContextConfig httpConfig = new HttpContext.HttpContextConfig(); + httpConfig.lazyHeader = true; + httpConfig.sameHeader = true; + httpConfig.maxHeader = 16 * 1024; + httpConfig.maxBody = 64 * 1024; + HttpContext context = new HttpContext(httpConfig); + HttpRequestX req = new HttpRequestX(context); + Assertions.assertEquals(0, req.readHeader(ByteBuffer.wrap(REQ_TEXT.getBytes()), -1)); + int phLength = req.headerLength(); + + req = new HttpRequestX(context); + int sublen = "GET /test/getinfo HTTP/1.1\r\n".length() + 3; + String text1 = REQ_TEXT.substring(0, sublen); + int r1 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), phLength); + Assertions.assertEquals(1, r1); + Assertions.assertEquals(3, req.headerHalfLen()); + System.out.println(req.headerHalfLen()); + + text1 = REQ_TEXT.substring(sublen, sublen + 7); + int r2 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), phLength); + Assertions.assertEquals(1, r2); + Assertions.assertEquals(10, req.headerHalfLen()); + + text1 = REQ_TEXT.substring(sublen + 7); + int r3 = req.readHeader(ByteBuffer.wrap(text1.getBytes()), phLength); + Assertions.assertEquals(0, r3); + } + + public static class HttpRequestX extends HttpRequest { + + public HttpRequestX(HttpContext context) { + super(context); + } + + @Override + public int readHeader(final ByteBuffer buf, final int pipelineHeaderLength) { + return super.readHeader(buf, pipelineHeaderLength); + } + + public int headerLength() { + return this.headerLength; + } + + public int headerHalfLen() { + return this.headerHalfLen; + } + } +}