springboot运行一段时间报错 nvalid character found in method name. HTTP method names must be tokens
springboot项目在服务器上运行一段时间后,可能会报出这样一个异常。java.lang.IllegalArgumentException: Invalid character found in method name [0x160x030x010x020x000x010x000x010xfc0x030x030x920xff0xdbN0xb40x890xa8q0x9d0x1c0xde0x0dZ
springboot项目在服务器上运行一段时间后,可能会报出这样一个异常。
java.lang.IllegalArgumentException: Invalid character found in method name [0x160x030x010x020x000x010x000x010xfc0x030x030x920xff0xdbN0xb40x890xa8q0x9d0x1c0xde0x0dZ0xb6:0xb00xbe0xd8_0x850xb10x950xeeB0xbbk0xdb0xf00xd60xeb0xe0u]. HTTP method names must be tokens
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:417) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
这是从Http11InputBuffer里抛出的一个异常,这个类是tomcat-enbed-core-9.0.41-source里的。研究了一下 还是有些心得的。首先是在417这一行抛出的异常,(这是阶段2,检查http请求方式是否有非法字符,类中用这个变量表示parsingRequestLinePhase),就是下面代码的最后一行抛出
int pos = byteBuffer.position();
chr = byteBuffer.get();
if (chr == Constants.SP || chr == Constants.HT) {
space = true;
request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,
pos - parsingRequestLineStart);
} else if (!HttpParser.isToken(chr)) {
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
String invalidMethodValue = parseInvalid(parsingRequestLineStart, byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidmethod", invalidMethodValue));
这一部分代码解析的是http头部信息,头部信息长这个样子
GET / HTTP/1.1
Host: www.enjoytoday.cn
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://www.enjoytoday.cn/posts/326
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1466032270994; UM_distinctid=15c4ef2ac4e2e4-0d13269271b947-1b2a120b-1fa400-15c4ef2ac4f7b5; un=aGZjYWk=; comment_author=aGZjYWk=; comment_author_email=1710600212@qq.com; comment_author_url=http://www.enjoytoday.cn; c_id=dUhIaTlndmc4MVVYbjRQTGxMRTotMTpFODg3QjgzQjg1NjgxQjQxRUYxNjg2QzJFRkMyQjI2QQ==; JSESSIONID=ADBC8C3DADF6C815D778450C193C6637.ajp13_worker; Hm_lvt_ce55bfda158556585a8b7b246346c8ba=1498560244,1498739070,1498833193,1498917432; Hm_lpvt_ce55bfda158556585a8b7b246346c8ba=1498917597; CNZZDATA1262047894=1598545996-1495973145-%7C1498917578
username=hfcai&sex=man
所以如果是一个Get请求 那么此时buffer中头三个就是 71 69 84,如下图所示。
然后就逐个读取,等于空格或者是水平制表符的时候,就跳出。不等于的时候进入HttpParser的isToken方法检验。HttpParser中有若干个boolean数组,每个数组大小都是128。下面是他的初始化数组.
static {
for (int i = 0; i < ARRAY_SIZE; i++) {
// Control> 0-31, 127
if (i < 32 || i == 127) {
IS_CONTROL[i] = true;
}
//i小于32或者等于128 IS_CONTROL对应位置为真。
// Separator
if ( i == '(' || i == ')' || i == '<' || i == '>' || i == '@' ||
i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
i == '/' || i == '[' || i == ']' || i == '?' || i == '=' ||
i == '{' || i == '}' || i == ' ' || i == '\t') {
IS_SEPARATOR[i] = true;
}
//i为分隔符时,对应位置为真
// Token: Anything 0-127 that is not a control and not a separator
if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {
IS_TOKEN[i] = true;
}
//不是控制符,也不是分隔符,且小于128,对应位置为真
。。。
}
public static boolean isToken(int c) {
// Fast for correct values, slower for incorrect ones
try {
return IS_TOKEN[c];
} catch (ArrayIndexOutOfBoundsException ex) {
return false;
}
}
也就是说,这个异常的最终原因是因为http 请求方式中,也就是GET Post中,包含了非法字符。接下来试一试捕获这个异常。postman会检查http请求方式,不太好用,请求方法中含有非法字符的请求发不出去。最终发现在idea中Tools-HTTPClients可以发出这种请求。
抛出的异常如下,可以从HttpInputBuffer的417行抛出,可以看出来跟上面是同一个异常。
java.lang.IllegalArgumentException: Invalid character found in method name [GET[]. HTTP method names must be tokens
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:417) ~[tomcat-embed-core-9.0.41.jar:9.0.41]
如果实在http11InputBuffer533行报的错,那很明显就是在解析 GET / HTTP/1.1 其中的 HTTP/1.1 时报的错,可以看到 532行是HttpParser.isHttpProtocal(chr), 点开httpParser
static{
...
if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
IS_HTTP_PROTOCOL[i] = true;
}
...
}
public static boolean isHttpProtocol(int c) {
// Fast for valid HTTP protocol characters, slower for some incorrect
// ones
try {
return IS_HTTP_PROTOCOL[c];
} catch (ArrayIndexOutOfBoundsException ex) {
return false;
}
}
也就是说,这个位置只要不是H, T , P , / , . 或者数字,那就都会在533行报错。
关于解决方案:
1、 尝试用controller统一处理,失败,代码如下
@ControllerAdvice
public abstract class BaseController {
@ExceptionHandler({IllegalArgumentException.class })
public String parseHttpException(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = new HashMap<>();
map.put("status", "-1001");
map.put("message", "http解析错误");
System.out.println(map.toString());
return null;
}
}
结果就是拦截不到这个异常,该抛出还是抛出了。
2、尝试用aop,织入http11inputbuffer,代码如下
public class CatchIllegalHttpMethodAdvice {
//匹配Http11InputBuffer抛出的异常
@Pointcut("execution(boolean org.apache.coyote.http11.Http11InputBuffer.*(..))")
public void phase2Exception(){}
@AfterThrowing(value = "phase2Exception()",throwing = "e")
public void handlePhase2Exception(JoinPoint joinPoint,RuntimeException e){
System.out.println("========================================");
System.out.println(String.valueOf(e.getMessage()));
}
}
还是拦截不到。。。
暂时想不到别的办法了,大家有什么想法的话可以写在评论区。
更多推荐
所有评论(0)