From 345bf1bcb4e318ebb3c3d10bb2d980ec776c51b7 Mon Sep 17 00:00:00 2001 From: e035214 Date: Tue, 27 Sep 2016 17:10:01 -0500 Subject: [PATCH] Force the LBAPI responses to be treated as UTF-8 Currently, the string handling within the toolkit leaves string encoding up to the platform which is often wrong. The updated code forces the response handling to treat the incoming response body as a UTF-8 encoded stream before handing off to GSON to deserialize. Updated the integration test to include a name that contains unicode characters and compares the result. --- .../com/rallydev/lookback/LookbackApi.java | 54 +++++++------------ .../lookback/LookbackIntegrationTest.java | 18 +++---- .../lookback/ProxiedLookbackApiTest.java | 11 ++-- src/test/resources/1.json | 4 +- 4 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/rallydev/lookback/LookbackApi.java b/src/main/java/com/rallydev/lookback/LookbackApi.java index ac0cbe6..f8520fb 100644 --- a/src/main/java/com/rallydev/lookback/LookbackApi.java +++ b/src/main/java/com/rallydev/lookback/LookbackApi.java @@ -1,11 +1,12 @@ package com.rallydev.lookback; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import org.apache.commons.codec.binary.Base64; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; + import org.apache.http.HttpEntity; import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; @@ -16,13 +17,10 @@ import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.CoreProtocolPNames; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Scanner; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; /** * LookbackApi objects provide an API for communicating with Rally's Lookback API service. @@ -58,7 +56,6 @@ public class LookbackApi { public LookbackApi() { this(new DefaultHttpClient()); Runnable shutdown = new Runnable() { - @Override public void run() { client.getConnectionManager().shutdown(); } @@ -235,15 +232,21 @@ LookbackResult executeQuery(LookbackQuery query) throws IOException { private HttpResponse executeRequest(String requestJson) throws IOException { HttpUriRequest request = createRequest(requestJson); - applyCredentials(); return client.execute(request); } private LookbackResult buildLookbackResult(HttpResponse response) throws IOException { HttpEntity responseBody = validateResponse(response); - String json = getResponseJson(responseBody); - return serializeLookbackResultFromJson(json); + JsonReader reader = new JsonReader(new InputStreamReader(responseBody.getContent(), "UTF-8")); + LookbackResult resultFromJson = null; + try { + resultFromJson = serializeLookbackResultFromJson(reader); + } finally { + reader.close(); + } + + return resultFromJson; } private HttpUriRequest createRequest(String requestJson) throws IOException { @@ -263,18 +266,9 @@ private HttpEntity validateResponse(HttpResponse response) { return responseBody; } - private String getResponseJson(HttpEntity responseBody) throws IOException { - InputStream responseStream = responseBody.getContent(); - try { - return readFromStream(responseStream); - } finally { - responseStream.close(); - } - } - - private LookbackResult serializeLookbackResultFromJson(String json) { + private LookbackResult serializeLookbackResultFromJson(JsonReader reader) { Gson serializer = new GsonBuilder().serializeNulls().create(); - return serializer.fromJson(json, LookbackResult.class); + return serializer.fromJson(reader, LookbackResult.class); } private boolean authorizationFailed(HttpResponse response) { @@ -291,16 +285,6 @@ private String buildUrl() { serverUrl.toExternalForm(), buildApiVersion(), workspace); } - private String readFromStream(InputStream stream) { - Scanner scanner = new Scanner(stream); - scanner.useDelimiter("\\A"); - if (scanner.hasNext()) { - return scanner.next(); - } else { - return ""; - } - } - private String buildApiVersion() { return "v" + versionMajor + "." + versionMinor; } diff --git a/src/test/java/com/rallydev/lookback/LookbackIntegrationTest.java b/src/test/java/com/rallydev/lookback/LookbackIntegrationTest.java index 400b702..efe54a8 100644 --- a/src/test/java/com/rallydev/lookback/LookbackIntegrationTest.java +++ b/src/test/java/com/rallydev/lookback/LookbackIntegrationTest.java @@ -1,5 +1,13 @@ package com.rallydev.lookback; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Assert; @@ -10,14 +18,6 @@ import org.simpleframework.http.core.ContainerServer; import org.simpleframework.transport.connect.SocketConnection; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - public class LookbackIntegrationTest { protected String username = "test1"; @@ -129,7 +129,7 @@ protected Container returnLbapiResults() { try { InputStream is = getClass().getResource("/1.json").openStream(); Assert.assertNotNull(is); - json = IOUtils.toString(is); + json = IOUtils.toString(is, "UTF-8"); } catch (Exception e) { Assert.fail(e.getMessage()); } diff --git a/src/test/java/com/rallydev/lookback/ProxiedLookbackApiTest.java b/src/test/java/com/rallydev/lookback/ProxiedLookbackApiTest.java index a18d79a..bc84e77 100644 --- a/src/test/java/com/rallydev/lookback/ProxiedLookbackApiTest.java +++ b/src/test/java/com/rallydev/lookback/ProxiedLookbackApiTest.java @@ -1,5 +1,8 @@ package com.rallydev.lookback; +import java.io.InputStream; +import java.util.Stack; + import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; @@ -10,9 +13,6 @@ import org.junit.Test; import org.simpleframework.http.core.Container; -import java.io.InputStream; -import java.util.Stack; - public class ProxiedLookbackApiTest extends LookbackIntegrationTest { private String proxyuser = "test"; @@ -53,6 +53,8 @@ public void worksThroughProxy() { .execute(); Assert.assertEquals("there should be 2 results", 2, result.Results.size()); + Assert.assertEquals("Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa", result.Results.get(0).get("Name")); + } @@ -74,6 +76,7 @@ protected Container requestProxyAuth() { ret = capturesRequest(ret); ret = respondWith(ret, 407); ret = addsHeader(ret, "Proxy-Authenticate", "Basic realm=\"theproxy\""); + ret = addsHeader(ret, "Content-Type", "application/json; charset=UTF-8"); return ret; } @@ -91,6 +94,7 @@ protected Container returnLbapiResults() { ret = capturesRequest(ret); ret = assertsHeader(ret, "Proxy-Authorization", "Basic dGVzdDpwYXNz"); ret = assertsHeader(ret, "Authorization", "Basic dGVzdDE6cGFzczE="); + ret = addsHeader(ret, "Content-Type", "application/json; charset=UTF-8"); return ret; } @@ -99,6 +103,7 @@ protected Container requestLbapiAuth() { ret = capturesRequest(ret); ret = assertsHeader(ret, "Proxy-Authorization", "Basic dGVzdDpwYXNz"); ret = addsHeader(ret, "WWW-Authenticate", "Basic realm=\"myRealm\""); + ret = addsHeader(ret, "Content-Type", "application/json; charset=UTF-8"); ret = respondWith(ret, 401); return ret; } diff --git a/src/test/resources/1.json b/src/test/resources/1.json index e6cacb9..80dcbd7 100644 --- a/src/test/resources/1.json +++ b/src/test/resources/1.json @@ -44,7 +44,7 @@ "PageSize": 100, "ETLDate": "2014-08-20T18:29:22.379Z", "Results": [ - {"ObjectID":5.387234184E9,"Project":2.79050021E8,"_ValidFrom":"2012-02-08T14:42:14.147Z","_ValidTo":"2012-02-09T19:16:26.890Z","_id":"53e110d3e4b03369a36c47e1"}, - {"ObjectID":4.9413018E9,"Project":2.79050021E8,"_ValidFrom":"2012-02-08T14:13:16.509Z","_ValidTo":"2012-02-08T14:13:20.022Z","_id":"53e110d3e4b03369a36c47c1"} + {"ObjectID":5.387234184E9,"Name":"Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa","Project":2.79050021E8,"_ValidFrom":"2012-02-08T14:42:14.147Z","_ValidTo":"2012-02-09T19:16:26.890Z","_id":"53e110d3e4b03369a36c47e1"}, + {"ObjectID":4.9413018E9,"Name":"Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa","Project":2.79050021E8,"_ValidFrom":"2012-02-08T14:13:16.509Z","_ValidTo":"2012-02-08T14:13:20.022Z","_id":"53e110d3e4b03369a36c47c1"} ] } \ No newline at end of file