@@ -125,6 +125,57 @@ final class RequestBagTests: XCTestCase {
125125
126126 XCTAssertNoThrow ( try bag. task. futureResult. wait ( ) , " The request has succeeded " )
127127 }
128+
129+ func testTaskIsFailedIfWritingFails( ) {
130+ struct TestError : Error , Equatable { }
131+
132+ let embeddedEventLoop = EmbeddedEventLoop ( )
133+ defer { XCTAssertNoThrow ( try embeddedEventLoop. syncShutdownGracefully ( ) ) }
134+ let logger = Logger ( label: " test " )
135+
136+ let requestBody : HTTPClient . Body = . stream( length: 12 ) { writer -> EventLoopFuture < Void > in
137+
138+ writer. write ( . byteBuffer( ByteBuffer ( bytes: 0 ... 3 ) ) ) . flatMap { _ -> EventLoopFuture < Void > in
139+ embeddedEventLoop. makeFailedFuture ( TestError ( ) )
140+ }
141+ }
142+
143+ var maybeRequest : HTTPClient . Request ?
144+ XCTAssertNoThrow ( maybeRequest = try HTTPClient . Request ( url: " https://swift.org " , method: . POST, body: requestBody) )
145+ guard let request = maybeRequest else { return XCTFail ( " Expected to have a request " ) }
146+
147+ let delegate = UploadCountingDelegate ( eventLoop: embeddedEventLoop)
148+ let bag = RequestBag (
149+ request: request,
150+ eventLoopPreference: . delegate( on: embeddedEventLoop) ,
151+ task: . init( eventLoop: embeddedEventLoop, logger: logger) ,
152+ redirectHandler: nil ,
153+ connectionDeadline: . now( ) + . seconds( 30 ) ,
154+ idleReadTimeout: nil ,
155+ delegate: delegate
156+ )
157+ XCTAssert ( bag. task. eventLoop === embeddedEventLoop)
158+
159+ let executor = MockRequestExecutor ( )
160+
161+ XCTAssertTrue ( bag. willExecuteRequest ( executor) , " false indicates request is cancelled " )
162+
163+ XCTAssertEqual ( delegate. hitDidSendRequestHead, 0 )
164+ bag. requestHeadSent ( bag. requestHead)
165+ XCTAssertEqual ( delegate. hitDidSendRequestHead, 1 )
166+ XCTAssertEqual ( delegate. hitDidSendRequestPart, 0 )
167+ bag. resumeRequestBodyStream ( )
168+ XCTAssertEqual ( delegate. hitDidSendRequestPart, 1 )
169+ XCTAssertEqual ( delegate. hitDidReceiveError, 1 )
170+ XCTAssertEqual ( delegate. lastError as? TestError , TestError ( ) )
171+
172+ XCTAssertTrue ( executor. isCancelled)
173+
174+ XCTAssertThrowsError ( try bag. task. futureResult. wait ( ) ) {
175+ XCTAssertEqual ( $0 as? TestError , TestError ( ) )
176+ }
177+ }
178+
128179
129180 func testCancelFailsTaskBeforeRequestIsSent( ) {
130181 let embeddedEventLoop = EmbeddedEventLoop ( )
@@ -280,10 +331,12 @@ class UploadCountingDelegate: HTTPClientResponseDelegate {
280331 private( set) var hitDidSendRequestPart = 0
281332 private( set) var hitDidSendRequest = 0
282333 private( set) var hitDidReceiveResponse = 0
334+ private( set) var hitDidReceiveError = 0
283335
284336 private( set) var receivedHead : HTTPResponseHead ?
285337 private( set) var lastBodyPart : ByteBuffer ?
286338 private( set) var backpressurePromise : EventLoopPromise < Void > ?
339+ private( set) var lastError : Error ?
287340
288341 init ( eventLoop: EventLoop ) {
289342 self . eventLoop = eventLoop
@@ -315,6 +368,11 @@ class UploadCountingDelegate: HTTPClientResponseDelegate {
315368 func didFinishRequest( task: HTTPClient . Task < Void > ) throws {
316369 self . hitDidReceiveResponse += 1
317370 }
371+
372+ func didReceiveError( task: HTTPClient . Task < Void > , _ error: Error ) {
373+ self . hitDidReceiveError += 1
374+ self . lastError = error
375+ }
318376
319377 private func createBackpressurePromise( ) -> EventLoopFuture < Void > {
320378 assert ( self . backpressurePromise == nil )
0 commit comments