In the client, the create/update operations on a Binary resource (which

use the raw binary's content type as opposed to the FHIR content type)
were not including any request headers (Content-Type, User-Agent, etc.) Thanks to Peter Van Houte of Agfa Healthcare for reporting!
This commit is contained in:
James Agnew 2016-02-25 12:54:52 -08:00
parent d11cbab15c
commit 4a26064cd6
6 changed files with 161 additions and 7 deletions

View File

@ -93,12 +93,13 @@ public abstract class BaseHttpClientInvocation {
}
}
theHttpRequest.addHeader("User-Agent", "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client)");
String versionString = VersionUtil.getVersion();
theHttpRequest.addHeader("User-Agent", "HAPI-FHIR/" + versionString + " (FHIR Client)");
theHttpRequest.addHeader("Accept-Charset", "utf-8");
theHttpRequest.addHeader("Accept-Encoding", "gzip");
if (theEncoding == null) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_ALL);
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON);
} else if (theEncoding == EncodingEnum.JSON) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
} else if (theEncoding == EncodingEnum.XML) {

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR - Core Library
@ -213,9 +215,13 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
* whatever reason.
*/
ByteArrayEntity entity = new ByteArrayEntity(binary.getContent());
entity.setContentType(binary.getContentType());
HttpRequestBase retVal = createRequest(url, entity);
addMatchHeaders(retVal, url);
super.addHeadersToRequest(retVal, null);
if (isNotBlank(binary.getContentType())) {
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, binary.getContentType());
}
return retVal;
}

View File

@ -54,7 +54,7 @@ public class Constants {
public static final String FORMAT_XML = "xml";
public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
public static final String HEADER_ACCEPT_VALUE_ALL = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
public static final String HEADER_ALLOW = "Allow";
public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";

View File

@ -143,7 +143,7 @@ public class GenericClientDstu2Test {
client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON));
idx++;
client.fetchConformance().ofType(Conformance.class).encodedJson().execute();
@ -192,12 +192,12 @@ public class GenericClientDstu2Test {
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://" + methodName + ".example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertEquals("http://" + methodName + ".example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
}

View File

@ -0,0 +1,140 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Conformance;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.VersionUtil;
public class GenericClientDstu3Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu3Test.class);
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
private int myResponseCount = 0;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
myResponseCount = 0;
}
private byte[] extractBodyAsByteArray(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
byte[] body = IOUtils.toByteArray(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent());
return body;
}
@Test
public void testUserAgentForConformance() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
}
@Test
public void testUserAgentForBinary() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Binary bin = new Binary();
bin.setContentType("application/foo");
bin.setContent(new byte[] {0, 1, 2, 3, 4});
client.create().resource(bin).execute();
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue());
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
assertArrayEquals(new byte[] {0,1,2,3,4}, extractBodyAsByteArray(capt));
}
private void validateUserAgent(ArgumentCaptor<HttpUriRequest> capt) {
assertEquals(1, capt.getAllValues().get(0).getHeaders("User-Agent").length);
assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
}
private String expectedUserAgent() {
return "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client)";
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
}

View File

@ -77,6 +77,13 @@
know if you need the ability to add this header automatically. Thanks
to Lars Kristian Roland for pointing this out.
</action>
<action type="fix">
In the client, the create/update operations on a Binary resource
(which use the raw binary's content type as opposed to the FHIR
content type) were not including any request headers (Content-Type,
User-Agent, etc.) Thanks to Peter Van Houte of Agfa Healthcare for
reporting!
</action>
</release>
<release version="1.4" date="2016-02-04">
<action type="add">