tomcat的Http11InputBuffer在读取请求行时会先检查是否是http2请求前缀,这儿其实是有bug的,bug在这个检查上:byteBuffer.limit() >= CLIENT_PREFACE_START.length - 1,正确的逻辑最后不应该-1。
if (!keptAlive && byteBuffer.position() == 0 && byteBuffer.limit() >= CLIENT_PREFACE_START.length - 1) {
boolean prefaceMatch = true;
for (int i = 0; i < CLIENT_PREFACE_START.length && prefaceMatch; i++) {
if (CLIENT_PREFACE_START[i] != byteBuffer.get(i)) {
prefaceMatch = false;
}
}
if (prefaceMatch) {
// HTTP/2 preface matched
parsingRequestLinePhase = -1;
return false;
}
}
bug影响,如果向tomcat的服务器发送如下字符串:PRI * HTTP/2.0\r\n\r\nSM\r\n\r(比正常http2升级前缀少了最后一个字节),就会引起tomcat报错:
信息: Error parsing HTTP request header
Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
java.lang.IndexOutOfBoundsException
at java.nio.Buffer.checkIndex(Buffer.java:540)
at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:139)
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:368)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:260)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
该bug我看到在最新的tomcat-embed10.1.0-M17里仍然存在,我已经向tomcat作者提了修改,其实光改了这一处仍然是有bug的,如果发送不完整的http2前缀,tomcat会跳过检查而当作正常请求继续处理,导致报错不能正常升级。一般网络状况良好收到的数据长度肯定大于前缀长度,所以问题没有表现出来。
该bug未修复前可以作为使用了tomcat服务器的探查,向服务器发送该请求,如果报错,大概率使用的是tomcat了。