@@ -435,4 +435,122 @@ class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase {
435435 }
436436 XCTAssertEqual ( eventLoop. id, el1. id)
437437 }
438+
439+ func testGoAwayOnIdleConnection( ) {
440+ let elg = EmbeddedEventLoopGroup ( loops: 1 )
441+ let el1 = elg. next ( )
442+
443+ // establish one idle http2 connection
444+ let idGenerator = HTTPConnectionPool . Connection. ID. Generator ( )
445+ var http1Conns = HTTPConnectionPool . HTTP1Connections ( maximumConcurrentConnections: 8 , generator: idGenerator)
446+ let conn1ID = http1Conns. createNewConnection ( on: el1)
447+ var state = HTTPConnectionPool . HTTP2StateMachine ( idGenerator: idGenerator)
448+ let migrationAction = state. migrateConnectionsFromHTTP1 (
449+ connections: http1Conns,
450+ requests: HTTPConnectionPool . RequestQueue ( )
451+ )
452+ XCTAssertEqual ( migrationAction, . none)
453+ let conn1 = HTTPConnectionPool . Connection. __testOnly_connection ( id: conn1ID, eventLoop: el1)
454+ let connectAction = state. newHTTP2ConnectionEstablished ( conn1, maxConcurrentStreams: 100 )
455+ XCTAssertEqual ( connectAction. request, . none)
456+ XCTAssertEqual ( connectAction. connection, . scheduleTimeoutTimer( conn1ID, on: el1) )
457+
458+ let goAwayAction = state. http2ConnectionGoAwayReceived ( conn1ID)
459+ XCTAssertEqual ( goAwayAction. request, . none)
460+ XCTAssertEqual ( goAwayAction. connection, . none, " Connection is automatically closed by HTTP2IdleHandler " )
461+ }
462+
463+ func testGoAwayWithLeasedStream( ) {
464+ let elg = EmbeddedEventLoopGroup ( loops: 1 )
465+ let el1 = elg. next ( )
466+
467+ // establish one idle http2 connection
468+ let idGenerator = HTTPConnectionPool . Connection. ID. Generator ( )
469+ var http1Conns = HTTPConnectionPool . HTTP1Connections ( maximumConcurrentConnections: 8 , generator: idGenerator)
470+ let conn1ID = http1Conns. createNewConnection ( on: el1)
471+ var state = HTTPConnectionPool . HTTP2StateMachine ( idGenerator: idGenerator)
472+ let migrationAction = state. migrateConnectionsFromHTTP1 (
473+ connections: http1Conns,
474+ requests: HTTPConnectionPool . RequestQueue ( )
475+ )
476+ XCTAssertEqual ( migrationAction, . none)
477+ let conn1 = HTTPConnectionPool . Connection. __testOnly_connection ( id: conn1ID, eventLoop: el1)
478+ let connectAction = state. newHTTP2ConnectionEstablished ( conn1, maxConcurrentStreams: 100 )
479+ XCTAssertEqual ( connectAction. request, . none)
480+ XCTAssertEqual ( connectAction. connection, . scheduleTimeoutTimer( conn1ID, on: el1) )
481+
482+ // execute request on idle connection
483+ let mockRequest1 = MockHTTPRequest ( eventLoop: el1)
484+ let request1 = HTTPConnectionPool . Request ( mockRequest1)
485+ let request1Action = state. executeRequest ( request1)
486+ XCTAssertEqual ( request1Action. request, . executeRequest( request1, conn1, cancelTimeout: false ) )
487+ XCTAssertEqual ( request1Action. connection, . cancelTimeoutTimer( conn1ID) )
488+
489+ let goAwayAction = state. http2ConnectionGoAwayReceived ( conn1ID)
490+ XCTAssertEqual ( goAwayAction. request, . none)
491+ XCTAssertEqual ( goAwayAction. connection, . none)
492+
493+ // close stream
494+ let closeStream1Action = state. http2ConnectionStreamClosed ( conn1ID)
495+ XCTAssertEqual ( closeStream1Action. request, . none)
496+ XCTAssertEqual ( closeStream1Action. connection, . none, " Connection is automatically closed by HTTP2IdleHandler " )
497+ }
498+
499+ func testGoAwayWithPendingRequestsStartsNewConnection( ) {
500+ let elg = EmbeddedEventLoopGroup ( loops: 1 )
501+ let el1 = elg. next ( )
502+
503+ // establish one idle http2 connection
504+ let idGenerator = HTTPConnectionPool . Connection. ID. Generator ( )
505+ var http1Conns = HTTPConnectionPool . HTTP1Connections ( maximumConcurrentConnections: 8 , generator: idGenerator)
506+ let conn1ID = http1Conns. createNewConnection ( on: el1)
507+ var state = HTTPConnectionPool . HTTP2StateMachine ( idGenerator: idGenerator)
508+ let migrationAction = state. migrateConnectionsFromHTTP1 (
509+ connections: http1Conns,
510+ requests: HTTPConnectionPool . RequestQueue ( )
511+ )
512+ XCTAssertEqual ( migrationAction, . none)
513+ let conn1 = HTTPConnectionPool . Connection. __testOnly_connection ( id: conn1ID, eventLoop: el1)
514+ let connectAction1 = state. newHTTP2ConnectionEstablished ( conn1, maxConcurrentStreams: 1 )
515+ XCTAssertEqual ( connectAction1. request, . none)
516+ XCTAssertEqual ( connectAction1. connection, . scheduleTimeoutTimer( conn1ID, on: el1) )
517+
518+ // execute request
519+ let mockRequest1 = MockHTTPRequest ( eventLoop: el1)
520+ let request1 = HTTPConnectionPool . Request ( mockRequest1)
521+ let request1Action = state. executeRequest ( request1)
522+ XCTAssertEqual ( request1Action. request, . executeRequest( request1, conn1, cancelTimeout: false ) )
523+ XCTAssertEqual ( request1Action. connection, . cancelTimeoutTimer( conn1ID) )
524+
525+ // queue request
526+ let mockRequest2 = MockHTTPRequest ( eventLoop: el1)
527+ let request2 = HTTPConnectionPool . Request ( mockRequest2)
528+ let request2Action = state. executeRequest ( request2)
529+ XCTAssertEqual ( request2Action. request, . scheduleRequestTimeout( for: request2, on: el1) )
530+ XCTAssertEqual ( request2Action. connection, . none)
531+
532+ // go away should create a new connection
533+ let goAwayAction = state. http2ConnectionGoAwayReceived ( conn1ID)
534+ XCTAssertEqual ( goAwayAction. request, . none)
535+ guard case . createConnection( let conn2ID, let eventLoop) = goAwayAction. connection else {
536+ return XCTFail ( " unexpected connection action \( goAwayAction. connection) " )
537+ }
538+ XCTAssertEqual ( el1. id, eventLoop. id)
539+
540+ // new connection should execute pending request
541+ let conn2 = HTTPConnectionPool . Connection. __testOnly_connection ( id: conn2ID, eventLoop: el1)
542+ let connectAction2 = state. newHTTP2ConnectionEstablished ( conn2, maxConcurrentStreams: 1 )
543+ XCTAssertEqual ( connectAction2. request, . executeRequestsAndCancelTimeouts( [ request2] , conn2) )
544+ XCTAssertEqual ( connectAction2. connection, . none)
545+
546+ // close stream from conn1
547+ let closeStream1Action = state. http2ConnectionStreamClosed ( conn1ID)
548+ XCTAssertEqual ( closeStream1Action. request, . none)
549+ XCTAssertEqual ( closeStream1Action. connection, . none, " Connection is automatically closed by HTTP2IdleHandler " )
550+
551+ // close stream from conn2
552+ let closeStream2Action = state. http2ConnectionStreamClosed ( conn2ID)
553+ XCTAssertEqual ( closeStream2Action. request, . none)
554+ XCTAssertEqual ( closeStream2Action. connection, . scheduleTimeoutTimer( conn2ID, on: el1) )
555+ }
438556}
0 commit comments