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 cf1906aeb..972629ad9 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.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 @@ * @author Sam Kruglov * @author Wojciech Mąka * @author Dangzhicairang(小水牛) + * @author changjin wei(魏昌进) */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Feign.class) @@ -259,11 +261,13 @@ public okhttp3.OkHttpClient okHttpClient(okhttp3.OkHttpClient.Builder builder, C 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 88ac5682d..63c9b9aa4 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 static class OkHttp { */ 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 void setReadTimeout(Duration readTimeout) { 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 a50f538c1..ec55790b6 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 @@ -21,6 +21,7 @@ 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 +35,7 @@ /** * @author Ryan Baxter + * @author changjin wei(魏昌进) */ class FeignOkHttpConfigurationTests { @@ -45,7 +47,8 @@ void setUp() { .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 +74,13 @@ void shouldConfigureReadTimeout() { assertThat(httpClient.readTimeoutMillis()).isEqualTo(9000); } + @Test + void shouldResolveProtocolFromProperties() { + OkHttpClient httpClient = context.getBean(OkHttpClient.class); + + assertThat(httpClient.protocols()).containsExactly(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/FeignOkProtocolsTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkProtocolsTests.java new file mode 100644 index 000000000..951fd062e --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkProtocolsTests.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 java.lang.reflect.Field; + +import feign.Client; +import okhttp3.Protocol; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; +import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; +import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author changjin wei(魏昌进) + */ +@SpringBootTest(classes = FeignOkProtocolsTests.Application.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; + + @Test + void shouldCreateCorrectFeignClientBeanWithProtocolFromProperties() { + assertThat(feignClient).isInstanceOf(FeignBlockingLoadBalancerClient.class); + FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) feignClient; + Client delegate = client.getDelegate(); + assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class); + okhttp3.OkHttpClient OkHttpClient = (okhttp3.OkHttpClient) getField(delegate, "delegate"); + assertThat(OkHttpClient.protocols()).containsExactly(Protocol.H2_PRIOR_KNOWLEDGE); + } + + protected Object getField(Object target, String name) { + Field field = ReflectionUtils.findField(target.getClass(), name); + ReflectionUtils.makeAccessible(field); + return ReflectionUtils.getField(field, target); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @RestController + @LoadBalancerClients + @Import(NoSecurityConfiguration.class) + protected static class Application { + + } + +}