github.com/kobeld/docker@v1.12.0-rc1/api/client/hijack.go (about)

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