| package client |
| |
| import ( |
| "io" |
| "sync" |
| |
| "golang.org/x/net/context" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/pkg/stdcopy" |
| "github.com/docker/engine-api/types" |
| ) |
| |
| // HoldHijackedConnection handles copying input to and output from streams to the |
| // connection |
| func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error { |
| var ( |
| err error |
| restoreOnce sync.Once |
| ) |
| if inputStream != nil && tty { |
| if err := cli.setRawTerminal(); err != nil { |
| return err |
| } |
| defer func() { |
| restoreOnce.Do(func() { |
| cli.restoreTerminal(inputStream) |
| }) |
| }() |
| } |
| |
| receiveStdout := make(chan error, 1) |
| if outputStream != nil || errorStream != nil { |
| go func() { |
| // When TTY is ON, use regular copy |
| if tty && outputStream != nil { |
| _, err = io.Copy(outputStream, resp.Reader) |
| // we should restore the terminal as soon as possible once connection end |
| // so any following print messages will be in normal type. |
| if inputStream != nil { |
| restoreOnce.Do(func() { |
| cli.restoreTerminal(inputStream) |
| }) |
| } |
| } else { |
| _, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader) |
| } |
| |
| logrus.Debug("[hijack] End of stdout") |
| receiveStdout <- err |
| }() |
| } |
| |
| stdinDone := make(chan struct{}) |
| go func() { |
| if inputStream != nil { |
| io.Copy(resp.Conn, inputStream) |
| // we should restore the terminal as soon as possible once connection end |
| // so any following print messages will be in normal type. |
| if tty { |
| restoreOnce.Do(func() { |
| cli.restoreTerminal(inputStream) |
| }) |
| } |
| logrus.Debug("[hijack] End of stdin") |
| } |
| |
| if err := resp.CloseWrite(); err != nil { |
| logrus.Debugf("Couldn't send EOF: %s", err) |
| } |
| close(stdinDone) |
| }() |
| |
| select { |
| case err := <-receiveStdout: |
| if err != nil { |
| logrus.Debugf("Error receiveStdout: %s", err) |
| return err |
| } |
| case <-stdinDone: |
| if outputStream != nil || errorStream != nil { |
| select { |
| case err := <-receiveStdout: |
| if err != nil { |
| logrus.Debugf("Error receiveStdout: %s", err) |
| return err |
| } |
| case <-ctx.Done(): |
| } |
| } |
| case <-ctx.Done(): |
| } |
| |
| return nil |
| } |