hijack.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package client
  2. import (
  3. "io"
  4. "sync"
  5. "golang.org/x/net/context"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/pkg/stdcopy"
  8. "github.com/docker/engine-api/types"
  9. )
  10. // HoldHijackedConnection handles copying input to and output from streams to the
  11. // connection
  12. func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
  13. var (
  14. err error
  15. restoreOnce sync.Once
  16. )
  17. if inputStream != nil && tty {
  18. if err := cli.setRawTerminal(); err != nil {
  19. return err
  20. }
  21. defer func() {
  22. restoreOnce.Do(func() {
  23. cli.restoreTerminal(inputStream)
  24. })
  25. }()
  26. }
  27. receiveStdout := make(chan error, 1)
  28. if outputStream != nil || errorStream != nil {
  29. go func() {
  30. // When TTY is ON, use regular copy
  31. if tty && outputStream != nil {
  32. _, err = io.Copy(outputStream, resp.Reader)
  33. // we should restore the terminal as soon as possible once connection end
  34. // so any following print messages will be in normal type.
  35. if inputStream != nil {
  36. restoreOnce.Do(func() {
  37. cli.restoreTerminal(inputStream)
  38. })
  39. }
  40. } else {
  41. _, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)
  42. }
  43. logrus.Debug("[hijack] End of stdout")
  44. receiveStdout <- err
  45. }()
  46. }
  47. stdinDone := make(chan struct{})
  48. go func() {
  49. if inputStream != nil {
  50. io.Copy(resp.Conn, inputStream)
  51. // we should restore the terminal as soon as possible once connection end
  52. // so any following print messages will be in normal type.
  53. if tty {
  54. restoreOnce.Do(func() {
  55. cli.restoreTerminal(inputStream)
  56. })
  57. }
  58. logrus.Debug("[hijack] End of stdin")
  59. }
  60. if err := resp.CloseWrite(); err != nil {
  61. logrus.Debugf("Couldn't send EOF: %s", err)
  62. }
  63. close(stdinDone)
  64. }()
  65. select {
  66. case err := <-receiveStdout:
  67. if err != nil {
  68. logrus.Debugf("Error receiveStdout: %s", err)
  69. return err
  70. }
  71. case <-stdinDone:
  72. if outputStream != nil || errorStream != nil {
  73. select {
  74. case err := <-receiveStdout:
  75. if err != nil {
  76. logrus.Debugf("Error receiveStdout: %s", err)
  77. return err
  78. }
  79. case <-ctx.Done():
  80. }
  81. }
  82. case <-ctx.Done():
  83. }
  84. return nil
  85. }