3535import java .util .ArrayList ;
3636import java .util .List ;
3737import java .util .Objects ;
38+ import java .util .Set ;
3839import java .util .concurrent .atomic .AtomicInteger ;
3940import java .util .function .Function ;
4041import java .util .regex .Pattern ;
@@ -100,7 +101,7 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal
100101
101102 this .tcpTransport = tcpTransport ;
102103 this .quicTransport = quicTransport ;
103- this .originEndpoints = new OriginResolver <>(vertx , resolveAll );
104+ this .originEndpoints = new OriginResolver <>(vertx , resolveAll , this );
104105 this .resolver = (EndpointResolverInternal ) resolver ;
105106 this .originResolver = new EndpointResolverImpl <>(vertx , originEndpoints , resolveAll ? loadBalancer : LoadBalancer .FIRST , resolverKeepAlive .toMillis ());
106107 this .poolOptions = poolOptions ;
@@ -187,9 +188,10 @@ private Function<EndpointKey, SharedHttpClientConnectionGroup> httpEndpointProvi
187188 ClientMetrics clientMetrics = HttpClientImpl .this .httpMetrics != null ? HttpClientImpl .this .httpMetrics .createEndpointMetrics (address , maxPoolSize ) : null ;
188189 PoolMetrics poolMetrics = HttpClientImpl .this .httpMetrics != null ? vertx .metrics ().createPoolMetrics ("http" , key .authority .toString (), maxPoolSize ) : null ;
189190 ProxyOptions proxyOptions = key .proxyOptions ;
191+ ClientSSLOptions sslOptions = key .sslOptions ;
190192 if (proxyOptions != null && !key .ssl && proxyOptions .getType () == ProxyType .HTTP ) {
191193 SocketAddress server = SocketAddress .inetSocketAddress (proxyOptions .getPort (), proxyOptions .getHost ());
192- key = new EndpointKey (key .ssl , key .protocol , key . sslOptions , proxyOptions , server , key .authority );
194+ key = new EndpointKey (key .ssl , key .protocol , sslOptions , proxyOptions , server , key .authority );
193195 proxyOptions = null ;
194196 }
195197 HttpVersion protocol = key .protocol ;
@@ -199,7 +201,7 @@ private Function<EndpointKey, SharedHttpClientConnectionGroup> httpEndpointProvi
199201 } else {
200202 protocols = List .of (protocol );
201203 }
202- HttpConnectParams params = new HttpConnectParams (protocols , key . sslOptions , proxyOptions , key .ssl );
204+ HttpConnectParams params = new HttpConnectParams (protocols , sslOptions , proxyOptions , key .ssl );
203205 Function <SharedHttpClientConnectionGroup , SharedHttpClientConnectionGroup .Pool > p = group -> {
204206 int queueMaxSize = poolOptions .getMaxWaitQueueSize ();
205207 int http1MaxSize = poolOptions .getHttp1MaxSize ();
@@ -220,7 +222,7 @@ private Function<EndpointKey, SharedHttpClientConnectionGroup> httpEndpointProvi
220222 if (altSvc instanceof AltSvc .Clear ) {
221223 originEndpoints .clearAlternatives (evt .origin );
222224 } else if (altSvc instanceof AltSvc .ListOfValue ) {
223- originEndpoints .updateAlternatives (evt .origin , (AltSvc .ListOfValue )altSvc );
225+ originEndpoints .updateAlternatives (sslOptions , evt .origin , (AltSvc .ListOfValue )altSvc );
224226 }
225227 });
226228 }
@@ -593,17 +595,49 @@ private Future<HttpClientRequest> doRequest(
593595 // For HTTPS we must handle SNI to consider an alternative
594596 HostAndPort altUsed ;
595597 if (followAlternativeServices && server instanceof Origin && ("https" .equals ((originServer = (Origin )server ).scheme ) && originServer .host .indexOf ('.' ) > 0 )) {
596- lookup = endpoint .selectServer (s -> {
597- OriginServer unwrap = (OriginServer ) s .unwrap ();
598- return protocol_ == unwrap .protocol ;
599- });
600- protocol = protocol_ ;
598+ if (protocol_ != null ) {
599+ ProtocolFilter filter ;
600+ switch (protocol_ ) {
601+ case H3 :
602+ filter = ProtocolFilter .H3 ;
603+ break ;
604+ case H2 :
605+ filter = ProtocolFilter .H2 ;
606+ break ;
607+ case HTTP_1_1 :
608+ filter = ProtocolFilter .HTTP_1_1 ;
609+ break ;
610+ case HTTP_1_0 :
611+ filter = ProtocolFilter .HTTP_1_0 ;
612+ break ;
613+ default :
614+ throw new AssertionError ();
615+ }
616+ lookup = endpoint .selectServer (filter );
617+ protocol = protocol_ ;
618+ } else {
619+ Set <String > protocols = endpoint .protocols ();
620+ if (!protocols .isEmpty ()) {
621+ List <ProtocolFilter > list = List .of (ProtocolFilter .H3 , ProtocolFilter .H2 , ProtocolFilter .HTTP_1_1 , ProtocolFilter .HTTP_1_0 );
622+ lookup = null ;
623+ protocol = null ;
624+ for (ProtocolFilter candidate : list ) {
625+ if (protocols .contains (candidate .protocol .id ())) {
626+ lookup = endpoint .selectServer (candidate );
627+ protocol = candidate .protocol ;
628+ }
629+ }
630+ } else {
631+ lookup = null ;
632+ protocol = null ;
633+ }
634+ }
601635 if (lookup == null ) {
602636 altUsed = null ;
603637 lookup = endpoint .selectServer ();
604638 } else {
605639 OriginServer unwrap = (OriginServer ) lookup .unwrap ();
606- altUsed = unwrap .authority ;
640+ altUsed = unwrap .primary ? null : unwrap . authority ;
607641 }
608642 } else {
609643 protocol = protocol_ ;
@@ -615,23 +649,32 @@ private Future<HttpClientRequest> doRequest(
615649 throw new IllegalStateException ("No results for " + server );
616650 }
617651 SocketAddress address = lookup2 .address ();
618- EndpointKey key = new EndpointKey (useSSL , protocol != null ? protocol .version () : null , sslOptions , null , address , authority != null ? authority : HostAndPort .create (address .host (), address .port ()));
619- return resourceManager .withResourceAsync (key , httpEndpointProvider (followAlternativeServices && useSSL , transport ), (e , created ) -> {
620- Future <Lease <HttpClientConnection >> fut2 = e .requestConnection (streamCtx , connectTimeout );
621- ServerInteraction endpointRequest = lookup2 .newInteraction ();
622- return fut2 .andThen (ar -> {
623- if (ar .failed ()) {
624- endpointRequest .reportFailure (ar .cause ());
652+ return getPool (
653+ followAlternativeServices && useSSL && altUsed == null ,
654+ useSSL ,
655+ protocol ,
656+ sslOptions ,
657+ address ,
658+ authority != null ? authority : HostAndPort .create (address .host (), address .port ()),
659+ new Function <SharedHttpClientConnectionGroup , Future <ConnectionObtainedResult >>() {
660+ @ Override
661+ public Future <ConnectionObtainedResult > apply (SharedHttpClientConnectionGroup pool ) {
662+ Future <Lease <HttpClientConnection >> fut2 = pool .requestConnection (streamCtx , connectTimeout );
663+ ServerInteraction endpointRequest = lookup2 .newInteraction ();
664+ return fut2 .andThen (ar -> {
665+ if (ar .failed ()) {
666+ endpointRequest .reportFailure (ar .cause ());
667+ }
668+ }).compose (lease -> {
669+ HttpClientConnection conn = lease .get ();
670+ return conn .createStream (streamCtx ).map (stream -> {
671+ HttpClientStream wrapped = new StatisticsGatheringHttpClientStream (stream , endpointRequest );
672+ wrapped .closeHandler (v -> lease .recycle ());
673+ return new ConnectionObtainedResult (wrapped , lease , altUsed );
674+ });
675+ });
625676 }
626- }).compose (lease -> {
627- HttpClientConnection conn = lease .get ();
628- return conn .createStream (streamCtx ).map (stream -> {
629- HttpClientStream wrapped = new StatisticsGatheringHttpClientStream (stream , endpointRequest );
630- wrapped .closeHandler (v -> lease .recycle ());
631- return new ConnectionObtainedResult (wrapped , lease , altUsed );
632- });
633677 });
634- });
635678 });
636679 if (future == null ) {
637680 // I think this is not possible - so remove it
@@ -641,6 +684,42 @@ private Future<HttpClientRequest> doRequest(
641684 }
642685 }
643686
687+ Future <?> checkConnect (OriginServer primary , OriginAlternative alternative , OriginServer server , ClientSSLOptions sslOptions ) {
688+ return getPool (false , true , alternative .protocol , sslOptions , server .address , primary .authority , new Function <SharedHttpClientConnectionGroup , Future <Boolean >>() {
689+ @ Override
690+ public Future <Boolean > apply (SharedHttpClientConnectionGroup group ) {
691+ if (group .size () > 0 ) {
692+ return Future .succeededFuture ();
693+ } else {
694+ // Get something better
695+ Future <Lease <HttpClientConnection >> f = group .requestConnection (vertx .getOrCreateContext (), 10_000 );
696+ return f .map (lease -> {
697+ lease .recycle ();
698+ return null ;
699+ });
700+ }
701+ }
702+ });
703+ }
704+
705+ <T > Future <T > getPool (boolean resolveOrigin ,
706+ boolean useSSL ,
707+ HttpProtocol protocol ,
708+ ClientSSLOptions sslOptions ,
709+ SocketAddress server ,
710+ HostAndPort authority ,
711+ Function <SharedHttpClientConnectionGroup , Future <T >> function ) {
712+ EndpointKey key = new EndpointKey (useSSL , protocol != null ? protocol .version () : null , sslOptions , null , server , authority );
713+ HttpClientTransport transport ;
714+ if (protocol != null && protocol .version () == HttpVersion .HTTP_3 ) {
715+ transport = quicTransport ;
716+ } else {
717+ transport = tcpTransport ;
718+ }
719+ Function <EndpointKey , SharedHttpClientConnectionGroup > provider = httpEndpointProvider (resolveOrigin , transport );
720+ return resourceManager .withResourceAsync (key , provider , (group , created ) -> function .apply (group ));
721+ }
722+
644723 private Future <HttpClientRequest > wrap (HttpMethod method ,
645724 String requestURI ,
646725 MultiMap headers ,
0 commit comments