Added support for using HTTP proxy 54/114654/2
authorPatrikBuhr <patrik.buhr@est.tech>
Fri, 6 Nov 2020 11:07:28 +0000 (12:07 +0100)
committerPatrikBuhr <patrik.buhr@est.tech>
Fri, 6 Nov 2020 13:44:55 +0000 (14:44 +0100)
A HTTP proxy may be used to access NearRT-RICs
For other HTTP acces; Consul; DMAAP, local, and R-APP callbacks no proxy is used.

Change-Id: Ibc3ed8bada76cafb23323bf03b28a9876bed60eb
Issue-ID: CCSDK-2502
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
13 files changed:
a1-policy-management/config/application.yaml
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/AsyncRestClient.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/AsyncRestClientFactory.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/CcsdkA1AdapterClient.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfig.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/WebClientConfig.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/ServiceCallbacks.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/PolicyController.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/dmaap/DmaapMessageConsumer.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/AsyncRestClientTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/CcsdkA1AdapterClientTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v1/ApplicationTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java

index b5cfd51..823b93b 100644 (file)
@@ -56,4 +56,6 @@ app:
     trust-store-used: false
     trust-store-password: policy_agent
     trust-store: /opt/app/policy-agent/etc/cert/truststore.jks
+    http.proxy-host: 
+    http.proxy-port: 0
 
index 7f453a2..cba1c7c 100644 (file)
@@ -28,6 +28,7 @@ import io.netty.handler.timeout.WriteTimeoutHandler;
 import java.lang.invoke.MethodHandles;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig.HttpProxyConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
@@ -42,6 +43,7 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
 import reactor.core.publisher.Mono;
 import reactor.netty.http.client.HttpClient;
 import reactor.netty.resources.ConnectionProvider;
+import reactor.netty.tcp.ProxyProvider.Proxy;
 import reactor.netty.tcp.TcpClient;
 
 /**
@@ -54,19 +56,12 @@ public class AsyncRestClient {
     private final String baseUrl;
     private static final AtomicInteger sequenceNumber = new AtomicInteger();
     private final SslContext sslContext;
+    private final HttpProxyConfig httpProxyConfig;
 
-    /**
-     * Note that only http (not https) will work when this constructor is used.
-     * 
-     * @param baseUrl
-     */
-    public AsyncRestClient(String baseUrl) {
-        this(baseUrl, null);
-    }
-
-    public AsyncRestClient(String baseUrl, SslContext sslContext) {
+    public AsyncRestClient(String baseUrl, @Nullable SslContext sslContext, @Nullable HttpProxyConfig httpProxyConfig) {
         this.baseUrl = baseUrl;
         this.sslContext = sslContext;
+        this.httpProxyConfig = httpProxyConfig;
     }
 
     public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
@@ -200,27 +195,32 @@ public class AsyncRestClient {
         }
     }
 
-    private TcpClient createTcpClientSecure(SslContext sslContext) {
-        return TcpClient.create(ConnectionProvider.newConnection()) //
-                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
-                .secure(c -> c.sslContext(sslContext)) //
-                .doOnConnected(connection -> {
-                    connection.addHandlerLast(new ReadTimeoutHandler(30));
-                    connection.addHandlerLast(new WriteTimeoutHandler(30));
-                });
+    private boolean isHttpProxyConfigured() {
+        return httpProxyConfig != null && httpProxyConfig.httpProxyPort() > 0
+                && !httpProxyConfig.httpProxyHost().isEmpty();
     }
 
-    private TcpClient createTcpClientInsecure() {
-        return TcpClient.create(ConnectionProvider.newConnection()) //
+    private TcpClient createTcpClient() {
+        TcpClient client = TcpClient.create(ConnectionProvider.newConnection()) //
                 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
                 .doOnConnected(connection -> {
                     connection.addHandlerLast(new ReadTimeoutHandler(30));
                     connection.addHandlerLast(new WriteTimeoutHandler(30));
                 });
+        if (this.sslContext != null) {
+            client = client.secure(c -> c.sslContext(sslContext));
+        }
+        if (isHttpProxyConfigured()) {
+            client = client.proxy(proxy -> {
+                proxy.type(Proxy.HTTP).host(httpProxyConfig.httpProxyHost()).port(httpProxyConfig.httpProxyPort());
+            });
+        }
+        return client;
     }
 
     private WebClient createWebClient(String baseUrl, TcpClient tcpClient) {
         HttpClient httpClient = HttpClient.from(tcpClient);
+
         ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
         ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder() //
                 .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)) //
@@ -235,13 +235,8 @@ public class AsyncRestClient {
     private Mono<WebClient> getWebClient() {
         if (this.webClient == null) {
             try {
-                if (this.sslContext != null) {
-                    TcpClient tcpClient = createTcpClientSecure(sslContext);
-                    this.webClient = createWebClient(this.baseUrl, tcpClient);
-                } else {
-                    TcpClient tcpClient = createTcpClientInsecure();
-                    this.webClient = createWebClient(this.baseUrl, tcpClient);
-                }
+                TcpClient tcpClient = createTcpClient();
+                this.webClient = createWebClient(this.baseUrl, tcpClient);
             } catch (Exception e) {
                 logger.error("Could not create WebClient {}", e.getMessage());
                 return Mono.error(e);
index 5b88fce..b5590cc 100644 (file)
@@ -42,6 +42,7 @@ import java.util.stream.Collectors;
 import javax.net.ssl.KeyManagerFactory;
 
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig;
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig.HttpProxyConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.util.ResourceUtils;
@@ -49,29 +50,43 @@ import org.springframework.util.ResourceUtils;
 /**
  * Factory for a generic reactive REST client.
  */
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
 public class AsyncRestClientFactory {
     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
     private final SslContextFactory sslContextFactory;
+    private final HttpProxyConfig httpProxyConfig;
 
     public AsyncRestClientFactory(WebClientConfig clientConfig) {
         if (clientConfig != null) {
             this.sslContextFactory = new CachingSslContextFactory(clientConfig);
+            this.httpProxyConfig = clientConfig.httpProxyConfig();
         } else {
+            logger.warn("HTTPS will not work");
             this.sslContextFactory = null;
+            this.httpProxyConfig = null;
         }
     }
 
+    public AsyncRestClient createRestClientNoHttpProxy(String baseUrl) {
+        return createRestClient(baseUrl, false);
+    }
+
     public AsyncRestClient createRestClient(String baseUrl) {
+        return createRestClient(baseUrl, true);
+    }
+
+    private AsyncRestClient createRestClient(String baseUrl, boolean useHttpProxy) {
         if (this.sslContextFactory != null) {
             try {
-                return new AsyncRestClient(baseUrl, this.sslContextFactory.createSslContext());
+                return new AsyncRestClient(baseUrl, this.sslContextFactory.createSslContext(),
+                        useHttpProxy ? httpProxyConfig : null);
             } catch (Exception e) {
                 String exceptionString = e.toString();
                 logger.error("Could not init SSL context, reason: {}", exceptionString);
             }
         }
-        return new AsyncRestClient(baseUrl);
+        return new AsyncRestClient(baseUrl, null, httpProxyConfig);
     }
 
     private class SslContextFactory {
index 3b581b6..448b7b6 100644 (file)
@@ -81,7 +81,8 @@ public class CcsdkA1AdapterClient implements A1Client {
      * Constructor that creates the REST client to use.
      *
      * @param protocolType the southbound protocol of the controller. Supported
-     *        protocols are CCSDK_A1_ADAPTER_STD_V1_1, CCSDK_A1_ADAPTER_OSC_V1 and
+     *        protocols are CCSDK_A1_ADAPTER_STD_V1_1,
+     *        CCSDK_A1_ADAPTER_OSC_V1 and
      *        CCSDK_A1_ADAPTER_STD_V2_0_0 with
      * @param ricConfig the configuration of the Near-RT RIC to communicate
      *        with
@@ -92,14 +93,15 @@ public class CcsdkA1AdapterClient implements A1Client {
     public CcsdkA1AdapterClient(A1ProtocolType protocolType, RicConfig ricConfig, ControllerConfig controllerConfig,
             AsyncRestClientFactory restClientFactory) {
         this(protocolType, ricConfig, controllerConfig,
-                restClientFactory.createRestClient(controllerConfig.baseUrl() + "/restconf/operations"));
+                restClientFactory.createRestClientNoHttpProxy(controllerConfig.baseUrl() + "/restconf/operations"));
     }
 
     /**
      * Constructor where the REST client to use is provided.
      *
      * @param protocolType the southbound protocol of the controller. Supported
-     *        protocols are CCSDK_A1_ADAPTER_STD_V1_1, CCSDK_A1_ADAPTER_OSC_V1 and
+     *        protocols are CCSDK_A1_ADAPTER_STD_V1_1,
+     *        CCSDK_A1_ADAPTER_OSC_V1 and
      *        CCSDK_A1_ADAPTER_STD_V2_0_0 with
      * @param ricConfig the configuration of the Near-RT RIC to communicate
      *        with
@@ -108,7 +110,7 @@ public class CcsdkA1AdapterClient implements A1Client {
      *
      * @throws IllegalArgumentException when the protocolType is illegal.
      */
-    public CcsdkA1AdapterClient(A1ProtocolType protocolType, RicConfig ricConfig, ControllerConfig controllerConfig,
+    CcsdkA1AdapterClient(A1ProtocolType protocolType, RicConfig ricConfig, ControllerConfig controllerConfig,
             AsyncRestClient restClient) {
         if (A1ProtocolType.CCSDK_A1_ADAPTER_STD_V1_1.equals(protocolType) //
                 || A1ProtocolType.CCSDK_A1_ADAPTER_OSC_V1.equals(protocolType) //
index 65c6daa..d6dc397 100644 (file)
@@ -29,6 +29,7 @@ import javax.validation.constraints.NotEmpty;
 
 import lombok.Getter;
 
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig.HttpProxyConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -64,6 +65,12 @@ public class ApplicationConfig {
     @Value("${app.webclient.trust-store}")
     private String sslTrustStore = "";
 
+    @Value("${app.webclient.http.proxy-host}")
+    private String httpProxyHost = "";
+
+    @Value("${app.webclient.http.proxy-port}")
+    private int httpProxyPort = 0;
+
     private Map<String, RicConfig> ricConfigs = new HashMap<>();
 
     @Getter
@@ -79,6 +86,11 @@ public class ApplicationConfig {
     }
 
     public WebClientConfig getWebClientConfig() {
+        HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
+                .httpProxyHost(this.httpProxyHost) //
+                .httpProxyPort(this.httpProxyPort) //
+                .build();
+
         return ImmutableWebClientConfig.builder() //
                 .keyStoreType(this.sslKeyStoreType) //
                 .keyStorePassword(this.sslKeyStorePassword) //
@@ -87,6 +99,7 @@ public class ApplicationConfig {
                 .isTrustStoreUsed(this.sslTrustStoreUsed) //
                 .trustStore(this.sslTrustStore) //
                 .trustStorePassword(this.sslTrustStorePassword) //
+                .httpProxyConfig(httpProxyConfig) //
                 .build();
     }
 
index ef7bd87..beb6e51 100644 (file)
@@ -42,4 +42,13 @@ public interface WebClientConfig {
 
     public String trustStore();
 
+    @Value.Immutable
+    public interface HttpProxyConfig {
+        public String httpProxyHost();
+
+        public int httpProxyPort();
+    }
+
+    public HttpProxyConfig httpProxyConfig();
+
 }
index d6286ee..f9e446e 100644 (file)
@@ -48,7 +48,7 @@ public class ServiceCallbacks {
     private final AsyncRestClient restClient;
 
     public ServiceCallbacks(AsyncRestClientFactory restClientFactory) {
-        this.restClient = restClientFactory.createRestClient("");
+        this.restClient = restClientFactory.createRestClientNoHttpProxy("");
     }
 
     public void notifyServicesRicSynchronized(Ric ric, Services services) {
index 8dd2df6..ec77604 100644 (file)
@@ -97,7 +97,8 @@ public class PolicyController {
     private static Gson gson = new GsonBuilder() //
             .create(); //
 
-    @GetMapping(path = "/v2/policy-types/{policytype_id:.+}", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(path = Consts.V2_API_ROOT + "/policy-types/{policytype_id:.+}",
+            produces = MediaType.APPLICATION_JSON_VALUE)
     @ApiOperation(value = "Returns a policy type definition")
     @ApiResponses(value = { //
             @ApiResponse(code = 200, message = "Policy type", response = PolicyTypeInfo.class), //
index f948e5f..3a06e71 100644 (file)
@@ -182,7 +182,7 @@ public class DmaapMessageConsumer {
 
     protected Mono<String> getFromMessageRouter(String topicUrl) {
         logger.trace("getFromMessageRouter {}", topicUrl);
-        AsyncRestClient c = restClientFactory.createRestClient("");
+        AsyncRestClient c = restClientFactory.createRestClientNoHttpProxy("");
         return c.get(topicUrl);
     }
 
@@ -229,9 +229,9 @@ public class DmaapMessageConsumer {
     private DmaapMessageHandler getDmaapMessageHandler() {
         if (this.dmaapMessageHandler == null) {
             String pmsBaseUrl = "http://localhost:" + this.localServerHttpPort;
-            AsyncRestClient pmsClient = restClientFactory.createRestClient(pmsBaseUrl);
+            AsyncRestClient pmsClient = restClientFactory.createRestClientNoHttpProxy(pmsBaseUrl);
             AsyncRestClient producer =
-                    restClientFactory.createRestClient(this.applicationConfig.getDmaapProducerTopicUrl());
+                    restClientFactory.createRestClientNoHttpProxy(this.applicationConfig.getDmaapProducerTopicUrl());
             this.dmaapMessageHandler = new DmaapMessageHandler(producer, pmsClient);
         }
         return this.dmaapMessageHandler;
index 36bbda5..2e583f6 100644 (file)
@@ -58,7 +58,7 @@ class AsyncRestClientTest {
         InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE);
         Loggers.useJdkLoggers();
         mockWebServer = new MockWebServer();
-        clientUnderTest = new AsyncRestClient(mockWebServer.url(BASE_URL).toString());
+        clientUnderTest = new AsyncRestClient(mockWebServer.url(BASE_URL).toString(), null, null);
     }
 
     @AfterAll
index aad1ed0..985ce55 100644 (file)
@@ -85,7 +85,7 @@ class CcsdkA1AdapterClientTest {
     @Test
     void createClientWithWrongProtocol_thenErrorIsThrown() {
         assertThrows(IllegalArgumentException.class, () -> {
-            new CcsdkA1AdapterClient(A1ProtocolType.STD_V1_1, null, null, new AsyncRestClient("", null));
+            new CcsdkA1AdapterClient(A1ProtocolType.STD_V1_1, null, null, new AsyncRestClient("", null, null));
         });
     }
 
index 551ed59..b516488 100644 (file)
@@ -720,10 +720,11 @@ class ApplicationTest {
                 .isTrustStoreUsed(useTrustValidation) //
                 .trustStore(config.trustStore()) //
                 .trustStorePassword(config.trustStorePassword()) //
+                .httpProxyConfig(config.httpProxyConfig()) //
                 .build();
 
         AsyncRestClientFactory f = new AsyncRestClientFactory(config);
-        return f.createRestClient(baseUrl());
+        return f.createRestClientNoHttpProxy(baseUrl());
     }
 
     private AsyncRestClient restClient() {
index a0cb2af..5e089a0 100644 (file)
@@ -790,10 +790,11 @@ class ApplicationTest {
                 .isTrustStoreUsed(useTrustValidation) //
                 .trustStore(config.trustStore()) //
                 .trustStorePassword(config.trustStorePassword()) //
+                .httpProxyConfig(config.httpProxyConfig()) //
                 .build();
 
         AsyncRestClientFactory f = new AsyncRestClientFactory(config);
-        return f.createRestClient(baseUrl);
+        return f.createRestClientNoHttpProxy(baseUrl);
 
     }