@@ -634,6 +634,9 @@ extension HTTPClient {
634
634
self . promise. fail ( error)
635
635
connection. channel. close ( promise: nil )
636
636
}
637
+ } else {
638
+ // this is used in tests where we don't want to bootstrap the whole connection pool
639
+ self . promise. fail ( error)
637
640
}
638
641
}
639
642
@@ -665,11 +668,11 @@ internal struct TaskCancelEvent {}
665
668
internal class TaskHandler < Delegate: HTTPClientResponseDelegate > : RemovableChannelHandler {
666
669
enum State {
667
670
case idle
668
- case bodySent
669
- case sent
670
- case head
671
+ case sendingBodyWaitingResponseHead
672
+ case sendingBodyResponseHeadReceived
673
+ case bodySentWaitingResponseHead
674
+ case bodySentResponseHeadReceived
671
675
case redirected( HTTPResponseHead , URL )
672
- case body
673
676
case endOrError
674
677
}
675
678
@@ -794,7 +797,8 @@ extension TaskHandler: ChannelDuplexHandler {
794
797
typealias OutboundOut = HTTPClientRequestPart
795
798
796
799
func write( context: ChannelHandlerContext , data: NIOAny , promise: EventLoopPromise < Void > ? ) {
797
- self . state = . idle
800
+ self . state = . sendingBodyWaitingResponseHead
801
+
798
802
let request = self . unwrapOutboundIn ( data)
799
803
800
804
var head = HTTPRequestHead ( version: HTTPVersion ( major: 1 , minor: 1 ) ,
@@ -840,23 +844,37 @@ extension TaskHandler: ChannelDuplexHandler {
840
844
self . writeBody ( request: request, context: context)
841
845
} . flatMap {
842
846
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.
844
863
return context. eventLoop. makeSucceededFuture ( ( ) )
845
864
}
846
865
847
- self . state = . bodySent
848
866
if let expectedBodyLength = self . expectedBodyLength, expectedBodyLength != self . actualBodyLength {
849
867
let error = HTTPClientError . bodyLengthMismatch
850
868
return context. eventLoop. makeFailedFuture ( error)
851
869
}
852
870
return context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) )
853
871
} . map {
854
872
context. eventLoop. assertInEventLoop ( )
873
+
855
874
if case . endOrError = self . state {
856
875
return
857
876
}
858
877
859
- self . state = . sent
860
878
self . callOutToDelegateFireAndForget ( self . delegate. didSendRequest)
861
879
} . flatMapErrorThrowing { error in
862
880
context. eventLoop. assertInEventLoop ( )
@@ -903,6 +921,9 @@ extension TaskHandler: ChannelDuplexHandler {
903
921
private func writeBodyPart( context: ChannelHandlerContext , part: IOData , promise: EventLoopPromise < Void > ) {
904
922
switch self . state {
905
923
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:
906
927
if let limit = self . expectedBodyLength, self . actualBodyLength + part. readableBytes > limit {
907
928
let error = HTTPClientError . bodyLengthMismatch
908
929
self . errorCaught ( context: context, error: error)
@@ -911,7 +932,7 @@ extension TaskHandler: ChannelDuplexHandler {
911
932
}
912
933
self . actualBodyLength += part. readableBytes
913
934
context. writeAndFlush ( self . wrapOutboundOut ( . body( part) ) , promise: promise)
914
- default :
935
+ case . bodySentWaitingResponseHead , . bodySentResponseHeadReceived , . endOrError :
915
936
let error = HTTPClientError . writeAfterRequestSent
916
937
self . errorCaught ( context: context, error: error)
917
938
promise. fail ( error)
@@ -931,7 +952,18 @@ extension TaskHandler: ChannelDuplexHandler {
931
952
let response = self . unwrapInboundIn ( data)
932
953
switch response {
933
954
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:
935
967
return
936
968
}
937
969
@@ -942,7 +974,6 @@ extension TaskHandler: ChannelDuplexHandler {
942
974
if let redirectURL = self . redirectHandler? . redirectTarget ( status: head. status, headers: head. headers) {
943
975
self . state = . redirected( head, redirectURL)
944
976
} else {
945
- self . state = . head
946
977
self . mayRead = false
947
978
self . callOutToDelegate ( value: head, channelEventLoop: context. eventLoop, self . delegate. didReceiveHead)
948
979
. whenComplete { result in
@@ -954,7 +985,6 @@ extension TaskHandler: ChannelDuplexHandler {
954
985
case . redirected, . endOrError:
955
986
break
956
987
default :
957
- self . state = . body
958
988
self . mayRead = false
959
989
self . callOutToDelegate ( value: body, channelEventLoop: context. eventLoop, self . delegate. didReceiveBodyPart)
960
990
. whenComplete { result in
@@ -1009,10 +1039,10 @@ extension TaskHandler: ChannelDuplexHandler {
1009
1039
1010
1040
func channelInactive( context: ChannelHandlerContext ) {
1011
1041
switch self . state {
1042
+ case . idle, . sendingBodyWaitingResponseHead, . sendingBodyResponseHeadReceived, . bodySentWaitingResponseHead, . bodySentResponseHeadReceived, . redirected:
1043
+ self . errorCaught ( context: context, error: HTTPClientError . remoteConnectionClosed)
1012
1044
case . endOrError:
1013
1045
break
1014
- case . body, . head, . idle, . redirected, . sent, . bodySent:
1015
- self . errorCaught ( context: context, error: HTTPClientError . remoteConnectionClosed)
1016
1046
}
1017
1047
context. fireChannelInactive ( )
1018
1048
}
@@ -1025,8 +1055,8 @@ extension TaskHandler: ChannelDuplexHandler {
1025
1055
/// Some HTTP Servers can 'forget' to respond with CloseNotify when client is closing connection,
1026
1056
/// this could lead to incomplete SSL shutdown. But since request is already processed, we can ignore this error.
1027
1057
break
1028
- case . head where self . ignoreUncleanSSLShutdown,
1029
- . body where self . ignoreUncleanSSLShutdown:
1058
+ case . sendingBodyResponseHeadReceived where self . ignoreUncleanSSLShutdown,
1059
+ . bodySentResponseHeadReceived where self . ignoreUncleanSSLShutdown:
1030
1060
/// We can also ignore this error like `.end`.
1031
1061
break
1032
1062
default :
@@ -1035,7 +1065,7 @@ extension TaskHandler: ChannelDuplexHandler {
1035
1065
}
1036
1066
default :
1037
1067
switch self . state {
1038
- case . idle, . bodySent , . sent , . head , . redirected , . body :
1068
+ case . idle, . sendingBodyWaitingResponseHead , . sendingBodyResponseHeadReceived , . bodySentWaitingResponseHead , . bodySentResponseHeadReceived , . redirected :
1039
1069
self . state = . endOrError
1040
1070
self . failTaskAndNotifyDelegate ( error: error, self . delegate. didReceiveError)
1041
1071
case . endOrError:
0 commit comments