HttpRequest
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
132
src/test/java/org/redkale/test/httpparser/HttpRequestTest.java
Normal file
132
src/test/java/org/redkale/test/httpparser/HttpRequestTest.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user