diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index cf1906ae..972629ad 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -41,6 +41,7 @@ import feign.hc5.ApacheHttp5Client; import feign.okhttp.OkHttpClient; import jakarta.annotation.PreDestroy; import okhttp3.ConnectionPool; +import okhttp3.Protocol; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -92,6 +93,7 @@ import org.springframework.util.ClassUtils; * @author Sam Kruglov * @author Wojciech Mąka * @author Dangzhicairang(小水牛) + * @author changjin wei(魏昌进) */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Feign.class) @@ -259,11 +261,13 @@ public class FeignAutoConfiguration { int connectTimeout = httpClientProperties.getConnectionTimeout(); boolean disableSslValidation = httpClientProperties.isDisableSslValidation(); Duration readTimeout = httpClientProperties.getOkHttp().getReadTimeout(); + List protocols = httpClientProperties.getOkHttp().getProtocols(); if (disableSslValidation) { disableSsl(builder); } this.okHttpClient = builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) - .followRedirects(followRedirects).readTimeout(readTimeout).connectionPool(connectionPool).build(); + .followRedirects(followRedirects).readTimeout(readTimeout).connectionPool(connectionPool) + .protocols(protocols).build(); return this.okHttpClient; } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java index 88ac5682..63c9b9aa 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java @@ -17,9 +17,11 @@ package org.springframework.cloud.openfeign.support; import java.time.Duration; +import java.util.List; import java.util.concurrent.TimeUnit; import feign.okhttp.OkHttpClient; +import okhttp3.Protocol; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -336,6 +338,11 @@ public class FeignHttpClientProperties { */ private Duration readTimeout = Duration.ofSeconds(60); + /** + * Configure the protocols used by this client to communicate with remote servers. + */ + private List protocols = List.of(Protocol.HTTP_2, Protocol.HTTP_1_1); + public Duration getReadTimeout() { return readTimeout; } @@ -344,6 +351,14 @@ public class FeignHttpClientProperties { this.readTimeout = readTimeout; } + public List getProtocols() { + return protocols; + } + + public void setProtocols(List protocols) { + this.protocols = protocols; + } + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java index a50f538c..21ebb844 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java @@ -17,10 +17,12 @@ package org.springframework.cloud.openfeign; import java.lang.reflect.Field; +import java.util.List; import javax.net.ssl.HostnameVerifier; import okhttp3.OkHttpClient; +import okhttp3.Protocol; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter + * @author changjin wei(魏昌进) */ class FeignOkHttpConfigurationTests { @@ -45,7 +48,8 @@ class FeignOkHttpConfigurationTests { .properties("debug=true", "spring.cloud.openfeign.httpclient.disableSslValidation=true", "spring.cloud.openfeign.okhttp.enabled=true", "spring.cloud.openfeign.httpclient.hc5.enabled=false", - "spring.cloud.openfeign.httpclient.okhttp.read-timeout=9s") + "spring.cloud.openfeign.httpclient.okhttp.read-timeout=9s", + "spring.cloud.openfeign.httpclient.okhttp.protocols=H2_PRIOR_KNOWLEDGE") .web(WebApplicationType.NONE).sources(FeignAutoConfiguration.class).run(); } @@ -71,6 +75,13 @@ class FeignOkHttpConfigurationTests { assertThat(httpClient.readTimeoutMillis()).isEqualTo(9000); } + @Test + void shouldProtocols() { + OkHttpClient httpClient = context.getBean(OkHttpClient.class); + + assertThat(httpClient.protocols()).isEqualTo(List.of(Protocol.H2_PRIOR_KNOWLEDGE)); + } + protected Object getField(Object target, String name) { Field field = ReflectionUtils.findField(target.getClass(), name); ReflectionUtils.makeAccessible(field); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/Application.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/Application.java new file mode 100644 index 00000000..49fa84b3 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/Application.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.openfeign.protocol; + +import jakarta.servlet.http.HttpServletRequest; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author changjin wei(魏昌进) + */ +public class Application { + + @FeignClient("localapp1") + protected interface ProtocolClient { + + @GetMapping("/protocol") + String getProtocol(); + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @RestController + @EnableFeignClients(clients = { ProtocolClient.class }) + @LoadBalancerClients({ @LoadBalancerClient(name = "localapp1", configuration = LocalClientConfiguration.class) }) + @Import(NoSecurityConfiguration.class) + protected static class ProtocolController { + + @GetMapping("/protocol") + public String getProtocol(HttpServletRequest request) { + return request.getProtocol(); + } + + } + + @Configuration(proxyBeanMethods = false) + static class LocalClientConfiguration { + + @LocalServerPort + private int port = 0; + + @Bean + public ServiceInstanceListSupplier staticServiceInstanceListSupplier() { + return ServiceInstanceListSuppliers.from("local", + new DefaultServiceInstance("local-1", "local", "localhost", port, false)); + } + + } + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkProtocolsTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkProtocolsTests.java new file mode 100644 index 00000000..4848f087 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkProtocolsTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.openfeign.protocol; + +import feign.Client; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; +import org.springframework.test.annotation.DirtiesContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author changjin wei(魏昌进) + */ +@SpringBootTest(classes = Application.ProtocolController.class, webEnvironment = WebEnvironment.RANDOM_PORT, + value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.circuitbreaker.enabled=false", + "spring.cloud.openfeign.httpclient.hc5.enabled=false", "spring.cloud.openfeign.okhttp.enabled=true", + "spring.cloud.httpclientfactories.ok.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false", + "server.http2.enabled=true", "spring.cloud.openfeign.httpclient.okhttp.protocols=H2_PRIOR_KNOWLEDGE" }) +@DirtiesContext +class FeignOkProtocolsTests { + + @Autowired + private Client feignClient; + + @Autowired + private Application.ProtocolClient protocolClient; + + @Test + void testFeignClientType() { + assertThat(feignClient).isInstanceOf(FeignBlockingLoadBalancerClient.class); + FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) feignClient; + Client delegate = client.getDelegate(); + assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class); + } + + @Test + void shouldHttp2() { + String protocol = protocolClient.getProtocol(); + assertThat(protocol).isEqualTo("HTTP/2.0"); + } + +}