@@ -365,6 +365,131 @@ class HTTP1ConnectionTests: XCTestCase {
365365        XCTAssertEqual ( httpBin. activeConnections,  0 ) 
366366    } 
367367
368+     func  testConnectionIsClosedAfterSwitchingProtocols( )  { 
369+         let  embedded  =  EmbeddedChannel ( ) 
370+         let  logger  =  Logger ( label:  " test.http1.connection " ) 
371+ 
372+         XCTAssertNoThrow ( try   embedded. connect ( to:  SocketAddress ( ipAddress:  " 127.0.0.1 " ,  port:  3000 ) ) . wait ( ) ) 
373+ 
374+         var  maybeConnection :  HTTP1Connection ? 
375+         let  connectionDelegate  =  MockConnectionDelegate ( ) 
376+         XCTAssertNoThrow ( maybeConnection =  try   HTTP1Connection . start ( 
377+             channel:  embedded, 
378+             connectionID:  0 , 
379+             delegate:  connectionDelegate, 
380+             configuration:  . init( decompression:  . enabled( limit:  . ratio( 4 ) ) ) , 
381+             logger:  logger
382+         ) ) 
383+         guard  let  connection =  maybeConnection else  {  return  XCTFail ( " Expected to have a connection at this point. " )  } 
384+ 
385+         var  maybeRequest :  HTTPClient . Request ? 
386+         XCTAssertNoThrow ( maybeRequest =  try   HTTPClient . Request ( url:  " http://swift.org/ " ) ) 
387+         guard  let  request =  maybeRequest else  {  return  XCTFail ( " Expected to be able to create a request " )  } 
388+ 
389+         let  delegate  =  ResponseAccumulator ( request:  request) 
390+         var  maybeRequestBag :  RequestBag < ResponseAccumulator > ? 
391+         XCTAssertNoThrow ( maybeRequestBag =  try   RequestBag ( 
392+             request:  request, 
393+             eventLoopPreference:  . delegate( on:  embedded. eventLoop) , 
394+             task:  . init( eventLoop:  embedded. eventLoop,  logger:  logger) , 
395+             redirectHandler:  nil , 
396+             connectionDeadline:  . now( )  +  . seconds( 30 ) , 
397+             requestOptions:  . forTests( ) , 
398+             delegate:  delegate
399+         ) ) 
400+         guard  let  requestBag =  maybeRequestBag else  {  return  XCTFail ( " Expected to be able to create a request bag " )  } 
401+ 
402+         connection. executeRequest ( requestBag) 
403+ 
404+         XCTAssertNoThrow ( try   embedded. readOutbound ( as:  ByteBuffer . self) )  // head
405+         XCTAssertNoThrow ( try   embedded. readOutbound ( as:  ByteBuffer . self) )  // end
406+ 
407+         let  responseString  =  """ 
408+         HTTP/1.1 101 Switching Protocols \r \n \ 
409+         Upgrade: websocket \r \n \ 
410+         Sec-WebSocket-Accept: xAMUK7/Il9bLRFJrikq6mm8CNZI= \r \n \ 
411+         Connection: upgrade \r \n \ 
412+         date: Mon, 27 Sep 2021 17:53:14 GMT \r \n \ 
413+          \r \n \ 
414+          \r \n foo bar baz 
415+          """ 
416+ 
417+         XCTAssertTrue ( embedded. isActive) 
418+         XCTAssertEqual ( connectionDelegate. hitConnectionClosed,  0 ) 
419+         XCTAssertEqual ( connectionDelegate. hitConnectionReleased,  0 ) 
420+         XCTAssertNoThrow ( try   embedded. writeInbound ( ByteBuffer ( string:  responseString) ) ) 
421+         XCTAssertFalse ( embedded. isActive) 
422+         ( embedded. eventLoop as!  EmbeddedEventLoop ) . run ( )  // tick once to run futures.
423+         XCTAssertEqual ( connectionDelegate. hitConnectionClosed,  1 ) 
424+         XCTAssertEqual ( connectionDelegate. hitConnectionReleased,  0 ) 
425+ 
426+         var  response :  HTTPClient . Response ? 
427+         XCTAssertNoThrow ( response =  try   requestBag. task. futureResult. wait ( ) ) 
428+         XCTAssertEqual ( response? . status,  . switchingProtocols) 
429+         XCTAssertEqual ( response? . headers. count,  4 ) 
430+         XCTAssertEqual ( response? . body,  nil ) 
431+     } 
432+ 
433+     func  testConnectionDoesntCrashAfterConnectionCloseAndEarlyHints( )  { 
434+         let  embedded  =  EmbeddedChannel ( ) 
435+         let  logger  =  Logger ( label:  " test.http1.connection " ) 
436+ 
437+         XCTAssertNoThrow ( try   embedded. connect ( to:  SocketAddress ( ipAddress:  " 127.0.0.1 " ,  port:  3000 ) ) . wait ( ) ) 
438+ 
439+         var  maybeConnection :  HTTP1Connection ? 
440+         let  connectionDelegate  =  MockConnectionDelegate ( ) 
441+         XCTAssertNoThrow ( maybeConnection =  try   HTTP1Connection . start ( 
442+             channel:  embedded, 
443+             connectionID:  0 , 
444+             delegate:  connectionDelegate, 
445+             configuration:  . init( decompression:  . enabled( limit:  . ratio( 4 ) ) ) , 
446+             logger:  logger
447+         ) ) 
448+         guard  let  connection =  maybeConnection else  {  return  XCTFail ( " Expected to have a connection at this point. " )  } 
449+ 
450+         var  maybeRequest :  HTTPClient . Request ? 
451+         XCTAssertNoThrow ( maybeRequest =  try   HTTPClient . Request ( url:  " http://swift.org/ " ) ) 
452+         guard  let  request =  maybeRequest else  {  return  XCTFail ( " Expected to be able to create a request " )  } 
453+ 
454+         let  delegate  =  ResponseAccumulator ( request:  request) 
455+         var  maybeRequestBag :  RequestBag < ResponseAccumulator > ? 
456+         XCTAssertNoThrow ( maybeRequestBag =  try   RequestBag ( 
457+             request:  request, 
458+             eventLoopPreference:  . delegate( on:  embedded. eventLoop) , 
459+             task:  . init( eventLoop:  embedded. eventLoop,  logger:  logger) , 
460+             redirectHandler:  nil , 
461+             connectionDeadline:  . now( )  +  . seconds( 30 ) , 
462+             requestOptions:  . forTests( ) , 
463+             delegate:  delegate
464+         ) ) 
465+         guard  let  requestBag =  maybeRequestBag else  {  return  XCTFail ( " Expected to be able to create a request bag " )  } 
466+ 
467+         connection. executeRequest ( requestBag) 
468+ 
469+         XCTAssertNoThrow ( try   embedded. readOutbound ( as:  ByteBuffer . self) )  // head
470+         XCTAssertNoThrow ( try   embedded. readOutbound ( as:  ByteBuffer . self) )  // end
471+ 
472+         let  responseString  =  """ 
473+         HTTP/1.1 103 Early Hints \r \n \ 
474+         date: Mon, 27 Sep 2021 17:53:14 GMT \r \n \ 
475+          \r \n \ 
476+          \r \n 
477+          """ 
478+ 
479+         XCTAssertTrue ( embedded. isActive) 
480+         XCTAssertEqual ( connectionDelegate. hitConnectionClosed,  0 ) 
481+         XCTAssertEqual ( connectionDelegate. hitConnectionReleased,  0 ) 
482+         XCTAssertNoThrow ( try   embedded. writeInbound ( ByteBuffer ( string:  responseString) ) ) 
483+         XCTAssertFalse ( embedded. isActive) 
484+         ( embedded. eventLoop as!  EmbeddedEventLoop ) . run ( )  // tick once to run futures.
485+         XCTAssertEqual ( connectionDelegate. hitConnectionClosed,  1 ) 
486+         XCTAssertEqual ( connectionDelegate. hitConnectionReleased,  0 ) 
487+ 
488+         XCTAssertThrowsError ( try   requestBag. task. futureResult. wait ( ) )  { 
489+             XCTAssertEqual ( $0 as?  HTTPClientError ,  . httpEndReceivedAfterHeadWith1xx) 
490+         } 
491+     } 
492+ 
368493    // In order to test backpressure we need to make sure that reads will not happen
369494    // until the backpressure promise is succeeded. Since we cannot guarantee when
370495    // messages will be delivered to a client pipeline and we need this test to be
0 commit comments