Improve Test coverage of DFC
[nonrtric/plt/ranpm.git] / datafilecollector / src / main / java / org / oran / datafile / http / DfcHttpsClient.java
1 /*-
2  * ============LICENSE_START======================================================================
3  * Copyright (C) 2021 Nokia. All rights reserved.
4  * Copyright (C) 2023 Nordix Foundation.
5  * ===============================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7  * in compliance with the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software distributed under the License
12  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing permissions and limitations under
14  * the License.
15  * ============LICENSE_END========================================================================
16  */
17 package org.oran.datafile.http;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.UnknownHostException;
22 import java.nio.file.Files;
23 import java.nio.file.Path;
24 import java.nio.file.StandardCopyOption;
25
26 import javax.net.ssl.SSLHandshakeException;
27 import javax.net.ssl.SSLPeerUnverifiedException;
28
29 import org.apache.http.HttpEntity;
30 import org.apache.http.HttpResponse;
31 import org.apache.http.client.config.RequestConfig;
32 import org.apache.http.client.methods.CloseableHttpResponse;
33 import org.apache.http.client.methods.HttpGet;
34 import org.apache.http.config.SocketConfig;
35 import org.apache.http.conn.ConnectTimeoutException;
36 import org.apache.http.conn.HttpHostConnectException;
37 import org.apache.http.impl.client.CloseableHttpClient;
38 import org.apache.http.impl.client.HttpClients;
39 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
40 import org.apache.http.util.EntityUtils;
41 import org.oran.datafile.commons.FileCollectClient;
42 import org.oran.datafile.exceptions.DatafileTaskException;
43 import org.oran.datafile.exceptions.NonRetryableDatafileTaskException;
44 import org.oran.datafile.model.FileServerData;
45 import org.oran.datafile.oauth2.SecurityContext;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * Gets file from PNF with HTTPS protocol.
51  *
52  */
53 public class DfcHttpsClient implements FileCollectClient {
54
55     protected CloseableHttpClient httpsClient;
56
57     private static final Logger logger = LoggerFactory.getLogger(DfcHttpsClient.class);
58     private static final int FIFTEEN_SECONDS = 15 * 1000;
59
60     private final FileServerData fileServerData;
61     private final PoolingHttpClientConnectionManager connectionManager;
62     private final SecurityContext securityContext;
63
64     public DfcHttpsClient(SecurityContext securityContext, FileServerData fileServerData,
65         PoolingHttpClientConnectionManager connectionManager) {
66         this.fileServerData = fileServerData;
67         this.connectionManager = connectionManager;
68         this.securityContext = securityContext;
69     }
70
71     @Override
72     public void open() {
73         logger.trace("Setting httpsClient for file download.");
74         SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).build();
75
76         RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(FIFTEEN_SECONDS).build();
77
78         httpsClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultSocketConfig(socketConfig)
79             .setDefaultRequestConfig(requestConfig).build();
80
81         logger.trace("httpsClient prepared for connection.");
82     }
83
84     @Override
85     @SuppressWarnings("java:S2139")
86     public void collectFile(String remoteFile, Path localFile) throws DatafileTaskException {
87         logger.trace("Prepare to collectFile {}", localFile);
88         HttpGet httpGet = new HttpGet(HttpUtils.prepareHttpsUri(fileServerData, remoteFile));
89
90         String authorizationContent = this.securityContext.getBearerAuthToken();
91         if (!authorizationContent.isEmpty()) {
92             httpGet.addHeader("Authorization", "Bearer " + authorizationContent);
93         } else if (!this.fileServerData.password.isEmpty()) {
94             authorizationContent = HttpUtils.basicAuthContent(this.fileServerData.userId, this.fileServerData.password);
95             httpGet.addHeader("Authorization", authorizationContent);
96         }
97         try {
98             HttpResponse httpResponse = makeCall(httpGet);
99             processResponse(httpResponse, localFile);
100         } catch (IOException e) {
101             logger.error("Error downloading file from server. Details: {}", e.getMessage());
102             throw new DatafileTaskException("Error downloading file from server. ", e);
103         }
104         logger.trace("HTTPS collectFile OK");
105     }
106
107     @SuppressWarnings("java:S2139")
108     HttpResponse makeCall(HttpGet httpGet) throws IOException, DatafileTaskException {
109         try {
110             HttpResponse httpResponse = executeHttpClient(httpGet);
111             if (isResponseOk(httpResponse)) {
112                 return httpResponse;
113             }
114
115             EntityUtils.consume(httpResponse.getEntity());
116             if (isErrorInConnection(httpResponse)) {
117                 logger.warn("Failed to download file, reason: {}, code: {}",
118                     httpResponse.getStatusLine().getReasonPhrase(), httpResponse.getStatusLine());
119                 throw new NonRetryableDatafileTaskException(HttpUtils.retryableResponse(getResponseCode(httpResponse)));
120             }
121             throw new DatafileTaskException(HttpUtils.nonRetryableResponse(getResponseCode(httpResponse)));
122         } catch (ConnectTimeoutException | UnknownHostException | HttpHostConnectException | SSLHandshakeException
123             | SSLPeerUnverifiedException e) {
124             logger.warn("Unable to get file from xNF: {}", e.getMessage());
125             throw new NonRetryableDatafileTaskException("Unable to get file from xNF. No retry attempts will be done.",
126                 e);
127         }
128     }
129
130     CloseableHttpResponse executeHttpClient(HttpGet httpGet) throws IOException {
131         return httpsClient.execute(httpGet);
132     }
133
134     boolean isResponseOk(HttpResponse httpResponse) {
135         return getResponseCode(httpResponse) == 200;
136     }
137
138     private int getResponseCode(HttpResponse httpResponse) {
139         return httpResponse.getStatusLine().getStatusCode();
140     }
141
142     boolean isErrorInConnection(HttpResponse httpResponse) {
143         return getResponseCode(httpResponse) >= 400;
144     }
145
146     void processResponse(HttpResponse response, Path localFile) throws IOException {
147         logger.trace("Starting to process response.");
148         HttpEntity entity = response.getEntity();
149         InputStream stream = entity.getContent();
150         long numBytes = writeFile(localFile, stream);
151         stream.close();
152         EntityUtils.consume(entity);
153         logger.trace("Transmission was successful - {} bytes downloaded.", numBytes);
154     }
155
156     long writeFile(Path localFile, InputStream stream) throws IOException {
157         return Files.copy(stream, localFile, StandardCopyOption.REPLACE_EXISTING);
158     }
159
160     @Override
161     public void close() {
162         logger.trace("Https client has ended downloading process.");
163     }
164 }