@@ -634,6 +634,9 @@ extension HTTPClient{
634634self . 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 {
666669enum State {
667670case idle
668- case bodySent
669- case sent
670- case head
671+ case sendingBodyWaitingResponseHead
672+ case sendingBodyResponseHeadReceived
673+ case bodySentWaitingResponseHead
674+ case bodySentResponseHeadReceived
671675case redirected( HTTPResponseHead , URL )
672- case body
673676case endOrError
674677}
675678
@@ -794,7 +797,8 @@ extension TaskHandler: ChannelDuplexHandler{
794797typealias OutboundOut = HTTPClientRequestPart
795798
796799func write( context: ChannelHandlerContext , data: NIOAny , promise: EventLoopPromise < Void > ? ) {
797- self . state = . idle
800+ self . state = . sendingBodyWaitingResponseHead
801+
798802let request = self . unwrapOutboundIn ( data)
799803
800804var head = HTTPRequestHead ( version: HTTPVersion ( major: 1 , minor: 1 ) ,
@@ -840,23 +844,37 @@ extension TaskHandler: ChannelDuplexHandler{
840844self . 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.
844863return context. eventLoop. makeSucceededFuture ( ( ) )
845864}
846865
847- self . state = . bodySent
848866if let expectedBodyLength = self . expectedBodyLength, expectedBodyLength != self . actualBodyLength {
849867let error = HTTPClientError . bodyLengthMismatch
850868return context. eventLoop. makeFailedFuture ( error)
851869}
852870return context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) )
853871} . map {
854872 context. eventLoop. assertInEventLoop ( )
873+
855874if case . endOrError = self . state {
856875return
857876}
858877
859- self . state = . sent
860878self . callOutToDelegateFireAndForget ( self . delegate. didSendRequest)
861879} . flatMapErrorThrowing { error in
862880 context. eventLoop. assertInEventLoop ( )
@@ -903,6 +921,9 @@ extension TaskHandler: ChannelDuplexHandler{
903921private func writeBodyPart( context: ChannelHandlerContext , part: IOData , promise: EventLoopPromise < Void > ) {
904922switch self . state {
905923case . 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:
906927if let limit = self . expectedBodyLength, self . actualBodyLength + part. readableBytes > limit {
907928let error = HTTPClientError . bodyLengthMismatch
908929self . errorCaught ( context: context, error: error)
@@ -911,7 +932,7 @@ extension TaskHandler: ChannelDuplexHandler{
911932}
912933self . actualBodyLength += part. readableBytes
913934 context. writeAndFlush ( self . wrapOutboundOut ( . body( part) ) , promise: promise)
914- default :
935+ case . bodySentWaitingResponseHead , . bodySentResponseHeadReceived , . endOrError :
915936let error = HTTPClientError . writeAfterRequestSent
916937self . errorCaught ( context: context, error: error)
917938 promise. fail ( error)
@@ -931,7 +952,18 @@ extension TaskHandler: ChannelDuplexHandler{
931952let response = self . unwrapInboundIn ( data)
932953switch response {
933954case . 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:
935967return
936968}
937969
@@ -942,7 +974,6 @@ extension TaskHandler: ChannelDuplexHandler{
942974if let redirectURL = self . redirectHandler? . redirectTarget ( status: head. status, headers: head. headers) {
943975self . state = . redirected( head, redirectURL)
944976} else {
945- self . state = . head
946977self . mayRead = false
947978self . callOutToDelegate ( value: head, channelEventLoop: context. eventLoop, self . delegate. didReceiveHead)
948979. whenComplete { result in
@@ -954,7 +985,6 @@ extension TaskHandler: ChannelDuplexHandler{
954985case . redirected, . endOrError:
955986break
956987default :
957- self . state = . body
958988self . mayRead = false
959989self . callOutToDelegate ( value: body, channelEventLoop: context. eventLoop, self . delegate. didReceiveBodyPart)
960990. whenComplete { result in
@@ -1009,10 +1039,10 @@ extension TaskHandler: ChannelDuplexHandler{
10091039
10101040func channelInactive( context: ChannelHandlerContext ) {
10111041switch self . state {
1042+ case . idle, . sendingBodyWaitingResponseHead, . sendingBodyResponseHeadReceived, . bodySentWaitingResponseHead, . bodySentResponseHeadReceived, . redirected:
1043+ self . errorCaught ( context: context, error: HTTPClientError . remoteConnectionClosed)
10121044case . endOrError:
10131045break
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.
10271057break
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`.
10311061break
10321062default :
@@ -1035,7 +1065,7 @@ extension TaskHandler: ChannelDuplexHandler{
10351065}
10361066default :
10371067switch self . state {
1038- case . idle, . bodySent , . sent , . head , . redirected , . body :
1068+ case . idle, . sendingBodyWaitingResponseHead , . sendingBodyResponseHeadReceived , . bodySentWaitingResponseHead , . bodySentResponseHeadReceived , . redirected :
10391069self . state = . endOrError
10401070self . failTaskAndNotifyDelegate ( error: error, self . delegate. didReceiveError)
10411071case . endOrError:
0 commit comments