476c1606898e7c20330ca13da25e090218ab51e9
[ccsdk/oran.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * ONAP : ccsdk oran
4  * ======================================================================
5  * Copyright (C) 2019-2022 Nordix Foundation. All rights reserved.
6  * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved.
7  * ======================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ========================LICENSE_END===================================
20  */
21
22 package org.onap.ccsdk.oran.a1policymanagementservice.clients;
23
24 import io.netty.channel.ChannelOption;
25 import io.netty.handler.ssl.SslContext;
26 import io.netty.handler.timeout.ReadTimeoutHandler;
27 import io.netty.handler.timeout.WriteTimeoutHandler;
28
29 import java.lang.invoke.MethodHandles;
30
31 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig.HttpProxyConfig;
32 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientUtil;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import org.springframework.http.MediaType;
36 import org.springframework.http.ResponseEntity;
37 import org.springframework.lang.Nullable;
38 import org.springframework.web.reactive.function.client.WebClient;
39 import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
40 import org.springframework.web.reactive.function.client.WebClientResponseException;
41
42 import reactor.core.publisher.Mono;
43 import reactor.netty.http.client.HttpClient;
44 import reactor.netty.transport.ProxyProvider;
45
46 /**
47  * Generic reactive REST client.
48  */
49 public class AsyncRestClient {
50
51     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
52     private WebClient webClient = null;
53     private final String baseUrl;
54     private final SslContext sslContext;
55     private final HttpProxyConfig httpProxyConfig;
56     private final SecurityContext securityContext;
57
58     public AsyncRestClient(String baseUrl, @Nullable SslContext sslContext, @Nullable HttpProxyConfig httpProxyConfig,
59             SecurityContext securityContext) {
60         this.baseUrl = baseUrl;
61         this.sslContext = sslContext;
62         this.httpProxyConfig = httpProxyConfig;
63         this.securityContext = securityContext;
64     }
65
66     public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
67         Mono<String> bodyProducer = body != null ? Mono.just(body) : Mono.empty();
68
69         RequestHeadersSpec<?> request = getWebClient() //
70                 .post() //
71                 .uri(uri) //
72                 .contentType(MediaType.APPLICATION_JSON) //
73                 .body(bodyProducer, String.class);
74         return retrieve(request);
75     }
76
77     public Mono<String> post(String uri, @Nullable String body) {
78         return postForEntity(uri, body) //
79                 .map(this::toBody);
80     }
81
82     public Mono<String> postWithAuthHeader(String uri, String body, String username, String password) {
83         RequestHeadersSpec<?> request = getWebClient() //
84                 .post() //
85                 .uri(uri) //
86                 .headers(headers -> headers.setBasicAuth(username, password)) //
87                 .contentType(MediaType.APPLICATION_JSON) //
88                 .bodyValue(body);
89         return retrieve(request) //
90                 .map(this::toBody);
91     }
92
93     public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
94         RequestHeadersSpec<?> request = getWebClient() //
95                 .put() //
96                 .uri(uri) //
97                 .contentType(MediaType.APPLICATION_JSON) //
98                 .bodyValue(body);
99         return retrieve(request);
100     }
101
102     public Mono<ResponseEntity<String>> putForEntity(String uri) {
103         RequestHeadersSpec<?> request = getWebClient() //
104                 .put() //
105                 .uri(uri);
106         return retrieve(request);
107     }
108
109     public Mono<String> put(String uri, String body) {
110         return putForEntity(uri, body) //
111                 .map(this::toBody);
112     }
113
114     public Mono<ResponseEntity<String>> getForEntity(String uri) {
115         RequestHeadersSpec<?> request = getWebClient().get().uri(uri);
116         return retrieve(request);
117     }
118
119     public Mono<String> get(String uri) {
120         return getForEntity(uri) //
121                 .map(this::toBody);
122     }
123
124     public Mono<ResponseEntity<String>> deleteForEntity(String uri) {
125         RequestHeadersSpec<?> request = getWebClient().delete().uri(uri);
126         return retrieve(request);
127     }
128
129     public Mono<String> delete(String uri) {
130         return deleteForEntity(uri) //
131                 .map(this::toBody);
132     }
133
134     private Mono<ResponseEntity<String>> retrieve(RequestHeadersSpec<?> request) {
135         if (securityContext.isConfigured()) {
136             request.headers(h -> h.setBearerAuth(securityContext.getBearerAuthToken()));
137         }
138         return request.retrieve() //
139                 .toEntity(String.class) //
140                 .doOnError(this::onError);
141
142     }
143
144     private void onError(Throwable t) {
145         if (t instanceof WebClientResponseException) {
146             WebClientResponseException e = (WebClientResponseException) t;
147             logger.debug("Response error: {}", e.getResponseBodyAsString());
148         }
149     }
150
151     private String toBody(ResponseEntity<String> entity) {
152         if (entity.getBody() == null) {
153             return "";
154         } else {
155             return entity.getBody();
156         }
157     }
158
159     private boolean isHttpProxyConfigured() {
160         return httpProxyConfig != null && httpProxyConfig.getHttpProxyPort() > 0
161                 && !httpProxyConfig.getHttpProxyHost().isEmpty();
162     }
163
164     private HttpClient buildHttpClient() {
165         HttpClient httpClient = HttpClient.create() //
166                 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
167                 .doOnConnected(connection -> {
168                     connection.addHandlerLast(new ReadTimeoutHandler(30));
169                     connection.addHandlerLast(new WriteTimeoutHandler(30));
170                 });
171
172         if (this.sslContext != null) {
173             httpClient = httpClient.secure(ssl -> ssl.sslContext(sslContext));
174         }
175
176         if (isHttpProxyConfigured()) {
177             httpClient = httpClient.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP)
178                     .host(httpProxyConfig.getHttpProxyHost()).port(httpProxyConfig.getHttpProxyPort()));
179         }
180         return httpClient;
181     }
182
183     public WebClient buildWebClient(String baseUrl) {
184         final HttpClient httpClient = buildHttpClient();
185         return WebClientUtil.buildWebClient(baseUrl, httpClient);
186     }
187
188     private WebClient getWebClient() {
189         if (this.webClient == null) {
190             this.webClient = buildWebClient(baseUrl);
191         }
192         return this.webClient;
193     }
194 }