@@ -76,12 +76,10 @@ class HTTPRequestStateMachineTests: XCTestCase {
7676let part1 = IOData . byteBuffer ( ByteBuffer ( bytes: [ 0 , 1 , 2 , 3 ] ) )
7777XCTAssertEqual ( state. requestStreamPartReceived ( part0) , . sendBodyPart( part0) )
7878
79- let failAction = state. requestStreamPartReceived ( part1)
80- guard case. failRequest( let error, . close) = failActionelse {
81- return XCTFail ( " Unexpected action: \( failAction) " )
82- }
79+ state. requestStreamPartReceived ( part1) . assertFailRequest ( HTTPClientError . bodyLengthMismatch, . close)
8380
84- XCTAssertEqual ( erroras? HTTPClientError , . bodyLengthMismatch)
81+ // if another error happens the new one is ignored
82+ XCTAssertEqual ( state. errorHappened ( HTTPClientError . remoteConnectionClosed) , . wait)
8583}
8684
8785func testPOSTContentLengthIsTooShort( ) {
@@ -92,12 +90,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
9290let part0 = IOData . byteBuffer ( ByteBuffer ( bytes: [ 0 , 1 , 2 , 3 ] ) )
9391XCTAssertEqual ( state. requestStreamPartReceived ( part0) , . sendBodyPart( part0) )
9492
95- let failAction = state. requestStreamFinished ( )
96- guard case. failRequest( let error, . close) = failActionelse {
97- return XCTFail ( " Unexpected action: \( failAction) " )
98- }
99-
100- XCTAssertEqual ( erroras? HTTPClientError , . bodyLengthMismatch)
93+ state. requestStreamFinished ( ) . assertFailRequest ( HTTPClientError . bodyLengthMismatch, . close)
10194}
10295
10396func testRequestBodyStreamIsCancelledIfServerRespondsWith301( ) {
@@ -206,7 +199,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
206199
207200let part1 = IOData . byteBuffer ( ByteBuffer ( bytes: 4 ... 7 ) )
208201XCTAssertEqual ( state. requestStreamPartReceived ( part1) , . sendBodyPart( part1) )
209- XCTAssertEqual ( state. requestStreamFinished ( ) , . failRequest ( HTTPClientError . bodyLengthMismatch, . close) )
202+ state. requestStreamFinished ( ) . assertFailRequest ( HTTPClientError . bodyLengthMismatch, . close)
210203XCTAssertEqual ( state. channelInactive ( ) , . wait)
211204}
212205
@@ -224,7 +217,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
224217
225218let part1 = IOData . byteBuffer ( ByteBuffer ( bytes: 4 ... 7 ) )
226219XCTAssertEqual ( state. requestStreamPartReceived ( part1) , . sendBodyPart( part1) )
227- XCTAssertEqual ( state. requestStreamFinished ( ) , . failRequest ( HTTPClientError . bodyLengthMismatch, . close) )
220+ state. requestStreamFinished ( ) . assertFailRequest ( HTTPClientError . bodyLengthMismatch, . close)
228221XCTAssertEqual ( state. channelRead ( . end( nil ) ) , . wait)
229222}
230223
@@ -249,7 +242,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
249242let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " )
250243let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
251244XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . wait)
252- XCTAssertEqual ( state. channelInactive ( ) , . failRequest ( HTTPClientError . remoteConnectionClosed, . none) )
245+ state. channelInactive ( ) . assertFailRequest ( HTTPClientError . remoteConnectionClosed, . none)
253246}
254247
255248func testResponseReadingWithBackpressure( ) {
@@ -339,15 +332,15 @@ class HTTPRequestStateMachineTests: XCTestCase {
339332
340333func testCancellingARequestInStateInitializedKeepsTheConnectionAlive( ) {
341334var state = HTTPRequestStateMachine ( isChannelWritable: false )
342- XCTAssertEqual ( state. requestCancelled ( ) , . failRequest ( HTTPClientError . cancelled, . none) )
335+ state. requestCancelled ( ) . assertFailRequest ( HTTPClientError . cancelled, . none)
343336}
344337
345338func testCancellingARequestBeforeBeingSendKeepsTheConnectionAlive( ) {
346339var state = HTTPRequestStateMachine ( isChannelWritable: false )
347340let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " )
348341let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
349342XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . wait)
350- XCTAssertEqual ( state. requestCancelled ( ) , . failRequest ( HTTPClientError . cancelled, . none) )
343+ state. requestCancelled ( ) . assertFailRequest ( HTTPClientError . cancelled, . none)
351344}
352345
353346func testConnectionBecomesWritableBeforeFirstRequest( ) {
@@ -373,15 +366,15 @@ class HTTPRequestStateMachineTests: XCTestCase {
373366let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " )
374367let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
375368XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . sendRequestHead( requestHead, startBody: false ) )
376- XCTAssertEqual ( state. requestCancelled ( ) , . failRequest ( HTTPClientError . cancelled, . close) )
369+ state. requestCancelled ( ) . assertFailRequest ( HTTPClientError . cancelled, . close)
377370}
378371
379372func testRemoteSuddenlyClosesTheConnection( ) {
380373var state = HTTPRequestStateMachine ( isChannelWritable: true )
381374let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " , headers: . init( [ ( " content-length " , " 4 " ) ] ) )
382375let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 4 ) )
383376XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . sendRequestHead( requestHead, startBody: true ) )
384- XCTAssertEqual ( state. requestCancelled ( ) , . failRequest ( HTTPClientError . remoteConnectionClosed , . close) )
377+ state. requestCancelled ( ) . assertFailRequest ( HTTPClientError . cancelled , . close)
385378XCTAssertEqual ( state. requestStreamPartReceived ( . byteBuffer( . init( bytes: 1 ... 3 ) ) ) , . wait)
386379}
387380
@@ -395,7 +388,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
395388XCTAssertEqual ( state. channelRead ( . head( responseHead) ) , . forwardResponseHead( responseHead, pauseRequestBodyStream: false ) )
396389let part0 = ByteBuffer ( bytes: 0 ... 3 )
397390XCTAssertEqual ( state. channelRead ( . body( part0) ) , . wait)
398- XCTAssertEqual ( state. idleReadTimeoutTriggered ( ) , . failRequest ( HTTPClientError . readTimeout, . close) )
391+ state. idleReadTimeoutTriggered ( ) . assertFailRequest ( HTTPClientError . readTimeout, . close)
399392XCTAssertEqual ( state. channelRead ( . body( ByteBuffer ( bytes: 4 ... 7 ) ) ) , . wait)
400393XCTAssertEqual ( state. channelRead ( . body( ByteBuffer ( bytes: 8 ... 11 ) ) ) , . wait)
401394XCTAssertEqual ( state. demandMoreResponseBodyParts ( ) , . wait)
@@ -448,7 +441,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
448441let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
449442XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . sendRequestHead( requestHead, startBody: false ) )
450443
451- XCTAssertEqual ( state. errorHappened ( HTTPParserError . invalidChunkSize) , . failRequest ( HTTPParserError . invalidChunkSize, . close) )
444+ state. errorHappened ( HTTPParserError . invalidChunkSize) . assertFailRequest ( HTTPParserError . invalidChunkSize, . close)
452445XCTAssertEqual ( state. requestCancelled ( ) , . wait, " A cancellation that happens to late is ignored " )
453446}
454447
@@ -502,7 +495,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
502495XCTAssertEqual ( state. read ( ) , . read)
503496XCTAssertEqual ( state. channelReadComplete ( ) , . wait)
504497XCTAssertEqual ( state. channelRead ( . body( body) ) , . wait)
505- XCTAssertEqual ( state. channelRead ( . end( nil ) ) , . failRequest ( HTTPClientError . remoteConnectionClosed, . close) )
498+ state. channelRead ( . end( nil ) ) . assertFailRequest ( HTTPClientError . remoteConnectionClosed, . close)
506499XCTAssertEqual ( state. channelInactive ( ) , . wait)
507500}
508501
@@ -517,11 +510,32 @@ class HTTPRequestStateMachineTests: XCTestCase {
517510XCTAssertEqual ( state. channelRead ( . head( responseHead) ) , . forwardResponseHead( responseHead, pauseRequestBodyStream: false ) )
518511XCTAssertEqual ( state. demandMoreResponseBodyParts ( ) , . wait)
519512XCTAssertEqual ( state. channelRead ( . body( body) ) , . wait)
520- XCTAssertEqual ( state. errorHappened ( NIOSSLError . uncleanShutdown) , . failRequest ( NIOSSLError . uncleanShutdown, . close) )
513+ state. errorHappened ( NIOSSLError . uncleanShutdown) . assertFailRequest ( NIOSSLError . uncleanShutdown, . close)
521514XCTAssertEqual ( state. channelRead ( . end( nil ) ) , . wait)
522515XCTAssertEqual ( state. channelInactive ( ) , . wait)
523516}
524517
518+ func testNIOSSLErrorUncleanShutdownShouldBeTreatedAsRemoteConnectionCloseWhileInWaitingForHeadState( ) {
519+ var state = HTTPRequestStateMachine ( isChannelWritable: true )
520+ let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " )
521+ let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
522+ XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . sendRequestHead( requestHead, startBody: false ) )
523+
524+ XCTAssertEqual ( state. errorHappened ( NIOSSLError . uncleanShutdown) , . wait)
525+ state. channelInactive ( ) . assertFailRequest ( HTTPClientError . remoteConnectionClosed, . none)
526+ }
527+
528+ func testArbitraryErrorShouldBeTreatedAsARequestFailureWhileInWaitingForHeadState( ) {
529+ struct ArbitraryError : Error , Equatable { }
530+ var state = HTTPRequestStateMachine ( isChannelWritable: true )
531+ let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " )
532+ let metadata = RequestFramingMetadata ( connectionClose: false , body: . fixedSize( 0 ) )
533+ XCTAssertEqual ( state. startRequest ( head: requestHead, metadata: metadata) , . sendRequestHead( requestHead, startBody: false ) )
534+
535+ state. errorHappened ( ArbitraryError ( ) ) . assertFailRequest ( ArbitraryError ( ) , . close)
536+ XCTAssertEqual ( state. channelInactive ( ) , . wait)
537+ }
538+
525539func testFailHTTP1RequestWithContentLengthWithNIOSSLErrorUncleanShutdownButIgnoreIt( ) {
526540var state = HTTPRequestStateMachine ( isChannelWritable: true )
527541let requestHead = HTTPRequestHead ( version: . http1_1, method: . GET, uri: " / " )
@@ -536,7 +550,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
536550XCTAssertEqual ( state. channelRead ( . body( body) ) , . wait)
537551XCTAssertEqual ( state. channelReadComplete ( ) , . forwardResponseBodyParts( [ body] ) )
538552XCTAssertEqual ( state. errorHappened ( NIOSSLError . uncleanShutdown) , . wait)
539- XCTAssertEqual ( state. errorHappened ( HTTPParserError . invalidEOFState) , . failRequest ( HTTPParserError . invalidEOFState, . close) )
553+ state. errorHappened ( HTTPParserError . invalidEOFState) . assertFailRequest ( HTTPParserError . invalidEOFState, . close)
540554XCTAssertEqual ( state. channelInactive ( ) , . wait)
541555}
542556
@@ -558,7 +572,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
558572
559573XCTAssertEqual ( state. channelRead ( . body( ByteBuffer ( string: " baz lightyear " ) ) ) , . wait)
560574XCTAssertEqual ( state. channelReadComplete ( ) , . wait)
561- XCTAssertEqual ( state. channelInactive ( ) , . failRequest ( HTTPClientError . remoteConnectionClosed, . none) )
575+ state. channelInactive ( ) . assertFailRequest ( HTTPClientError . remoteConnectionClosed, . none)
562576}
563577
564578func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForRead( ) {
@@ -579,7 +593,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
579593
580594XCTAssertEqual ( state. channelRead ( . body( ByteBuffer ( string: " baz lightyear " ) ) ) , . wait)
581595XCTAssertEqual ( state. channelReadComplete ( ) , . wait)
582- XCTAssertEqual ( state. channelInactive ( ) , . failRequest ( HTTPClientError . remoteConnectionClosed, . none) )
596+ state. channelInactive ( ) . assertFailRequest ( HTTPClientError . remoteConnectionClosed, . none)
583597}
584598
585599func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForReadAndDemand( ) {
@@ -599,7 +613,7 @@ class HTTPRequestStateMachineTests: XCTestCase {
599613
600614XCTAssertEqual ( state. channelRead ( . body( ByteBuffer ( string: " baz lightyear " ) ) ) , . wait)
601615XCTAssertEqual ( state. channelReadComplete ( ) , . wait)
602- XCTAssertEqual ( state. channelInactive ( ) , . failRequest ( HTTPClientError . remoteConnectionClosed, . none) )
616+ state. channelInactive ( ) . assertFailRequest ( HTTPClientError . remoteConnectionClosed, . none)
603617}
604618
605619func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForReadAndDemandMultipleTimes( ) {
@@ -676,3 +690,22 @@ extension HTTPRequestStateMachine.Action: Equatable {
676690}
677691}
678692}
693+
694+ extension HTTPRequestStateMachine . Action {
695+ fileprivate func assertFailRequest< Error> (
696+ _ expectedError: Error ,
697+ _ expectedFinalStreamAction: HTTPRequestStateMachine . Action . FinalStreamAction ,
698+ file: StaticString = #file,
699+ line: UInt = #line
700+ ) where Error: Swift . Error & Equatable {
701+ guard case. failRequest( let actualError, let actualFinalStreamAction) = self else {
702+ return XCTFail ( " expected .failRequest( \( expectedError) , \( expectedFinalStreamAction) ) but got \( self ) " , file: file, line: line)
703+ }
704+ if let actualError= actualErroras? Error {
705+ XCTAssertEqual ( actualError, expectedError, file: file, line: line)
706+ } else {
707+ XCTFail ( " \( actualError) is not equal to \( expectedError) " , file: file, line: line)
708+ }
709+ XCTAssertEqual ( actualFinalStreamAction, expectedFinalStreamAction, file: file, line: line)
710+ }
711+ }