Simplify Netty pipeline using Buffering handler
- Dominant language
- Java
- Stars
- 12.1k
- Forks
- 4k
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 37
Description
The current pipeline configurations using one of the buffering handlers (tls, plaintext, etc.) are rather complicated and it's not always clear which handlers will handle exceptions in various cases.
We currently add the buffering handler and the HTTP/2 handler at startup. The buffering handler holds any writes until the startup handshake (e.g. SSL/TLS) completes, at which point it directs all buffered writes to the HTTP/2 handler. While those writes are occuring, the buffering handler stays in the pipeline (this is due to threading behavior of Netty WRT writes occuring outside of the event loop). If any problems occur while those writes are taking place, exception handling could occur in either the buffering handler or the HTTP/2 handler. It would be desirable to guarantee that exception handling can occur in only a single place at any point in time.
Proposed change:
Part 1): Add a ChannelHandlerAdapter as the last handler in the pipeline. Netty has a race condition when writes occur from outside of the event loop. The last ChannelHandlerContext is extracted in this thread and then the write is called. If however, the pipeline is changed between when the context is obtained and the write occurs ... badness ensues. As a workaround, there is some hacky code in the buffering handler to account for this race. A better solution to this problem would be to simply enforce the existence of a handler at the tail of the pipeline which never changes. This will just be a pass-through, but must implement the `write` method (this is to avoid another Netty gotcha, where it will skip handlers if it has determined that they are uninterested in the event).
Part 2): With the handler from Part 1 in place, the installation of the buffering and HTTP/2 handlers can be modified to a `replace`. Initially, only the buffering handler is installed (not the HTTP/2 handler). When the startup handshake completes successfully, the buffering handler will replace itself with the HTTP/2 handler, and then empty it's queued writes to the HTTP/2 handler.
In this way we guarantee that only one of these handlers exists in the pipeline at a time. Failures due to the initial handshake will be handled by the buffering handler. Failures due to writes will always be handled by the HTTP/2 handler.
@ejona86 FYI
Contributor guide
Assessment
This issue has not been assessed yet.