java - Tomcat 9:标题行不符合 RFC 7230
问题描述
我有一个在 Tomcat 8.5.38 上运行的应用程序。现在我决定升级到 Tomcat 9.0.27,GET request 和RFC 7230, Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing出现问题。
请求:
/api/vehicle/power_off?vehicleId=1428714&dtStart=2019-10-21 08:00:00&dtEnd=2019-10-21 08:30:00
它在浏览器(任何 - IE、Opera、Chrome、FF)和另一个客户端(1C ERP 系统)上都能完美运行。
从浏览器升级版本后,它仍然可以正常工作,但从 1C 则不能。Tomcat 显示错误:
28-Oct-2019 17:29:26.201 INFO [http-nio-8080-exec-3] org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: The HTTP header line [get /api/vehicle/power_off?deviceId=1428714&dtStart=2019-10-21%2008:00:00&dtEnd=2019-10-21%2008:30:00 HTTP/1.1: ] does not conform to RFC 7230 and has been ignored.
at org.apache.coyote.http11.Http11InputBuffer.skipLine(Http11InputBuffer.java:962)
at org.apache.coyote.http11.Http11InputBuffer.parseHeader(Http11InputBuffer.java:825)
at org.apache.coyote.http11.Http11InputBuffer.parseHeaders(Http11InputBuffer.java:564)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:309)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)
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)
同样的错误出现在我的开发机器(MacOS + Tomcat 9.0.24)和生产服务器(Ubuntu 16.04 + Tomcat 9.0.27)上。
原因在于日期时间参数中的冒号。当我从查询字符串中删除冒号(只留下“2019-10-21 080000”)时,请求按预期工作(错误“无法解析日期时间......”)。此外,当我手动将冒号更改为“%3A”时,请求有效并返回正常结果。
然后我用冒号向Tomcat连接器添加了relaxedQueryChars参数(尽管冒号是允许的符号):
relaxedQueryChars=':[]|{}^\`"<>'
它仍然失败。
我的请求在 8 中有效但在 9 中无效的 8 和 9 Tomcat 版本有什么区别?我可以在 Tomcat 中做些什么来使这个请求有效吗?在客户端更改请求是一项非常艰巨的任务......
解决方案
我的请求在 8 中有效但在 9 中无效的 8 和 9 Tomcat 版本有什么区别?
我认为不同之处在于 Tomcat 9.x 已经收紧了应该允许在 URL 中未编码的内容,因此从技术角度来看 Tomcat 9.x 没有问题;问题在于早期的 Tomcat 版本,以及没有严格遵循规范的浏览器。
也就是说,我无法为您确定任何触发此问题的特定修复程序,也无法在发行说明中看到任何内容。
我用冒号(虽然冒号是允许的符号)向Tomcat连接器添加了relaxedQueryChars参数......但它仍然失败。
来自Tomcat 9.0relaxedQueryChars
文档:
HTTP/1.1 规范要求某些字符在用于 URI 查询字符串时进行 %nn 编码。不幸的是,包括所有主要浏览器在内的许多用户代理都不符合该规范,并以未编码的形式使用这些字符。为防止 Tomcat 拒绝此类请求,此属性可用于指定允许的附加字符。如果未指定,则不允许使用其他字符。该值可以是以下字符的任意组合:" < > [ \ ] ^ ` { | } 。该值中存在的任何其他字符都将被忽略。
注意最后两句。冒号字符没有被提及,所以它“将被忽略”。
我可以在 Tomcat 中做些什么来使这个请求生效吗?
我不这么认为,但真正的问题是您没有在参数中编码冒号,并且您已经提到这可以解决问题。请参阅此 SO 答案,尤其是最后一句话:
There are reserved characters, that have a reserved meanings, those are delimiters — :/?#[]@ — and subdelimiters — !$&'()*+,;=
There is also a set of characters called unreserved characters — alphanumerics and -._~ — which are not to be encoded.
That means, that anything that doesn't belong to unreserved characters set is supposed to be %-encoded, when they do not have special meaning (e.g. when passed as a part of GET parameter).
冒号是具有特殊含义的保留字符,因此必须在参数中对其进行编码。
笔记:
另请参阅错误 62273 - 添加对备用 URL 规范的支持。尽管它没有专门解决您的冒号问题,但有一个有趣的讨论是关于浏览器如何不遵守RFC 3986(统一资源标识符(URI):通用语法)。
您从 Tomcat 收到的错误消息含糊不清,可能会得到改进。也许向他们提出错误报告?
推荐阅读
- c# - WPF:MemoryStream 占用大量内存
- java - 解析/转换包含字符和数字的任务
- sql - 按日期移动总和 (MS Access SQL)
- python - Python2.7 subprocess32:如何在通过 Popen 执行的两个脚本之间共享环境?
- gcov - 在 GCDA 文件中保留 Gcov 测试名称
- python - 如何在安全的 Nginx 上修复“从上游读取响应标头时上游过早关闭的连接”
- java - JAVA 和 PYTHON 中的 SHA256 加密产生不同的结果
- xml - XSLT - 递归循环基本案例
- docker - Docker 镜像 gcr.io/google_containers/kube2sky:1.15 源码位置
- c# - 使用access数据库将用户登录数据从form1传递到form2 listview