Add Honeynode emulator for device221
[transportpce.git] / tests / honeynode221 / netconf-netty-util / src / main / java / org / opendaylight / netconf / nettyutil / handler / NetconfXMLToMessageDecoder.java
1 /*
2  * Copyright (c) 2014 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.nettyutil.handler;
9
10 import io.netty.buffer.ByteBuf;
11 import io.netty.buffer.ByteBufInputStream;
12 import io.netty.buffer.ByteBufUtil;
13 import io.netty.buffer.Unpooled;
14 import io.netty.channel.ChannelHandlerContext;
15 import io.netty.handler.codec.ByteToMessageDecoder;
16 import java.io.IOException;
17 import java.util.List;
18 import org.opendaylight.controller.config.util.xml.XmlUtil;
19 import org.opendaylight.netconf.api.FailedNetconfMessage;
20 import org.opendaylight.netconf.api.NetconfMessage;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23 import org.xml.sax.SAXException;
24 import org.xml.sax.SAXParseException;
25
26 public final class NetconfXMLToMessageDecoder extends ByteToMessageDecoder {
27     private static final Logger LOG = LoggerFactory.getLogger(NetconfXMLToMessageDecoder.class);
28
29     @Override
30     public void decode(final ChannelHandlerContext ctx, final ByteBuf in,
31                        final List<Object> out) throws IOException, SAXException {
32         if (in.isReadable()) {
33             if (LOG.isTraceEnabled()) {
34                 LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in));
35             }
36
37             /* According to the XML 1.0 specifications, when there is an XML declaration
38              * at the beginning of an XML document, it is invalid to have
39              * white spaces before that declaration (reminder: a XML declaration looks like:
40              * <?xml version="1.0" encoding="UTF-8"?>). In contrast, when there is no XML declaration,
41              * it is valid to have white spaces at the beginning of the document.
42              *
43              * When they send a NETCONF message, several NETCONF servers start with a new line (either
44              * LF or CRLF), presumably to improve readability in interactive sessions with a human being.
45              * Some NETCONF servers send an XML declaration, some others do not.
46              *
47              * If a server starts a NETCONF message with white spaces and follows with an XML
48              * declaration, XmlUtil.readXmlToDocument() will fail because this is invalid XML.
49              * But in the spirit of the "NETCONF over SSH" RFC 4742 and to improve interoperability, we want
50              * to accept those messages.
51              *
52              * To do this, the following code strips the leading bytes before the start of the XML messages.
53              */
54
55             // Skip all leading whitespaces by moving the reader index to the first non whitespace character
56             while (in.isReadable()) {
57                 if (!isWhitespace(in.readByte())) {
58                     // return reader index to the first non whitespace character
59                     in.readerIndex(in.readerIndex() - 1);
60                     break;
61                 }
62             }
63
64             // Warn about leading whitespaces
65             if (in.readerIndex() != 0 && LOG.isWarnEnabled()) {
66                 final byte[] strippedBytes = new byte[in.readerIndex()];
67                 in.getBytes(0, strippedBytes, 0, in.readerIndex());
68                 LOG.warn("XML message with unwanted leading bytes detected. Discarded the {} leading byte(s): '{}'",
69                         in.readerIndex(), ByteBufUtil.hexDump(Unpooled.wrappedBuffer(strippedBytes)));
70             }
71         }
72         if (in.isReadable()) {
73             NetconfMessage msg;
74
75             try {
76                 msg = new NetconfMessage(XmlUtil.readXmlToDocument(new ByteBufInputStream(in)));
77             } catch (SAXParseException exception) {
78                 LOG.error("Failed to parse received message", exception);
79                 msg = new FailedNetconfMessage(exception);
80             }
81
82             out.add(msg);
83         } else {
84             LOG.debug("No more content in incoming buffer.");
85         }
86     }
87
88     /**
89      * Check whether a byte is whitespace/control character. Considered whitespace characters: <br/>
90      * SPACE, \t, \n, \v, \r, \f
91      *
92      * @param byteToCheck byte to check
93      * @return true if the byte is a whitespace/control character
94      */
95     private static boolean isWhitespace(final byte byteToCheck) {
96         return byteToCheck <= 0x0d && byteToCheck >= 0x09 || byteToCheck == 0x20;
97     }
98 }