HttpRequest

This commit is contained in:
redkale
2024-09-13 08:34:58 +08:00
parent a757504877
commit 28aa2285f5
2 changed files with 179 additions and 39 deletions

View File

@@ -182,7 +182,7 @@ public class HttpRequest extends Request<HttpContext> {
private final ByteArray bodyBytes;
private byte[] headerBytes;
private final ByteArray headerBytes;
private boolean headerParsed = false;
@@ -203,11 +203,12 @@ public class HttpRequest extends Request<HttpContext> {
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<HttpContext> {
@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<HttpContext> {
@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<HttpContext> {
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<HttpContext> {
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<HttpContext> {
}
// 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<HttpContext> {
+ 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<HttpContext> {
}
// 文件上传、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<HttpContext> {
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<HttpContext> {
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<HttpContext> {
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<HttpContext> {
// header
this.headerLength = 0;
this.headerHalfLen = 0;
this.headerBytes = null;
this.headerBytes.clear();
this.headerParsed = false;
this.contentType = null;
this.contentLength = -1;

View File

@@ -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;
}
}
}