lazyHeader

This commit is contained in:
redkale
2024-08-16 09:37:50 +08:00
parent 60b4425a47
commit e7ed4ae9bc
5 changed files with 102 additions and 85 deletions

View File

@@ -43,14 +43,15 @@ public class HttpContext extends Context {
protected final AnyValue rpcAuthenticatorConfig;
// 所有Servlet方法都不需要读取http-header且RestBaseServlet不是自定义HttpServlet且不存在HttpFilter、WebSocket的情况下lazyHeaders=true
protected boolean lazyHeaders; // 存在动态改值
// 延迟解析header
protected final boolean lazyHeaders; // 存在动态改值
// 不带通配符的mapping url的缓存对象
final Map<ByteArray, String>[] uriPathCaches = new Map[100];
public HttpContext(HttpContextConfig config) {
super(config);
this.lazyHeaders = config.lazyHeaders;
this.remoteAddrHeader = config.remoteAddrHeader;
this.remoteAddrHeaders = config.remoteAddrHeaders;
this.localHeader = config.localHeader;
@@ -232,6 +233,8 @@ public class HttpContext extends Context {
}
public static class HttpContextConfig extends ContextConfig {
// 是否延迟解析http-header
public boolean lazyHeaders;
public String remoteAddrHeader;

View File

@@ -48,9 +48,6 @@ public class HttpDispatcherServlet
protected HttpContext context;
// 所有Servlet方法都不需要读取http-header且不存在HttpFilter的情况下lazyHeaders=true
protected boolean lazyHeaders = true;
private Map<String, BiPredicate<String, String>> forbidURIMaps; // 禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致
private BiPredicate<String, String>[] forbidURIPredicates; // 禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致
@@ -259,7 +256,6 @@ public class HttpDispatcherServlet
public void init(HttpContext context, AnyValue config) {
super.init(context, config); // 必须要执行
this.context = context;
context.lazyHeaders = lazyHeaders;
Collection<HttpServlet> servlets = getServlets();
servlets.forEach(s -> {
s.preInit(application, context, getServletConf(s));
@@ -381,10 +377,6 @@ public class HttpDispatcherServlet
@Override
public void addFilter(Filter<HttpContext, HttpRequest, HttpResponse> filter, AnyValue conf) {
super.addFilter(filter, conf);
this.lazyHeaders = false;
if (context != null) {
context.lazyHeaders = this.lazyHeaders; // 启动后运行过程中执行addFilter
}
}
/**
@@ -409,12 +401,6 @@ public class HttpDispatcherServlet
}
}
}
if (this.lazyHeaders && !Rest.isSimpleRestDyn(servlet)) {
this.lazyHeaders = false;
if (context != null) {
context.lazyHeaders = this.lazyHeaders; // 启动后运行过程中执行addServlet
}
}
allMapLock.lock();
try { // 需要整段锁住
for (String mappingPath : mappingPaths) {

View File

@@ -123,6 +123,8 @@ public class HttpRequest extends Request<HttpContext> {
protected int chunkedLength = -1;
protected int chunkedCurrOffset = -1;
protected byte[] chunkedHalfLenBytes;
protected boolean rpc;
@@ -172,11 +174,13 @@ public class HttpRequest extends Request<HttpContext> {
protected String locale;
private ByteArray array;
private String lastPathString;
private byte[] lastPathBytes;
private final ByteArray array;
private final ByteArray body;
private byte[] headerBytes;
@@ -201,9 +205,9 @@ public class HttpRequest extends Request<HttpContext> {
this(context, new ByteArray());
}
protected HttpRequest(HttpContext context, ByteArray array) {
protected HttpRequest(HttpContext context, ByteArray body) {
super(context);
this.array = array;
this.body = body;
this.remoteAddrHeader = context.remoteAddrHeader;
this.remoteAddrHeaders = context.remoteAddrHeaders;
this.localHeader = context.localHeader;
@@ -214,7 +218,7 @@ public class HttpRequest extends Request<HttpContext> {
@SuppressWarnings("OverridableMethodCallInConstructor")
protected HttpRequest(HttpContext context, WebRequest req) {
super(context);
this.array = new ByteArray();
this.body = new ByteArray();
this.remoteAddrHeader = null;
this.remoteAddrHeaders = null;
this.localHeader = null;
@@ -230,7 +234,7 @@ public class HttpRequest extends Request<HttpContext> {
this.rpc = req.rpc;
this.traceid = req.getTraceid();
if (req.getBody() != null) {
this.array.put(req.getBody());
this.body.put(req.getBody());
}
if (req.getHeaders() != null) {
this.headers.setAll(req.getHeaders());
@@ -261,7 +265,7 @@ public class HttpRequest extends Request<HttpContext> {
public WebRequest createSimpleRequest(String contextPath) {
WebRequest req = new WebRequest();
req.setBody(array.length() == 0 ? null : array.getBytes());
req.setBody(body.length() == 0 ? null : body.getBytes());
if (!getHeaders().isEmpty()) {
req.setHeaders(headers);
if (headers.contains(Rest.REST_HEADER_RPC)) { // 外部request不能包含RPC的header信息
@@ -319,7 +323,7 @@ public class HttpRequest extends Request<HttpContext> {
@Override
protected int readHeader(final ByteBuffer buf, final Request last) {
final ByteBuffer buffer = buf;
ByteArray bytes = array;
ByteArray bytes = body;
if (this.readState == READ_STATE_ROUTE) {
int rs = readMethodUriLine(buffer);
if (rs != 0) {
@@ -350,6 +354,7 @@ public class HttpRequest extends Request<HttpContext> {
this.expect = httpLast.expect;
this.chunked = httpLast.chunked;
this.chunkedLength = httpLast.chunkedLength;
this.chunkedCurrOffset = httpLast.chunkedCurrOffset;
this.rpc = httpLast.rpc;
this.traceid = httpLast.traceid;
this.currentUserid = httpLast.currentUserid;
@@ -362,7 +367,7 @@ public class HttpRequest extends Request<HttpContext> {
this.headerBytes = httpLast.headerBytes;
this.headerParsed = httpLast.headerParsed;
this.headers.setAll(httpLast.headers);
} else if (context.lazyHeaders && getmethod) { // 非GET必须要读header会有Content-Length
} else if (context.lazyHeaders) { // 非lazy必须要读header会有Content-Length
int rs = loadHeaderBytes(buffer);
if (rs != 0) {
buffer.clear();
@@ -395,8 +400,7 @@ public class HttpRequest extends Request<HttpContext> {
}
if (this.readState == READ_STATE_BODY) {
if (this.chunked) {
// TODO 待实现
return -1;
return readChunkedBody(buffer);
}
if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) {
if (this.contentLength > context.getMaxBody()) {
@@ -432,9 +436,18 @@ public class HttpRequest extends Request<HttpContext> {
return 0;
}
// TODO 待实现
private int readChunkedBody(final ByteBuffer buf) {
if (this.chunkedLength < 0) {
int size;
}
ByteArray bodyBytes = this.body;
return -1;
}
private int loadHeaderBytes(final ByteBuffer buf) {
final ByteBuffer buffer = buf;
ByteArray bytes = array;
ByteArray bytes = body; // body当temp buffer使用
int remain = buffer.remaining();
byte b1, b2, b3, b4;
for (; ; ) {
@@ -507,7 +520,7 @@ public class HttpRequest extends Request<HttpContext> {
Charset charset = this.context.getCharset();
int remain = buffer.remaining();
int size;
ByteArray bytes = array;
ByteArray bytes = body; // body当temp buffer使用
// 读method
if (this.method == null) {
boolean flag = false;
@@ -954,10 +967,10 @@ public class HttpRequest extends Request<HttpContext> {
if (headerBytes == null) {
return;
}
if (array.isEmpty()) {
readHeaderLines(ByteBuffer.wrap(headerBytes), array);
array.clear();
} else { // array存有body数据
if (body.isEmpty()) { // body当temp buffer使用
readHeaderLines(ByteBuffer.wrap(headerBytes), body);
body.clear();
} else { // 存有body数据
readHeaderLines(ByteBuffer.wrap(headerBytes), new ByteArray());
}
}
@@ -1090,7 +1103,7 @@ public class HttpRequest extends Request<HttpContext> {
if (!PIPELINE_SAME_HEADERS || !context.lazyHeaders) {
return null;
}
HttpRequest req = new HttpRequest(context, this.array);
HttpRequest req = new HttpRequest(context, this.body);
req.headerLength = this.headerLength;
req.headerBytes = this.headerBytes;
req.headerParsed = this.headerParsed;
@@ -1143,6 +1156,7 @@ public class HttpRequest extends Request<HttpContext> {
this.expect = false;
this.chunked = false;
this.chunkedLength = -1;
this.chunkedCurrOffset = -1;
this.chunkedHalfLenBytes = null;
this.rpc = false;
this.readState = READ_STATE_ROUTE;
@@ -1167,7 +1181,7 @@ public class HttpRequest extends Request<HttpContext> {
this.annotations = null;
this.remoteAddr = null;
this.params.clear();
this.array.clear();
this.body.clear();
// 内部
this.actionEntry = null;
super.recycle();
@@ -1186,29 +1200,36 @@ public class HttpRequest extends Request<HttpContext> {
unzipEncoding();
}
if (this.getContentType() != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) {
addParameter(array, true, 0, array.length());
addParameter(body, true, 0, body.length());
}
}
protected ByteArray array() {
if (array == null) {
array = new ByteArray();
}
return array;
}
protected void unzipEncoding() {
try {
if ("gzip".contains(this.contentEncoding)) {
if ("gzip".equalsIgnoreCase(this.contentEncoding)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(array.content(), 0, array.length());
ByteArrayInputStream in = new ByteArrayInputStream(body.content(), 0, body.length());
GZIPInputStream ungzip = new GZIPInputStream(in);
int n;
byte[] buffer = new byte[512];
while ((n = ungzip.read(buffer)) >= 0) {
byte[] buffer = array().content();
while ((n = ungzip.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
array.clear();
array.put(out.toByteArray());
} else if ("deflate".contains(this.contentEncoding)) {
body.clear();
body.put(out.toByteArray());
} else if ("deflate".equalsIgnoreCase(this.contentEncoding)) {
Inflater infl = new Inflater();
infl.setInput(array.content(), 0, array.length());
infl.setInput(body.content(), 0, body.length());
ByteArrayOutputStream out = new ByteArrayOutputStream();
int n;
byte[] buffer = new byte[512];
byte[] buffer = array().content();
while (!infl.finished()) {
n = infl.inflate(buffer);
if (n == 0) {
@@ -1217,37 +1238,37 @@ public class HttpRequest extends Request<HttpContext> {
out.write(buffer, 0, n);
}
infl.end();
array.clear();
array.put(out.toByteArray());
body.clear();
body.put(out.toByteArray());
}
} catch (Exception e) {
// do nothing
}
}
private void addParameter(final ByteArray array, final boolean body, final int offset, final int len) {
private void addParameter(final ByteArray bytes, final boolean body, final int offset, final int len) {
if (len < 1) {
return;
}
Charset charset = this.context.getCharset();
int limit = offset + len;
int keypos = array.indexOf(offset, limit, '=');
int valpos = array.indexOf(offset, limit, '&');
int keypos = bytes.indexOf(offset, limit, '=');
int valpos = bytes.indexOf(offset, limit, '&');
if (keypos <= 0 || (valpos >= 0 && valpos < keypos)) {
if (valpos > 0) {
addParameter(array, body, valpos + 1, limit - valpos - 1);
addParameter(bytes, body, valpos + 1, limit - valpos - 1);
}
return;
}
String name = toDecodeString(array, offset, keypos - offset, charset);
String name = toDecodeString(bytes, offset, keypos - offset, charset);
if (body && !name.isEmpty() && name.charAt(0) == '<') {
return; // 内容可能是xml格式; 如: <?xml version="1.0"
}
++keypos;
String value = toDecodeString(array, keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset);
String value = toDecodeString(bytes, keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset);
this.params.put(name, value);
if (valpos >= 0) {
addParameter(array, body, valpos + 1, limit - valpos - 1);
addParameter(bytes, body, valpos + 1, limit - valpos - 1);
}
}
@@ -1297,8 +1318,8 @@ public class HttpRequest extends Request<HttpContext> {
return this;
}
protected static String toDecodeString(ByteArray array, int offset, int len, final Charset charset) {
byte[] content = array.content();
protected static String toDecodeString(ByteArray bytes, int offset, int len, final Charset charset) {
byte[] content = bytes.content();
if (len == 1) {
return Character.toString(content[offset]);
} else if (len == 2 && content[offset] >= 0x20 && content[offset] < 0x80) {
@@ -1672,7 +1693,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public String getBody(final Charset charset) {
return charset == null ? array.toString() : array.toString(charset);
return charset == null ? body.toString() : body.toString(charset);
}
/**
@@ -1682,7 +1703,7 @@ public class HttpRequest extends Request<HttpContext> {
*/
@ConvertDisabled
public String getBodyUTF8() {
return array.toString(StandardCharsets.UTF_8);
return body.toString(StandardCharsets.UTF_8);
}
/**
@@ -1693,7 +1714,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public <T> T getBodyJson(java.lang.reflect.Type type) {
if (isEmpty(array)) {
if (isEmpty(body)) {
return null;
}
Convert convert = this.reqConvert;
@@ -1701,9 +1722,9 @@ public class HttpRequest extends Request<HttpContext> {
convert = context.getJsonConvert();
}
if (type == byte[].class) {
return (T) array.getBytes();
return (T) body.getBytes();
}
return (T) convert.convertFrom(type, array.content());
return (T) convert.convertFrom(type, body.content());
}
/**
@@ -1715,13 +1736,13 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public <T> T getBodyJson(Convert convert, java.lang.reflect.Type type) {
if (isEmpty(array)) {
if (isEmpty(body)) {
return null;
}
if (type == byte[].class) {
return (T) array.getBytes();
return (T) body.getBytes();
}
return (T) convert.convertFrom(type, array.content());
return (T) convert.convertFrom(type, body.content());
}
/**
@@ -1730,7 +1751,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容
*/
public byte[] getBody() {
return array.length() == 0 ? null : array.getBytes();
return body.length() == 0 ? null : body.getBytes();
}
/**
@@ -1740,7 +1761,7 @@ public class HttpRequest extends Request<HttpContext> {
*/
@ConvertDisabled
protected ByteArray getDirectBody() {
return array;
return body;
}
@Override
@@ -1761,8 +1782,8 @@ public class HttpRequest extends Request<HttpContext> {
+ (this.getHost() != null ? (", \r\n host: " + this.host) : "")
+ (this.getContentLength() >= 0 ? (", \r\n contentLength: " + this.contentLength) : "")
+ (this.contentEncoding != null ? (", \r\n contentEncoding: " + this.contentEncoding) : "")
+ (this.array.length() > 0 ? (", \r\n bodyLength: " + this.array.length()) : "")
+ (this.boundary || this.array.isEmpty()
+ (this.body.length() > 0 ? (", \r\n bodyLength: " + this.body.length()) : "")
+ (this.boundary || this.body.isEmpty()
? ""
: (", \r\n bodyContent: "
+ (this.respConvertType == null || this.respConvertType == ConvertType.JSON
@@ -1815,10 +1836,10 @@ public class HttpRequest extends Request<HttpContext> {
context.getCharset(),
this.getContentType(),
this.params.map(),
new BufferedInputStream(in, Math.max(array.length(), 8192)) {
new BufferedInputStream(in, Math.max(body.length(), 8192)) {
{
array.copyTo(this.buf);
this.count = array.length();
body.copyTo(this.buf);
this.count = body.length();
}
},
null);

View File

@@ -398,6 +398,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
final List<String[]> defaultAddHeaders = new ArrayList<>();
final List<String[]> defaultSetHeaders = new ArrayList<>();
boolean autoOptions = false;
boolean lazyHeader = false;
int datePeriod = 0;
String plainContentType = null;
String jsonContentType = null;
@@ -408,10 +409,12 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
AnyValue rpcAuthenticatorConfig = null;
if (config != null) {
AnyValue reqs = config.getAnyValue("request");
if (reqs != null) {
rpcAuthenticatorConfig = reqs.getAnyValue("rpc");
AnyValue raddr = reqs.getAnyValue("remoteaddr");
lazyHeader = config.getBoolValue("lazy", false); // 兼容旧配置
AnyValue reqConf = config.getAnyValue("request");
if (reqConf != null) {
lazyHeader = reqConf.getBoolValue("lazyHeader", lazyHeader);
rpcAuthenticatorConfig = reqConf.getAnyValue("rpc");
AnyValue raddr = reqConf.getAnyValue("remoteaddr");
remoteAddrHeader = raddr == null ? null : raddr.getValue("value");
if (remoteAddrHeader != null) {
if (remoteAddrHeader.startsWith("request.headers.")) {
@@ -420,7 +423,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
remoteAddrHeader = null;
}
}
AnyValue rlocale = reqs.getAnyValue("locale");
AnyValue rlocale = reqConf.getAnyValue("locale");
String vlocale = rlocale == null ? null : rlocale.getValue("value");
if (vlocale != null && !vlocale.isEmpty()) {
if (vlocale.startsWith("request.headers.")) {
@@ -436,17 +439,17 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
}
}
AnyValue resps = config.getAnyValue("response");
if (resps != null) {
AnyValue contenttypes = resps.getAnyValue("content-type");
AnyValue respConf = config.getAnyValue("response");
if (respConf != null) {
AnyValue contenttypes = respConf.getAnyValue("content-type");
if (contenttypes == null) {
contenttypes = resps.getAnyValue("contenttype"); // 兼容旧的
contenttypes = respConf.getAnyValue("contenttype"); // 兼容旧的
}
if (contenttypes != null) {
plainContentType = contenttypes.getValue("plain");
jsonContentType = contenttypes.getValue("json");
}
AnyValue[] addHeaders = resps.getAnyValues("addheader");
AnyValue[] addHeaders = respConf.getAnyValues("addheader");
if (addHeaders.length > 0) {
for (AnyValue addHeader : addHeaders) {
String val = addHeader.getValue("value");
@@ -471,7 +474,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
}
}
}
AnyValue[] setHeaders = resps.getAnyValues("setheader");
AnyValue[] setHeaders = respConf.getAnyValues("setheader");
if (setHeaders.length > 0) {
for (AnyValue setHeader : setHeaders) {
String val = setHeader.getValue("value");
@@ -496,7 +499,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
}
}
}
AnyValue defcookieValue = resps.getAnyValue("defcookie");
AnyValue defcookieValue = respConf.getAnyValue("defcookie");
if (defcookieValue != null) {
String domain = defcookieValue.getValue("domain");
String path = defcookieValue.getValue("path");
@@ -506,10 +509,10 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
defaultCookie.setPath(path);
}
}
AnyValue options = resps.getAnyValue("options");
AnyValue options = respConf.getAnyValue("options");
autoOptions = options != null && options.getBoolValue("auto", false);
AnyValue dates = resps.getAnyValue("date");
AnyValue dates = respConf.getAnyValue("date");
datePeriod = dates == null ? 0 : dates.getIntValue("period", 0);
}
}
@@ -549,7 +552,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
}
}
HttpRender httpRender = null;
AnyValue renderConfig = null;
AnyValue renderConfig;
{ // 设置TemplateEngine
renderConfig = config.getAnyValue("render");
if (renderConfig != null) {
@@ -592,6 +595,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
contextConfig.remoteAddrHeader = addrHeader;
contextConfig.remoteAddrHeaders = null;
}
contextConfig.lazyHeaders = lazyHeader;
contextConfig.localHeader = localHeader;
contextConfig.localParameter = localParameter;
contextConfig.rpcAuthenticatorConfig = rpcAuthenticatorConfig;

View File

@@ -54,12 +54,15 @@ public class RestSleepTest {
OutputStream out = socket.getOutputStream();
out.write(("GET /test/sleep200 HTTP/1.1\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 0\r\n"
+ "\r\n"
+ "GET /test/sleep300 HTTP/1.1\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 0\r\n"
+ "\r\n"
+ "GET /test/sleep500 HTTP/1.1\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 0\r\n"
+ "\r\n")
.getBytes());
InputStream in = socket.getInputStream();