X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fnetconf-netty-util%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fnettyutil%2Fhandler%2FNetconfXMLToMessageDecoder.java;h=07fb789a08a5c1d6170df05c6e0bbf6b339a3e6e;hb=refs%2Fchanges%2F13%2F23413%2F26;hp=69c0d53fc12144ad7e780126e5f8d3fe6372a571;hpb=b80124e3f7b11cf2f5e5bd4a6b033d855ff4d0d4;p=controller.git diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToMessageDecoder.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToMessageDecoder.java index 69c0d53fc1..07fb789a08 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToMessageDecoder.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToMessageDecoder.java @@ -7,33 +7,80 @@ */ package org.opendaylight.controller.netconf.nettyutil.handler; -import java.util.List; - -import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; +import java.io.IOException; +import java.util.List; +import org.opendaylight.controller.config.util.xml.XmlUtil; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; public final class NetconfXMLToMessageDecoder extends ByteToMessageDecoder { private static final Logger LOG = LoggerFactory.getLogger(NetconfXMLToMessageDecoder.class); @Override - @VisibleForTesting - public void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + public void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List out) throws IOException, SAXException { + if (in.isReadable()) { + if (LOG.isTraceEnabled()) { + LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in)); + } + + /* According to the XML 1.0 specifications, when there is an XML declaration + * at the beginning of an XML document, it is invalid to have + * white spaces before that declaration (reminder: a XML declaration looks like: + * ). In contrast, when there is no XML declaration, + * it is valid to have white spaces at the beginning of the document. + * + * When they send a NETCONF message, several NETCONF servers start with a new line (either + * LF or CRLF), presumably to improve readability in interactive sessions with a human being. + * Some NETCONF servers send an XML declaration, some others do not. + * + * If a server starts a NETCONF message with white spaces and follows with an XML + * declaration, XmlUtil.readXmlToDocument() will fail because this is invalid XML. + * But in the spirit of the "NETCONF over SSH" RFC 4742 and to improve interoperability, we want + * to accept those messages. + * + * To do this, the following code strips the leading bytes before the start of the XML messages. + */ - if (in.readableBytes() != 0) { - LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in)); + // Skip all leading whitespaces by moving the reader index to the first non whitespace character + while (in.isReadable()) { + if (!isWhitespace(in.readByte())) { + // return reader index to the first non whitespace character + in.readerIndex(in.readerIndex() - 1); + break; + } + } + + // Warn about leading whitespaces + if (in.readerIndex() != 0 && LOG.isWarnEnabled()) { + final byte[] strippedBytes = new byte[in.readerIndex()]; + in.getBytes(0, strippedBytes, 0, in.readerIndex()); + LOG.warn("XML message with unwanted leading bytes detected. Discarded the {} leading byte(s): '{}'", + in.readerIndex(), ByteBufUtil.hexDump(Unpooled.wrappedBuffer(strippedBytes))); + } + } + if (in.isReadable()) { out.add(new NetconfMessage(XmlUtil.readXmlToDocument(new ByteBufInputStream(in)))); } else { LOG.debug("No more content in incoming buffer."); } } + + /** + * Check whether a byte is whitespace/control character. Considered whitespace characters:
+ * SPACE, \t, \n, \v, \r, \f + * + * @param b byte to check + * @return true if the byte is a whitespace/control character + */ + private static boolean isWhitespace(final byte b) { + return b <= 0x0d && b >= 0x09 || b == 0x20; + } }