github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/httpstream/spdy/connection_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package spdy 18 19 import ( 20 "fmt" 21 "io" 22 "net" 23 "net/http" 24 "sync" 25 "sync/atomic" 26 "testing" 27 "time" 28 29 "github.com/moby/spdystream" 30 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/httpstream" 31 ) 32 33 func runProxy(t *testing.T, backendUrl string, proxyUrl chan<- string, proxyDone chan<- struct{}, errCh chan<- error) { 34 listener, err := net.Listen("tcp4", "localhost:0") 35 if err != nil { 36 errCh <- err 37 return 38 } 39 defer listener.Close() 40 41 proxyUrl <- listener.Addr().String() 42 43 clientConn, err := listener.Accept() 44 if err != nil { 45 t.Errorf("proxy: error accepting client connection: %v", err) 46 return 47 } 48 49 backendConn, err := net.Dial("tcp4", backendUrl) 50 if err != nil { 51 t.Errorf("proxy: error dialing backend: %v", err) 52 return 53 } 54 defer backendConn.Close() 55 56 var wg sync.WaitGroup 57 wg.Add(2) 58 59 go func() { 60 defer wg.Done() 61 io.Copy(backendConn, clientConn) 62 }() 63 64 go func() { 65 defer wg.Done() 66 io.Copy(clientConn, backendConn) 67 }() 68 69 wg.Wait() 70 71 proxyDone <- struct{}{} 72 } 73 74 func runServer(t *testing.T, backendUrl chan<- string, serverDone chan<- struct{}, errCh chan<- error) { 75 listener, err := net.Listen("tcp4", "localhost:0") 76 if err != nil { 77 errCh <- err 78 return 79 } 80 defer listener.Close() 81 82 backendUrl <- listener.Addr().String() 83 84 conn, err := listener.Accept() 85 if err != nil { 86 t.Errorf("server: error accepting connection: %v", err) 87 return 88 } 89 90 streamChan := make(chan httpstream.Stream) 91 replySentChan := make(chan (<-chan struct{})) 92 spdyConn, err := NewServerConnection(conn, func(stream httpstream.Stream, replySent <-chan struct{}) error { 93 streamChan <- stream 94 replySentChan <- replySent 95 return nil 96 }) 97 if err != nil { 98 t.Errorf("server: error creating spdy connection: %v", err) 99 return 100 } 101 102 stream := <-streamChan 103 replySent := <-replySentChan 104 <-replySent 105 106 buf := make([]byte, 1) 107 _, err = stream.Read(buf) 108 if err != io.EOF { 109 t.Errorf("server: unexpected read error: %v", err) 110 return 111 } 112 113 <-spdyConn.CloseChan() 114 raw := spdyConn.(*connection).conn 115 if err := raw.Wait(15 * time.Second); err != nil { 116 t.Errorf("server: timed out waiting for connection closure: %v", err) 117 } 118 119 serverDone <- struct{}{} 120 } 121 122 func TestConnectionCloseIsImmediateThroughAProxy(t *testing.T) { 123 errCh := make(chan error) 124 125 serverDone := make(chan struct{}, 1) 126 backendUrlChan := make(chan string) 127 go runServer(t, backendUrlChan, serverDone, errCh) 128 129 var backendUrl string 130 select { 131 case err := <-errCh: 132 t.Fatalf("server: error listening: %v", err) 133 case backendUrl = <-backendUrlChan: 134 } 135 136 proxyDone := make(chan struct{}, 1) 137 proxyUrlChan := make(chan string) 138 go runProxy(t, backendUrl, proxyUrlChan, proxyDone, errCh) 139 140 var proxyUrl string 141 select { 142 case err := <-errCh: 143 t.Fatalf("error listening: %v", err) 144 case proxyUrl = <-proxyUrlChan: 145 } 146 147 conn, err := net.Dial("tcp4", proxyUrl) 148 if err != nil { 149 t.Fatalf("client: error connecting to proxy: %v", err) 150 } 151 152 spdyConn, err := NewClientConnection(conn) 153 if err != nil { 154 t.Fatalf("client: error creating spdy connection: %v", err) 155 } 156 157 if _, err := spdyConn.CreateStream(http.Header{}); err != nil { 158 t.Fatalf("client: error creating stream: %v", err) 159 } 160 161 spdyConn.Close() 162 raw := spdyConn.(*connection).conn 163 if err := raw.Wait(15 * time.Second); err != nil { 164 t.Fatalf("client: timed out waiting for connection closure: %v", err) 165 } 166 167 expired := time.NewTimer(15 * time.Second) 168 defer expired.Stop() 169 i := 0 170 for { 171 select { 172 case <-expired.C: 173 t.Fatalf("timed out waiting for proxy and/or server closure") 174 case <-serverDone: 175 i++ 176 case <-proxyDone: 177 i++ 178 } 179 if i == 2 { 180 break 181 } 182 } 183 } 184 185 func TestConnectionPings(t *testing.T) { 186 const pingPeriod = 10 * time.Millisecond 187 timeout := time.After(10 * time.Second) 188 189 // Set up server connection. 190 listener, err := net.Listen("tcp4", "localhost:0") 191 if err != nil { 192 t.Fatal(err) 193 } 194 defer listener.Close() 195 196 srvErr := make(chan error, 1) 197 go func() { 198 defer close(srvErr) 199 200 srvConn, err := listener.Accept() 201 if err != nil { 202 srvErr <- fmt.Errorf("server: error accepting connection: %v", err) 203 return 204 } 205 defer srvConn.Close() 206 207 spdyConn, err := spdystream.NewConnection(srvConn, true) 208 if err != nil { 209 srvErr <- fmt.Errorf("server: error creating spdy connection: %v", err) 210 return 211 } 212 213 var pingsSent int64 214 srvSPDYConn := newConnection( 215 spdyConn, 216 func(stream httpstream.Stream, replySent <-chan struct{}) error { 217 // Echo all the incoming data. 218 go io.Copy(stream, stream) 219 return nil 220 }, 221 pingPeriod, 222 func() (time.Duration, error) { 223 atomic.AddInt64(&pingsSent, 1) 224 return 0, nil 225 }) 226 defer srvSPDYConn.Close() 227 228 // Wait for the connection to close, to prevent defers from running 229 // early. 230 select { 231 case <-timeout: 232 srvErr <- fmt.Errorf("server: timeout waiting for connection to close") 233 return 234 case <-srvSPDYConn.CloseChan(): 235 } 236 237 // Count pings sent by the server. 238 gotPings := atomic.LoadInt64(&pingsSent) 239 if gotPings < 1 { 240 t.Errorf("server: failed to send any pings (check logs)") 241 } 242 }() 243 244 // Set up client connection. 245 clConn, err := net.Dial("tcp4", listener.Addr().String()) 246 if err != nil { 247 t.Fatalf("client: error connecting to proxy: %v", err) 248 } 249 defer clConn.Close() 250 start := time.Now() 251 clSPDYConn, err := NewClientConnection(clConn) 252 if err != nil { 253 t.Fatalf("client: error creating spdy connection: %v", err) 254 } 255 defer clSPDYConn.Close() 256 clSPDYStream, err := clSPDYConn.CreateStream(http.Header{}) 257 if err != nil { 258 t.Fatalf("client: error creating stream: %v", err) 259 } 260 defer clSPDYStream.Close() 261 262 // Send some data both ways, to make sure pings don't interfere with 263 // regular messages. 264 in := "foo" 265 if _, err := fmt.Fprintln(clSPDYStream, in); err != nil { 266 t.Fatalf("client: error writing data to stream: %v", err) 267 } 268 var out string 269 if _, err := fmt.Fscanln(clSPDYStream, &out); err != nil { 270 t.Fatalf("client: error reading data from stream: %v", err) 271 } 272 if in != out { 273 t.Errorf("client: received data doesn't match sent data: got %q, want %q", out, in) 274 } 275 276 // Wait for at least 2 pings to get sent each way before closing the 277 // connection. 278 elapsed := time.Since(start) 279 if elapsed < 3*pingPeriod { 280 time.Sleep(3*pingPeriod - elapsed) 281 } 282 clSPDYConn.Close() 283 284 select { 285 case err, ok := <-srvErr: 286 if ok && err != nil { 287 t.Error(err) 288 } 289 case <-timeout: 290 t.Errorf("timed out waiting for server to exit") 291 } 292 } 293 294 type fakeStream struct{ id uint32 } 295 296 func (*fakeStream) Read(p []byte) (int, error) { return 0, nil } 297 func (*fakeStream) Write(p []byte) (int, error) { return 0, nil } 298 func (*fakeStream) Close() error { return nil } 299 func (*fakeStream) Reset() error { return nil } 300 func (*fakeStream) Headers() http.Header { return nil } 301 func (f *fakeStream) Identifier() uint32 { return f.id } 302 303 func TestConnectionRemoveStreams(t *testing.T) { 304 c := &connection{streams: make(map[uint32]httpstream.Stream)} 305 stream0 := &fakeStream{id: 0} 306 stream1 := &fakeStream{id: 1} 307 stream2 := &fakeStream{id: 2} 308 309 c.registerStream(stream0) 310 c.registerStream(stream1) 311 312 if len(c.streams) != 2 { 313 t.Fatalf("should have two streams, has %d", len(c.streams)) 314 } 315 316 // not exists 317 c.RemoveStreams(stream2) 318 319 if len(c.streams) != 2 { 320 t.Fatalf("should have two streams, has %d", len(c.streams)) 321 } 322 323 // remove all existing 324 c.RemoveStreams(stream0, stream1) 325 326 // remove nil stream should not crash 327 c.RemoveStreams(nil) 328 329 if len(c.streams) != 0 { 330 t.Fatalf("should not have any streams, has %d", len(c.streams)) 331 } 332 333 }