Improve NETCONF session ID handling
[netconf.git] / plugins / netconf-server-mdsal / src / main / java / org / opendaylight / netconf / mdsal / connector / ops / AbstractConfigOperation.java
1 /*
2  * Copyright (c) 2018 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.mdsal.connector.ops;
9
10 import static java.nio.charset.StandardCharsets.UTF_8;
11
12 import com.google.common.base.Strings;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.net.MalformedURLException;
16 import java.net.URL;
17 import java.net.URLConnection;
18 import java.net.URLStreamHandler;
19 import java.util.Base64;
20 import java.util.Optional;
21 import org.opendaylight.netconf.api.DocumentedException;
22 import org.opendaylight.netconf.api.xml.XmlElement;
23 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
24 import org.opendaylight.netconf.api.xml.XmlUtil;
25 import org.opendaylight.netconf.server.api.operations.AbstractSingletonNetconfOperation;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
27 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
28 import org.opendaylight.yangtools.yang.common.ErrorTag;
29 import org.opendaylight.yangtools.yang.common.ErrorType;
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.NodeList;
33 import org.xml.sax.SAXException;
34
35 abstract class AbstractConfigOperation extends AbstractSingletonNetconfOperation {
36     static final String URL_KEY = "url";
37     static final String CONFIG_KEY = "config";
38     private static final int TIMEOUT_MS = 5000;
39
40     protected AbstractConfigOperation(final SessionIdType sessionId) {
41         super(sessionId);
42     }
43
44     protected static NodeList getElementsByTagName(final XmlElement parent, final String key)
45             throws DocumentedException {
46         final Element domParent = parent.getDomElement();
47         final NodeList elementsByTagName;
48
49         if (Strings.isNullOrEmpty(domParent.getPrefix())) {
50             elementsByTagName = domParent.getElementsByTagName(key);
51         } else {
52             elementsByTagName = domParent.getElementsByTagNameNS(parent.getNamespace(), key);
53         }
54
55         return elementsByTagName;
56     }
57
58     static XmlElement getConfigElement(final XmlElement parent) throws DocumentedException {
59         final Optional<XmlElement> configElement = parent.getOnlyChildElementOptionally(CONFIG_KEY);
60         if (configElement.isPresent()) {
61             return configElement.orElseThrow();
62         }
63
64         final XmlElement urlElement = parent.getOnlyChildElementOptionally(URL_KEY)
65             .orElseThrow(() -> new DocumentedException("Invalid RPC, neither <config> not <url> element is present",
66                 ErrorType.PROTOCOL, ErrorTag.MISSING_ELEMENT, ErrorSeverity.ERROR));
67
68         final Document document = getDocumentFromUrl(urlElement.getTextContent());
69         return XmlElement.fromDomElementWithExpected(document.getDocumentElement(), CONFIG_KEY,
70             XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
71     }
72
73     /**
74      * Parses XML Document available at given URL.
75      *
76      * <p>JDK8 supports URL schemes that include http, https, file, and jar, but {@link URLStreamHandler}s for other
77      * protocols (e.g. ftp) may be available.
78      *
79      * @param url URL as defined in RFC 2396
80      * @see URL#URL(String, String, int, String)
81      */
82     private static Document getDocumentFromUrl(final String url) throws DocumentedException {
83         try (InputStream input = openConnection(new URL(url))) {
84             return XmlUtil.readXmlToDocument(input);
85         } catch (MalformedURLException e) {
86             throw new DocumentedException(url + " URL is invalid or unsupported", e,
87                 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, ErrorSeverity.ERROR);
88         } catch (IOException e) {
89             throw new DocumentedException("Could not open URL " + url, e,
90                 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR);
91         } catch (SAXException e) {
92             throw new DocumentedException("Could not parse XML at" + url, e,
93                 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR);
94         }
95     }
96
97     private static InputStream openConnection(final URL url) throws IOException {
98         final URLConnection connection = url.openConnection();
99         connection.setConnectTimeout(TIMEOUT_MS);
100         connection.setReadTimeout(TIMEOUT_MS);
101
102         // Support Basic Authentication scheme, e.g. http://admin:admin@localhost:8000/config.conf
103         if (url.getUserInfo() != null) {
104             String basicAuth = "Basic " + Base64.getUrlEncoder().encodeToString(url.getUserInfo().getBytes(UTF_8));
105             connection.setRequestProperty("Authorization", basicAuth);
106         }
107         return connection.getInputStream();
108     }
109 }