From 2b8a7df25878f546ace25373f44baeb026cf6d2b Mon Sep 17 00:00:00 2001 From: Christof Rabensteiner Date: Tue, 20 Aug 2019 11:13:28 +0200 Subject: Add Custom Http Headers to HTTP Clients - Change app2mzs schema: Add Custom Headers to ClientType Element. - Set custom headers in ClientFactory. - Parse custom headers from spring environment. - Merge custom headers from two different profiles. - Add example to application.yaml. - Test conversion of custom headers from spring profile - Test merging and overriding custom headers. --- .../at/gv/egiz/moazs/client/ClientFactory.java | 10 ++- .../moazs/preprocess/ConfigProfileGenerator.java | 3 +- .../at/gv/egiz/moazs/preprocess/ConfigUtil.java | 62 +++++++++++++++++ src/main/resources/application.yaml | 10 ++- src/main/resources/mzs/app2mzs.xsd | 9 +++ src/test/java/at/gv/egiz/moazs/ConfigUtilTest.java | 80 ++++++++++++++++++++++ 6 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/test/java/at/gv/egiz/moazs/ConfigUtilTest.java diff --git a/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java b/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java index d0a445b..94eb712 100644 --- a/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java +++ b/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java @@ -1,5 +1,6 @@ package at.gv.egiz.moazs.client; +import at.gv.egiz.moazs.preprocess.ConfigUtil; import at.gv.egiz.moazs.util.FileUtils; import at.gv.egiz.moazs.util.StoreSOAPBodyBinaryInRepositoryInterceptor; import at.gv.zustellung.app2mzs.xsd.ClientType; @@ -10,6 +11,7 @@ import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.jaxws.JaxWsClientFactoryBean; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; +import org.apache.cxf.message.Message; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; import org.slf4j.Logger; @@ -23,6 +25,7 @@ import javax.xml.ws.BindingProvider; import javax.xml.ws.soap.SOAPBinding; import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder; +import static java.util.stream.Collectors.toMap; @Component public class ClientFactory { @@ -32,14 +35,16 @@ public class ClientFactory { private final StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor; private final SSLContextCreator sslContextCreator; private final FileUtils fileUtils; + private final ConfigUtil configUtil; @Autowired public ClientFactory(StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor, SSLContextCreator creator, - FileUtils fileUtils) { + FileUtils fileUtils, ConfigUtil configUtil) { this.storeResponseInterceptor = storeResponseInterceptor; this.sslContextCreator = creator; this.fileUtils = fileUtils; + this.configUtil = configUtil; } /** @@ -68,6 +73,9 @@ public class ClientFactory { httpClientPolicy.setReceiveTimeout(params.getReceiveTimeout().longValueExact()); http.setClient(httpClientPolicy); + var customHeaders = configUtil.convertHeadersToMap(params.getCustomHTTPHeader()); + client.getRequestContext().put(Message.PROTOCOL_HEADERS, customHeaders); + if (params.getURL().startsWith("https")) { TLSClientParameters tlsParams = setupTLSParams(params.getSSL()); http.setTlsClientParameters(tlsParams); diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java index 0637f98..ed67154 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java @@ -43,7 +43,8 @@ public class ConfigProfileGenerator { private ConfigProfileGenerator( SpringPropertiesFacade properties, ConfigUtil util, - MzsDeliveryRequestValidator validator, boolean verifyCompletenessOfDefaultConfiguration, + MzsDeliveryRequestValidator validator, + boolean verifyCompletenessOfDefaultConfiguration, String profilePrefix, String defaultConfigKey) { this.util = util; diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java index f49132f..ec7b7c8 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigUtil.java @@ -5,15 +5,19 @@ import at.gv.zustellung.app2mzs.xsd.*; import org.springframework.stereotype.Component; import java.math.BigInteger; +import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static at.gv.zustellung.app2mzs.xsd.ClientType.clientTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder; +import static at.gv.zustellung.app2mzs.xsd.CustomHTTPHeaderType.*; import static at.gv.zustellung.app2mzs.xsd.ForwardResponseToServiceType.forwardResponseToServiceTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.MsgResponseSinksType.msgResponseSinksTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.SSLType.SSLTypeBuilder; import static at.gv.zustellung.app2mzs.xsd.SaveResponseToFileType.saveResponseToFileTypeBuilder; +import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; @Component @@ -41,6 +45,7 @@ public class ConfigUtil { public static final String FORWARD_RESPONSE_TO_SERVICE_KEY = "forward-response-to-service"; public static final String MZS_CLIENT_KEY = "mzs-client"; public static final String SERVICE_TIMEOUT_KEY = "service-timeout"; + public static final String CUSTOM_HTTP_HEADERS_KEY = "custom-http-headers"; /** @@ -95,11 +100,16 @@ public class ConfigUtil { SSLType ssl = sslParams.isEmpty() ? null : buildSSL(sslParams); + var customHeaderParams = filterMapByPrefix(clientParams, CUSTOM_HTTP_HEADERS_KEY); + var customHeaders = customHeaderParams.isEmpty() + ? null : convertMapToHeaders(customHeaderParams); + return clientTypeBuilder() .withURL(url) .withSSL(ssl) .withConnectionTimeout(connectionTimeout) .withReceiveTimeout(receiveTimeout) + .withCustomHTTPHeader(customHeaders) .build(); } @@ -243,6 +253,10 @@ public class ConfigUtil { builder.withReceiveTimeout(primary.getReceiveTimeout()); } + if (primary.getCustomHTTPHeader() != null) { + builder.withCustomHTTPHeader(merge(primary.getCustomHTTPHeader(), fallback.getCustomHTTPHeader())); + } + return builder.build(); } @@ -282,6 +296,26 @@ public class ConfigUtil { } + private List merge(List primary, List fallback) { + + if (fallback == null) return primary; + + var primaryMap = convertHeadersToMap(primary); + var fallbackMap = convertHeadersToMap(fallback); + + Map mergedMap = Stream.of(fallbackMap, primaryMap) + .flatMap(map -> map.entrySet().stream()) + .collect(toMap( + Map.Entry::getKey, + entry -> entry.getValue().get(0), + (v1, v2) -> v2 + )); + + return convertMapToHeaders(mergedMap); + + } + + private MsgResponseSinksType merge(MsgResponseSinksType primary, MsgResponseSinksType fallback) { if (fallback == null) { @@ -340,4 +374,32 @@ public class ConfigUtil { } + /** + * Convert a Map that is indexed by header names into a List of CustomHTTPHeaders. + * @param customHeaderMap map of headers, indexed by header names. + * @return List of CustomHTTPHeaders + */ + public List convertMapToHeaders(Map customHeaderMap) { + return customHeaderMap.entrySet().stream() + .map(e -> customHTTPHeaderTypeBuilder() + .withName(e.getKey()) + .withValue(e.getValue()) + .build()) + .collect(toList()); + } + + + /** + * Convert a List of CustomHTTPHeaders into a Map that is indexed by the header names. Values are wrapped in Lists. + * @param customHeaders List of CustomHTTPHeaders + * @return Map of headers, indexed by header names. + */ + public Map> convertHeadersToMap(List customHeaders) { + return customHeaders + .stream() + .collect(toMap( + h -> h.getName(), + h -> List.of(h.getValue()))); + } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index ce4d892..0380cf5 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -61,11 +61,15 @@ delivery-request-configuration-profiles: # Mandatory url: http://localhost:8082/tnvz/ + connection-timeout: 0 + receive-timeout: 0 # Optional - # ssl: ... - # connection-timeout: ... - # receive-timeout: ... + #ssl: ... + #custom-http-headers: + # X-PVP-NAME-1: VALUE-X + # X-PVP-NAME-2: VALUE-Y + # ... # Mandatory # Parameters for the connection to msg. diff --git a/src/main/resources/mzs/app2mzs.xsd b/src/main/resources/mzs/app2mzs.xsd index d19ff3b..7ae243b 100644 --- a/src/main/resources/mzs/app2mzs.xsd +++ b/src/main/resources/mzs/app2mzs.xsd @@ -3,6 +3,7 @@ + @@ -98,6 +99,7 @@ + @@ -118,6 +120,13 @@ + + + + + + + diff --git a/src/test/java/at/gv/egiz/moazs/ConfigUtilTest.java b/src/test/java/at/gv/egiz/moazs/ConfigUtilTest.java new file mode 100644 index 0000000..d7ac0a1 --- /dev/null +++ b/src/test/java/at/gv/egiz/moazs/ConfigUtilTest.java @@ -0,0 +1,80 @@ +package at.gv.egiz.moazs; + +import at.gv.egiz.moazs.preprocess.ConfigUtil; +import at.gv.zustellung.app2mzs.xsd.ConfigType; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.Map; + +import static at.gv.zustellung.app2mzs.xsd.ClientType.clientTypeBuilder; +import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder; +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigUtilTest { + + private ConfigUtil configUtil; + + @Before + public void setup() { + configUtil = new ConfigUtil(); + } + + @Test + public void convertTNVZClientsCustomHttpHeaders() { + var prefix = ConfigUtil.TNVZ_CLIENT_KEY + "." + ConfigUtil.CUSTOM_HTTP_HEADERS_KEY + "."; + var map = Map.of( + prefix + "X-PVP-VERSION", "2.1", + prefix + "X-PVP-BINDING", "http", + prefix + "X-PVP-PARTICIPANT_ID", "AT:L9:MA2412" + ); + + ConfigType config = configUtil.convert(map); + var customHeaderMap = configUtil.convertHeadersToMap(config.getTNVZClient().getCustomHTTPHeader()); + + assertThat(customHeaderMap).containsEntry("X-PVP-VERSION", List.of("2.1")); + assertThat(customHeaderMap).containsEntry("X-PVP-BINDING", List.of("http")); + assertThat(customHeaderMap).containsEntry("X-PVP-PARTICIPANT_ID", List.of("AT:L9:MA2412")); + assertThat(customHeaderMap).hasSize(3); + } + + @Test + public void overrideCustomHeaders() { + + var fallbackConfig = createConfig(Map.of( + "X-header-1", "fallback-value-a", + "X-header-2", "fallback-value-b" + )); + + var primaryConfig = createConfig(Map.of( + "X-header-2", "primary-value-c", + "X-header-3", "primary-value-d" + )); + + ConfigType mergedConfig = configUtil.merge(primaryConfig, fallbackConfig); + var mergedHeaderMap = configUtil.convertHeadersToMap(mergedConfig.getTNVZClient().getCustomHTTPHeader()); + + assertThat(mergedHeaderMap).containsEntry("X-header-1", List.of("fallback-value-a")); + assertThat(mergedHeaderMap).containsEntry("X-header-2", List.of("primary-value-c")); + assertThat(mergedHeaderMap).containsEntry("X-header-3", List.of("primary-value-d")); + assertThat(mergedHeaderMap).hasSize(3); + + } + + private ConfigType createConfig(Map headerMap) { + + var headers = configUtil.convertMapToHeaders(headerMap); + + var client = clientTypeBuilder() + .withCustomHTTPHeader(headers) + .build(); + + return configTypeBuilder() + .withTNVZClient(client) + .build(); + } + + + +} -- cgit v1.2.3