- Create HapiFhirCliRestfulClientFactory to encapsulate new HTTPS functionality. Remove modifications to the FhirContext API.

This commit is contained in:
nathaniel.doef 2022-07-22 02:19:45 -04:00
parent 327603dad0
commit 388f1297cb
18 changed files with 384 additions and 240 deletions

View File

@ -899,23 +899,6 @@ public class FhirContext {
return getRestfulClientFactory().newGenericClient(theServerBase);
}
/**
* Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against
* a compliant server, but does not have methods defining the specific functionality required (as is the case with
* {@link #newRestfulClient(Class, String) non-generic clients}).
*
* <p>
* Performance Note: This method performs an additional GET request to /metadata before
* the desired request is performed.
* </p>
*
* @param theServerBase The URL of the base for the restful FHIR server to connect to
* @param theTlsAuthentication Configuration to authenticate HTTPS server requests
*/
public IGenericClient newRestfulGenericClient(final String theServerBase, final TlsAuthentication theTlsAuthentication) {
return getRestfulClientFactory().newTlsGenericClient(theServerBase, theTlsAuthentication);
}
public FhirTerser newTerser() {
return new FhirTerser(this);
}

View File

@ -25,7 +25,7 @@ public final class Msg {
/**
* IMPORTANT: Please update the following comment after you add a new code
* Last code value: 2118
* Last code value: 2120
*/
private Msg() {}

View File

@ -20,12 +20,11 @@ package ca.uhn.fhir.rest.client.api;
* #L%
*/
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.tls.TlsAuthentication;
import java.util.List;
import java.util.Map;
public interface IRestfulClientFactory {
@ -93,24 +92,6 @@ public interface IRestfulClientFactory {
*/
IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams, String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders);
/**
* Returns the HTTP client instance. This method will not return null.
* @param theUrl
* The complete FHIR url to which the http request will be sent
* @param theTlsAuthentication
* Configuration to authenticate HTTPS server requests
* @param theIfNoneExistParams
* The params for header "If-None-Exist" as a hashmap
* @param theIfNoneExistString
* The param for header "If-None-Exist" as a string
* @param theRequestType
* the type of HTTP request (GET, DELETE, ..)
* @param theHeaders
* the headers to be sent together with the http request
* @return the HTTP client instance
*/
IHttpClient getTlsHttpsClient(StringBuilder theUrl, TlsAuthentication theTlsAuthentication, Map<String, List<String>> theIfNoneExistParams, String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders);
/**
* @deprecated Use {@link #getServerValidationMode()} instead (this method is a synonym for that method, but this method is poorly named and will be removed at some point)
*/
@ -176,15 +157,6 @@ public interface IRestfulClientFactory {
*/
IGenericClient newGenericClient(String theServerBase);
/**
* Instantiates a new generic client instance
*
* @param theServerBase The URL of the base for the restful FHIR server to connect to
* @param theTlsAuthentication Configuration to authenticate HTTPS server requests
* @return A newly created client
*/
IGenericClient newTlsGenericClient(String theServerBase, TlsAuthentication theTlsAuthentication);
/**
* Sets the connection request timeout, in milliseconds. This is the amount of time that the HTTPClient connection
* pool may wait for an available connection before failing. This setting typically does not need to be adjusted.

View File

@ -20,11 +20,13 @@ package ca.uhn.fhir.cli;
* #L%
*/
import ca.uhn.fhir.cli.client.HapiFhirCliRestfulClientFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.tls.KeyStoreInfo;
@ -488,13 +490,15 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
protected IGenericClient newClientWithBaseUrl(CommandLine theCommandLine, String theBaseUrl, String theBasicAuthOptionName,
String theBearerTokenOptionName, String theTlsAuthOptionName) throws ParseException {
myFhirCtx.getRestfulClientFactory().setSocketTimeout((int) DateUtils.MILLIS_PER_HOUR);
Optional<TlsAuthentication> tlsConfig = createTlsConfig(theCommandLine, theTlsAuthOptionName);
IGenericClient retVal = tlsConfig.isPresent()
? myFhirCtx.newRestfulGenericClient(theBaseUrl, tlsConfig.get())
: myFhirCtx.newRestfulGenericClient(theBaseUrl);
RestfulClientFactory restfulClientFactory = tlsConfig.isPresent()
? new HapiFhirCliRestfulClientFactory(myFhirCtx, tlsConfig.get())
: new HapiFhirCliRestfulClientFactory(myFhirCtx);
myFhirCtx.setRestfulClientFactory(restfulClientFactory);
myFhirCtx.getRestfulClientFactory().setSocketTimeout((int) DateUtils.MILLIS_PER_HOUR);
IGenericClient retVal = myFhirCtx.newRestfulGenericClient(theBaseUrl);
String basicAuthHeaderValue = getAndParseOptionBasicAuthHeader(theCommandLine, theBasicAuthOptionName);
if (isNotBlank(basicAuthHeaderValue)) {

View File

@ -0,0 +1,149 @@
package ca.uhn.fhir.cli.client;
/*
* #%L
* HAPI FHIR - Command Line Client - API
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.apache.ApacheHttpClient;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.rest.client.tls.TlsAuthenticationSvc;
import ca.uhn.fhir.tls.TlsAuthentication;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import javax.net.ssl.SSLContext;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Intended for use with the HapiFhir CLI only.
* <br/><br/>
* A Restful Factory to create clients, requests and responses based on the Apache httpclient library.
* This class supports HTTP and HTTPS protocol and attempts to use the same client whenever possible.
* The method {@link HapiFhirCliRestfulClientFactory#useHttp()} should be used if the protocol needs to be changed to HTTP.
* Similarly, the method {@link HapiFhirCliRestfulClientFactory#useHttps(TlsAuthentication)} should be used if the protocol
* needs to be changed to HTTPS or if new TLS credentials are required for a client request.
*/
public class HapiFhirCliRestfulClientFactory extends RestfulClientFactory {
private HttpClient myHttpClient;
private TlsAuthentication myTlsAuthentication;
public HapiFhirCliRestfulClientFactory(FhirContext theFhirContext) {
this(theFhirContext, null);
}
public HapiFhirCliRestfulClientFactory(FhirContext theFhirContext, TlsAuthentication theTlsAuthentication) {
super(theFhirContext);
myTlsAuthentication = theTlsAuthentication;
}
@Override
protected synchronized IHttpClient getHttpClient(String theServerBase) {
return getHttpClient(new StringBuilder(theServerBase), null, null, null, null);
}
@Override
public synchronized IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
}
public HttpClient getNativeHttpClient() {
if (myHttpClient == null) {
RequestConfig defaultRequestConfig =
RequestConfig.custom()
.setSocketTimeout(getSocketTimeout())
.setConnectTimeout(getConnectTimeout())
.setConnectionRequestTimeout(getConnectionRequestTimeout())
.setStaleConnectionCheckEnabled(true)
.build();
HttpClientBuilder builder = HttpClients.custom()
.useSystemProperties()
.setDefaultRequestConfig(defaultRequestConfig)
.disableCookieManagement();
PoolingHttpClientConnectionManager connectionManager;
if(myTlsAuthentication != null){
SSLContext sslContext = TlsAuthenticationSvc.createSslContext(myTlsAuthentication);
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
builder.setSSLSocketFactory(sslConnectionSocketFactory);
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("https", sslConnectionSocketFactory)
.build();
connectionManager = new PoolingHttpClientConnectionManager(
registry, null, null, null, 5000, TimeUnit.MILLISECONDS
);
}
else {
connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
}
connectionManager.setMaxTotal(getPoolMaxTotal());
connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute());
builder.setConnectionManager(connectionManager);
myHttpClient = builder.build();
}
return myHttpClient;
}
@Override
protected void resetHttpClient() {
myHttpClient = null;
}
public void useHttp(){
myTlsAuthentication = null;
resetHttpClient();
}
public void useHttps(TlsAuthentication theTlsAuthentication){
myTlsAuthentication = theTlsAuthentication;
resetHttpClient();
}
@Override
public synchronized void setHttpClient(Object theHttpClient) {
throw new UnsupportedOperationException(Msg.code(2119));
}
@Override
public void setProxy(String theHost, Integer thePort) {
throw new UnsupportedOperationException(Msg.code(2120));
}
}

View File

@ -37,22 +37,6 @@ public class ApacheRestfulClientFactoryTest extends BaseFhirVersionParameterized
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttps(FhirVersionEnum theFhirVersion) throws Exception {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
ApacheRestfulClientFactory clientFactory = new ApacheRestfulClientFactory(fhirVersionParams.getFhirContext());
HttpClient authenticatedClient = clientFactory.getNativeHttpClient(getTlsAuthentication());
HttpUriRequest request = new HttpGet(fhirVersionParams.getSecuredPatientEndpoint());
HttpResponse response = authenticatedClient.execute(request);
assertEquals(200, response.getStatusLine().getStatusCode());
String json = EntityUtils.toString(response.getEntity());
IBaseResource bundle = fhirVersionParams.parseResource(json);
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {
@ -81,16 +65,6 @@ public class ApacheRestfulClientFactoryTest extends BaseFhirVersionParameterized
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttps(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
String secureBase = fhirVersionParams.getSecureBase();
FhirContext context = fhirVersionParams.getFhirContext();
IBaseResource bundle = context.newRestfulGenericClient(secureBase, getTlsAuthentication()).search().forResource("Patient").execute();
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {

View File

@ -0,0 +1,163 @@
package ca.uhn.fhir.cli.client;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.test.BaseFhirVersionParameterizedTest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.util.EntityUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import javax.net.ssl.SSLHandshakeException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class HapiFhirCliRestfulClientFactoryTest extends BaseFhirVersionParameterizedTest{
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttp(FhirVersionEnum theFhirVersion) throws Exception {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
HapiFhirCliRestfulClientFactory clientFactory = new HapiFhirCliRestfulClientFactory(fhirVersionParams.getFhirContext());
HttpClient client = clientFactory.getNativeHttpClient();
HttpUriRequest request = new HttpGet(fhirVersionParams.getPatientEndpoint());
HttpResponse response = client.execute(request);
assertEquals(200, response.getStatusLine().getStatusCode());
String json = EntityUtils.toString(response.getEntity());
IBaseResource bundle = fhirVersionParams.parseResource(json);
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttps(FhirVersionEnum theFhirVersion) throws Exception {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
HapiFhirCliRestfulClientFactory clientFactory = new HapiFhirCliRestfulClientFactory(fhirVersionParams.getFhirContext(), getTlsAuthentication());
HttpClient authenticatedClient = clientFactory.getNativeHttpClient();
HttpUriRequest request = new HttpGet(fhirVersionParams.getSecuredPatientEndpoint());
HttpResponse response = authenticatedClient.execute(request);
assertEquals(200, response.getStatusLine().getStatusCode());
String json = EntityUtils.toString(response.getEntity());
IBaseResource bundle = fhirVersionParams.parseResource(json);
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
HapiFhirCliRestfulClientFactory clientFactory = new HapiFhirCliRestfulClientFactory(fhirVersionParams.getFhirContext());
HttpClient unauthenticatedClient = clientFactory.getNativeHttpClient();
try{
HttpUriRequest request = new HttpGet(fhirVersionParams.getSecuredPatientEndpoint());
unauthenticatedClient.execute(request);
fail();
}
catch(Exception e){
assertEquals(SSLHandshakeException.class, e.getClass());
}
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttp(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
String base = fhirVersionParams.getBase();
FhirContext context = fhirVersionParams.getFhirContext();
context.setRestfulClientFactory(new HapiFhirCliRestfulClientFactory(context));
IBaseResource bundle = context.newRestfulGenericClient(base).search().forResource("Patient").execute();
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttps(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
String secureBase = fhirVersionParams.getSecureBase();
FhirContext context = fhirVersionParams.getFhirContext();
context.setRestfulClientFactory(new HapiFhirCliRestfulClientFactory(context, getTlsAuthentication()));
IBaseResource bundle = context.newRestfulGenericClient(secureBase).search().forResource("Patient").execute();
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
String secureBase = fhirVersionParams.getSecureBase();
FhirContext context = fhirVersionParams.getFhirContext();
context.setRestfulClientFactory(new HapiFhirCliRestfulClientFactory(context));
try {
context.newRestfulGenericClient(secureBase).search().forResource("Patient").execute();
fail();
} catch (Exception e) {
assertTrue(e.getMessage().contains("HAPI-1357: Failed to retrieve the server metadata statement during client initialization"));
assertEquals(SSLHandshakeException.class, e.getCause().getCause().getClass());
}
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientProtocolChanges(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
FhirContext context = fhirVersionParams.getFhirContext();
String secureBase = fhirVersionParams.getSecureBase();
String base = fhirVersionParams.getBase();
// https
HapiFhirCliRestfulClientFactory restfulClientFactory = new HapiFhirCliRestfulClientFactory(context, getTlsAuthentication());
context.setRestfulClientFactory(restfulClientFactory);
IBaseResource bundle = context.newRestfulGenericClient(secureBase).search().forResource("Patient").execute();
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
// http
restfulClientFactory.useHttp();
bundle = context.newRestfulGenericClient(base).search().forResource("Patient").execute();
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
// https
restfulClientFactory.useHttps(getTlsAuthentication());
bundle = context.newRestfulGenericClient(secureBase).search().forResource("Patient").execute();
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testSetHttpClient(FhirVersionEnum theFhirVersion){
try {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
FhirContext fhirContext = fhirVersionParams.getFhirContext();
HapiFhirCliRestfulClientFactory hapiFhirCliRestfulClientFactory = new HapiFhirCliRestfulClientFactory(fhirContext);
hapiFhirCliRestfulClientFactory.setHttpClient(new Object());
} catch (UnsupportedOperationException e){
assertEquals(Msg.code(2119), e.getMessage());
}
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testSetProxy(FhirVersionEnum theFhirVersion){
try {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
FhirContext fhirContext = fhirVersionParams.getFhirContext();
HapiFhirCliRestfulClientFactory hapiFhirCliRestfulClientFactory = new HapiFhirCliRestfulClientFactory(fhirContext);
hapiFhirCliRestfulClientFactory.setProxy("proxy", 1);
} catch (UnsupportedOperationException e){
assertEquals(Msg.code(2120), e.getMessage());
}
}
}

View File

@ -59,11 +59,6 @@ public class OkHttpRestfulClientFactory extends RestfulClientFactory {
return getHttpClient(new StringBuilder(theServerBase), null, null, null, null);
}
@Override
protected IHttpClient getHttpClient(String theServerBase, TlsAuthentication theTlsAuthentication) {
return getTlsHttpsClient(new StringBuilder(theServerBase), theTlsAuthentication, null, null, null, null);
}
@Override
protected void resetHttpClient() {
myNativeClient = null;
@ -82,10 +77,6 @@ public class OkHttpRestfulClientFactory extends RestfulClientFactory {
return myNativeClient;
}
public synchronized Call.Factory getNativeClient(TlsAuthentication theTlsAuthentication) {
throw new UnsupportedOperationException(Msg.code(2118)+"HTTPS not supported for OkHttpCLient");
}
@Override
public IHttpClient getHttpClient(StringBuilder theUrl,
Map<String, List<String>> theIfNoneExistParams,
@ -95,16 +86,6 @@ public class OkHttpRestfulClientFactory extends RestfulClientFactory {
return new OkHttpRestfulClient(getNativeClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
}
@Override
public IHttpClient getTlsHttpsClient(StringBuilder theUrl,
TlsAuthentication theTlsAuthentication,
Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString,
RequestTypeEnum theRequestType,
List<Header> theHeaders) {
return new OkHttpRestfulClient(getNativeClient(theTlsAuthentication), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
}
/**
* Only accepts clients of type {@link OkHttpClient}
*

View File

@ -89,19 +89,6 @@ public class OkHttpRestfulClientFactoryTest extends BaseFhirVersionParameterized
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttps(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
OkHttpRestfulClientFactory clientFactory = new OkHttpRestfulClientFactory(fhirVersionParams.getFhirContext());
try {
clientFactory.getNativeClient(getTlsAuthentication());
fail();
} catch (Exception e) {
assertEquals(Msg.code(2118)+"HTTPS not supported for OkHttpCLient", e.getMessage());
}
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {
@ -131,22 +118,6 @@ public class OkHttpRestfulClientFactoryTest extends BaseFhirVersionParameterized
assertEquals(theFhirVersion, bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttps(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
String secureBase = fhirVersionParams.getSecureBase();
FhirContext context = fhirVersionParams.getFhirContext();
context.setRestfulClientFactory(new OkHttpRestfulClientFactory(context));
try {
context.newRestfulGenericClient(secureBase, getTlsAuthentication()).search().forResource("Patient").execute();
fail();
} catch (Exception e) {
assertEquals(Msg.code(2118) + "HTTPS not supported for OkHttpCLient", e.getMessage());
}
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testGenericClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {

View File

@ -25,28 +25,20 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.rest.client.tls.TlsAuthenticationSvc;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import javax.net.ssl.SSLContext;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -83,29 +75,13 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory {
return getHttpClient(new StringBuilder(theServerBase), null, null, null, null);
}
@Override
protected synchronized IHttpClient getHttpClient(String theServerBase, TlsAuthentication theTlsAuthentication) {
return getTlsHttpsClient(new StringBuilder(theServerBase), theTlsAuthentication, null, null, null, null);
}
@Override
public synchronized IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
}
@Override
public synchronized IHttpClient getTlsHttpsClient(StringBuilder theUrl, TlsAuthentication theTlsAuthentication,
Map<String, List<String>> theIfNoneExistParams, String theIfNoneExistString,
RequestTypeEnum theRequestType, List<Header> theHeaders) {
return new ApacheHttpClient(getNativeHttpClient(theTlsAuthentication), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType,theHeaders);
}
public HttpClient getNativeHttpClient() {
return getNativeHttpClient(null);
}
public HttpClient getNativeHttpClient(TlsAuthentication theTlsAuthentication) {
if (myHttpClient == null) {
//TODO: Use of a deprecated method should be resolved.
@ -123,23 +99,7 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory {
.setDefaultRequestConfig(defaultRequestConfig)
.disableCookieManagement();
PoolingHttpClientConnectionManager connectionManager;
if(theTlsAuthentication != null){
SSLContext sslContext = TlsAuthenticationSvc.createSslContext(theTlsAuthentication);
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
builder.setSSLSocketFactory(sslConnectionSocketFactory);
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("https", sslConnectionSocketFactory)
.build();
connectionManager = new PoolingHttpClientConnectionManager(
registry, null, null, null, 5000, TimeUnit.MILLISECONDS
);
}
else {
connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
}
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
connectionManager.setMaxTotal(getPoolMaxTotal());
connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute());
builder.setConnectionManager(connectionManager);

View File

@ -19,24 +19,35 @@ package ca.uhn.fhir.rest.client.impl;
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import java.lang.reflect.*;
import java.util.*;
import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException;
import ca.uhn.fhir.rest.client.method.BaseMethodBinding;
import ca.uhn.fhir.util.FhirTerser;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException;
import ca.uhn.fhir.rest.client.method.BaseMethodBinding;
import ca.uhn.fhir.util.FhirTerser;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Base class for a REST client factory implementation
@ -179,14 +190,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
return new GenericClient(myContext, httpClient, theServerBase, this);
}
@Override
public synchronized IGenericClient newTlsGenericClient(String theServerBase, TlsAuthentication theTlsAuthentication) {
validateConfigured();
IHttpClient httpClient = getHttpClient(theServerBase, theTlsAuthentication);
return new GenericClient(myContext, httpClient, theServerBase, this);
}
private String normalizeBaseUrlForMap(String theServerBase) {
String serverBase = theServerBase;
if (!serverBase.endsWith("/")) {
@ -383,17 +386,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
*/
protected abstract IHttpClient getHttpClient(String theServerBase);
/**
* Get the http client for the given server base
*
* @param theServerBase
* the server base
* @param theTlsAuthentication
* Configuration to authenticate HTTPS server requests
* @return the http client
*/
protected abstract IHttpClient getHttpClient(String theServerBase, TlsAuthentication theTlsAuthentication);
/**
* Reset the http client. This method is used when parameters have been set and a
* new http client needs to be created

View File

@ -3,4 +3,6 @@ type: add
issue: 3776
jira: SMILE-651
title: "Previously, the HAPI FHIR CLI commands that made HTTP requests could only be used for HTTP endpoints.
This feature adds support for HTTPS endpoints by using TLS authentication."
This feature adds support for HTTPS endpoints by using TLS authentication for `ExampleDataUploader`,
`ExportConceptMapToCsvCommand`, `ImportCsvToConceptMapCommand`, `ReindexTerminologyCommand` and
`UploadTerminologyCommand`."

View File

@ -65,16 +65,8 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
}
public synchronized Client getNativeClientClient() {
return getNativeClientClient(null);
}
public synchronized Client getNativeClientClient(TlsAuthentication theTlsAuthentication) {
if (myNativeClient == null) {
ClientBuilder builder = ClientBuilder.newBuilder();
if(theTlsAuthentication != null){
SSLContext sslContext = TlsAuthenticationSvc.createSslContext(theTlsAuthentication);
builder.sslContext(sslContext);
}
myNativeClient = builder.build();
}
@ -93,12 +85,6 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
return new JaxRsHttpClient(client, url, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
}
@Override
public synchronized IHttpClient getTlsHttpsClient(StringBuilder url, TlsAuthentication theTlsAuthentication, Map<String, List<String>> theIfNoneExistParams, String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
Client client = getNativeClientClient(theTlsAuthentication);
return new JaxRsHttpClient(client, url, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
}
/***
* Not supported with default Jax-Rs client implementation
* @param theHost
@ -134,11 +120,6 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
return new JaxRsHttpClient(getNativeClientClient(), new StringBuilder(theServerBase), null, null, null, null);
}
@Override
protected synchronized JaxRsHttpClient getHttpClient(String theServerBase, TlsAuthentication theTlsAuthentication) {
return new JaxRsHttpClient(getNativeClientClient(theTlsAuthentication), new StringBuilder(theServerBase), null, null, null, null);
}
@Override
protected void resetHttpClient() {
if (myNativeClient != null)

View File

@ -85,24 +85,6 @@ public class JaxRsRestfulClientFactoryTest extends BaseFhirVersionParameterizedT
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttps(FhirVersionEnum theFhirVersion) {
FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion);
JaxRsRestfulClientFactory factory = new JaxRsRestfulClientFactory(fhirVersionParams.getFhirContext());
Client authenticatedClient = factory.getNativeClientClient(getTlsAuthentication());
Response response = authenticatedClient
.target(fhirVersionParams.getSecuredPatientEndpoint())
.request(MediaType.JSON_UTF_8.toString())
.get(Response.class);
assertEquals(200, response.getStatus());
String json = response.readEntity(String.class);
IBaseResource bundle = fhirVersionParams.parseResource(json);
assertEquals(fhirVersionParams.getFhirVersion(), bundle.getStructureFhirVersionEnum());
}
@ParameterizedTest
@MethodSource("baseParamsProvider")
public void testNativeClientHttpsNoCredentials(FhirVersionEnum theFhirVersion) {

View File

@ -25,8 +25,10 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.tls.KeyStoreType;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
import ca.uhn.fhir.tls.KeyStoreType;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
@ -38,6 +40,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.jupiter.api.extension.RegisterExtension;
import javax.servlet.Servlet;
import java.net.ServerSocket;
@ -58,6 +61,9 @@ public abstract class BaseRestServerHelper {
protected String mySecureBase;
protected IGenericClient myClient;
@RegisterExtension
public TlsAuthenticationTestHelper myTlsAuthenticationTestHelper = new TlsAuthenticationTestHelper();
public BaseRestServerHelper(FhirContext theFhirContext) {
myFhirContext = theFhirContext;
}
@ -105,6 +111,7 @@ public abstract class BaseRestServerHelper {
myFhirContext.getRestfulClientFactory().setConnectTimeout(60000);
myFhirContext.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myClient = myFhirContext.newRestfulGenericClient(myBase);
myClient.registerInterceptor(new LoggingInterceptor(false));
}
@ -191,4 +198,12 @@ public abstract class BaseRestServerHelper {
public abstract IIdType createObservationForPatient(IIdType theFirstTargetPatientId);
public abstract IIdType createObservation(IBaseResource theBaseResource);
public void setServerAddressStrategy(boolean theUseHttps){
String path = theUseHttps ? mySecureBase : myBase;
HardcodedServerAddressStrategy strategy = new HardcodedServerAddressStrategy(path);
setServerAddressStrategy(strategy);
}
protected abstract void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy);
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.test.utilities;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.rest.annotation.Transaction;
@ -28,6 +29,7 @@ import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
@ -341,6 +343,11 @@ public class RestServerDstu3Helper extends BaseRestServerHelper implements IPoin
return myRestServer.getObservationResourceProvider().store((Observation) theBaseResource);
}
@Override
protected void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
myRestServer.setServerAddressStrategy(theServerAddressStrategy);
}
public void setConceptMapResourceProvider(HashMapResourceProvider<ConceptMap> theResourceProvider) {
myRestServer.setConceptMapResourceProvider(theResourceProvider);
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.test.utilities;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
@ -28,6 +29,8 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
@ -243,6 +246,11 @@ public class RestServerR4Helper extends BaseRestServerHelper implements BeforeEa
return myRestServer.getInterceptorService();
}
@Override
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy){
myRestServer.setServerAddressStrategy(theServerAddressStrategy);
}
private static class MyRestfulServer extends RestfulServer {
private boolean myFailNextPut;
private HashMapResourceProvider<Patient> myPatientResourceProvider;

View File

@ -100,7 +100,7 @@ public class TlsAuthenticationTestHelper implements AfterEachCallback {
return myTlsAuthentication;
}
private File createTlsAuthenticationFile() {
public File createTlsAuthenticationFile() {
try {
ObjectMapper mapper = new ObjectMapper();