@@ -376,6 +376,56 @@ class HTTP1ClientChannelHandlerTests: XCTestCase {
376376 }
377377 }
378378
379+ func testIdleWriteTimeoutRaceToEnd( ) {
380+ let embedded = EmbeddedChannel ( )
381+ var maybeTestUtils : HTTP1TestTools ?
382+ XCTAssertNoThrow ( maybeTestUtils = try embedded. setupHTTP1Connection ( ) )
383+ guard let testUtils = maybeTestUtils else { return XCTFail ( " Expected connection setup works " ) }
384+
385+ var maybeRequest : HTTPClient . Request ?
386+ XCTAssertNoThrow ( maybeRequest = try HTTPClient . Request ( url: " http://localhost/ " , method: . POST, body: . stream { _ in
387+ // Advance time by more than the idle write timeout (that's 1 millisecond) to trigger the timeout.
388+ let scheduled = embedded. embeddedEventLoop. flatScheduleTask ( in: . milliseconds( 2 ) ) {
389+ embedded. embeddedEventLoop. makeSucceededVoidFuture ( )
390+ }
391+ return scheduled. futureResult
392+ } ) )
393+
394+ guard let request = maybeRequest else { return XCTFail ( " Expected to be able to create a request " ) }
395+
396+ let delegate = ResponseAccumulator ( request: request)
397+ var maybeRequestBag : RequestBag < ResponseAccumulator > ?
398+ XCTAssertNoThrow ( maybeRequestBag = try RequestBag (
399+ request: request,
400+ eventLoopPreference: . delegate( on: embedded. eventLoop) ,
401+ task: . init( eventLoop: embedded. eventLoop, logger: testUtils. logger) ,
402+ redirectHandler: nil ,
403+ connectionDeadline: . now( ) + . seconds( 30 ) ,
404+ requestOptions: . forTests( idleWriteTimeout: . milliseconds( 5 ) ) ,
405+ delegate: delegate
406+ ) )
407+ guard let requestBag = maybeRequestBag else { return XCTFail ( " Expected to be able to create a request bag " ) }
408+
409+ embedded. isWritable = true
410+ embedded. pipeline. fireChannelWritabilityChanged ( )
411+ testUtils. connection. executeRequest ( requestBag)
412+ let expectedHeaders : HTTPHeaders = [ " host " : " localhost " , " Transfer-Encoding " : " chunked " ]
413+ XCTAssertEqual (
414+ try embedded. readOutbound ( as: HTTPClientRequestPart . self) ,
415+ . head( HTTPRequestHead ( version: . http1_1, method: . POST, uri: " / " , headers: expectedHeaders) )
416+ )
417+
418+ // change the writability to false.
419+ embedded. isWritable = false
420+ embedded. pipeline. fireChannelWritabilityChanged ( )
421+ embedded. embeddedEventLoop. run ( )
422+
423+ // let the writer, write an end (while writability is false)
424+ embedded. embeddedEventLoop. advanceTime ( by: . milliseconds( 2 ) )
425+
426+ XCTAssertEqual ( try embedded. readOutbound ( as: HTTPClientRequestPart . self) , . end( nil ) )
427+ }
428+
379429 func testIdleWriteTimeoutWritabilityChanged( ) {
380430 let embedded = EmbeddedChannel ( )
381431 let testWriter = TestBackpressureWriter ( eventLoop: embedded. eventLoop, parts: 5 )
0 commit comments