-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTPServerErrorHandler: Improve Error Handling for HTTPParserError #2922
Conversation
Could you have a look at the proposed changes and trigger the workflow when you have some time @0xTim ? |
I had another look at the code and figured out that I was most likely not adding the Now I put the Can one of you possibly take another look at the changes @Joannis @0xTim ? |
For the context again: If a vapor app is supporting HTTP without TLS/SSL and is running behind nginx proxy, a single faulty (invalid HTTP) request can easily bring down all upstream servers for a time amount of the upstreams Another side note: we have been using this fix on one of our vapor based apis (approx. 20 Million req/day) for over a month now and have not experienced any problems so far. Any thoughts or suggestions on this MR are very appreciated! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if !self.hasUnterminatedResponse { | ||
self.logger.debug("Bad Request - Invalid HTTP: \(error)") | ||
let headers = HTTPHeaders([("Connection", "close"), ("Content-Length", "0")]) | ||
let head = HTTPResponseHead(version: .http1_1, status: .badRequest, headers: headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this fit in when running Vapor as an HTTP/2 server? Can we get the version from the current request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the easiest way to deal with this would be to have a separate HTTP1ServerErrorHandler
and HTTP2ServerErrorHandler
that are added in addVaporHTTP1Handlers
and addVaporHTTP2Handlers
respectively?
As far as I understand getting the HTTP version from the current request will not be easily possible because the error is thrown in the feedInput
method of the BetterHTTPParser
when the HTTPRequestDecoder
is used to transform the InboundIn ByteBuffer
to the InboundOut HTTPServerRequestPart
(in the Http1 pipeline). So we never actually get to see the HTTPServerRequestPart
when an error is thrown.
I am also wondering if this is even possibly happening in the HTTP/2 pipeline, because the HTTP2FramePayloadToHTTP1ServerCodec
transforms a HTTP2Frame.FramePayload
to a HTTPServerRequestPart
. The only errors that are being thrown on the way should be NIOHTTP2Errors
, so maybe a separate handler would be needed to handle such errors as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that makes sense. @FranzBusch I'd like to ask your thoughts here as the resident NIO expert! And if you have chance for a once-over in general as well that would be ace. This has been running fine in a prod system so I'm happy with the overall stability but just a sanity check to make sure we're not doing anything silly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is by itself fine and writing out a badRequest
in response to seeing an HTTPParserError
is good. As you noted above though you also need to handle the HTTP2Error
s somewhere. Either in a separate handler or by modifying this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am just having a look at this again. I am wondering if the error handling logic that is implemented in the decodeFrame
method and the processFrame
method of the NIOHTTP2Handler
is not already good enough when running Vapor as an HTTP/2 server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any opinions on my above comment @0xTim @FranzBusch ?
let errorHandler = HTTPServerErrorHandler(logger: configuration.logger) | ||
handlers.append(errorHandler) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to the discussion here, this should either be replaced by an appropriate ChannelHandler
for HTTP2Error
s at the correct place in the ChannelPipeline or be removed entirely
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #2922 +/- ##
==========================================
+ Coverage 76.21% 76.32% +0.10%
==========================================
Files 211 211
Lines 7821 7864 +43
==========================================
+ Hits 5961 6002 +41
- Misses 1860 1862 +2
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only comment here is the one where I resurrected someone else's old comment 😅 But other than that, LGTM
@fred-sch Slightly off-topic, is it intentional that the Vapor CI workflows are being run in your fork? (Such as the API docs CI, although it fails immediately since the credentials secrets obviously aren't available.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM at this point, though I wouldn't mind at least one more set of eyes on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're good to merge!
These changes are now available in 4.83.2 |
…2922) * fix: HTTPServerErrorHandler catch HTTPParserError Refs: #2921 * add reference to https://github.com/apple/swift-nio/blob/00341c92770e0a7bebdc5fda783f08765eb3ff56/Sources/NIOHTTP1/HTTPServerProtocolErrorHandler.swift * set hasUnterminatedResponse in write * error-handler needs to be before response encoder * add test for invalid http * rephrase and improve comments slightly * remove http1 error handler from http2 pipeline --------- Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> Co-authored-by: Gwynne Raskind <gwynne@vapor.codes>
* Make use of NIOFileHandle safe * Fix sendable closure warnings in FileIO * Make BodyStreamWriter Sendable * Fix Sendable warnings in HTTPServerHandler * Fix Sendable warnings in HTTPServerRequestDecoder * Fix Sendable warnings in ChannelResponseBodyStream * Fix Sendable warnings in HTTPServerUpgradeHandler * Fix warnings in Tests * Fix some test warnings * Make Request+BodyStream sendable * Use LoopBound instead of Locks as we know we're on an EventLoop * Fix some test warnings * Fix some test warnings * Fix the last warnings in the tests * Remove the unchecked Sendable on request and response * Changes from code review * Reduce inits of NIOLoopBound * Remove unnecessary checks on NIOLoopBound * Remove reminders * Revert "Remove unnecessary checks on NIOLoopBound" This reverts commit de0d52f. * Improve routes * Remove unnecessary checks on NIOLoopBound * Show error * Compiles but failing test * Don't make stuff sendable when we don't need to be * Fix code review comments * Fix a couple of logic bugs in response * Fix some outdated comment docs * PR Review * HTTPServerErrorHandler: Improve Error Handling for HTTPParserError (#2922) * fix: HTTPServerErrorHandler catch HTTPParserError Refs: #2921 * add reference to https://github.com/apple/swift-nio/blob/00341c92770e0a7bebdc5fda783f08765eb3ff56/Sources/NIOHTTP1/HTTPServerProtocolErrorHandler.swift * set hasUnterminatedResponse in write * error-handler needs to be before response encoder * add test for invalid http * rephrase and improve comments slightly * remove http1 error handler from http2 pipeline --------- Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> Co-authored-by: Gwynne Raskind <gwynne@vapor.codes> * Fix mismatching manifests * PR feedback * Trigger CodeQL * Trip CI --------- Co-authored-by: Gwynne Raskind <gwynne@vapor.codes> Co-authored-by: fred-sch <73998525+fred-sch@users.noreply.github.com>
Invalid HTTP previously just closed the connection, but did not actually handle the errors.
This PR uses an adapted version of https://github.com/apple/swift-nio/blob/main/Sources/NIOHTTP1/HTTPServerProtocolErrorHandler.swift to properly catch exceptions if they occur.
Closes: #2921