github.com/damirazo/docker@v1.9.0/integration-cli/docker_api_attach_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "io" 6 "net/http" 7 "net/http/httputil" 8 "strings" 9 "time" 10 11 "github.com/go-check/check" 12 "golang.org/x/net/websocket" 13 ) 14 15 func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) { 16 testRequires(c, DaemonIsLinux) 17 out, _ := dockerCmd(c, "run", "-dit", "busybox", "cat") 18 19 rwc, err := sockConn(time.Duration(10 * time.Second)) 20 if err != nil { 21 c.Fatal(err) 22 } 23 24 cleanedContainerID := strings.TrimSpace(out) 25 config, err := websocket.NewConfig( 26 "/containers/"+cleanedContainerID+"/attach/ws?stream=1&stdin=1&stdout=1&stderr=1", 27 "http://localhost", 28 ) 29 if err != nil { 30 c.Fatal(err) 31 } 32 33 ws, err := websocket.NewClient(config, rwc) 34 if err != nil { 35 c.Fatal(err) 36 } 37 defer ws.Close() 38 39 expected := []byte("hello") 40 actual := make([]byte, len(expected)) 41 42 outChan := make(chan error) 43 go func() { 44 _, err := ws.Read(actual) 45 outChan <- err 46 close(outChan) 47 }() 48 49 inChan := make(chan error) 50 go func() { 51 _, err := ws.Write(expected) 52 inChan <- err 53 close(inChan) 54 }() 55 56 select { 57 case err := <-inChan: 58 if err != nil { 59 c.Fatal(err) 60 } 61 case <-time.After(5 * time.Second): 62 c.Fatal("Timeout writing to ws") 63 } 64 65 select { 66 case err := <-outChan: 67 if err != nil { 68 c.Fatal(err) 69 } 70 case <-time.After(5 * time.Second): 71 c.Fatal("Timeout reading from ws") 72 } 73 74 if !bytes.Equal(expected, actual) { 75 c.Fatal("Expected output on websocket to match input") 76 } 77 } 78 79 // regression gh14320 80 func (s *DockerSuite) TestPostContainersAttachContainerNotFound(c *check.C) { 81 status, body, err := sockRequest("POST", "/containers/doesnotexist/attach", nil) 82 c.Assert(status, check.Equals, http.StatusNotFound) 83 c.Assert(err, check.IsNil) 84 expected := "no such id: doesnotexist\n" 85 if !strings.Contains(string(body), expected) { 86 c.Fatalf("Expected response body to contain %q", expected) 87 } 88 } 89 90 func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *check.C) { 91 status, body, err := sockRequest("GET", "/containers/doesnotexist/attach/ws", nil) 92 c.Assert(status, check.Equals, http.StatusNotFound) 93 c.Assert(err, check.IsNil) 94 expected := "no such id: doesnotexist\n" 95 if !strings.Contains(string(body), expected) { 96 c.Fatalf("Expected response body to contain %q", expected) 97 } 98 } 99 100 func (s *DockerSuite) TestPostContainersAttach(c *check.C) { 101 testRequires(c, DaemonIsLinux) 102 out, _ := dockerCmd(c, "run", "-dit", "busybox", "cat") 103 104 r, w := io.Pipe() 105 defer r.Close() 106 defer w.Close() 107 108 conn, err := sockConn(time.Duration(10 * time.Second)) 109 c.Assert(err, check.IsNil) 110 111 containerID := strings.TrimSpace(out) 112 113 req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) 114 c.Assert(err, check.IsNil) 115 116 client := httputil.NewClientConn(conn, nil) 117 defer client.Close() 118 119 // Do POST attach request 120 resp, err := client.Do(req) 121 c.Assert(resp.StatusCode, check.Equals, http.StatusOK) 122 // If we check the err, we get a ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"} 123 // This means that the remote requested this be the last request serviced, is this okay? 124 125 // Test read and write to the attached container 126 expected := []byte("hello") 127 actual := make([]byte, len(expected)) 128 129 outChan := make(chan error) 130 go func() { 131 _, err := r.Read(actual) 132 outChan <- err 133 close(outChan) 134 }() 135 136 inChan := make(chan error) 137 go func() { 138 _, err := w.Write(expected) 139 inChan <- err 140 close(inChan) 141 }() 142 143 select { 144 case err := <-inChan: 145 c.Assert(err, check.IsNil) 146 case <-time.After(5 * time.Second): 147 c.Fatal("Timeout writing to stdout") 148 } 149 150 select { 151 case err := <-outChan: 152 c.Assert(err, check.IsNil) 153 case <-time.After(5 * time.Second): 154 c.Fatal("Timeout reading from stdin") 155 } 156 157 if !bytes.Equal(expected, actual) { 158 c.Fatal("Expected output to match input") 159 } 160 161 resp.Body.Close() 162 } 163 164 func (s *DockerSuite) TestPostContainersAttachStderr(c *check.C) { 165 testRequires(c, DaemonIsLinux) 166 out, _ := dockerCmd(c, "run", "-dit", "busybox", "/bin/sh", "-c", "cat >&2") 167 168 r, w := io.Pipe() 169 defer r.Close() 170 defer w.Close() 171 172 conn, err := sockConn(time.Duration(10 * time.Second)) 173 c.Assert(err, check.IsNil) 174 175 containerID := strings.TrimSpace(out) 176 177 req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) 178 c.Assert(err, check.IsNil) 179 180 client := httputil.NewClientConn(conn, nil) 181 defer client.Close() 182 183 // Do POST attach request 184 resp, err := client.Do(req) 185 c.Assert(resp.StatusCode, check.Equals, http.StatusOK) 186 // If we check the err, we get a ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"} 187 // This means that the remote requested this be the last request serviced, is this okay? 188 189 // Test read and write to the attached container 190 expected := []byte("hello") 191 actual := make([]byte, len(expected)) 192 193 outChan := make(chan error) 194 go func() { 195 _, err := r.Read(actual) 196 outChan <- err 197 close(outChan) 198 }() 199 200 inChan := make(chan error) 201 go func() { 202 _, err := w.Write(expected) 203 inChan <- err 204 close(inChan) 205 }() 206 207 select { 208 case err := <-inChan: 209 c.Assert(err, check.IsNil) 210 case <-time.After(5 * time.Second): 211 c.Fatal("Timeout writing to stdout") 212 } 213 214 select { 215 case err := <-outChan: 216 c.Assert(err, check.IsNil) 217 case <-time.After(5 * time.Second): 218 c.Fatal("Timeout reading from stdin") 219 } 220 221 if !bytes.Equal(expected, actual) { 222 c.Fatal("Expected output to match input") 223 } 224 225 resp.Body.Close() 226 }