From eb9e2c974c9ae8d3f042c1d18359c75d50029415 Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Wed, 21 Dec 2022 11:26:36 -0800 Subject: [PATCH] sshforward: skip conn close on stream CloseSend. The GRPC docs on RecvMsg say: > RecvMsg blocks until it receives a message into m or the stream is > done. It returns io.EOF when the client has performed a CloseSend. > On any non-EOF error, the stream is aborted and the error contains > the RPC status. So if EOF is received that just means the client won't be sending anymore data. But it may still be expecting to read data, so we shouldn't close the conn yet. This was encountered in real life when forwarding a docker socket to a container, where it appears that the docker CLI closes its write side of the connection when requesting the stdout/stderr but then expects to read data after that. Signed-off-by: Erik Sipsma --- session/sshforward/copy.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/session/sshforward/copy.go b/session/sshforward/copy.go index 8d05e9484144..936e1826af92 100644 --- a/session/sshforward/copy.go +++ b/session/sshforward/copy.go @@ -14,16 +14,19 @@ type Stream interface { } func Copy(ctx context.Context, conn io.ReadWriteCloser, stream Stream, closeStream func() error) error { + defer conn.Close() g, ctx := errgroup.WithContext(ctx) g.Go(func() (retErr error) { p := &BytesMessage{} for { if err := stream.RecvMsg(p); err != nil { - conn.Close() if err == io.EOF { + // indicates client performed CloseSend, but they may still be + // reading data, so don't close conn yet return nil } + conn.Close() return errors.WithStack(err) } select {