types/nettype: add HalfCloser type

We have multiple situations where we have a `net.Conn` representing a
TCP connection for a proxy and we need access to the underlying
`CloseWrite()` and `CloseRead()` functions to properly pass through
half-closes (see #16462, #20883). Add an interface we can cast to in
order to get access to these functions.

Updates #20883.

Signed-off-by: Naman Sood <mail@nsood.in>
This commit is contained in:
Naman Sood committed 2026-09-17 17:22:32 -04:00
1 parent 16d19a7e5e
commit 35848427ce
2 files changed
+16 -12

No files matched your search

+11
View File
@@ -63,3 +63,14 @@ type ConnPacketConn interface {
net.Conn
net.PacketConn
}
// HalfCloser is an interface to abstract around various Conn types that
// allow closing of the read and write streams independently of each other.
// It is normally used as a typecast for a `net.Conn` to get access to these
// functions. If you use it for this purpose, you should either be absolutely
// certain that the `net.Conn` you have represents a half-closable connection
// (eg. TCP or unix socket), or check for and handle a failure to cast.
type HalfCloser interface {
CloseRead() error
CloseWrite() error
}
+5 -12
View File
@@ -1762,13 +1762,6 @@ func (ns *Impl) acceptTCP(r *tcp.ForwarderRequest) {
}
}
// tcpCloser is an interface to abstract around various TCPConn types that
// allow closing of the read and write streams independently of each other.
type tcpCloser interface {
CloseRead() error
CloseWrite() error
}
func (ns *Impl) forwardTCP(getClient func(...tcpip.SettableSocketOption) *gonet.TCPConn, clientRemoteIP netip.Addr, wq *waiter.Queue, dialAddr netip.AddrPort, isLocal bool) (handled bool) {
dialAddrStr := dialAddr.String()
if debugNetstack() {
@@ -1841,7 +1834,7 @@ func (ns *Impl) forwardTCP(getClient func(...tcpip.SettableSocketOption) *gonet.
// from stdDialer.DialContext (which has the requisite functions),
// or nil from hangDialer in tests (in which case we would have
// errored out by now), so this conversion should always succeed.
backendTCPCloser, backendIsTCPCloser := backend.(tcpCloser)
backendHalfCloser, backendIsHalfCloser := backend.(nettype.HalfCloser)
connClosed := make(chan error, 2)
go func() {
_, err := io.Copy(backend, client)
@@ -1850,8 +1843,8 @@ func (ns *Impl) forwardTCP(getClient func(...tcpip.SettableSocketOption) *gonet.
}
connClosed <- err
err = nil
if backendIsTCPCloser {
err = backendTCPCloser.CloseWrite()
if backendIsHalfCloser {
err = backendHalfCloser.CloseWrite()
}
err = errors.Join(err, client.CloseRead())
if err != nil {
@@ -1865,8 +1858,8 @@ func (ns *Impl) forwardTCP(getClient func(...tcpip.SettableSocketOption) *gonet.
}
connClosed <- err
err = nil
if backendIsTCPCloser {
err = backendTCPCloser.CloseRead()
if backendIsHalfCloser {
err = backendHalfCloser.CloseRead()
}
err = errors.Join(err, client.CloseWrite())
if err != nil {