From c1e6f82385056ae433d3157025cf0effeca5ffe7 Mon Sep 17 00:00:00 2001 From: Jenni Syed Date: Fri, 27 Jan 2017 17:43:07 -0600 Subject: [PATCH] Fix #546 CapturingInterceptor will now buffer response if the entity is not repeatable. --- .../interceptor/CapturingInterceptor.java | 24 ++- .../rest/client/CapturingInterceptorTest.java | 147 ++++++++++++++++++ 2 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/CapturingInterceptorTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java index f7e9e949af8..a3e02c64ea2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java @@ -23,6 +23,11 @@ import ca.uhn.fhir.rest.client.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; + +import java.io.IOException; /** * Client interceptor which simply captures request and response objects and stores them so that they can be inspected after a client @@ -55,8 +60,23 @@ public void interceptRequest(IHttpRequest theRequest) { } @Override - public void interceptResponse(IHttpResponse theRequest) { - myLastResponse = theRequest; + public void interceptResponse(IHttpResponse theResponse) { + //Buffer the reponse to avoid errors when content has already been read and the entity is not repeatable + try { + if(theResponse.getResponse() instanceof HttpResponse) { + HttpEntity entity = ((HttpResponse) theResponse.getResponse()).getEntity(); + if( entity != null && !entity.isRepeatable()){ + theResponse.bufferEntity(); + } + } else { + theResponse.bufferEntity(); + } + } catch (IOException e) { + throw new InternalErrorException("Unable to buffer the entity for capturing", e); + } + + + myLastResponse = theResponse; } } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/CapturingInterceptorTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/CapturingInterceptorTest.java new file mode 100644 index 00000000000..7e297f7962a --- /dev/null +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/CapturingInterceptorTest.java @@ -0,0 +1,147 @@ +package ca.uhn.fhir.rest.client; + +import ca.uhn.fhir.rest.client.apache.ApacheHttpResponse; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.entity.BasicHttpEntity; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHttpRequest; +import org.apache.http.message.BasicHttpResponse; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Spy; + +import java.io.IOException; +import java.nio.charset.Charset; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.*; + + +public class CapturingInterceptorTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + + @Test + public void testRequest() { + IHttpRequest expectedRequest = mock(IHttpRequest.class); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptRequest(expectedRequest); + + assertEquals(expectedRequest, interceptor.getLastRequest()); + } + + @Test + public void testResponse() throws Exception { + IHttpResponse expectedResponse = mock(IHttpResponse.class); + doNothing().when(expectedResponse).bufferEntity(); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptResponse(expectedResponse); + + assertEquals(expectedResponse, interceptor.getLastResponse()); + verify(expectedResponse).bufferEntity(); + } + + @Test + public void testResponseException() throws Exception { + IHttpResponse response = mock(IHttpResponse.class); + IOException expectedCause = new IOException(); + doThrow(expectedCause).when(response).bufferEntity(); + + thrown.expect(InternalErrorException.class); + thrown.expectMessage("Unable to buffer the entity for capturing"); + thrown.expectCause(equalTo(expectedCause)); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptResponse(response); + } + + @Test + public void testResponseBufferApache() throws Exception{ + HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + response.setEntity(new InputStreamEntity(IOUtils.toInputStream("Some content", Charset.defaultCharset()))); + IHttpResponse expectedResponse = spy(new ApacheHttpResponse(response)); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptResponse(expectedResponse); + IHttpResponse actualResponse = interceptor.getLastResponse(); + + assertEquals(expectedResponse, actualResponse); + assertThat("Some content", equalTo(IOUtils.toString(actualResponse.createReader()))); + verify(expectedResponse).bufferEntity(); + + //A second call should not throw an exception (InpuStreamEntity is not repeatable) + IOUtils.toString(actualResponse.createReader()); + } + + @Test + public void testResponseRepeatable() throws Exception{ + HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + response.setEntity(new StringEntity("Some content")); + IHttpResponse expectedResponse = spy(new ApacheHttpResponse(response)); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptResponse(expectedResponse); + IHttpResponse actualResponse = interceptor.getLastResponse(); + + assertEquals(expectedResponse, actualResponse); + assertThat("Some content", equalTo(IOUtils.toString(actualResponse.createReader()))); + verify(expectedResponse, times(0)).bufferEntity(); + + //A second call should not throw an exception (StringEntity is repeatable) + IOUtils.toString(actualResponse.createReader()); + } + + @Test + public void testResponseBufferOther() throws Exception { + Object response = mock(Object.class); + IHttpResponse expectedResponse = mock(IHttpResponse.class); + when(expectedResponse.getResponse()).thenReturn(response); + doNothing().when(expectedResponse).bufferEntity(); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptResponse(expectedResponse); + IHttpResponse actualResponse = interceptor.getLastResponse(); + + assertEquals(expectedResponse, actualResponse); + verify(expectedResponse).bufferEntity(); + } + + @Test + public void testClear(){ + IHttpRequest expectedRequest = mock(IHttpRequest.class); + IHttpResponse expectedResponse = mock(IHttpResponse.class); + Object response = mock(Object.class); + when(expectedResponse.getResponse()).thenReturn(response); + + CapturingInterceptor interceptor = new CapturingInterceptor(); + interceptor.interceptResponse(expectedResponse); + interceptor.interceptRequest(expectedRequest); + + assertEquals(expectedRequest, interceptor.getLastRequest()); + assertEquals(expectedResponse, interceptor.getLastResponse()); + + interceptor.clear(); + + assertNull(interceptor.getLastRequest()); + assertNull(interceptor.getLastResponse()); + } + +}