@@ -2,6 +2,7 @@ package connection
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "io"
78 "io/ioutil"
2728)
2829
2930func newTestHTTP2Connection () (* HTTP2Connection , net.Conn ) {
30- edgeConn , originConn := net .Pipe ()
31+ edgeConn , cfdConn := net .Pipe ()
3132 var connIndex = uint8 (0 )
3233 log := zerolog .Nop ()
3334 obs := NewObserver (& log , & log , false )
@@ -41,7 +42,8 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) {
4142 1 * time .Second ,
4243 )
4344 return NewHTTP2Connection (
44- originConn ,
45+ cfdConn ,
46+ // OriginProxy is set in testConfig
4547 testConfig ,
4648 & pogs.ConnectionOptions {},
4749 obs ,
@@ -166,6 +168,8 @@ type wsRespWriter struct {
166168 * httptest.ResponseRecorder
167169 readPipe * io.PipeReader
168170 writePipe * io.PipeWriter
171+ closed bool
172+ panicked bool
169173}
170174
171175func newWSRespWriter () * wsRespWriter {
@@ -174,46 +178,59 @@ func newWSRespWriter() *wsRespWriter {
174178 httptest .NewRecorder (),
175179 readPipe ,
176180 writePipe ,
181+ false ,
182+ false ,
177183 }
178184}
179185
186+ type nowriter struct {
187+ io.Reader
188+ }
189+
190+ func (nowriter ) Write (_ []byte ) (int , error ) {
191+ return 0 , fmt .Errorf ("writer not implemented" )
192+ }
193+
180194func (w * wsRespWriter ) RespBody () io.ReadWriter {
181195 return nowriter {w .readPipe }
182196}
183197
184198func (w * wsRespWriter ) Write (data []byte ) (n int , err error ) {
199+ if w .closed {
200+ w .panicked = true
201+ return 0 , errors .New ("wsRespWriter panicked" )
202+ }
185203 return w .writePipe .Write (data )
186204}
187205
206+ func (w * wsRespWriter ) close () {
207+ w .closed = true
208+ }
209+
188210func TestServeWS (t * testing.T ) {
189211 http2Conn , _ := newTestHTTP2Connection ()
190212
191213 ctx , cancel := context .WithCancel (context .Background ())
192- var wg sync.WaitGroup
193- wg .Add (1 )
194- go func () {
195- defer wg .Done ()
196- http2Conn .Serve (ctx )
197- }()
198214
199215 respWriter := newWSRespWriter ()
200216 readPipe , writePipe := io .Pipe ()
201217
202- req , err := http .NewRequestWithContext (ctx , http .MethodGet , "http://localhost:8080/ws" , readPipe )
218+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , "http://localhost:8080/ws/echo " , readPipe )
203219 require .NoError (t , err )
204220 req .Header .Set (InternalUpgradeHeader , WebsocketUpgrade )
205221
206- wg . Add ( 1 )
222+ serveDone := make ( chan struct {} )
207223 go func () {
208- defer wg . Done ( )
224+ defer close ( serveDone )
209225 http2Conn .ServeHTTP (respWriter , req )
226+ respWriter .close ()
210227 }()
211228
212229 data := []byte ("test websocket" )
213- err = wsutil .WriteClientText (writePipe , data )
230+ err = wsutil .WriteClientBinary (writePipe , data )
214231 require .NoError (t , err )
215232
216- respBody , err := wsutil .ReadServerText (respWriter .RespBody ())
233+ respBody , err := wsutil .ReadServerBinary (respWriter .RespBody ())
217234 require .NoError (t , err )
218235 require .Equal (t , data , respBody , fmt .Sprintf ("Expect %s, got %s" , string (data ), string (respBody )))
219236
@@ -223,7 +240,65 @@ func TestServeWS(t *testing.T) {
223240 require .Equal (t , http .StatusOK , resp .StatusCode )
224241 require .Equal (t , responseMetaHeaderOrigin , resp .Header .Get (ResponseMetaHeader ))
225242
243+ <- serveDone
244+ require .False (t , respWriter .panicked )
245+ }
246+
247+ // TestNoWriteAfterServeHTTPReturns is a regression test of https://jira.cfops.it/browse/TUN-5184
248+ // to make sure we don't write to the ResponseWriter after the ServeHTTP method returns
249+ func TestNoWriteAfterServeHTTPReturns (t * testing.T ) {
250+ cfdHTTP2Conn , edgeTCPConn := newTestHTTP2Connection ()
251+
252+ ctx , cancel := context .WithCancel (context .Background ())
253+ var wg sync.WaitGroup
254+
255+ serverDone := make (chan struct {})
256+ go func () {
257+ defer close (serverDone )
258+ cfdHTTP2Conn .Serve (ctx )
259+ }()
260+
261+ edgeTransport := http2.Transport {}
262+ edgeHTTP2Conn , err := edgeTransport .NewClientConn (edgeTCPConn )
263+ require .NoError (t , err )
264+ message := []byte (t .Name ())
265+
266+ for i := 0 ; i < 100 ; i ++ {
267+ wg .Add (1 )
268+ go func () {
269+ defer wg .Done ()
270+ readPipe , writePipe := io .Pipe ()
271+ reqCtx , reqCancel := context .WithCancel (ctx )
272+ req , err := http .NewRequestWithContext (reqCtx , http .MethodGet , "http://localhost:8080/ws/flaky" , readPipe )
273+ require .NoError (t , err )
274+ req .Header .Set (InternalUpgradeHeader , WebsocketUpgrade )
275+
276+ resp , err := edgeHTTP2Conn .RoundTrip (req )
277+ require .NoError (t , err )
278+ // http2RespWriter should rewrite status 101 to 200
279+ require .Equal (t , http .StatusOK , resp .StatusCode )
280+
281+ wg .Add (1 )
282+ go func () {
283+ defer wg .Done ()
284+ for {
285+ select {
286+ case <- reqCtx .Done ():
287+ return
288+ default :
289+ }
290+ _ = wsutil .WriteClientBinary (writePipe , message )
291+ }
292+ }()
293+
294+ time .Sleep (time .Millisecond * 100 )
295+ reqCancel ()
296+ }()
297+ }
298+
226299 wg .Wait ()
300+ cancel ()
301+ <- serverDone
227302}
228303
229304func TestServeControlStream (t * testing.T ) {
0 commit comments