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-Charset", "utf-8");
theHttpRequest.addHeader("Accept-Encoding", "gzip"); theHttpRequest.addHeader("Accept-Encoding", "gzip");
if (theEncoding == null) { 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) { } else if (theEncoding == EncodingEnum.JSON) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON); theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
} else if (theEncoding == EncodingEnum.XML) { } else if (theEncoding == EncodingEnum.XML) {

View File

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

View File

@ -54,7 +54,7 @@ public class Constants {
public static final String FORMAT_XML = "xml"; public static final String FORMAT_XML = "xml";
public static final String HEADER_ACCEPT = "Accept"; public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; 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_ALLOW = "Allow";
public static final String HEADER_AUTHORIZATION = "Authorization"; public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic "; public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";

View File

@ -143,7 +143,7 @@ public class GenericClientDstu2Test {
client.fetchConformance().ofType(Conformance.class).execute(); client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length); 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++; idx++;
client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); 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("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://" + methodName + ".example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString()); assertEquals("http://" + methodName + ".example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length); 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_XML));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON)); 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("http://" + methodName + ".example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length); 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_XML));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON)); 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 know if you need the ability to add this header automatically. Thanks
to Lars Kristian Roland for pointing this out. to Lars Kristian Roland for pointing this out.
</action> </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>
<release version="1.4" date="2016-02-04"> <release version="1.4" date="2016-02-04">
<action type="add"> <action type="add">