Author: markt Date: Mon Mar 4 20:40:39 2013 New Revision: 1452501 URL: http://svn.apache.org/r1452501 Log: Make UTF-8 test cases public so they can be used for other tests. Add tests for request bodies using UTF-8. Fix failures for UTF-8 request bodies where incomplete sequences at the end of the stream were ignored. They now throw an exception as they do if they appear in the middle of the stream.
Added: tomcat/trunk/test/org/apache/catalina/connector/TestInputBuffer.java (with props) Modified: tomcat/trunk/java/org/apache/catalina/connector/InputBuffer.java tomcat/trunk/test/org/apache/tomcat/util/buf/TestUtf8Extended.java Modified: tomcat/trunk/java/org/apache/catalina/connector/InputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/InputBuffer.java?rev=1452501&r1=1452500&r2=1452501&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/InputBuffer.java (original) +++ tomcat/trunk/java/org/apache/catalina/connector/InputBuffer.java Mon Mar 4 20:40:39 2013 @@ -363,10 +363,12 @@ public class InputBuffer extends Reader setConverter(); } + boolean eof = false; + if (bb.getLength() <= 0) { int nRead = realReadBytes(bb.getBytes(), 0, bb.getBytes().length); if (nRead < 0) { - return -1; + eof = true; } } @@ -385,10 +387,13 @@ public class InputBuffer extends Reader } state = CHAR_STATE; - conv.convert(bb, cb); - - return cb.getLength(); + conv.convert(bb, cb, eof); + if (cb.getLength() == 0 && eof) { + return -1; + } else { + return cb.getLength(); + } } Added: tomcat/trunk/test/org/apache/catalina/connector/TestInputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/connector/TestInputBuffer.java?rev=1452501&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/connector/TestInputBuffer.java (added) +++ tomcat/trunk/test/org/apache/catalina/connector/TestInputBuffer.java Mon Mar 4 20:40:39 2013 @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 org.apache.catalina.connector; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.B2CConverter; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.TestUtf8Extended; +import org.apache.tomcat.util.buf.TestUtf8Extended.Utf8TestCase; + +public class TestInputBuffer extends TomcatBaseTest { + + @Test + public void testUtf8Body() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + Tomcat.addServlet(root, "Echo", new Utf8Echo()); + root.addServletMapping("/test", "Echo"); + + tomcat.getConnector().setProperty("soTimeout", "300000"); + tomcat.start(); + + for (Utf8TestCase testCase : TestUtf8Extended.TEST_CASES) { + String expected = null; + if (testCase.invalidIndex == -1) { + expected = testCase.outputReplaced; + } + doUtf8BodyTest(testCase.description, testCase.input, expected); + } + } + + + private void doUtf8BodyTest(String description, int[] input, + String expected) throws Exception { + + System.out.println(description); + + byte[] bytes = new byte[input.length]; + for (int i = 0; i < input.length; i++) { + bytes[i] = (byte) input[i]; + } + + ByteChunk bc = new ByteChunk(); + int rc = postUrl(bytes, "http://localhost:" + getPort() + "/test", bc, + null); + + if (expected == null) { + Assert.assertEquals(description, + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc); + } else if (expected.length() == 0) { + Assert.assertNull(description, bc.toString()); + } else { + bc.setCharset(B2CConverter.UTF_8); + Assert.assertEquals(description, expected, bc.toString()); + } + } + + + private static class Utf8Echo extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + // Should use POST + resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + req.setCharacterEncoding("UTF-8"); + Reader r = req.getReader(); + + resp.setCharacterEncoding("UTF-8"); + resp.setContentType("text/plain"); + Writer w = resp.getWriter(); + + // Copy one character at a time + int c = r.read(); + while (c != -1) { + w.write(c); + c = r.read(); + } + w.close(); + } + } +} Propchange: tomcat/trunk/test/org/apache/catalina/connector/TestInputBuffer.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/test/org/apache/tomcat/util/buf/TestUtf8Extended.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/buf/TestUtf8Extended.java?rev=1452501&r1=1452500&r2=1452501&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/util/buf/TestUtf8Extended.java (original) +++ tomcat/trunk/test/org/apache/tomcat/util/buf/TestUtf8Extended.java Mon Mar 4 20:40:39 2013 @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.List; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; /** @@ -49,116 +48,115 @@ public class TestUtf8Extended { // previous error private static final int REPLACE_SWALLOWS_TRAILER = 8; - private List<Utf8TestCase> testCases = new ArrayList<>(); + public static final List<Utf8TestCase> TEST_CASES = new ArrayList<>(); - @Before - public void setup() { - testCases.add(new Utf8TestCase( + static { + TEST_CASES.add(new Utf8TestCase( "Zero length input", new int[] {}, -1, "")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid one byte sequence", new int[] {0x41}, -1, "A")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid two byte sequence", new int[] {0xC2, 0xA9}, -1, "\u00A9")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid three byte sequence", new int[] {0xE0, 0xA4, 0x87}, -1, "\u0907")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid four byte sequence", new int[] {0xF0, 0x90, 0x90, 0x80}, -1, "\uD801\uDC00")); // JVM decoder does not report error until all 4 bytes are available - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid code point - out of range", new int[] {0x41, 0xF4, 0x90, 0x80, 0x80, 0x41}, 2, "A\uFFFD\uFFFD\uFFFD\uFFFDA").addForJvm(ERROR_POS_PLUS2)); // JVM decoder does not report error until all 2 bytes are available - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid sequence padded from one byte to two", new int[] {0x41, 0xC0, 0xC1, 0x41}, 1, "A\uFFFD\uFFFDA").addForJvm(ERROR_POS_PLUS1)); // JVM decoder does not report error until all 3 bytes are available - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid sequence padded from one byte to three", new int[] {0x41, 0xE0, 0x80, 0xC1, 0x41}, 2, "A\uFFFD\uFFFD\uFFFDA").addForJvm(ERROR_POS_PLUS1)); // JVM decoder does not report error until all 4 bytes are available - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid sequence padded from one byte to four", new int[] {0x41, 0xF0, 0x80, 0x80, 0xC1, 0x41}, 2, "A\uFFFD\uFFFD\uFFFD\uFFFDA").addForJvm(ERROR_POS_PLUS2)); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid one byte 1111 1111", new int[] {0x41, 0xFF, 0x41}, 1, "A\uFFFDA")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid one byte 1111 0000", new int[] {0x41, 0xF0, 0x41}, 2, "A\uFFFDA").addForJvm(REPLACE_SWALLOWS_TRAILER)); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid one byte 1110 0000", new int[] {0x41, 0xE0, 0x41}, 2, "A\uFFFDA").addForJvm(REPLACE_SWALLOWS_TRAILER)); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid one byte 1100 0000", new int[] {0x41, 0xC0, 0x41}, 1, "A\uFFFDA").addForJvm(ERROR_POS_PLUS1)); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid one byte 1000 000", new int[] {0x41, 0x80, 0x41}, 1, "A\uFFFDA")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Invalid sequence from unicode 6.2 spec, table 3-8", new int[] {0x61, 0xF1, 0x80, 0x80, 0xE1, 0x80, 0xC2, 0x62, 0x80, 0x63, 0x80, 0xBF, 0x64}, 4, "a\uFFFD\uFFFD\uFFFDb\uFFFDc\uFFFD\uFFFDd")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid 4-byte sequence truncated to 3 bytes", new int[] {0x61, 0xF0, 0x90, 0x90}, 3, "a\uFFFD")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid 4-byte sequence truncated to 2 bytes", new int[] {0x61, 0xF0, 0x90}, 2, "a\uFFFD")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid 4-byte sequence truncated to 1 byte", new int[] {0x61, 0xF0}, 1, "a\uFFFD")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid 4-byte sequence truncated to 3 bytes with trailer", new int[] {0x61, 0xF0, 0x90, 0x90, 0x61}, 4, "a\uFFFDa")); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid 4-byte sequence truncated to 2 bytes with trailer", new int[] {0x61, 0xF0, 0x90, 0x61}, 3, "a\uFFFDa").addForJvm(REPLACE_SWALLOWS_TRAILER)); - testCases.add(new Utf8TestCase( + TEST_CASES.add(new Utf8TestCase( "Valid 4-byte sequence truncated to 1 byte with trailer", new int[] {0x61, 0xF0, 0x61}, 2, @@ -168,7 +166,7 @@ public class TestUtf8Extended { @Test public void testHarmonyDecoder() { CharsetDecoder decoder = new Utf8Decoder(); - for (Utf8TestCase testCase : testCases) { + for (Utf8TestCase testCase : TEST_CASES) { doTest(decoder, testCase, 0); } } @@ -177,7 +175,7 @@ public class TestUtf8Extended { @Test public void testJvmDecoder() { CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder(); - for (Utf8TestCase testCase : testCases) { + for (Utf8TestCase testCase : TEST_CASES) { doTest(decoder, testCase, testCase.flagsJvm); } } @@ -256,12 +254,12 @@ public class TestUtf8Extended { /** * Encapsulates a single UTF-8 test case */ - private static class Utf8TestCase { - private final String description; - private final int[] input; - private final int invalidIndex; - private final String outputReplaced; - private int flagsJvm = 0; + public static class Utf8TestCase { + public final String description; + public final int[] input; + public final int invalidIndex; + public final String outputReplaced; + public int flagsJvm = 0; public Utf8TestCase(String description, int[] input, int invalidIndex, String outputReplaced) { --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org