/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.CharsetUtil;
import org.junit.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
public class XmlFrameDecoderTest {
private final List<String> xmlSamples;
public XmlFrameDecoderTest() throws IOException, URISyntaxException {
xmlSamples = Arrays.asList(
sample("01"), sample("02"), sample("03"),
sample("04"), sample("05"), sample("06")
);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithIllegalArgs01() {
new XmlFrameDecoder(0);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorWithIllegalArgs02() {
new XmlFrameDecoder(-23);
}
@Test(expected = TooLongFrameException.class)
public void testDecodeWithFrameExceedingMaxLength() {
XmlFrameDecoder decoder = new XmlFrameDecoder(3);
EmbeddedChannel ch = new EmbeddedChannel(decoder);
ch.writeInbound(Unpooled.copiedBuffer("<v/>", CharsetUtil.UTF_8));
}
@Test(expected = CorruptedFrameException.class)
public void testDecodeWithInvalidInput() {
XmlFrameDecoder decoder = new XmlFrameDecoder(1048576);
EmbeddedChannel ch = new EmbeddedChannel(decoder);
ch.writeInbound(Unpooled.copiedBuffer("invalid XML", CharsetUtil.UTF_8));
}
@Test(expected = CorruptedFrameException.class)
public void testDecodeWithInvalidContentBeforeXml() {
XmlFrameDecoder decoder = new XmlFrameDecoder(1048576);
EmbeddedChannel ch = new EmbeddedChannel(decoder);
ch.writeInbound(Unpooled.copiedBuffer("invalid XML<foo/>", CharsetUtil.UTF_8));
}
@Test
public void testDecodeShortValidXml() {
testDecodeWithXml("<xxx/>", "<xxx/>");
}
@Test
public void testDecodeShortValidXmlWithLeadingWhitespace01() {
testDecodeWithXml(" <xxx/>", "<xxx/>");
}
@Test
public void testDecodeShortValidXmlWithLeadingWhitespace02() {
testDecodeWithXml(" \n\r \t<xxx/>\t", "<xxx/>");
}
@Test
public void testDecodeShortValidXmlWithLeadingWhitespace02AndTrailingGarbage() {
testDecodeWithXml(" \n\r \t<xxx/>\ttrash", "<xxx/>", CorruptedFrameException.class);
}
@Test
public void testDecodeInvalidXml() {
testDecodeWithXml("<a></", new Object[0]);
testDecodeWithXml("<a></a", new Object[0]);
}
@Test
public void testDecodeWithCDATABlock() {
final String xml = "<book>" +
"<![CDATA[K&R, a.k.a. Kernighan & Ritchie]]>" +
"</book>";
testDecodeWithXml(xml, xml);
}
@Test
public void testDecodeWithCDATABlockContainingNestedUnbalancedXml() {
// <br> isn't closed, also <a> should have been </a>
final String xml = "<info>" +
"<![CDATA[Copyright 2012-2013,<br><a href=\"http://www.acme.com\">ACME Inc.<a>]]>" +
"</info>";
testDecodeWithXml(xml, xml);
}
@Test
public void testDecodeWithMultipleMessages() {
final String input = "<root xmlns=\"http://www.acme.com/acme\" status=\"loginok\" " +
"timestamp=\"1362410583776\"/>\n\n" +
"<root xmlns=\"http://www.acme.com/acme\" status=\"start\" time=\"0\" " +
"timestamp=\"1362410584794\">\n<child active=\"1\" status=\"started\" id=\"935449\" " +
"msgnr=\"2\"/>\n</root>" +
"<root xmlns=\"http://www.acme.com/acme\" status=\"logout\" timestamp=\"1362410584795\"/>";
final String frame1 = "<root xmlns=\"http://www.acme.com/acme\" status=\"loginok\" " +
"timestamp=\"1362410583776\"/>";
final String frame2 = "<root xmlns=\"http://www.acme.com/acme\" status=\"start\" time=\"0\" " +
"timestamp=\"1362410584794\">\n<child active=\"1\" status=\"started\" id=\"935449\" " +
"msgnr=\"2\"/>\n</root>";
final String frame3 = "<root xmlns=\"http://www.acme.com/acme\" status=\"logout\" " +
"timestamp=\"1362410584795\"/>";
testDecodeWithXml(input, frame1, frame2, frame3);
}
@Test
public void testFraming() {
testDecodeWithXml(Arrays.asList("<abc", ">123</a", "bc>"), "<abc>123</abc>");
}
@Test
public void testDecodeWithSampleXml() {
for (final String xmlSample : xmlSamples) {
testDecodeWithXml(xmlSample, xmlSample);
}
}
private static void testDecodeWithXml(List<String> xmlFrames, Object... expected) {
EmbeddedChannel ch = new EmbeddedChannel(new XmlFrameDecoder(1048576));
Exception cause = null;
try {
for (String xmlFrame : xmlFrames) {
ch.writeInbound(Unpooled.copiedBuffer(xmlFrame, CharsetUtil.UTF_8));
}
} catch (Exception e) {
cause = e;
}
List<Object> actual = new ArrayList<Object>();
for (;;) {
ByteBuf buf = ch.readInbound();
if (buf == null) {
break;
}
actual.add(buf.toString(CharsetUtil.UTF_8));
buf.release();
}
if (cause != null) {
actual.add(cause.getClass());
}
try {
List<Object> expectedList = new ArrayList<Object>();
Collections.addAll(expectedList, expected);
assertThat(actual, is(expectedList));
} finally {
ch.finish();
}
}
private static void testDecodeWithXml(String xml, Object... expected) {
testDecodeWithXml(Collections.singletonList(xml), expected);
}
private String sample(String number) throws IOException, URISyntaxException {
String path = "io/netty/handler/codec/xml/sample-" + number + ".xml";
URL url = getClass().getClassLoader().getResource(path);
if (url == null) {
throw new IllegalArgumentException("file not found: " + path);
}
byte[] buf = Files.readAllBytes(Paths.get(url.toURI()));
return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buf)).toString();
}
}