首页 > 解决方案 > Spring MVC 挂起带有 Content-Length 标头但没有 POST 正文的 POST 请求

问题描述

我有一个用 Java 编写的服务器,它在 Tomcat 下运行 Spring MVC(Spring BOOT)。我们注意到一些客户端正在发送一个带有内容长度标头但没有实体主体的 POST 请求。当此请求进入时,请求会挂起 20 秒,然后服务器会返回响应 (400)。

附加分析器显示挂起的服务器线程具有以下堆栈。

“http-nio-31114-exec-4”#52 守护进程 prio=5 os_prio=31 tid=0x00007fc26e6b6000 nid=0x9f03 等待条件 [0x0000700010a1f000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe .park(Native Method) - 在 java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) at java.util.concurrent.locks 停车等待 (a java.util.concurrent.CountDownLatch$Sync) .AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1037) at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1328) at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:277) at org. org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper 上的 apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.awaitLatch(NioEndpoint.java:1106)。awaitReadLatch(NioEndpoint.java:1108) at org.apache.tomcat.util.net.NioBlockingSelector.read(NioBlockingSelector.java:184) at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:235)在 org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:216) 在 org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1233) 在 org.apache.tomcat .util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1182) 在 org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:713) 在 org.apache.coyote.http11.Http11InputBuffer.access$300( Http11InputBuffer.java:40) 在 org.apache.coyote.http11.Http11InputBuffer$SocketInputBuffer.doRead(Http11InputBuffer.java:1063) 在 org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:140) 在 org.apache.coyote.http11.Http11InputBuffer.doRead(Http11InputBuffer.java:257) 在 org.apache.coyote.Request.doRead(Request.java:541) 在 org.apache.catalina.connector.InputBuffer.realReadBytes (InputBuffer.java:326) 在 org.apache.catalina.connector.InputBuffer.checkByteBufferEof(InputBuffer.java:634) 在 org.apache.catalina.connector.InputBuffer.readByte(InputBuffer.java:337) 在 org.apache。 catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:93) at java.io.FilterInputStream.read(FilterInputStream.java:83) at java.io.PushbackInputStream.read(PushbackInputStream.java:139) at org.springframework.web org.springframework.web 上的 .servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver$EmptyBodyCheckingHttpInputMessage.(AbstractMessageConverterMethodArgumentResolver.java:319)。servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:192) at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:157) at org.springframework.web。 servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130) 在 org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) 在 org.springframework.web.method.support。 InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) 在 org.springframework.web.method.support.InvocableHandlerMethod。在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter 的 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) 的 invokeForRequest(InvocableHandlerMethod.java:131)。 invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle( AbstractHandlerMethodAdapter.java:87) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) 在 org.springframework.web .servlet。FrameworkServlet.processRequest(FrameworkServlet.java:974) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) at org.springframework .web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231 ) 在 org.springframework.web.filter.OncePerRequestFilter 的 org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) 的 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)。 doFilter(OncePerRequestFilter.java:107) 在 org.apache.catalina.core.ApplicationFilterChain。internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) at org.springframework .web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java :166) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.apache.catalina.core 的 org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)。应用过滤链。internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.cloud.sleuth.instrument.web.ExceptionLoggingFilter.doFilter(ExceptionLoggingFilter.java:48)在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 brave.servlet.TracingFilter.doFilter(TracingFilter.java :61) 在 org.springframework.boot.web 的 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 的 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)。 servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:130) 在 org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:66) 在 org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:105) 在 org.springframework。 web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:123) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter( ApplicationFilterChain.java:193) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 在 org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) 在 org.springframework.web .filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.apache.catalina.core。ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org .apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) 在 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve .java:140) 在 org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624) 在 org.apache.catalina.org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)。 core.StandardEngineValve.invoke(StandardEngineValve.java:87) 在 com.fabrikam.valve.RequestTraceValve。com.fabrikam.valve.RequestTraceValve$$Lambda$423/855335917.accept 的 lambda$invoke$0(RequestTraceValve.java:41) com.fabrikam.tomcat.request.trace.RequestTraceInitializer.invoke(RequestTraceInitializer.java: 140) 在 com.fabrikam.valve.RequestTraceValve.invoke(RequestTraceValve.java:39) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) 在 org.apache.coyote.http11.Http11Processor.service (Http11Processor.java:799) 在 org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) 在 org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) 在 org.apache.tomcat。 util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) 在 org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) - 在 java.util.concurrent.ThreadPoolExecutor$Worker.run( ThreadPoolExecutor.java:617) 在 org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 在 java.lang.Thread.run(Thread.java:745)

servlet 如下所示:

@CrossOrigin @RequestMapping(path = "/event/{apikey}/{anonymousId}", method = RequestMethod.POST,produces = "application/json") public void clickRawPost(@PathVariable String apikey, @PathVariable String anonymousId, @RequestBody (required = false) String postContent, HttpServletRequest request, HttpServletResponse response) throws IOException {

repro 命令如下:

$ time curl -s http://localhost:8080/event/test/foo -H 'content-type: application/json' -H 'content-length: 100' -X POST -v

该命令在 20 秒后超时。

我原以为它会很快失败,因为请求中不存在实体主体。

标签: spring-mvcspring-boottomcat8

解决方案


推荐阅读