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