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 final ByteArray bodyBytes;
private byte[] headerBytes; private final ByteArray headerBytes;
private boolean headerParsed = false; private boolean headerParsed = false;
@@ -203,11 +203,12 @@ public class HttpRequest extends Request<HttpContext> {
HttpServlet.ActionEntry actionEntry; HttpServlet.ActionEntry actionEntry;
public HttpRequest(HttpContext context) { 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); super(context);
this.headerBytes = headerBytes;
this.bodyBytes = bodyBytes; this.bodyBytes = bodyBytes;
this.remoteAddrHeader = context.remoteAddrHeader; this.remoteAddrHeader = context.remoteAddrHeader;
this.remoteAddrHeaders = context.remoteAddrHeaders; this.remoteAddrHeaders = context.remoteAddrHeaders;
@@ -219,6 +220,7 @@ public class HttpRequest extends Request<HttpContext> {
@SuppressWarnings("OverridableMethodCallInConstructor") @SuppressWarnings("OverridableMethodCallInConstructor")
protected HttpRequest(HttpContext context, WebRequest req) { protected HttpRequest(HttpContext context, WebRequest req) {
super(context); super(context);
this.headerBytes = new ByteArray();
this.bodyBytes = new ByteArray(); this.bodyBytes = new ByteArray();
this.remoteAddrHeader = null; this.remoteAddrHeader = null;
this.remoteAddrHeaders = null; this.remoteAddrHeaders = null;
@@ -333,27 +335,30 @@ public class HttpRequest extends Request<HttpContext> {
@Override @Override
protected int readHeader(final ByteBuffer buf, final int pipelineHeaderLength) { protected int readHeader(final ByteBuffer buf, final int pipelineHeaderLength) {
final ByteBuffer buffer = buf; final ByteBuffer buffer = buf;
ByteArray bytes = bodyBytes;
if (this.readState == READ_STATE_ROUTE) { if (this.readState == READ_STATE_ROUTE) {
int rs = readMethodUriLine(buffer); int rs = readMethodUriLine(buffer);
if (rs != 0) { if (rs != 0) {
return rs; return rs;
} }
this.headerBytes.clear();
this.headerLength = 0;
this.headerHalfLen = 0;
this.readState = READ_STATE_HEADER; this.readState = READ_STATE_HEADER;
} }
if (this.readState == READ_STATE_HEADER) { if (this.readState == READ_STATE_HEADER) {
if (pipelineHeaderLength > 0) { if (pipelineHeaderLength > 0) {
ByteArray hbytes = this.headerBytes;
int bufremain = buffer.remaining(); int bufremain = buffer.remaining();
int remainHalf = pipelineHeaderLength - this.headerHalfLen; int remainHalf = pipelineHeaderLength - this.headerHalfLen;
if (remainHalf > bufremain) { if (remainHalf > bufremain) {
bytes.put(buffer); hbytes.put(buffer);
this.headerHalfLen += bufremain; this.headerHalfLen += bufremain;
buffer.clear(); buffer.clear();
return 1; return 1;
} }
bytes.put(buffer, remainHalf); hbytes.put(buffer, remainHalf);
this.headerBytes = bytes.getBytes(); this.headerLength = this.headerBytes.length();
this.headerLength = this.headerBytes.length; this.headerHalfLen = this.headerLength;
this.headerParsed = false; this.headerParsed = false;
} else if (context.lazyHeader && getmethod) { // 非GET必须要读header会有Content-Length } else if (context.lazyHeader && getmethod) { // 非GET必须要读header会有Content-Length
int rs = loadHeaderBytes(buffer); int rs = loadHeaderBytes(buffer);
@@ -372,7 +377,8 @@ public class HttpRequest extends Request<HttpContext> {
this.headerParsed = false; this.headerParsed = false;
} else { } else {
int startpos = buffer.position(); 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; this.headerLength = buffer.position() - startpos + this.headerHalfLen;
if (rs >= 0 && this.headerLength > context.getMaxHeader()) { if (rs >= 0 && this.headerLength > context.getMaxHeader()) {
context.getLogger() context.getLogger()
@@ -383,14 +389,13 @@ public class HttpRequest extends Request<HttpContext> {
return -1; return -1;
} }
if (rs != 0) { if (rs != 0) {
this.headerHalfLen = bytes.length(); this.headerHalfLen = hbytes.length();
buffer.clear(); buffer.clear();
return rs; return rs;
} }
this.headerParsed = true; this.headerParsed = true;
this.headerHalfLen = this.headerLength; this.headerHalfLen = this.headerLength;
} }
bytes.clear();
if (this.contentType != null && this.contentType.contains("boundary=")) { if (this.contentType != null && this.contentType.contains("boundary=")) {
this.boundary = true; this.boundary = true;
} }
@@ -400,12 +405,14 @@ public class HttpRequest extends Request<HttpContext> {
} }
// completed=true时ProtocolCodec会继续读下一个request // completed=true时ProtocolCodec会继续读下一个request
this.completed = !this.boundary && !maybews; this.completed = !this.boundary && !maybews;
this.bodyBytes.clear();
this.readState = READ_STATE_BODY; this.readState = READ_STATE_BODY;
} }
if (this.readState == READ_STATE_BODY) { if (this.readState == READ_STATE_BODY) {
if (this.chunked) { if (this.chunked) {
return readChunkedBody(buffer); return readChunkedBody(buffer);
} }
ByteArray bbytes = this.bodyBytes;
if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) { if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) {
if (this.contentLength > context.getMaxBody()) { if (this.contentLength > context.getMaxBody()) {
context.getLogger() context.getLogger()
@@ -415,12 +422,12 @@ public class HttpRequest extends Request<HttpContext> {
+ this.contentLength + ", path: " + requestPath); + this.contentLength + ", path: " + requestPath);
return -1; return -1;
} }
bytes.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); bbytes.put(buffer, Math.min((int) this.contentLength, buffer.remaining()));
int lr = (int) this.contentLength - bytes.length(); int lr = (int) this.contentLength - bbytes.length();
if (lr == 0) { if (lr == 0) {
this.readState = READ_STATE_END; this.readState = READ_STATE_END;
if (bytes.isEmpty()) { if (bbytes.isEmpty()) {
this.bodyParsed = true; // no bodyBytes data this.bodyParsed = true; // no body data
} }
} else { } else {
buffer.clear(); buffer.clear();
@@ -429,11 +436,11 @@ public class HttpRequest extends Request<HttpContext> {
} }
// 文件上传、HTTP1.0或Connection:close // 文件上传、HTTP1.0或Connection:close
if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) { if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) {
bytes.put(buffer, buffer.remaining()); bbytes.put(buffer, buffer.remaining());
} }
this.readState = READ_STATE_END; this.readState = READ_STATE_END;
if (bytes.isEmpty()) { if (bbytes.isEmpty()) {
this.bodyParsed = true; // no bodyBytes data this.bodyParsed = true; // no body data
} else if (!getmethod && this.contentLength < 0 && keepAlive) { } else if (!getmethod && this.contentLength < 0 && keepAlive) {
// keep-alive=true: Content-Length和chunk必然是二选一。 // keep-alive=true: Content-Length和chunk必然是二选一。
// keep-alive=false: Content-Length可有可无. // keep-alive=false: Content-Length可有可无.
@@ -567,29 +574,29 @@ public class HttpRequest extends Request<HttpContext> {
private int loadHeaderBytes(final ByteBuffer buf) { private int loadHeaderBytes(final ByteBuffer buf) {
final ByteBuffer buffer = buf; final ByteBuffer buffer = buf;
ByteArray bytes = bodyBytes; // body当temp buffer使用 ByteArray hbytes = this.headerBytes;
int remain = buffer.remaining(); int remain = buffer.remaining();
byte b1, b2, b3, b4; byte b1, b2, b3, b4;
for (; ; ) { for (; ; ) {
if (remain-- < 4) { // bytes不存放\r\n\r\n这4个字节 if (remain-- < 4) { // bytes不存放\r\n\r\n这4个字节
bytes.put(buffer); hbytes.put(buffer);
buffer.clear(); buffer.clear();
if (bytes.length() > 0) { if (hbytes.length() > 0) {
byte rn1 = 0, rn2 = 0, rn3 = 0; byte rn1 = 0, rn2 = 0, rn3 = 0;
byte b = bytes.getLastByte(); byte b = hbytes.getLastByte();
if (b == '\r' || b == '\n') { if (b == '\r' || b == '\n') {
rn3 = b; rn3 = b;
bytes.backCount(); hbytes.backCount();
if (bytes.length() > 0) { if (hbytes.length() > 0) {
b = bytes.getLastByte(); b = hbytes.getLastByte();
if (b == '\r' || b == '\n') { if (b == '\r' || b == '\n') {
rn2 = b; rn2 = b;
bytes.backCount(); hbytes.backCount();
if (bytes.length() > 0) { if (hbytes.length() > 0) {
b = bytes.getLastByte(); b = hbytes.getLastByte();
if (b == '\r' || b == '\n') { if (b == '\r' || b == '\n') {
rn1 = b; rn1 = b;
bytes.backCount(); hbytes.backCount();
} }
} }
} }
@@ -605,30 +612,31 @@ public class HttpRequest extends Request<HttpContext> {
buffer.put(rn3); buffer.put(rn3);
} }
} }
this.headerHalfLen = hbytes.length();
return 1; return 1;
} }
b1 = buffer.get(); b1 = buffer.get();
bytes.put(b1); hbytes.put(b1);
if (b1 == '\r') { if (b1 == '\r') {
remain--; remain--;
b2 = buffer.get(); b2 = buffer.get();
bytes.put(b2);
if (b2 == '\n') { if (b2 == '\n') {
remain--; remain--;
b3 = buffer.get(); b3 = buffer.get();
bytes.put(b3);
if (b3 == '\r') { if (b3 == '\r') {
remain--; remain--;
b4 = buffer.get(); b4 = buffer.get();
bytes.put(b4); hbytes.put(b2, b3, b4);
if (b4 == '\n') { if (b4 == '\n') {
this.headerBytes = Utility.append(this.headerBytes, bytes.content(), 0, bytes.length()); this.headerLength = hbytes.length();
this.headerLength = this.headerBytes.length;
this.headerHalfLen = this.headerLength; this.headerHalfLen = this.headerLength;
bytes.clear();
return 0; return 0;
} }
} else {
hbytes.put(b2, b3);
} }
} else {
hbytes.put(b2);
} }
} }
} }
@@ -1088,10 +1096,10 @@ public class HttpRequest extends Request<HttpContext> {
return; return;
} }
if (bodyBytes.isEmpty()) { // body当temp buffer使用 if (bodyBytes.isEmpty()) { // body当temp buffer使用
readHeaderLines(ByteBuffer.wrap(headerBytes), bodyBytes); readHeaderLines(this.headerBytes.wrapByteBuffer(), bodyBytes);
bodyBytes.clear(); bodyBytes.clear();
} else { // 存有body数据 } 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 // header
this.headerLength = 0; this.headerLength = 0;
this.headerHalfLen = 0; this.headerHalfLen = 0;
this.headerBytes = null; this.headerBytes.clear();
this.headerParsed = false; this.headerParsed = false;
this.contentType = null; this.contentType = null;
this.contentLength = -1; 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;
}
}
}