1/* 2Copyright 2016 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package spdy 18 19import ( 20 "io" 21 "net" 22 "net/http" 23 "sync" 24 "testing" 25 "time" 26 27 "k8s.io/apimachinery/pkg/util/httpstream" 28) 29 30func runProxy(t *testing.T, backendUrl string, proxyUrl chan<- string, proxyDone chan<- struct{}) { 31 listener, err := net.Listen("tcp4", "localhost:0") 32 if err != nil { 33 t.Fatalf("error listening: %v", err) 34 } 35 defer listener.Close() 36 37 proxyUrl <- listener.Addr().String() 38 39 clientConn, err := listener.Accept() 40 if err != nil { 41 t.Errorf("proxy: error accepting client connection: %v", err) 42 return 43 } 44 45 backendConn, err := net.Dial("tcp4", backendUrl) 46 if err != nil { 47 t.Errorf("proxy: error dialing backend: %v", err) 48 return 49 } 50 defer backendConn.Close() 51 52 var wg sync.WaitGroup 53 wg.Add(2) 54 55 go func() { 56 defer wg.Done() 57 io.Copy(backendConn, clientConn) 58 }() 59 60 go func() { 61 defer wg.Done() 62 io.Copy(clientConn, backendConn) 63 }() 64 65 wg.Wait() 66 67 proxyDone <- struct{}{} 68} 69 70func runServer(t *testing.T, backendUrl chan<- string, serverDone chan<- struct{}) { 71 listener, err := net.Listen("tcp4", "localhost:0") 72 if err != nil { 73 t.Fatalf("server: error listening: %v", err) 74 } 75 defer listener.Close() 76 77 backendUrl <- listener.Addr().String() 78 79 conn, err := listener.Accept() 80 if err != nil { 81 t.Errorf("server: error accepting connection: %v", err) 82 return 83 } 84 85 streamChan := make(chan httpstream.Stream) 86 replySentChan := make(chan (<-chan struct{})) 87 spdyConn, err := NewServerConnection(conn, func(stream httpstream.Stream, replySent <-chan struct{}) error { 88 streamChan <- stream 89 replySentChan <- replySent 90 return nil 91 }) 92 if err != nil { 93 t.Errorf("server: error creating spdy connection: %v", err) 94 return 95 } 96 97 stream := <-streamChan 98 replySent := <-replySentChan 99 <-replySent 100 101 buf := make([]byte, 1) 102 _, err = stream.Read(buf) 103 if err != io.EOF { 104 t.Errorf("server: unexpected read error: %v", err) 105 return 106 } 107 108 <-spdyConn.CloseChan() 109 raw := spdyConn.(*connection).conn 110 if err := raw.Wait(15 * time.Second); err != nil { 111 t.Errorf("server: timed out waiting for connection closure: %v", err) 112 } 113 114 serverDone <- struct{}{} 115} 116 117func TestConnectionCloseIsImmediateThroughAProxy(t *testing.T) { 118 serverDone := make(chan struct{}) 119 backendUrlChan := make(chan string) 120 go runServer(t, backendUrlChan, serverDone) 121 backendUrl := <-backendUrlChan 122 123 proxyDone := make(chan struct{}) 124 proxyUrlChan := make(chan string) 125 go runProxy(t, backendUrl, proxyUrlChan, proxyDone) 126 proxyUrl := <-proxyUrlChan 127 128 conn, err := net.Dial("tcp4", proxyUrl) 129 if err != nil { 130 t.Fatalf("client: error connecting to proxy: %v", err) 131 } 132 133 spdyConn, err := NewClientConnection(conn) 134 if err != nil { 135 t.Fatalf("client: error creating spdy connection: %v", err) 136 } 137 138 if _, err := spdyConn.CreateStream(http.Header{}); err != nil { 139 t.Fatalf("client: error creating stream: %v", err) 140 } 141 142 spdyConn.Close() 143 raw := spdyConn.(*connection).conn 144 if err := raw.Wait(15 * time.Second); err != nil { 145 t.Fatalf("client: timed out waiting for connection closure: %v", err) 146 } 147 148 expired := time.NewTimer(15 * time.Second) 149 defer expired.Stop() 150 i := 0 151 for { 152 select { 153 case <-expired.C: 154 t.Fatalf("timed out waiting for proxy and/or server closure") 155 case <-serverDone: 156 i++ 157 case <-proxyDone: 158 i++ 159 } 160 if i == 2 { 161 break 162 } 163 } 164} 165