@@ -197,13 +197,15 @@ void testConstructorWithProvidedReplacement_1() {
197197 ops1 .retrying = true ;
198198 ops1 .redirecting = new RedirectClientException (new DefaultHttpHeaders ().add (HttpHeaderNames .LOCATION , "/" ),
199199 HttpResponseStatus .MOVED_PERMANENTLY );
200+ ops1 .authenticating = new HttpClientAuthenticationException ();
200201
201202 HttpClientOperations ops2 = new HttpClientOperations (ops1 );
202203
203204 assertThat (ops1 .channel ()).isSameAs (ops2 .channel ());
204205 assertThat (ops1 .started ).isSameAs (ops2 .started );
205206 assertThat (ops1 .retrying ).isSameAs (ops2 .retrying );
206207 assertThat (ops1 .redirecting ).isSameAs (ops2 .redirecting );
208+ assertThat (ops1 .authenticating ).isSameAs (ops2 .authenticating );
207209 assertThat (ops1 .redirectedFrom ).isSameAs (ops2 .redirectedFrom );
208210 assertThat (ops1 .isSecure ).isSameAs (ops2 .isSecure );
209211 assertThat (ops1 .nettyRequest ).isSameAs (ops2 .nettyRequest );
@@ -535,6 +537,7 @@ private static void checkRequest(HttpClientRequest request, HttpClientResponse r
535537 assertThat (req .isSecure ).isSameAs (res .isSecure );
536538 assertThat (req .nettyRequest ).isSameAs (res .nettyRequest );
537539 assertThat (req .followRedirectPredicate ).isSameAs (res .followRedirectPredicate );
540+ assertThat (req .authenticationPredicate ).isSameAs (res .authenticationPredicate );
538541 assertThat (req .requestHeaders ).isSameAs (res .requestHeaders );
539542 assertThat (req .cookieEncoder ).isSameAs (res .cookieEncoder );
540543 assertThat (req .cookieDecoder ).isSameAs (res .cookieDecoder );
@@ -555,6 +558,7 @@ private static void checkRequest(HttpClientRequest request, HttpClientResponse r
555558 else {
556559 assertThat (req .redirecting ).isSameAs (res .redirecting );
557560 }
561+ assertThat (req .authenticating ).isSameAs (res .authenticating );
558562 assertThat (req .responseState ).isNotSameAs (res .responseState );
559563 assertThat (req .version ).isNotSameAs (res .version );
560564 }
@@ -563,11 +567,82 @@ private static void checkRequest(HttpClientRequest request, HttpClientResponse r
563567 assertThat (req .asShortText ()).isSameAs (res .asShortText ());
564568 assertThat (req .started ).isSameAs (res .started );
565569 assertThat (req .redirecting ).isSameAs (res .redirecting );
570+ assertThat (req .authenticating ).isSameAs (res .authenticating );
566571 assertThat (req .responseState ).isSameAs (res .responseState );
567572 assertThat (req .version ).isSameAs (res .version );
568573 }
569574 }
570575
576+ @ ParameterizedTest
577+ @ MethodSource ("httpCompatibleProtocols" )
578+ void testConstructorWithProvidedAuthentication (HttpProtocol [] serverProtocols , HttpProtocol [] clientProtocols ,
579+ SslProvider .@ Nullable ProtocolSslContextSpec serverCtx , SslProvider .@ Nullable ProtocolSslContextSpec clientCtx ) throws Exception {
580+ ConnectionProvider provider = ConnectionProvider .create ("testConstructorWithProvidedReplacement_4" , 1 );
581+ try {
582+ HttpServer server = serverCtx == null ?
583+ createServer ().protocol (serverProtocols ) :
584+ createServer ().protocol (serverProtocols ).secure (spec -> spec .sslContext (serverCtx ));
585+
586+ disposableServer =
587+ server .route (r -> r .get ("/protected" , (req , res ) -> {
588+ String authHeader = req .requestHeaders ().get (HttpHeaderNames .AUTHORIZATION );
589+ if (authHeader == null || !authHeader .equals ("Bearer test-token" )) {
590+ return res .status (HttpResponseStatus .UNAUTHORIZED ).send ();
591+ }
592+ return res .sendString (Mono .just ("testConstructorWithProvidedReplacement_4" ));
593+ }))
594+ .bindNow ();
595+
596+ HttpClient client = clientCtx == null ?
597+ createClient (disposableServer .port ()).protocol (clientProtocols ) :
598+ createClient (disposableServer .port ()).protocol (clientProtocols ).secure (spec -> spec .sslContext (clientCtx ));
599+
600+ AtomicReference <@ Nullable HttpClientRequest > request = new AtomicReference <>();
601+ AtomicReference <@ Nullable HttpClientResponse > response = new AtomicReference <>();
602+ AtomicReference <@ Nullable Channel > requestChannel = new AtomicReference <>();
603+ AtomicReference <@ Nullable Channel > responseChannel = new AtomicReference <>();
604+ AtomicReference <@ Nullable ConnectionObserver > requestListener = new AtomicReference <>();
605+ AtomicReference <@ Nullable ConnectionObserver > responseListener = new AtomicReference <>();
606+ String result = httpAuthentication (client , request , response , requestChannel , responseChannel ,
607+ requestListener , responseListener );
608+ assertThat (result ).isNotNull ().isEqualTo ("testConstructorWithProvidedReplacement_4" );
609+ assertThat (requestListener .get ()).isSameAs (responseListener .get ());
610+ checkRequest (request .get (), response .get (), requestChannel .get (), responseChannel .get (), false , false );
611+ }
612+ finally {
613+ provider .disposeLater ()
614+ .block (Duration .ofSeconds (5 ));
615+ }
616+ }
617+
618+ private static String httpAuthentication (HttpClient originalClient , AtomicReference <@ Nullable HttpClientRequest > request ,
619+ AtomicReference <@ Nullable HttpClientResponse > response , AtomicReference <@ Nullable Channel > requestChannel ,
620+ AtomicReference <@ Nullable Channel > responseChannel , AtomicReference <@ Nullable ConnectionObserver > requestListener ,
621+ AtomicReference <@ Nullable ConnectionObserver > responseListener ) {
622+ HttpClient client = originalClient .httpAuthentication ((req , addr ) -> {
623+ req .header (HttpHeaderNames .AUTHORIZATION , "Bearer test-token" );
624+ return Mono .empty ();
625+ });
626+ return client .doAfterRequest ((req , conn ) -> {
627+ if (request .get () == null ) {
628+ requestChannel .set (conn .channel ());
629+ requestListener .set (((HttpClientOperations ) req ).listener ());
630+ request .set (req );
631+ }
632+ })
633+ .doAfterResponseSuccess ((res , conn ) -> {
634+ if (response .get () == null ) {
635+ responseChannel .set (conn .channel ());
636+ responseListener .set (((HttpClientOperations ) res ).listener ());
637+ response .set (res );
638+ }
639+ })
640+ .get ()
641+ .uri ("/protected" )
642+ .responseSingle ((res , bytes ) -> bytes .asString ())
643+ .block (Duration .ofSeconds (5 ));
644+ }
645+
571646 static Stream <Arguments > httpCompatibleProtocols () {
572647 return Stream .of (
573648 Arguments .of (new HttpProtocol []{HttpProtocol .HTTP11 }, new HttpProtocol []{HttpProtocol .HTTP11 }, null , null ),
0 commit comments