`copyEscapable` is a copy/paste of io.Copy with some added handling for
checking for the attach escape sequence.
This removes the copy/paste and uses `io.Copy` directly. To be able to
do this, it now implements an `io.Reader` which proxies to the main
reader but looks for the escape sequence.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -10,6 +10,8 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/pkg/promise" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
+var defaultEscapeSequence = []byte{16, 17} // ctrl-p, ctrl-q
|
|
| 14 |
+ |
|
| 13 | 15 |
// DetachError is special error which returned in case of container detach. |
| 14 | 16 |
type DetachError struct{}
|
| 15 | 17 |
|
| ... | ... |
@@ -153,57 +155,63 @@ func (c *Config) Attach(ctx context.Context, cfg *AttachConfig) chan error {
|
| 153 | 153 |
}) |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
-// Code c/c from io.Copy() modified to handle escape sequence |
|
| 157 |
-func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) {
|
|
| 158 |
- if len(keys) == 0 {
|
|
| 159 |
- // Default keys : ctrl-p ctrl-q |
|
| 160 |
- keys = []byte{16, 17}
|
|
| 156 |
+// ttyProxy is used only for attaches with a TTY. It is used to proxy |
|
| 157 |
+// stdin keypresses from the underlying reader and look for the passed in |
|
| 158 |
+// escape key sequence to signal a detach. |
|
| 159 |
+type ttyProxy struct {
|
|
| 160 |
+ escapeKeys []byte |
|
| 161 |
+ escapeKeyPos int |
|
| 162 |
+ r io.Reader |
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+func (r *ttyProxy) Read(buf []byte) (int, error) {
|
|
| 166 |
+ nr, err := r.r.Read(buf) |
|
| 167 |
+ |
|
| 168 |
+ preserve := func() {
|
|
| 169 |
+ // this preserves the original key presses in the passed in buffer |
|
| 170 |
+ nr += r.escapeKeyPos |
|
| 171 |
+ preserve := make([]byte, 0, r.escapeKeyPos+len(buf)) |
|
| 172 |
+ preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) |
|
| 173 |
+ preserve = append(preserve, buf...) |
|
| 174 |
+ r.escapeKeyPos = 0 |
|
| 175 |
+ copy(buf[0:nr], preserve) |
|
| 161 | 176 |
} |
| 162 |
- buf := make([]byte, 32*1024) |
|
| 163 |
- for {
|
|
| 164 |
- nr, er := src.Read(buf) |
|
| 165 |
- if nr > 0 {
|
|
| 166 |
- // ---- Docker addition |
|
| 167 |
- preservBuf := []byte{}
|
|
| 168 |
- for i, key := range keys {
|
|
| 169 |
- preservBuf = append(preservBuf, buf[0:nr]...) |
|
| 170 |
- if nr != 1 || buf[0] != key {
|
|
| 171 |
- break |
|
| 172 |
- } |
|
| 173 |
- if i == len(keys)-1 {
|
|
| 174 |
- src.Close() |
|
| 175 |
- return 0, DetachError{}
|
|
| 176 |
- } |
|
| 177 |
- nr, er = src.Read(buf) |
|
| 178 |
- } |
|
| 179 |
- var nw int |
|
| 180 |
- var ew error |
|
| 181 |
- if len(preservBuf) > 0 {
|
|
| 182 |
- nw, ew = dst.Write(preservBuf) |
|
| 183 |
- nr = len(preservBuf) |
|
| 184 |
- } else {
|
|
| 185 |
- // ---- End of docker |
|
| 186 |
- nw, ew = dst.Write(buf[0:nr]) |
|
| 187 |
- } |
|
| 188 |
- if nw > 0 {
|
|
| 189 |
- written += int64(nw) |
|
| 190 |
- } |
|
| 191 |
- if ew != nil {
|
|
| 192 |
- err = ew |
|
| 193 |
- break |
|
| 194 |
- } |
|
| 195 |
- if nr != nw {
|
|
| 196 |
- err = io.ErrShortWrite |
|
| 197 |
- break |
|
| 198 |
- } |
|
| 199 |
- } |
|
| 200 |
- if er == io.EOF {
|
|
| 201 |
- break |
|
| 177 |
+ |
|
| 178 |
+ if nr != 1 || err != nil {
|
|
| 179 |
+ if r.escapeKeyPos > 0 {
|
|
| 180 |
+ preserve() |
|
| 202 | 181 |
} |
| 203 |
- if er != nil {
|
|
| 204 |
- err = er |
|
| 205 |
- break |
|
| 182 |
+ return nr, err |
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ if buf[0] != r.escapeKeys[r.escapeKeyPos] {
|
|
| 186 |
+ if r.escapeKeyPos > 0 {
|
|
| 187 |
+ preserve() |
|
| 206 | 188 |
} |
| 189 |
+ return nr, nil |
|
| 190 |
+ } |
|
| 191 |
+ |
|
| 192 |
+ if r.escapeKeyPos == len(r.escapeKeys)-1 {
|
|
| 193 |
+ return 0, DetachError{}
|
|
| 207 | 194 |
} |
| 208 |
- return written, err |
|
| 195 |
+ |
|
| 196 |
+ // Looks like we've got an escape key, but we need to match again on the next |
|
| 197 |
+ // read. |
|
| 198 |
+ // Store the current escape key we found so we can look for the next one on |
|
| 199 |
+ // the next read. |
|
| 200 |
+ // Since this is an escape key, make sure we don't let the caller read it |
|
| 201 |
+ // If later on we find that this is not the escape sequence, we'll add the |
|
| 202 |
+ // keys back |
|
| 203 |
+ r.escapeKeyPos++ |
|
| 204 |
+ return nr - r.escapeKeyPos, nil |
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) {
|
|
| 208 |
+ if len(keys) == 0 {
|
|
| 209 |
+ keys = defaultEscapeSequence |
|
| 210 |
+ } |
|
| 211 |
+ pr := &ttyProxy{escapeKeys: keys, r: src}
|
|
| 212 |
+ defer src.Close() |
|
| 213 |
+ |
|
| 214 |
+ return io.Copy(dst, pr) |
|
| 209 | 215 |
} |