Fix issue where conn is stuck waiting for stdin

This was hard to repro, but found out it seems to only happen with TLS
connections.
When `docker run -i` was set, the client gets stuck waiting for sdin on
exit, and requires hitting return twice.

This fix ensures that:
1. When stdin is done, wait for stdout always
2. When stdout is done, close the stream and wait for stdin to finish

On 2, stdin copy should return immediately now since the out stream is closed.
Note that we probably don't actually even need to wait here.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2015-10-15 20:38:12 -04:00
parent bef2892cee
commit d7cb8baf09
1 changed files with 23 additions and 8 deletions

View File

@ -164,21 +164,36 @@ func hijack(tlsConfig *tls.Config, addr string, w http.ResponseWriter, r *http.R
return err
}
errc := make(chan error, 2)
cp := func(dst io.Writer, src io.Reader) {
_, err := io.Copy(dst, src)
cp := func(dst io.Writer, src io.Reader, chDone chan struct{}) {
io.Copy(dst, src)
if conn, ok := dst.(interface {
CloseWrite() error
}); ok {
conn.CloseWrite()
}
errc <- err
close(chDone)
}
go cp(d, nc)
go cp(nc, d)
<-errc
<-errc
inDone := make(chan struct{})
outDone := make(chan struct{})
go cp(d, nc, inDone)
go cp(nc, d, outDone)
// 1. When stdin is done, wait for stdout always
// 2. When stdout is done, close the stream and wait for stdin to finish
//
// On 2, stdin copy should return immediately now since the out stream is closed.
// Note that we probably don't actually even need to wait here.
//
// If we don't close the stream when stdout is done, in some cases stdin will hange
select {
case <-inDone:
// wait for out to be done
<-outDone
case <-outDone:
// close the conn and wait for stdin
nc.Close()
<-inDone
}
return nil
}