diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java index 917c0a54..efa084f1 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java @@ -1,6 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. - * + * Copyright 2013-2025 * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -34,6 +33,7 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration; import org.springframework.cloud.gateway.server.mvc.filter.FormFilter; @@ -47,6 +47,7 @@ import org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilte import org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilter; import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoConfiguration; import org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.context.ConfigurableApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -76,7 +77,7 @@ public class GatewayServerMvcAutoConfigurationTests { fl1PiUDyPRIl2cnOJd+wCHKsyym/FL7yzk0OSEZ81I92LpGd/0b2Ld3m/bpe+C4Z ILzLXTnC6AhrLcDc9QN/EO+BiCL52n7EplNLtSn1LQ== -----END CERTIFICATE----- - """; + """; private static final String key = """ -----BEGIN PRIVATE KEY----- @@ -112,44 +113,44 @@ public class GatewayServerMvcAutoConfigurationTests { @Test void filterEnabledPropertiesWork() { new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, - HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, - HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, - RestClientAutoConfiguration.class, SslAutoConfiguration.class)) - .withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false", - "spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-http2-status-response-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.weight-calculator-filter.enabled=false", - "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled=false") - .run(context -> { - assertThat(context).doesNotHaveBean(FormFilter.class); - assertThat(context).doesNotHaveBean(ForwardedRequestHeadersFilter.class); - assertThat(context).doesNotHaveBean(RemoveContentLengthRequestHeadersFilter.class); - assertThat(context).doesNotHaveBean(RemoveHopByHopRequestHeadersFilter.class); - assertThat(context).doesNotHaveBean(RemoveHopByHopResponseHeadersFilter.class); - assertThat(context).doesNotHaveBean(RemoveHttp2StatusResponseHeadersFilter.class); - assertThat(context).doesNotHaveBean(TransferEncodingNormalizationRequestHeadersFilter.class); - assertThat(context).doesNotHaveBean(WeightCalculatorFilter.class); - assertThat(context).doesNotHaveBean(XForwardedRequestHeadersFilter.class); - }); + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, + HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, + RestClientAutoConfiguration.class, SslAutoConfiguration.class)) + .withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false", + "spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled=false", + "spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false", + "spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled=false", + "spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled=false", + "spring.cloud.gateway.mvc.remove-http2-status-response-headers-filter.enabled=false", + "spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled=false", + "spring.cloud.gateway.mvc.weight-calculator-filter.enabled=false", + "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled=false") + .run(context -> { + assertThat(context).doesNotHaveBean(FormFilter.class); + assertThat(context).doesNotHaveBean(ForwardedRequestHeadersFilter.class); + assertThat(context).doesNotHaveBean(RemoveContentLengthRequestHeadersFilter.class); + assertThat(context).doesNotHaveBean(RemoveHopByHopRequestHeadersFilter.class); + assertThat(context).doesNotHaveBean(RemoveHopByHopResponseHeadersFilter.class); + assertThat(context).doesNotHaveBean(RemoveHttp2StatusResponseHeadersFilter.class); + assertThat(context).doesNotHaveBean(TransferEncodingNormalizationRequestHeadersFilter.class); + assertThat(context).doesNotHaveBean(WeightCalculatorFilter.class); + assertThat(context).doesNotHaveBean(XForwardedRequestHeadersFilter.class); + }); } @DisabledForJreRange(min = JRE.JAVA_23) @Test void gatewayHttpClientPropertiesWork() { ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfig.class) - .properties("spring.main.web-application-type=none", - "spring.cloud.gateway.mvc.http-client.connect-timeout=1s", - "spring.cloud.gateway.mvc.http-client.read-timeout=2s", - "spring.cloud.gateway.mvc.http-client.ssl-bundle=mybundle", - "spring.cloud.gateway.mvc.http-client.type=autodetect", - "spring.ssl.bundle.pem.mybundle.keystore.certificate=" + cert, - "spring.ssl.bundle.pem.mybundle.keystore.key=" + key) - .run(); + .properties("spring.main.web-application-type=none", + "spring.cloud.gateway.mvc.http-client.connect-timeout=1s", + "spring.cloud.gateway.mvc.http-client.read-timeout=2s", + "spring.cloud.gateway.mvc.http-client.ssl-bundle=mybundle", + "spring.cloud.gateway.mvc.http-client.type=autodetect", + "spring.ssl.bundle.pem.mybundle.keystore.certificate=" + cert, + "spring.ssl.bundle.pem.mybundle.keystore.key=" + key) + .run(); ClientHttpRequestFactorySettings settings = context.getBean(ClientHttpRequestFactorySettings.class); HttpClientProperties properties = context.getBean(HttpClientProperties.class); assertThat(properties.getConnectTimeout()).hasSeconds(1); @@ -165,39 +166,60 @@ public class GatewayServerMvcAutoConfigurationTests { @Test void bootHttpClientPropertiesWork() { new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, - HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, - HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, - RestClientAutoConfiguration.class, SslAutoConfiguration.class)) - .withPropertyValues("spring.http.client.connect-timeout=1s", "spring.http.client.read-timeout=2s", - "spring.http.client.ssl.bundle=mybundle", - "spring.ssl.bundle.pem.mybundle.keystore.certificate=" + cert, - "spring.ssl.bundle.pem.mybundle.keystore.key=" + key) - .run(context -> { - assertThat(context).hasSingleBean(ClientHttpRequestFactorySettings.class) - .hasSingleBean(HttpClientProperties.class); - HttpClientProperties httpClient = context.getBean(HttpClientProperties.class); - assertThat(httpClient.getConnectTimeout()).hasSeconds(1); - assertThat(httpClient.getReadTimeout()).hasSeconds(2); - assertThat(httpClient.getSsl().getBundle()).isEqualTo("mybundle"); - assertThat(httpClient.getFactory()).isNull(); - ClientHttpRequestFactorySettings settings = context.getBean(ClientHttpRequestFactorySettings.class); - assertThat(settings.readTimeout()).isEqualTo(Duration.ofSeconds(2)); - assertThat(settings.connectTimeout()).isEqualTo(Duration.ofSeconds(1)); - assertThat(settings.sslBundle()).isNotNull(); - // cant test redirects because EnvironmentPostProcessor is not run - }); + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, + HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, + RestClientAutoConfiguration.class, SslAutoConfiguration.class)) + .withPropertyValues("spring.http.client.connect-timeout=1s", "spring.http.client.read-timeout=2s", + "spring.http.client.ssl.bundle=mybundle", + "spring.ssl.bundle.pem.mybundle.keystore.certificate=" + cert, + "spring.ssl.bundle.pem.mybundle.keystore.key=" + key) + .run(context -> { + assertThat(context).hasSingleBean(ClientHttpRequestFactorySettings.class) + .hasSingleBean(HttpClientProperties.class); + HttpClientProperties httpClient = context.getBean(HttpClientProperties.class); + assertThat(httpClient.getConnectTimeout()).hasSeconds(1); + assertThat(httpClient.getReadTimeout()).hasSeconds(2); + assertThat(httpClient.getSsl().getBundle()).isEqualTo("mybundle"); + assertThat(httpClient.getFactory()).isNull(); + ClientHttpRequestFactorySettings settings = context.getBean(ClientHttpRequestFactorySettings.class); + assertThat(settings.readTimeout()).isEqualTo(Duration.ofSeconds(2)); + assertThat(settings.connectTimeout()).isEqualTo(Duration.ofSeconds(1)); + assertThat(settings.sslBundle()).isNotNull(); + // cant test redirects because EnvironmentPostProcessor is not run + }); } @Test void settingHttpClientFactoryWorks() { ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfig.class) - .properties("spring.main.web-application-type=none", "spring.http.client.factory=simple") - .run(); + .properties("spring.main.web-application-type=none", "spring.http.client.factory=simple") + .run(); ClientHttpRequestFactoryBuilder builder = context.getBean(ClientHttpRequestFactoryBuilder.class); assertThat(builder).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class); } + @Test + void loadBalancerFunctionHandlerAdded() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, + HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, + RestClientAutoConfiguration.class)) + .run(context -> assertThat(context).hasBean("lbHandlerFunctionDefinition")); + } + + @Test + void loadBalancerFunctionHandlerNotAddedWhenNoLoadBalancerClientOnClasspath() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, + HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, + RestClientAutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(LoadBalancerClient.class)) + .run(context -> assertThat(context).doesNotHaveBean("lbHandlerFunctionDefinition")); + } + @SpringBootConfiguration @EnableAutoConfiguration static class TestConfig {