hijack.go 3.3 KB

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