proxy.go 2.0 KB

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