github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/pkg/term/proxy.go (about)

     1  package term
     2  
     3  import (
     4  	"io"
     5  )
     6  
     7  // EscapeError is special error which returned by a TTY proxy reader's Read()
     8  // method in case its detach escape sequence is read.
     9  type EscapeError struct{}
    10  
    11  func (EscapeError) Error() string {
    12  	return "read escape sequence"
    13  }
    14  
    15  // escapeProxy is used only for attaches with a TTY. It is used to proxy
    16  // stdin keypresses from the underlying reader and look for the passed in
    17  // escape key sequence to signal a detach.
    18  type escapeProxy struct {
    19  	escapeKeys   []byte
    20  	escapeKeyPos int
    21  	r            io.Reader
    22  }
    23  
    24  // NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
    25  // and detects when the specified escape keys are read, in which case the Read
    26  // method will return an error of type EscapeError.
    27  func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
    28  	return &escapeProxy{
    29  		escapeKeys: escapeKeys,
    30  		r:          r,
    31  	}
    32  }
    33  
    34  func (r *escapeProxy) Read(buf []byte) (int, error) {
    35  	nr, err := r.r.Read(buf)
    36  
    37  	preserve := func() {
    38  		// this preserves the original key presses in the passed in buffer
    39  		nr += r.escapeKeyPos
    40  		preserve := make([]byte, 0, r.escapeKeyPos+len(buf))
    41  		preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
    42  		preserve = append(preserve, buf...)
    43  		r.escapeKeyPos = 0
    44  		copy(buf[0:nr], preserve)
    45  	}
    46  
    47  	if nr != 1 || err != nil {
    48  		if r.escapeKeyPos > 0 {
    49  			preserve()
    50  		}
    51  		return nr, err
    52  	}
    53  
    54  	if buf[0] != r.escapeKeys[r.escapeKeyPos] {
    55  		if r.escapeKeyPos > 0 {
    56  			preserve()
    57  		}
    58  		return nr, nil
    59  	}
    60  
    61  	if r.escapeKeyPos == len(r.escapeKeys)-1 {
    62  		return 0, EscapeError{}
    63  	}
    64  
    65  	// Looks like we've got an escape key, but we need to match again on the next
    66  	// read.
    67  	// Store the current escape key we found so we can look for the next one on
    68  	// the next read.
    69  	// Since this is an escape key, make sure we don't let the caller read it
    70  	// If later on we find that this is not the escape sequence, we'll add the
    71  	// keys back
    72  	r.escapeKeyPos++
    73  	return nr - r.escapeKeyPos, nil
    74  }