@@ -634,6 +634,9 @@ extension HTTPClient {
634634 self . promise. fail ( error)
635635 connection. channel. close ( promise: nil )
636636 }
637+ } else {
638+ // this is used in tests where we don't want to bootstrap the whole connection pool
639+ self . promise. fail ( error)
637640 }
638641 }
639642
@@ -665,11 +668,11 @@ internal struct TaskCancelEvent {}
665668internal class TaskHandler < Delegate: HTTPClientResponseDelegate > : RemovableChannelHandler {
666669 enum State {
667670 case idle
668- case bodySent
669- case sent
670- case head
671+ case sendingBodyWaitingResponseHead
672+ case sendingBodyResponseHeadReceived
673+ case bodySentWaitingResponseHead
674+ case bodySentResponseHeadReceived
671675 case redirected( HTTPResponseHead , URL )
672- case body
673676 case endOrError
674677 }
675678
@@ -794,7 +797,8 @@ extension TaskHandler: ChannelDuplexHandler {
794797 typealias OutboundOut = HTTPClientRequestPart
795798
796799 func write( context: ChannelHandlerContext , data: NIOAny , promise: EventLoopPromise < Void > ? ) {
797- self . state = . idle
800+ self . state = . sendingBodyWaitingResponseHead
801+
798802 let request = self . unwrapOutboundIn ( data)
799803
800804 var head = HTTPRequestHead ( version: HTTPVersion ( major: 1 , minor: 1 ) ,
@@ -840,23 +844,37 @@ extension TaskHandler: ChannelDuplexHandler {
840844 self . writeBody ( request: request, context: context)
841845 } . flatMap {
842846 context. eventLoop. assertInEventLoop ( )
843- if case . endOrError = self . state {
847+ switch self . state {
848+ case . idle:
849+ // since this code path is called from `write` and write sets state to sendingBody
850+ preconditionFailure ( " should not happen " )
851+ case . sendingBodyWaitingResponseHead:
852+ self . state = . bodySentWaitingResponseHead
853+ case . sendingBodyResponseHeadReceived:
854+ self . state = . bodySentResponseHeadReceived
855+ case . bodySentWaitingResponseHead, . bodySentResponseHeadReceived:
856+ preconditionFailure ( " should not happen, state is \( self . state) " )
857+ case . redirected:
858+ break
859+ case . endOrError:
860+ // If the state is .endOrError, it means that request was failed and there is nothing to do here:
861+ // we cannot write .end since channel is most likely closed, and we should not fail the future,
862+ // since the task would already be failed, no need to fail the writer too.
844863 return context. eventLoop. makeSucceededFuture ( ( ) )
845864 }
846865
847- self . state = . bodySent
848866 if let expectedBodyLength = self . expectedBodyLength, expectedBodyLength != self . actualBodyLength {
849867 let error = HTTPClientError . bodyLengthMismatch
850868 return context. eventLoop. makeFailedFuture ( error)
851869 }
852870 return context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) )
853871 } . map {
854872 context. eventLoop. assertInEventLoop ( )
873+
855874 if case . endOrError = self . state {
856875 return
857876 }
858877
859- self . state = . sent
860878 self . callOutToDelegateFireAndForget ( self . delegate. didSendRequest)
861879 } . flatMapErrorThrowing { error in
862880 context. eventLoop. assertInEventLoop ( )
@@ -903,6 +921,9 @@ extension TaskHandler: ChannelDuplexHandler {
903921 private func writeBodyPart( context: ChannelHandlerContext , part: IOData , promise: EventLoopPromise < Void > ) {
904922 switch self . state {
905923 case . idle:
924+ // this function is called on the codepath starting with write, so it cannot be in state .idle
925+ preconditionFailure ( " should not happen " )
926+ case . sendingBodyWaitingResponseHead, . sendingBodyResponseHeadReceived, . redirected:
906927 if let limit = self . expectedBodyLength, self . actualBodyLength + part. readableBytes > limit {
907928 let error = HTTPClientError . bodyLengthMismatch
908929 self . errorCaught ( context: context, error: error)
@@ -911,7 +932,7 @@ extension TaskHandler: ChannelDuplexHandler {
911932 }
912933 self . actualBodyLength += part. readableBytes
913934 context. writeAndFlush ( self . wrapOutboundOut ( . body( part) ) , promise: promise)
914- default :
935+ case . bodySentWaitingResponseHead , . bodySentResponseHeadReceived , . endOrError :
915936 let error = HTTPClientError . writeAfterRequestSent
916937 self . errorCaught ( context: context, error: error)
917938 promise. fail ( error)
@@ -931,7 +952,18 @@ extension TaskHandler: ChannelDuplexHandler {
931952 let response = self . unwrapInboundIn ( data)
932953 switch response {
933954 case . head( let head) :
934- if case . endOrError = self . state {
955+ switch self . state {
956+ case . idle:
957+ // should be prevented by NIO HTTP1 pipeline, see testHTTPResponseHeadBeforeRequestHead
958+ preconditionFailure ( " should not happen " )
959+ case . sendingBodyWaitingResponseHead:
960+ self . state = . sendingBodyResponseHeadReceived
961+ case . bodySentWaitingResponseHead:
962+ self . state = . bodySentResponseHeadReceived
963+ case . sendingBodyResponseHeadReceived, . bodySentResponseHeadReceived, . redirected:
964+ // should be prevented by NIO HTTP1 pipeline, aee testHTTPResponseDoubleHead
965+ preconditionFailure ( " should not happen " )
966+ case . endOrError:
935967 return
936968 }
937969
@@ -942,7 +974,6 @@ extension TaskHandler: ChannelDuplexHandler {
942974 if let redirectURL = self . redirectHandler? . redirectTarget ( status: head. status, headers: head. headers) {
943975 self . state = . redirected( head, redirectURL)
944976 } else {
945- self . state = . head
946977 self . mayRead = false
947978 self . callOutToDelegate ( value: head, channelEventLoop: context. eventLoop, self . delegate. didReceiveHead)
948979 . whenComplete { result in
@@ -954,7 +985,6 @@ extension TaskHandler: ChannelDuplexHandler {
954985 case . redirected, . endOrError:
955986 break
956987 default :
957- self . state = . body
958988 self . mayRead = false
959989 self . callOutToDelegate ( value: body, channelEventLoop: context. eventLoop, self . delegate. didReceiveBodyPart)
960990 . whenComplete { result in
@@ -1009,10 +1039,10 @@ extension TaskHandler: ChannelDuplexHandler {
10091039
10101040 func channelInactive( context: ChannelHandlerContext ) {
10111041 switch self . state {
1042+ case . idle, . sendingBodyWaitingResponseHead, . sendingBodyResponseHeadReceived, . bodySentWaitingResponseHead, . bodySentResponseHeadReceived, . redirected:
1043+ self . errorCaught ( context: context, error: HTTPClientError . remoteConnectionClosed)
10121044 case . endOrError:
10131045 break
1014- case . body, . head, . idle, . redirected, . sent, . bodySent:
1015- self . errorCaught ( context: context, error: HTTPClientError . remoteConnectionClosed)
10161046 }
10171047 context. fireChannelInactive ( )
10181048 }
@@ -1025,8 +1055,8 @@ extension TaskHandler: ChannelDuplexHandler {
10251055 /// Some HTTP Servers can 'forget' to respond with CloseNotify when client is closing connection,
10261056 /// this could lead to incomplete SSL shutdown. But since request is already processed, we can ignore this error.
10271057 break
1028- case . head where self . ignoreUncleanSSLShutdown,
1029- . body where self . ignoreUncleanSSLShutdown:
1058+ case . sendingBodyResponseHeadReceived where self . ignoreUncleanSSLShutdown,
1059+ . bodySentResponseHeadReceived where self . ignoreUncleanSSLShutdown:
10301060 /// We can also ignore this error like `.end`.
10311061 break
10321062 default :
@@ -1035,7 +1065,7 @@ extension TaskHandler: ChannelDuplexHandler {
10351065 }
10361066 default :
10371067 switch self . state {
1038- case . idle, . bodySent , . sent , . head , . redirected , . body :
1068+ case . idle, . sendingBodyWaitingResponseHead , . sendingBodyResponseHeadReceived , . bodySentWaitingResponseHead , . bodySentResponseHeadReceived , . redirected :
10391069 self . state = . endOrError
10401070 self . failTaskAndNotifyDelegate ( error: error, self . delegate. didReceiveError)
10411071 case . endOrError:
0 commit comments