hijack.go 3.1 KB

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