X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=restapi-call-node%2Fprovider%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fccsdk%2Fsli%2Fplugins%2Frestapicall%2FRestapiCallNode.java;h=b4d7e1ba8950d81929b74ef0a1dc6b5eabb9bba8;hb=refs%2Fheads%2Fmaster;hp=8038b94ce883febcebf3df5a39fa095ecf2ee7dd;hpb=c08a8a7b8ce929195bff089f7743b5f31d609849;p=ccsdk%2Fsli%2Fplugins.git diff --git a/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java b/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java index 8038b94c..3d704249 100755 --- a/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java +++ b/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java @@ -25,15 +25,22 @@ package org.onap.ccsdk.sli.plugins.restapicall; import static java.lang.Boolean.valueOf; import static javax.ws.rs.client.Entity.entity; import static org.onap.ccsdk.sli.plugins.restapicall.AuthType.fromString; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.ProtocolException; import java.net.SocketException; import java.net.URI; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyStore; import java.util.ArrayList; +import java.util.Base64; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -43,6 +50,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -72,8 +81,13 @@ import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.logging.filter.base.HttpURLConnectionMetricUtil; +import org.onap.logging.filter.base.MetricLogClientFilter; +import org.onap.logging.filter.base.ONAPComponents; +import org.onap.logging.ref.slf4j.ONAPLogConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; public class RestapiCallNode implements SvcLogicJavaPlugin { @@ -100,6 +114,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { protected Integer httpReadTimeout; protected HashMap partnerStore; + private static final Pattern retryPattern = Pattern.compile(".*,(http|https):.*"); public RestapiCallNode() { String configDir = System.getProperty(PROPERTIES_DIR_KEY, DEFAULT_PROPERTIES_DIR); @@ -110,7 +125,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { loadPartners(partners); log.info("Partners support enabled"); } catch (Exception e) { - log.warn("Partners file could not be read, Partner support will not be enabled.", e); + log.warn("Partners file could not be read, Partner support will not be enabled. " + e.getMessage()); } try (FileInputStream in = new FileInputStream(configDir + "/" + UEB_PROPERTIES_FILE_NAME)) { @@ -119,12 +134,13 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { uebServers = props.getProperty("servers"); log.info("UEB support enabled"); } catch (Exception e) { - log.warn("UEB properties could not be read, UEB support will not be enabled.", e); + log.warn("UEB properties could not be read, UEB support will not be enabled. " + e.getMessage()); } httpConnectTimeout = readOptionalInteger("HTTP_CONNECT_TIMEOUT_MS",DEFAULT_HTTP_CONNECT_TIMEOUT_MS); httpReadTimeout = readOptionalInteger("HTTP_READ_TIMEOUT_MS",DEFAULT_HTTP_READ_TIMEOUT_MS); } + @SuppressWarnings("unchecked") protected void loadPartners(JSONObject partners) { Iterator keys = partners.keys(); String partnerUserKey = "user"; @@ -209,12 +225,9 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { String skipSendingStr = paramMap.get(skipSendingMessage); p.skipSending = "true".equalsIgnoreCase(skipSendingStr); p.convertResponse = valueOf(parseParam(paramMap, "convertResponse", false, "true")); - p.trustStoreFileName = parseParam(paramMap, "trustStoreFileName", false, null); - p.trustStorePassword = parseParam(paramMap, "trustStorePassword", false, null); p.keyStoreFileName = parseParam(paramMap, "keyStoreFileName", false, null); p.keyStorePassword = parseParam(paramMap, "keyStorePassword", false, null); - p.ssl = p.trustStoreFileName != null && p.trustStorePassword != null && p.keyStoreFileName != null - && p.keyStorePassword != null; + p.ssl = p.keyStoreFileName != null && p.keyStorePassword != null; p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders", false, null); p.partner = parseParam(paramMap, "partner", false, null); p.dumpHeaders = valueOf(parseParam(paramMap, "dumpHeaders", false, null)); @@ -222,6 +235,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { p.accept = parseParam(paramMap, "accept", false, null); p.multipartFormData = valueOf(parseParam(paramMap, "multipartFormData", false, "false")); p.multipartFile = parseParam(paramMap, "multipartFile", false, null); + p.targetEntity = parseParam(paramMap, "targetEntity", false, null); return p; } @@ -232,8 +246,8 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { * @throws SvcLogicException when URL validation fails */ private static void validateUrl(String restapiUrl) throws SvcLogicException { - if (restapiUrl.contains(",")) { - String[] urls = restapiUrl.split(","); + if (containsMultipleUrls(restapiUrl)) { + String[] urls = getMultipleUrls(restapiUrl); for (String url : urls) { validateUrl(url); } @@ -462,12 +476,15 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { protected void sendRequest(Map paramMap, SvcLogicContext ctx, RetryPolicy retryPolicy) throws SvcLogicException { - HttpResponse r = new HttpResponse(); + HttpResponse r = new HttpResponse(); try { handlePartner(paramMap); Parameters p = getParameters(paramMap, new Parameters()); - if (p.restapiUrl.contains(",") && retryPolicy == null) { - String[] urls = p.restapiUrl.split(","); + if(p.targetEntity != null && !p.targetEntity.isEmpty()) { + MDC.put(ONAPLogConstants.MDCs.TARGET_ENTITY, p.targetEntity); + } + if (containsMultipleUrls(p.restapiUrl) && retryPolicy == null) { + String[] urls = getMultipleUrls(p.restapiUrl); retryPolicy = new RetryPolicy(urls, urls.length * 2); p.restapiUrl = urls[0]; } @@ -775,21 +792,19 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { ssl = createSSLContext(p); } Client client; - if (ssl != null) { HttpsURLConnection.setDefaultSSLSocketFactory(ssl.getSocketFactory()); client = ClientBuilder.newBuilder().sslContext(ssl).hostnameVerifier((s, sslSession) -> true).build(); } else { client = ClientBuilder.newBuilder().hostnameVerifier((s, sslSession) -> true).build(); } + setClientTimeouts(client); // Needed to support additional HTTP methods such as PATCH client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); - + client.register(new MetricLogClientFilter()); WebTarget webTarget = addAuthType(client, p).target(p.restapiUrl); - log.info("Sending request below to url " + p.restapiUrl); - log.info(request); long t1 = System.currentTimeMillis(); HttpResponse r = new HttpResponse(); @@ -821,14 +836,20 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { } } - invocationBuilder.header("X-ECOMP-RequestID", org.slf4j.MDC.get("X-ECOMP-RequestID")); - invocationBuilder.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true); Response response; try { - response = invocationBuilder.method(p.httpMethod.toString(), entity(request, contentType)); + // When the HTTP operation has no body do not set the content-type + //setting content-type has caused errors with some servers when no body is present + if (request == null) { + response = invocationBuilder.method(p.httpMethod.toString()); + } else { + log.info("Sending request below to url " + p.restapiUrl); + log.info(request); + response = invocationBuilder.method(p.httpMethod.toString(), entity(request, contentType)); + } } catch (ProcessingException | IllegalStateException e) { throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e); } @@ -869,8 +890,6 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { } } - invocationBuilder.header("X-ECOMP-RequestID", org.slf4j.MDC.get("X-ECOMP-RequestID")); - Response response; try { @@ -904,18 +923,12 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { protected SSLContext createSSLContext(Parameters p) { try (FileInputStream in = new FileInputStream(p.keyStoreFileName)) { - System.setProperty("jsse.enableSNIExtension", "false"); - System.setProperty("javax.net.ssl.trustStore", p.trustStoreFileName); - System.setProperty("javax.net.ssl.trustStorePassword", p.trustStorePassword); - HttpsURLConnection.setDefaultHostnameVerifier((string, ssls) -> true); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyStore ks = KeyStore.getInstance("PKCS12"); char[] pwd = p.keyStorePassword.toCharArray(); ks.load(in, pwd); kmf.init(ks, pwd); - SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(kmf.getKeyManagers(), null, null); return ctx; @@ -947,6 +960,16 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { byte[] data = Files.readAllBytes(Paths.get(p.fileName)); r = sendHttpData(data, p); + + for (int i = 0; i < 10 && r.code == 301; i++) { + String newUrl = r.headers2.get("Location").get(0); + + log.info("Got response code 301. Sending same request to URL: " + newUrl); + + p.url = newUrl; + r = sendHttpData(data, p); + } + setResponseStatus(ctx, p.responsePrefix, r); } catch (SvcLogicException | IOException e) { @@ -1021,12 +1044,27 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { } } - protected HttpResponse sendHttpData(byte[] data, FileParam p) throws SvcLogicException { + protected HttpResponse sendHttpData(byte[] data, FileParam p) throws IOException { + URL url = new URL(p.url); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); - Client client = ClientBuilder.newBuilder().build(); - setClientTimeouts(client); - client.property(ClientProperties.FOLLOW_REDIRECTS, true); - WebTarget webTarget = addAuthType(client, p).target(p.url); + log.info("Connection: " + con.getClass().getName()); + + con.setRequestMethod(p.httpMethod.toString()); + con.setRequestProperty("Content-Type", "application/octet-stream"); + con.setRequestProperty("Accept", "*/*"); + con.setRequestProperty("Expect", "100-continue"); + con.setFixedLengthStreamingMode(data.length); + con.setInstanceFollowRedirects(false); + + if (p.user != null && p.password != null) { + String authString = p.user + ":" + p.password; + String authStringEnc = Base64.getEncoder().encodeToString(authString.getBytes()); + con.setRequestProperty("Authorization", "Basic " + authStringEnc); + } + + con.setDoInput(true); + con.setDoOutput(true); log.info("Sending file"); long t1 = System.currentTimeMillis(); @@ -1035,69 +1073,46 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { r.code = 200; if (!p.skipSending) { - String tt = "application/octet-stream"; - Invocation.Builder invocationBuilder = webTarget.request(tt).accept(tt); + HttpURLConnectionMetricUtil util = new HttpURLConnectionMetricUtil(); + util.logBefore(con, ONAPComponents.DMAAP); - Response response; + con.connect(); + boolean continue100failed = false; try { - if (p.httpMethod == HttpMethod.POST) { - response = invocationBuilder.post(Entity.entity(data, tt)); - } else if (p.httpMethod == HttpMethod.PUT) { - response = invocationBuilder.put(Entity.entity(data, tt)); - } else { - throw new SvcLogicException("Http operation" + p.httpMethod + "not supported"); - } - } catch (ProcessingException e) { - throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e); + OutputStream os = con.getOutputStream(); + os.write(data); + os.flush(); + os.close(); + } catch (ProtocolException e) { + continue100failed = true; } - r.code = response.getStatus(); - r.headers = response.getStringHeaders(); - EntityTag etag = response.getEntityTag(); - if (etag != null) { - r.message = etag.getValue(); - } - if (response.hasEntity() && r.code != 204) { - r.body = response.readEntity(String.class); - } - - if (r.code == 301) { - String newUrl = response.getStringHeaders().getFirst("Location"); - - log.info("Got response code 301. Sending same request to URL: {}", newUrl); - - webTarget = client.target(newUrl); - invocationBuilder = webTarget.request(tt).accept(tt); + r.code = con.getResponseCode(); + r.headers2 = con.getHeaderFields(); - try { - if (p.httpMethod == HttpMethod.POST) { - response = invocationBuilder.post(Entity.entity(data, tt)); - } else if (p.httpMethod == HttpMethod.PUT) { - response = invocationBuilder.put(Entity.entity(data, tt)); - } else { - throw new SvcLogicException("Http operation" + p.httpMethod + "not supported"); - } - } catch (ProcessingException e) { - throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e); + if (r.code != 204 && !continue100failed) { + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); } + in.close(); - r.code = response.getStatus(); - etag = response.getEntityTag(); - if (etag != null) { - r.message = etag.getValue(); - } - if (response.hasEntity() && r.code != 204) { - r.body = response.readEntity(String.class); - } + r.body = response.toString(); } + + util.logAfter(con); + + con.disconnect(); } long t2 = System.currentTimeMillis(); - log.info(responseReceivedMessage, t2 - t1); - log.info(responseHttpCodeMessage, r.code); + log.info("Response received. Time: {}", t2 - t1); + log.info("HTTP response code: {}", r.code); log.info("HTTP response message: {}", r.message); - logHeaders(r.headers); + logHeaders(r.headers2); log.info("HTTP response: {}", r.body); return r; @@ -1145,6 +1160,26 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { } } + private void logHeaders(Map> mm) { + if (mm == null || mm.isEmpty()) { + return; + } + + List ll = new ArrayList<>(); + for (String s : mm.keySet()) { + if (s != null) { + ll.add(s); + } + } + Collections.sort(ll); + + for (String name : ll) { + List v = mm.get(name); + log.info("--- {}:{}", name, String.valueOf(mm.get(name))); + log.info("--- " + name + ": " + (v.size() == 1 ? v.get(0) : v)); + } + } + protected HttpResponse postOnUeb(String request, UebParam p) throws SvcLogicException { String[] urls = uebServers.split(" "); for (int i = 0; i < urls.length; i++) { @@ -1219,6 +1254,30 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { return defaultValue; } + protected static String[] getMultipleUrls(String restapiUrl) { + List urls = new ArrayList<>(); + int start = 0; + for (int i = 0; i < restapiUrl.length(); i++) { + if (restapiUrl.charAt(i) == ',') { + if (i + 9 < restapiUrl.length()) { + String part = restapiUrl.substring(i + 1, i + 9); + if (part.equals("https://") || part.startsWith("http://")) { + urls.add(restapiUrl.substring(start, i)); + start = i + 1; + } + } + } else if (i == restapiUrl.length() - 1) { + urls.add(restapiUrl.substring(start, i + 1)); + } + } + String[] arr = new String[urls.size()]; + return urls.toArray(arr); + } + + protected static boolean containsMultipleUrls(String restapiUrl) { + Matcher m = retryPattern.matcher(restapiUrl); + return m.matches(); + } private static class FileParam {