2727)
2828
2929func newTestHTTP2Connection () (* HTTP2Connection , net.Conn ) {
30- edgeConn , originConn := net .Pipe ()
30+ edgeConn , cfdConn := net .Pipe ()
3131 var connIndex = uint8 (0 )
3232 log := zerolog .Nop ()
3333 obs := NewObserver (& log , & log , false )
@@ -41,7 +41,8 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) {
4141 1 * time .Second ,
4242 )
4343 return NewHTTP2Connection (
44- originConn ,
44+ cfdConn ,
45+ // OriginProxy is set in testConfig
4546 testConfig ,
4647 & pogs.ConnectionOptions {},
4748 obs ,
@@ -166,6 +167,7 @@ type wsRespWriter struct {
166167 * httptest.ResponseRecorder
167168 readPipe * io.PipeReader
168169 writePipe * io.PipeWriter
170+ closed bool
169171}
170172
171173func newWSRespWriter () * wsRespWriter {
@@ -174,46 +176,58 @@ func newWSRespWriter() *wsRespWriter {
174176 httptest .NewRecorder (),
175177 readPipe ,
176178 writePipe ,
179+ false ,
177180 }
178181}
179182
183+ type nowriter struct {
184+ io.Reader
185+ }
186+
187+ func (nowriter ) Write (p []byte ) (int , error ) {
188+ return 0 , fmt .Errorf ("Writer not implemented" )
189+ }
190+
180191func (w * wsRespWriter ) RespBody () io.ReadWriter {
181192 return nowriter {w .readPipe }
182193}
183194
184195func (w * wsRespWriter ) Write (data []byte ) (n int , err error ) {
196+ if w .closed {
197+ // Simulate writing to http2 ResponseWriter after ServeHTTP has returned
198+ panic ("Write to closed ResponseWriter" )
199+ }
185200 return w .writePipe .Write (data )
186201}
187202
203+ func (w * wsRespWriter ) close () {
204+ w .closed = true
205+ }
206+
188207func TestServeWS (t * testing.T ) {
189208 http2Conn , _ := newTestHTTP2Connection ()
190209
191210 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- }()
198211
199212 respWriter := newWSRespWriter ()
200213 readPipe , writePipe := io .Pipe ()
201214
202- req , err := http .NewRequestWithContext (ctx , http .MethodGet , "http://localhost:8080/ws" , readPipe )
215+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , "http://localhost:8080/ws/echo " , readPipe )
203216 require .NoError (t , err )
204217 req .Header .Set (InternalUpgradeHeader , WebsocketUpgrade )
205218
206- wg . Add ( 1 )
219+ serveDone := make ( chan struct {} )
207220 go func () {
208- defer wg . Done ( )
221+ defer close ( serveDone )
209222 http2Conn .ServeHTTP (respWriter , req )
223+ respWriter .close ()
210224 }()
211225
212226 data := []byte ("test websocket" )
213- err = wsutil .WriteClientText (writePipe , data )
227+ err = wsutil .WriteClientBinary (writePipe , data )
214228 require .NoError (t , err )
215229
216- respBody , err := wsutil .ReadServerText (respWriter .RespBody ())
230+ respBody , err := wsutil .ReadServerBinary (respWriter .RespBody ())
217231 require .NoError (t , err )
218232 require .Equal (t , data , respBody , fmt .Sprintf ("Expect %s, got %s" , string (data ), string (respBody )))
219233
@@ -223,7 +237,64 @@ func TestServeWS(t *testing.T) {
223237 require .Equal (t , http .StatusOK , resp .StatusCode )
224238 require .Equal (t , responseMetaHeaderOrigin , resp .Header .Get (ResponseMetaHeader ))
225239
240+ <- serveDone
241+ }
242+
243+ // TestNoWriteAfterServeHTTPReturns is a regression test of https://jira.cfops.it/browse/TUN-5184
244+ // to make sure we don't write to the ResponseWriter after the ServeHTTP method returns
245+ func TestNoWriteAfterServeHTTPReturns (t * testing.T ) {
246+ cfdHTTP2Conn , edgeTCPConn := newTestHTTP2Connection ()
247+
248+ ctx , cancel := context .WithCancel (context .Background ())
249+ var wg sync.WaitGroup
250+
251+ serverDone := make (chan struct {})
252+ go func () {
253+ defer close (serverDone )
254+ cfdHTTP2Conn .Serve (ctx )
255+ }()
256+
257+ edgeTransport := http2.Transport {}
258+ edgeHTTP2Conn , err := edgeTransport .NewClientConn (edgeTCPConn )
259+ require .NoError (t , err )
260+ message := []byte (t .Name ())
261+
262+ for i := 0 ; i < 100 ; i ++ {
263+ wg .Add (1 )
264+ go func () {
265+ defer wg .Done ()
266+ readPipe , writePipe := io .Pipe ()
267+ reqCtx , reqCancel := context .WithCancel (ctx )
268+ req , err := http .NewRequestWithContext (reqCtx , http .MethodGet , "http://localhost:8080/ws/flaky" , readPipe )
269+ require .NoError (t , err )
270+ req .Header .Set (InternalUpgradeHeader , WebsocketUpgrade )
271+
272+ resp , err := edgeHTTP2Conn .RoundTrip (req )
273+ require .NoError (t , err )
274+ // http2RespWriter should rewrite status 101 to 200
275+ require .Equal (t , http .StatusOK , resp .StatusCode )
276+
277+ wg .Add (1 )
278+ go func () {
279+ defer wg .Done ()
280+ for {
281+ select {
282+ case <- reqCtx .Done ():
283+ return
284+ default :
285+ }
286+ _ = wsutil .WriteClientBinary (writePipe , message )
287+ }
288+ }()
289+
290+ time .Sleep (time .Millisecond * 100 )
291+ reqCancel ()
292+ }()
293+ }
294+
226295 wg .Wait ()
296+ cancel ()
297+ <- serverDone
227298}
228299
229300func TestServeControlStream (t * testing.T ) {
0 commit comments