Truc Nguyen created KAFKA-21341:
-----------------------------------

             Summary: SslTransportLayer does not report a disconnect when the 
peer sends close_notify without a TCP FIN
                 Key: KAFKA-21341
                 URL: https://issues.apache.org/jira/browse/KAFKA-21341
             Project: Kafka
          Issue Type: Bug
          Components: clients, network
    Affects Versions: 4.2.2, 4.3.1, 4.2.1, 4.1.2, 4.1.1, 4.0.2, 4.3.0, 4.2.0, 
4.1.0, 4.0.1, 4.0.0, 3.9.2, 3.9.1, 3.9.0, 3.8.1, 3.8.0, 3.7.2, 3.7.1, 3.7.0, 
3.6.2, 3.6.1, 3.6.0, 3.6.3, 3.7.3, 3.8.2, 3.9.3, 4.0.3, 4.1.3, 4.2.3, 4.3.2, 
4.3.3, 4.4.0, 4.4.1, 4.5.0
            Reporter: Truc Nguyen
            Assignee: Truc Nguyen


When a TLS close_notify arrives in the same socket read as application data, 
SslTransportLayer.read() returns the data and leaves the close to be handled by 
a later read. However, updateBytesBuffered() clears hasBytesBuffered once the 
buffers are empty, so the Selector only reads the channel again if the socket 
becomes readable, which requires a TCP FIN.

RFC 8446 section 6.1 allows a peer to send close_notify and keep the TCP 
connection open: close_notify closes only the sender's write side, and the 
standard leaves the transport lifecycle to the application. A TLS-terminating 
proxy that half-closes its TLS side (for example, Envoy's tcp_proxy) does 
exactly this. The client then never notices the disconnect, and an in-flight 
request waits for request.timeout.ms before it fails.

*Reproduction*
A TLS peer completes the handshake, then writes part (or all) of a response 
followed by close_notify in a single write, and keeps the TCP connection open. 
The client Selector never reports the connection as disconnected. If the full 
response was sent, the client receives it but still does not see the disconnect.

*Expected behaviour*
Kafka already treats close_notify as end-of-stream when it arrives on its own: 
the CLOSED unwrap makes read() throw EOFException and the channel is closed. It 
should do the same when close_notify arrives together with data. This matches 
RFC 8446 section 6.1, which requires a TLS implementation to receive 
close_notify before indicating end-of-data to the application, and does not 
require the receiver to keep its own write side open. Kafka has no half-close 
semantics: once the peer has sent close_notify it can send no more responses, 
so reporting the disconnect at once is the only useful behaviour. On close, 
SslTransportLayer still sends its own close_notify.

*Proposed fix*
* After a CLOSED unwrap, keep the channel in the Selector's buffered-read set 
(hasBytesBuffered = true) so the next poll() reads it again without waiting for 
OP_READ.
* At the start of read(), if the engine's inbound side is done and no decrypted 
data is left, throw EOFException so the channel is closed as a disconnect.
* In read(ByteBuffer[], int, int), return the bytes already read if a later 
read throws EOFException, instead of losing them.

A channel that is muted (for example while the broker handles a request that 
arrived with the close_notify) stays open until it is unmuted, so the response 
to that request is still sent before the disconnect is reported.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to