@@ -365,6 +365,131 @@ class HTTP1ConnectionTests: XCTestCase {
365
365
XCTAssertEqual ( httpBin. activeConnections, 0 )
366
366
}
367
367
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
+
368
493
// In order to test backpressure we need to make sure that reads will not happen
369
494
// until the backpressure promise is succeeded. Since we cannot guarantee when
370
495
// messages will be delivered to a client pipeline and we need this test to be
0 commit comments