hijack.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package client
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "io"
  6. "net"
  7. "net/http"
  8. "net/http/httputil"
  9. "os"
  10. "runtime"
  11. "strings"
  12. "github.com/docker/docker/api"
  13. "github.com/docker/docker/dockerversion"
  14. "github.com/docker/docker/pkg/log"
  15. "github.com/docker/docker/pkg/term"
  16. "github.com/docker/docker/utils"
  17. )
  18. func (cli *DockerCli) dial() (net.Conn, error) {
  19. if cli.tlsConfig != nil && cli.proto != "unix" {
  20. return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
  21. }
  22. return net.Dial(cli.proto, cli.addr)
  23. }
  24. func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
  25. defer func() {
  26. if started != nil {
  27. close(started)
  28. }
  29. }()
  30. req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
  31. if err != nil {
  32. return err
  33. }
  34. req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
  35. req.Header.Set("Content-Type", "plain/text")
  36. req.Host = cli.addr
  37. dial, err := cli.dial()
  38. if err != nil {
  39. if strings.Contains(err.Error(), "connection refused") {
  40. return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
  41. }
  42. return err
  43. }
  44. clientconn := httputil.NewClientConn(dial, nil)
  45. defer clientconn.Close()
  46. // Server hijacks the connection, error 'connection closed' expected
  47. clientconn.Do(req)
  48. rwc, br := clientconn.Hijack()
  49. defer rwc.Close()
  50. if started != nil {
  51. started <- rwc
  52. }
  53. var receiveStdout chan error
  54. var oldState *term.State
  55. if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
  56. oldState, err = term.SetRawTerminal(cli.terminalFd)
  57. if err != nil {
  58. return err
  59. }
  60. defer term.RestoreTerminal(cli.terminalFd, oldState)
  61. }
  62. if stdout != nil || stderr != nil {
  63. receiveStdout = utils.Go(func() (err error) {
  64. defer func() {
  65. if in != nil {
  66. if setRawTerminal && cli.isTerminal {
  67. term.RestoreTerminal(cli.terminalFd, oldState)
  68. }
  69. // For some reason this Close call blocks on darwin..
  70. // As the client exists right after, simply discard the close
  71. // until we find a better solution.
  72. if runtime.GOOS != "darwin" {
  73. in.Close()
  74. }
  75. }
  76. }()
  77. // When TTY is ON, use regular copy
  78. if setRawTerminal && stdout != nil {
  79. _, err = io.Copy(stdout, br)
  80. } else {
  81. _, err = utils.StdCopy(stdout, stderr, br)
  82. }
  83. log.Debugf("[hijack] End of stdout")
  84. return err
  85. })
  86. }
  87. sendStdin := utils.Go(func() error {
  88. if in != nil {
  89. io.Copy(rwc, in)
  90. log.Debugf("[hijack] End of stdin")
  91. }
  92. if tcpc, ok := rwc.(*net.TCPConn); ok {
  93. if err := tcpc.CloseWrite(); err != nil {
  94. log.Debugf("Couldn't send EOF: %s", err)
  95. }
  96. } else if unixc, ok := rwc.(*net.UnixConn); ok {
  97. if err := unixc.CloseWrite(); err != nil {
  98. log.Debugf("Couldn't send EOF: %s", err)
  99. }
  100. }
  101. // Discard errors due to pipe interruption
  102. return nil
  103. })
  104. if stdout != nil || stderr != nil {
  105. if err := <-receiveStdout; err != nil {
  106. log.Debugf("Error receiveStdout: %s", err)
  107. return err
  108. }
  109. }
  110. if !cli.isTerminal {
  111. if err := <-sendStdin; err != nil {
  112. log.Debugf("Error sendStdin: %s", err)
  113. return err
  114. }
  115. }
  116. return nil
  117. }