Issue with whitespace in header on Jetty 12.x
magdalynv opened this issue · 6 comments
Jetty Version
12.0.10
Jetty Environment
ee10
Java Version
22
Question
I have a question, since I noticed a difference in behaviour between Jetty 12.0.10 and 11.0.20 and I'm not sure how to resolve it. I have a following multipart request which I realize isn't formatted as it should be. Essentially this is an example of badly formatted request that we receive on our endpoint.
This request was processed successfully on Jetty 11.0.20, however after upgrade I'm getting following exception invalid leading whitespace before header
Is there a way to override this behavior since I'm not finding any way.
POST http://localhost:11185/test
Content-Type: multipart/form-data; boundary=boundary
Authorization: App **
--boundary
Content-Disposition: form-data;
name="from"
test@test.com
--boundary--
@magdalynv can you post the stacktrace?
I've attached the stacktrace. Thanks
stacktrace.txt
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:131)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:110)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:86)
at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:112)
at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1227)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:547)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:736)
at org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1614)
at org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:195)
at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1586)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1586)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1586)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)
at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1586)
at org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1547)
at org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:824)
at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:436)
at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:464)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:575)
at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:703)
at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:858)
at org.eclipse.jetty.server.Server.handle(Server.java:181)
at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:648)
at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:403)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:979)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1209)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1164)
at java.base/java.lang.Thread.run(Thread.java:1623)
Caused by: jakarta.servlet.ServletException: org.eclipse.jetty.http.BadMessageException: 400: bad multipart
at org.eclipse.jetty.ee10.servlet.ServletApiRequest.getParts(ServletApiRequest.java:720)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:93)
... 44 more
Caused by: org.eclipse.jetty.http.BadMessageException: 400: bad multipart
... 46 more
Caused by: org.eclipse.jetty.http.BadMessageException: 400: invalid leading whitespace before header
at org.eclipse.jetty.http.MultiPart$Parser.parseHeaderStart(MultiPart.java:1256)
at org.eclipse.jetty.http.MultiPart$Parser.parse(MultiPart.java:1080)
at org.eclipse.jetty.http.MultiPartFormData$Parser$1.parse(MultiPartFormData.java:311)
at org.eclipse.jetty.http.MultiPartFormData$Parser$1.parse(MultiPartFormData.java:301)
at org.eclipse.jetty.io.content.ContentSourceCompletableFuture.parse(ContentSourceCompletableFuture.java:104)
at org.eclipse.jetty.http.MultiPartFormData$Parser.parse(MultiPartFormData.java:326)
at org.eclipse.jetty.http.MultiPartFormData.from(MultiPartFormData.java:109)
at org.eclipse.jetty.ee10.servlet.ServletMultiPartFormData.from(ServletMultiPartFormData.java:138)
at org.eclipse.jetty.ee10.servlet.ServletMultiPartFormData.from(ServletMultiPartFormData.java:62)
at org.eclipse.jetty.ee10.servlet.ServletApiRequest.getParts(ServletApiRequest.java:637)
... 45 more
--boundary
Content-Disposition: form-data;
name="from"
The multipart/form-data
spec (RFC7578) says these 3 lines are ...
- A boundary line
- A
Content-Disposition
header with a single attributeform-data
(which has no value) - Invalid line that doesn't fit either a Header field or a end of headers line.
The failure "invalid leading whitespace before header" (line 3) is telling you that after the Content-Disposition
field, the parser didn't see a end-of-headers line, so it is expecting another header, but the parser didn't see the mandated non-whitespace ASCII character at the start of the line for a header field, hence the exception.
The form of header you are using is whats known as "line folding".
Line folding has never been supported for the multipart/form-data
or multipart/byteranges
content types (both declared in 1998) seen in HTTP.
Line folding is a concept for multipart/related
(RFC2110 declared in 1997) seen in email and MHTML where there are line length restrictions.
Note that content types multipart/mixed
, multipart/alternative
, multipart/digest
, and multipart/parallel
(RFC1341 declared in 1992) also do not support header "line folding".
You have a few choices in front of you ...
- (The Best Choice) - fix the
multipart/form-data
syntax that is being generated before submitting it to Jetty. - Fall back to ee8 / ee9 and use the LEGACY (buggy and SLOW)
multipart/form-data
parser via the MultiPartCompliance configuration in HttpConfiguration (not available on ee10 and newer). - (to give an idea on how slow, a 1MB multipart/form-data takes about 800ms to parse on the ee10 spec compliant parser, or about 3 minutes on the LEGACY parser) - Stick with ee10 and reject bad
multipart/form-data
syntax properly, dropping and ignoring badmultipart/form-data
Hi,
sorry for a later response. Thanks for the explanation.
I realize that the request itself is a bad request due to folding lines, but this is a request that was handled fine on the previous version of Jetty. I just wanted to check what caused this difference in behavior.
Thank you for the hint how to use legacy option if it comes to that.
I realize that the request itself is a bad request due to folding lines, but this is a request that was handled fine on the previous version of Jetty. I just wanted to check what caused this difference in behavior.
We constantly update the implementation to expose less attack surface, and folding lines are one such attack vectors, and that's why by default they now result in a 400 response.
If you really must support the old behavior, you can configure so.
Perfect, thank you for the reply and your help