From 2193263aac413c3a5bc5a9ee6ea28e0e6e4ec353 Mon Sep 17 00:00:00 2001 From: Andre Teigler Date: Thu, 12 Sep 2024 22:01:13 +0200 Subject: [PATCH] Make Content-Encoding configurable A new configuration property was added to make the Content-Encoding configurable when using the feign request compression. The default values are the same as the previous hardcoded values. Closes issue 1048 --- .../FeignClientEncodingProperties.java | 18 +- .../FeignContentGzipEncodingInterceptor.java | 9 +- ...ignContentGzipEncodingInterceptorTest.java | 198 ------------------ ...gnContentGzipEncodingInterceptorTests.java | 181 ++++++++++++++++ 4 files changed, 197 insertions(+), 209 deletions(-) delete mode 100644 spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTest.java create mode 100644 spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTests.java diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java index 98405d0c8..865a0294c 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -43,7 +43,7 @@ public class FeignClientEncodingProperties { /** * The list of content encodings (applicable encodings depend on the used client). */ - private String[] contentEncodings = new String[] { HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING }; + private String[] contentEncodingTypes = new String[] { HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING }; public String[] getMimeTypes() { return mimeTypes; @@ -61,12 +61,12 @@ public void setMinRequestSize(int minRequestSize) { this.minRequestSize = minRequestSize; } - public String[] getContentEncodings() { - return contentEncodings; + public String[] getContentEncodingTypes() { + return contentEncodingTypes; } - public void setContentEncodings(String[] contentEncodings) { - this.contentEncodings = contentEncodings; + public void setContentEncodingTypes(String[] contentEncodingTypes) { + this.contentEncodingTypes = contentEncodingTypes; } @Override @@ -79,7 +79,7 @@ public boolean equals(Object o) { } FeignClientEncodingProperties that = (FeignClientEncodingProperties) o; return Arrays.equals(mimeTypes, that.mimeTypes) && Objects.equals(minRequestSize, that.minRequestSize) - && Arrays.equals(contentEncodings, that.contentEncodings); + && Arrays.equals(contentEncodingTypes, that.contentEncodingTypes); } @Override @@ -95,8 +95,8 @@ public String toString() { .append("minRequestSize=") .append(minRequestSize) .append(", ") - .append("contentEncodings=") - .append(Arrays.toString(contentEncodings)) + .append("contentEncodingTypes=") + .append(Arrays.toString(contentEncodingTypes)) .append("}") .toString(); } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java index eb8277419..1e27486dd 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java @@ -20,6 +20,8 @@ import java.util.Map; import feign.RequestTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Enables the HTTP request payload compression by specifying the {@code Content-Encoding} @@ -29,6 +31,8 @@ */ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor { + private static final Log log = LogFactory.getLog(FeignContentGzipEncodingInterceptor.class); + /** * Creates new instance of {@link FeignContentGzipEncodingInterceptor}. * @param properties the encoding properties @@ -49,10 +53,11 @@ public void apply(RequestTemplate template) { } private String[] getContentEncodings() { - if (getProperties().getContentEncodings() != null && getProperties().getContentEncodings().length > 0) { - return getProperties().getContentEncodings(); + if (getProperties().getContentEncodingTypes() != null && getProperties().getContentEncodingTypes().length > 0) { + return getProperties().getContentEncodingTypes(); } + log.warn("Invalid content encoding configuration, falling back to default."); return new String[] { HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING }; } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTest.java deleted file mode 100644 index cbc7438f0..000000000 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTest.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2013-2022 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.encoding; - -import java.util.List; - -import feign.RequestTemplate; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for {@link FeignContentGzipEncodingInterceptor} - * - * @author André Teigler - */ -class FeignContentGzipEncodingInterceptorTest { - - @Test - void apply_SizeBelowThreshold_NoCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2047"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isFalse(); - } - - @Test - void apply_SizeEqualsThreshold_NoCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2048"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isFalse(); - } - - @Test - void apply_SizeAboveThreshold_DefaultCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isTrue(); - assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); - } - - @Test - void apply_MimeTypeMismatch_NoCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - properties.setMimeTypes(new String[] { "application/xml" }); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isFalse(); - } - - @Test - void apply_NoMimeTypeSet_DefaultCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - properties.setMimeTypes(new String[] {}); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isTrue(); - assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); - } - - @Test - void apply_NullMimeTypeSet_DefaultCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - properties.setMimeTypes(null); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isTrue(); - assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); - } - - @Test - void apply_NonDefaultContentEncodingSet_CustomCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - properties.setContentEncodings(new String[] { "gzip" }); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isTrue(); - assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip")); - } - - @Test - void apply_NoContentEncodingsSet_DefaultCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - properties.setContentEncodings(new String[] {}); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isTrue(); - assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); - } - - @Test - void apply_NullContentEncodingsSet_DefaultCompressionHeader() { - - final var properties = new FeignClientEncodingProperties(); - properties.setContentEncodings(null); - - final var template = new RequestTemplate(); - - template.header(HttpEncoding.CONTENT_LENGTH, "2049"); - template.header(HttpEncoding.CONTENT_TYPE, "application/json"); - - final var interceptor = new FeignContentGzipEncodingInterceptor(properties); - - interceptor.apply(template); - - assertThat(template.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)).isTrue(); - assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); - } - -} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTests.java new file mode 100644 index 000000000..91a010f06 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptorTests.java @@ -0,0 +1,181 @@ +/* + * Copyright 2013-2024 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.encoding; + +import java.util.List; + +import feign.RequestTemplate; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link FeignContentGzipEncodingInterceptor} + * + * @author André Teigler + */ +class FeignContentGzipEncodingInterceptorTests { + + @Test + void shouldNotAddCompressionHeaderWhenSizeBelowThreshold() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2047"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).doesNotContainKey(HttpEncoding.CONTENT_ENCODING_HEADER); + } + + @Test + void shouldNotAddCompressionHeaderWhenSizeEqualsThreshold() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2048"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).doesNotContainKey(HttpEncoding.CONTENT_ENCODING_HEADER); + } + + @Test + void shouldAddCompressionHeaderWhenSizeAboveThreshold() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).containsKey(HttpEncoding.CONTENT_ENCODING_HEADER); + assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); + } + + @Test + void shouldNotAddCompressionHeaderWhenTypeMismatch() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + properties.setMimeTypes(new String[] { "application/xml" }); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).doesNotContainKey(HttpEncoding.CONTENT_ENCODING_HEADER); + } + + @Test + void shouldAddDefaultCompressionHeaderWhenMimeTypeNotSet() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + properties.setMimeTypes(new String[] {}); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).containsKey(HttpEncoding.CONTENT_ENCODING_HEADER); + assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); + } + + @Test + void shouldAddDefaultCompressionHeaderWhenNullMimeTypeSet() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + properties.setMimeTypes(null); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).containsKey(HttpEncoding.CONTENT_ENCODING_HEADER); + assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); + } + + @Test + void shouldAddCustomCompressionHeader() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + properties.setContentEncodingTypes(new String[] { "gzip" }); + + final RequestTemplate template = new RequestTemplate(); + + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).containsKey(HttpEncoding.CONTENT_ENCODING_HEADER); + assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip")); + } + + @Test + void shouldAddDefaultCompressionHeaderWhenContentEncodingTypesAreEmpty() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + properties.setContentEncodingTypes(new String[] {}); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).containsKey(HttpEncoding.CONTENT_ENCODING_HEADER); + assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); + } + + @Test + void shouldAddDefaultCompressionHeaderWhenNullContentEncodingTypes() { + final FeignClientEncodingProperties properties = new FeignClientEncodingProperties(); + properties.setContentEncodingTypes(null); + + final RequestTemplate template = new RequestTemplate(); + template.header(HttpEncoding.CONTENT_LENGTH, "2049"); + template.header(HttpEncoding.CONTENT_TYPE, "application/json"); + + final FeignContentGzipEncodingInterceptor interceptor = new FeignContentGzipEncodingInterceptor(properties); + + interceptor.apply(template); + + assertThat(template.headers()).containsKey(HttpEncoding.CONTENT_ENCODING_HEADER); + assertThat(template.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)).isEqualTo(List.of("gzip", "deflate")); + } + +}