From 0bf958a3152acbb775746c1b80df742833b95d01 Mon Sep 17 00:00:00 2001 From: "jaime.pineiro" Date: Mon, 2 Nov 2020 15:00:36 +0100 Subject: [PATCH 1/4] If charset is set in Content-type, we decode response using that charset --- .../java/feign/jackson/JacksonDecoder.java | 32 ++++++++++++++++--- .../java/feign/jackson/JacksonCodecTest.java | 20 ++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/jackson/src/main/java/feign/jackson/JacksonDecoder.java b/jackson/src/main/java/feign/jackson/JacksonDecoder.java index f4fa985753..98c92b4242 100644 --- a/jackson/src/main/java/feign/jackson/JacksonDecoder.java +++ b/jackson/src/main/java/feign/jackson/JacksonDecoder.java @@ -13,15 +13,17 @@ */ package feign.jackson; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.RuntimeJsonMappingException; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.lang.reflect.Type; +import java.nio.charset.Charset; +import java.util.Collection; import java.util.Collections; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.RuntimeJsonMappingException; import feign.Response; import feign.Util; import feign.codec.Decoder; @@ -47,7 +49,8 @@ public JacksonDecoder(ObjectMapper mapper) { public Object decode(Response response, Type type) throws IOException { if (response.body() == null) return null; - Reader reader = response.body().asReader(Util.UTF_8); + Charset charset = getResponseCharset(response); + Reader reader = response.body().asReader(charset); if (!reader.markSupported()) { reader = new BufferedReader(reader, 1); } @@ -66,4 +69,23 @@ public Object decode(Response response, Type type) throws IOException { throw e; } } + + private Charset getResponseCharset(Response response) { + + Collection contentTypeHeaders = response.headers().get("Content-Type"); + + if(contentTypeHeaders != null) { + for(String contentTypeHeader : contentTypeHeaders) { + String[] contentTypeParmeters = contentTypeHeader.split(";"); + if(contentTypeParmeters.length > 1) { + String[] charsetParts = contentTypeParmeters[1].split("="); + if(charsetParts.length == 2 && "charset".equalsIgnoreCase(charsetParts[0].trim())) { + return Charset.forName(charsetParts[1]); + } + } + } + } + + return Util.UTF_8; + } } diff --git a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java index 2b0d4bfdd6..5d9a67a1f4 100644 --- a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java +++ b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java @@ -28,6 +28,7 @@ import org.junit.Test; import java.io.Closeable; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -168,6 +169,25 @@ public void customEncoder() { + " \"id\" : \"ABCD\"" + System.lineSeparator() + "} ]"); } + + @Test + public void decoderCharset() throws IOException { + Zone zone = new Zone("denominator.io.", "ÁÉÍÓÚÀÈÌÒÙÄËÏÖÜÑ"); + + Response response = Response.builder() + .status(200) + .reason("OK") + .request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) + .headers(Map.of("Content-Type", List.of("application/json;charset=ISO-8859-1"))) + .body(new String("" // + + "{" + System.lineSeparator() + + " \"name\" : \"DENOMINATOR.IO.\"," + System.lineSeparator() + + " \"id\" : \"ÁÉÍÓÚÀÈÌÒÙÄËÏÖÜÑ\"" + System.lineSeparator() + + "}").getBytes(StandardCharsets.ISO_8859_1)) + .build(); + assertEquals(zone.get("id"), + ((Zone)new JacksonDecoder().decode(response, new TypeReference() {}.getType())).get("id")); + } @Test public void decodesIterator() throws Exception { From 18190c83c3cd7688ee43fcffa985fbacc2d69936 Mon Sep 17 00:00:00 2001 From: "jaime.pineiro" Date: Mon, 2 Nov 2020 16:05:44 +0100 Subject: [PATCH 2/4] Java 8 compatibility --- jackson/src/test/java/feign/jackson/JacksonCodecTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java index 5d9a67a1f4..ccd74ac395 100644 --- a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java +++ b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java @@ -31,7 +31,9 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -174,11 +176,14 @@ public void customEncoder() { public void decoderCharset() throws IOException { Zone zone = new Zone("denominator.io.", "ÁÉÍÓÚÀÈÌÒÙÄËÏÖÜÑ"); + Map> headers = new HashMap>(); + headers.put("Content-Type", Arrays.asList("application/json;charset=ISO-8859-1")); + Response response = Response.builder() .status(200) .reason("OK") .request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) - .headers(Map.of("Content-Type", List.of("application/json;charset=ISO-8859-1"))) + .headers(headers) .body(new String("" // + "{" + System.lineSeparator() + " \"name\" : \"DENOMINATOR.IO.\"," + System.lineSeparator() From 2dd91fe5e2544682203b812497a27c058e3c8426 Mon Sep 17 00:00:00 2001 From: "jaime.pineiro" Date: Mon, 2 Nov 2020 17:23:13 +0100 Subject: [PATCH 3/4] Format fix --- .../java/feign/jackson/JacksonDecoder.java | 18 +++++++++--------- .../java/feign/jackson/JacksonCodecTest.java | 7 ++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/jackson/src/main/java/feign/jackson/JacksonDecoder.java b/jackson/src/main/java/feign/jackson/JacksonDecoder.java index 98c92b4242..5abf8e9567 100644 --- a/jackson/src/main/java/feign/jackson/JacksonDecoder.java +++ b/jackson/src/main/java/feign/jackson/JacksonDecoder.java @@ -69,23 +69,23 @@ public Object decode(Response response, Type type) throws IOException { throw e; } } - + private Charset getResponseCharset(Response response) { - + Collection contentTypeHeaders = response.headers().get("Content-Type"); - - if(contentTypeHeaders != null) { - for(String contentTypeHeader : contentTypeHeaders) { + + if (contentTypeHeaders != null) { + for (String contentTypeHeader : contentTypeHeaders) { String[] contentTypeParmeters = contentTypeHeader.split(";"); - if(contentTypeParmeters.length > 1) { + if (contentTypeParmeters.length > 1) { String[] charsetParts = contentTypeParmeters[1].split("="); - if(charsetParts.length == 2 && "charset".equalsIgnoreCase(charsetParts[0].trim())) { + if (charsetParts.length == 2 && "charset".equalsIgnoreCase(charsetParts[0].trim())) { return Charset.forName(charsetParts[1]); } } - } + } } - + return Util.UTF_8; } } diff --git a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java index ccd74ac395..2b8bffcdd3 100644 --- a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java +++ b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java @@ -171,14 +171,14 @@ public void customEncoder() { + " \"id\" : \"ABCD\"" + System.lineSeparator() + "} ]"); } - + @Test public void decoderCharset() throws IOException { Zone zone = new Zone("denominator.io.", "ÁÉÍÓÚÀÈÌÒÙÄËÏÖÜÑ"); Map> headers = new HashMap>(); headers.put("Content-Type", Arrays.asList("application/json;charset=ISO-8859-1")); - + Response response = Response.builder() .status(200) .reason("OK") @@ -191,7 +191,8 @@ public void decoderCharset() throws IOException { + "}").getBytes(StandardCharsets.ISO_8859_1)) .build(); assertEquals(zone.get("id"), - ((Zone)new JacksonDecoder().decode(response, new TypeReference() {}.getType())).get("id")); + ((Zone) new JacksonDecoder().decode(response, new TypeReference() {}.getType())) + .get("id")); } @Test From 4db613177d4d3e89df78b193fa9842def113eefe Mon Sep 17 00:00:00 2001 From: "jaime.pineiro" Date: Tue, 10 Nov 2020 10:21:12 +0100 Subject: [PATCH 4/4] Moving code to get charset to Response in order to be used by another decoders --- core/src/main/java/feign/Response.java | 19 +++++++++++++++++ .../java/feign/jackson/JacksonDecoder.java | 21 +------------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/feign/Response.java b/core/src/main/java/feign/Response.java index 1b06c9097e..9ed46e01dd 100644 --- a/core/src/main/java/feign/Response.java +++ b/core/src/main/java/feign/Response.java @@ -175,6 +175,25 @@ public Request request() { return request; } + public Charset charset() { + + Collection contentTypeHeaders = headers().get("Content-Type"); + + if (contentTypeHeaders != null) { + for (String contentTypeHeader : contentTypeHeaders) { + String[] contentTypeParmeters = contentTypeHeader.split(";"); + if (contentTypeParmeters.length > 1) { + String[] charsetParts = contentTypeParmeters[1].split("="); + if (charsetParts.length == 2 && "charset".equalsIgnoreCase(charsetParts[0].trim())) { + return Charset.forName(charsetParts[1]); + } + } + } + } + + return Util.UTF_8; + } + @Override public String toString() { StringBuilder builder = new StringBuilder("HTTP/1.1 ").append(status); diff --git a/jackson/src/main/java/feign/jackson/JacksonDecoder.java b/jackson/src/main/java/feign/jackson/JacksonDecoder.java index 5abf8e9567..d6c6bd84e7 100644 --- a/jackson/src/main/java/feign/jackson/JacksonDecoder.java +++ b/jackson/src/main/java/feign/jackson/JacksonDecoder.java @@ -49,8 +49,7 @@ public JacksonDecoder(ObjectMapper mapper) { public Object decode(Response response, Type type) throws IOException { if (response.body() == null) return null; - Charset charset = getResponseCharset(response); - Reader reader = response.body().asReader(charset); + Reader reader = response.body().asReader(response.charset()); if (!reader.markSupported()) { reader = new BufferedReader(reader, 1); } @@ -70,22 +69,4 @@ public Object decode(Response response, Type type) throws IOException { } } - private Charset getResponseCharset(Response response) { - - Collection contentTypeHeaders = response.headers().get("Content-Type"); - - if (contentTypeHeaders != null) { - for (String contentTypeHeader : contentTypeHeaders) { - String[] contentTypeParmeters = contentTypeHeader.split(";"); - if (contentTypeParmeters.length > 1) { - String[] charsetParts = contentTypeParmeters[1].split("="); - if (charsetParts.length == 2 && "charset".equalsIgnoreCase(charsetParts[0].trim())) { - return Charset.forName(charsetParts[1]); - } - } - } - } - - return Util.UTF_8; - } }