This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
/*
* 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.tomcat.util.buf;
import java.math.BigInteger;
import org.junit.Assert;
import org.junit.Test;
public class TestAscii {
@Test
public void testParseLong1() {
String value = "9223372036854775807"; // Long.MAX_VALUE
byte[] bytes = value.getBytes();
long result = Ascii.parseLong(bytes, 0, bytes.length);
Assert.assertEquals(value, String.valueOf(result));
}
@Test(expected = NumberFormatException.class)
public void testParseLong2() {
byte[] bytes = "9223372036854775808".getBytes(); // Long.MAX_VALUE + 1
long result = Ascii.parseLong(bytes, 0, bytes.length);
Assert.fail("NumberFormatException expected, got: " + result);
}
@Test(expected = NumberFormatException.class)
public void testParseLong3() {
byte[] bytes = "9223372036854775810".getBytes(); // Long.MAX_VALUE + 3
long result = Ascii.parseLong(bytes, 0, bytes.length);
Assert.fail("NumberFormatException expected, got: " + result);
}
@Test(expected = NumberFormatException.class)
public void testParseLong4() {
BigInteger x = BigInteger.valueOf(5000000000L).shiftLeft(32);
byte[] bytes = String.valueOf(x).getBytes();
long result = Ascii.parseLong(bytes, 0, bytes.length);
Assert.fail("NumberFormatException expected, got: " + result);
}
@Test
public void testParseLong5() {
String value = "9223372036854775806"; // Long.MAX_VALUE - 1
byte[] bytes = value.getBytes();
long result = Ascii.parseLong(bytes, 0, bytes.length);
Assert.assertEquals(value, String.valueOf(result));
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.tomcat.util.buf;
import java.nio.charset.Charset;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import org.junit.Assert;
import org.junit.Test;
public class TestB2CConverter {
private static final byte[] UTF16_MESSAGE =
new byte[] {-2, -1, 0, 65, 0, 66, 0, 67};
private static final byte[] UTF8_INVALID = new byte[] {-8, -69, -73, -77};
private static final byte[] UTF8_PARTIAL = new byte[] {-50};
@Test
public void testSingleMessage() throws Exception {
testMessages(1);
}
@Test
public void testTwoMessage() throws Exception {
testMessages(2);
}
@Test
public void testManyMessage() throws Exception {
testMessages(10);
}
private void testMessages(int msgCount) throws Exception {
B2CConverter conv = new B2CConverter(StandardCharsets.UTF_16);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk(32);
for (int i = 0; i < msgCount; i++) {
bc.append(UTF16_MESSAGE, 0, UTF16_MESSAGE.length);
conv.convert(bc, cc, true);
Assert.assertEquals("ABC", cc.toString());
bc.recycle();
cc.recycle();
conv.recycle();
}
System.out.println(cc);
}
@Test
public void testLeftoverSize() {
float maxLeftover = 0;
String charsetName = "UNSET";
for (Charset charset : Charset.availableCharsets().values()) {
float leftover;
if (charset.name().toLowerCase(Locale.ENGLISH).startsWith("x-")) {
// Non-standard charset that browsers won't be using
// Likely something used internally by the JRE
continue;
}
try {
leftover = charset.newEncoder().maxBytesPerChar();
} catch (UnsupportedOperationException uoe) {
// Skip it
continue;
}
if (leftover > maxLeftover) {
maxLeftover = leftover;
charsetName = charset.name();
}
}
Assert.assertTrue("Limit needs to be at least " + maxLeftover +
" (used in charset '" + charsetName + "')",
maxLeftover <= B2CConverter.LEFTOVER_SIZE);
}
@Test(expected=MalformedInputException.class)
public void testBug54602a() throws Exception {
// Check invalid input is rejected straight away
B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk();
bc.append(UTF8_INVALID, 0, UTF8_INVALID.length);
cc.allocate(bc.getLength(), -1);
conv.convert(bc, cc, false);
}
@Test(expected=MalformedInputException.class)
public void testBug54602b() throws Exception {
// Check partial input is rejected
B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk();
bc.append(UTF8_PARTIAL, 0, UTF8_PARTIAL.length);
cc.allocate(bc.getLength(), -1);
conv.convert(bc, cc, true);
}
@Test
public void testBug54602c() throws Exception {
// Check partial input is rejected once it is known to be all available
B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk();
bc.append(UTF8_PARTIAL, 0, UTF8_PARTIAL.length);
cc.allocate(bc.getLength(), -1);
conv.convert(bc, cc, false);
Exception e = null;
try {
conv.convert(bc, cc, true);
} catch (MalformedInputException mie) {
e = mie;
}
Assert.assertNotNull(e);
}
}

View File

@@ -0,0 +1,176 @@
/*
* 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.tomcat.util.buf;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.tomcat.util.buf.ByteChunk.ByteOutputChannel;
/**
* Test cases for {@link ByteChunk}.
*/
public class TestByteChunk {
@Test
public void testConvertToBytes() throws UnsupportedEncodingException {
String string = "HTTP/1.1 100 \r\n\r\n";
byte[] bytes = ByteChunk.convertToBytes(string);
byte[] expected = string.getBytes("ISO-8859-1");
Assert.assertTrue(Arrays.equals(bytes, expected));
}
/*
* Test for {@code findByte} vs. {@code indexOf} methods difference.
*
* <p>
* As discussed in the "Re: r944918" thread on dev@, {@code
* ByteChunk.indexOf()} works for 0-127 ASCII chars only, and cannot find
* any chars outside of the range. {@code ByteChunk.findByte()} works for
* any ISO-8859-1 chars.
*/
@Test
public void testFindByte() throws UnsupportedEncodingException {
// 0xa0 = 160 = &nbsp; character
byte[] bytes = "Hello\u00a0world".getBytes("ISO-8859-1");
final int len = bytes.length;
// indexOf() does not work outside of 0-127
Assert.assertEquals(5, ByteChunk.findByte(bytes, 0, len, (byte) '\u00a0'));
Assert.assertEquals(-1, ByteChunk.indexOf(bytes, 0, len, '\u00a0'));
Assert.assertEquals(0, ByteChunk.findByte(bytes, 0, len, (byte) 'H'));
Assert.assertEquals(0, ByteChunk.indexOf(bytes, 0, len, 'H'));
Assert.assertEquals(len - 1, ByteChunk.findByte(bytes, 0, len, (byte) 'd'));
Assert.assertEquals(len - 1, ByteChunk.indexOf(bytes, 0, len, 'd'));
Assert.assertEquals(-1, ByteChunk.findByte(bytes, 0, len, (byte) 'x'));
Assert.assertEquals(-1, ByteChunk.indexOf(bytes, 0, len, 'x'));
Assert.assertEquals(7, ByteChunk.findByte(bytes, 5, len, (byte) 'o'));
Assert.assertEquals(7, ByteChunk.indexOf(bytes, 5, len, 'o'));
Assert.assertEquals(-1, ByteChunk.findByte(bytes, 2, 5, (byte) 'w'));
Assert.assertEquals(-1, ByteChunk.indexOf(bytes, 5, 5, 'w'));
}
@Test
public void testIndexOf_Char() throws UnsupportedEncodingException {
byte[] bytes = "Hello\u00a0world".getBytes("ISO-8859-1");
final int len = bytes.length;
ByteChunk bc = new ByteChunk();
bc.setBytes(bytes, 0, len);
Assert.assertEquals(0, bc.indexOf('H', 0));
Assert.assertEquals(6, bc.indexOf('w', 0));
// Does not work outside of 0-127
Assert.assertEquals(-1, bc.indexOf('\u00a0', 0));
bc.setBytes(bytes, 6, 5);
Assert.assertEquals(1, bc.indexOf('o', 0));
bc.setBytes(bytes, 6, 2);
Assert.assertEquals(0, bc.indexOf('w', 0));
Assert.assertEquals(-1, bc.indexOf('d', 0));
}
@Test
public void testIndexOf_String() throws UnsupportedEncodingException {
byte[] bytes = "Hello\u00a0world".getBytes("ISO-8859-1");
final int len = bytes.length;
ByteChunk bc = new ByteChunk();
bc.setBytes(bytes, 0, len);
Assert.assertEquals(0, bc.indexOf("Hello", 0, "Hello".length(), 0));
Assert.assertEquals(2, bc.indexOf("ll", 0, 2, 0));
Assert.assertEquals(2, bc.indexOf("Hello", 2, 2, 0));
Assert.assertEquals(7, bc.indexOf("o", 0, 1, 5));
// Does not work outside of 0-127
Assert.assertEquals(-1, bc.indexOf("\u00a0", 0, 1, 0));
bc.setBytes(bytes, 6, 5);
Assert.assertEquals(1, bc.indexOf("o", 0, 1, 0));
bc.setBytes(bytes, 6, 2);
Assert.assertEquals(0, bc.indexOf("wo", 0, 1, 0));
Assert.assertEquals(-1, bc.indexOf("d", 0, 1, 0));
}
@Test
public void testFindBytes() throws UnsupportedEncodingException {
byte[] bytes = "Hello\u00a0world".getBytes("ISO-8859-1");
final int len = bytes.length;
Assert.assertEquals(0, ByteChunk.findBytes(bytes, 0, len, new byte[] { 'H' }));
Assert.assertEquals(5, ByteChunk.findBytes(bytes, 0, len, new byte[] {
(byte) '\u00a0', 'x' }));
Assert.assertEquals(5, ByteChunk.findBytes(bytes, 0, len - 4, new byte[] {
'x', (byte) '\u00a0' }));
Assert.assertEquals(len - 1, ByteChunk.findBytes(bytes, 2, len, new byte[] {
'x', 'd' }));
Assert.assertEquals(1, ByteChunk.findBytes(bytes, 0, len, new byte[] { 'o',
'e' }));
Assert.assertEquals(-1, ByteChunk.findBytes(bytes, 2, 5, new byte[] { 'w' }));
}
@Ignore // Requires a 6GB heap (on markt's desktop - YMMV)
@Test
public void testAppend() throws Exception {
ByteChunk bc = new ByteChunk();
bc.setByteOutputChannel(new Sink());
// Defaults to no limit
byte data[] = new byte[32 * 1024 * 1024];
for (int i = 0; i < 100; i++) {
bc.append(data, 0, data.length);
}
Assert.assertEquals(AbstractChunk.ARRAY_MAX_SIZE, bc.getBuffer().length);
}
public class Sink implements ByteOutputChannel {
@Override
public void realWriteBytes(byte[] cbuf, int off, int len) throws IOException {
// NO-OP
}
@Override
public void realWriteBytes(ByteBuffer from) throws IOException {
// NO-OP
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.tomcat.util.buf;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.tomcat.util.buf.CharChunk.CharOutputChannel;
/**
* Test cases for {@link CharChunk}.
*/
public class TestCharChunk {
@Test
public void testEndsWith() {
CharChunk cc = new CharChunk();
Assert.assertFalse(cc.endsWith("test"));
cc.setChars("xxtestxx".toCharArray(), 2, 4);
Assert.assertTrue(cc.endsWith(""));
Assert.assertTrue(cc.endsWith("t"));
Assert.assertTrue(cc.endsWith("st"));
Assert.assertTrue(cc.endsWith("test"));
Assert.assertFalse(cc.endsWith("x"));
Assert.assertFalse(cc.endsWith("xxtest"));
}
@Test
public void testIndexOf_String() {
char[] chars = "Hello\u00a0world".toCharArray();
final int len = chars.length;
CharChunk cc = new CharChunk();
cc.setChars(chars, 0, len);
Assert.assertEquals(0, cc.indexOf("Hello", 0, "Hello".length(), 0));
Assert.assertEquals(2, cc.indexOf("ll", 0, 2, 0));
Assert.assertEquals(2, cc.indexOf("Hello", 2, 2, 0));
Assert.assertEquals(7, cc.indexOf("o", 0, 1, 5));
// Does work outside of 0-127 (unlike ByteChunk)
Assert.assertEquals(5, cc.indexOf("\u00a0", 0, 1, 0));
cc.setChars(chars, 6, 5);
Assert.assertEquals(1, cc.indexOf("o", 0, 1, 0));
cc.setChars(chars, 6, 2);
Assert.assertEquals(0, cc.indexOf("wo", 0, 1, 0));
Assert.assertEquals(-1, cc.indexOf("d", 0, 1, 0));
}
@Ignore // Requires an 11GB heap (on markt's desktop - YMMV)
@Test
public void testAppend() throws Exception {
CharChunk cc = new CharChunk();
cc.setCharOutputChannel(new Sink());
// Defaults to no limit
char data[] = new char[32 * 1024 * 1024];
for (int i = 0; i < 100; i++) {
cc.append(data, 0, data.length);
}
Assert.assertEquals(AbstractChunk.ARRAY_MAX_SIZE, cc.getBuffer().length);
}
public class Sink implements CharOutputChannel {
@Override
public void realWriteChars(char[] cbuf, int off, int len) throws IOException {
// NO-OP
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.tomcat.util.buf;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
public class TestCharsetCache {
@Test
public void testAllKnownCharsets() {
Set<String> known = new HashSet<>();
known.addAll(Arrays.asList(CharsetCache.LAZY_CHARSETS));
Set<String> initial = new HashSet<>();
initial.addAll(Arrays.asList(CharsetCache.INITIAL_CHARSETS));
List<String> cacheMisses = new ArrayList<>();
for (Charset charset: Charset.availableCharsets().values()) {
String name = charset.name().toLowerCase(Locale.ENGLISH);
// No need to test the charsets that are pre-loaded
if (initial.contains(name)) {
continue;
}
if (!known.contains(name)) {
cacheMisses.add(name);
}
for (String alias : charset.aliases()) {
alias = alias.toLowerCase(Locale.ENGLISH);
if (!known.contains(alias)) {
cacheMisses.add(alias);
}
}
}
if (cacheMisses.size() != 0) {
StringBuilder sb = new StringBuilder();
Collections.sort(cacheMisses);
for (String name : cacheMisses) {
if (sb.length() == 0) {
sb.append('"');
} else {
sb.append(", \"");
}
sb.append(name.toLowerCase(Locale.ENGLISH));
sb.append('"');
}
System.out.println(sb.toString());
}
Assert.assertTrue(cacheMisses.size() == 0);
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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.tomcat.util.buf;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.Test;
public class TestCharsetCachePerformance {
@Test
public void testNoCsCache() throws Exception {
doTest(new NoCsCache());
}
@Test
public void testFullCsCache() throws Exception {
doTest(new FullCsCache());
}
@Test
public void testLazyCsCache() throws Exception {
doTest(new LazyCsCache());
}
private void doTest(CsCache cache) throws Exception {
int threadCount = 10;
int iterations = 10000000;
String[] lookupNames = new String[] {
"ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5" };
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = new TestCsCacheThread(iterations, cache, lookupNames);
}
long startTime = System.nanoTime();
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
long endTime = System.nanoTime();
System.out.println(cache.getClass().getName() + ": " + (endTime - startTime) + "ns");
}
private static interface CsCache {
Charset getCharset(String charsetName);
}
private static class NoCsCache implements CsCache {
@Override
public Charset getCharset(String charsetName) {
return Charset.forName(charsetName);
}
}
private static class FullCsCache implements CsCache {
private static final Map<String,Charset> cache = new HashMap<>();
static {
for (Charset charset: Charset.availableCharsets().values()) {
cache.put(charset.name().toLowerCase(Locale.ENGLISH), charset);
for (String alias : charset.aliases()) {
cache.put(alias.toLowerCase(Locale.ENGLISH), charset);
}
}
}
@Override
public Charset getCharset(String charsetName) {
return cache.get(charsetName.toLowerCase(Locale.ENGLISH));
}
}
private static class LazyCsCache implements CsCache {
private CharsetCache cache = new CharsetCache();
@Override
public Charset getCharset(String charsetName) {
return cache.getCharset(charsetName);
}
}
private static class TestCsCacheThread extends Thread {
private final int iterations;
private final CsCache cache;
private final String[] lookupNames;
private final int lookupNamesCount;
public TestCsCacheThread(int iterations, CsCache cache, String[] lookupNames) {
this.iterations = iterations;
this.cache = cache;
this.lookupNames = lookupNames;
this.lookupNamesCount = lookupNames.length;
}
@Override
public void run() {
for (int i = 0; i < iterations; i++) {
cache.getCharset(lookupNames[i % lookupNamesCount]);
}
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.tomcat.util.buf;
import java.nio.charset.StandardCharsets;
import org.junit.Assert;
import org.junit.Test;
/**
* Test cases for {@link HexUtils}.
*/
public class TestHexUtils {
private static final String TEST01_STRING = "Hello World";
private static final byte[] TEST01_BYTES = TEST01_STRING.getBytes(StandardCharsets.UTF_8);
private static final String TEST02_STRING = "foo";
private static final byte[] TEST02_BYTES = TEST02_STRING.getBytes(StandardCharsets.UTF_8);
@Test
public void testGetDec() {
Assert.assertEquals(0, HexUtils.getDec('0'));
Assert.assertEquals(9, HexUtils.getDec('9'));
Assert.assertEquals(10, HexUtils.getDec('a'));
Assert.assertEquals(15, HexUtils.getDec('f'));
Assert.assertEquals(10, HexUtils.getDec('A'));
Assert.assertEquals(15, HexUtils.getDec('F'));
Assert.assertEquals(-1, HexUtils.getDec(0));
Assert.assertEquals(-1, HexUtils.getDec('Z'));
Assert.assertEquals(-1, HexUtils.getDec(255));
Assert.assertEquals(-1, HexUtils.getDec(-60));
}
@Test
public void testRoundTrip01() {
Assert.assertArrayEquals(TEST01_STRING, TEST01_BYTES,
HexUtils.fromHexString(HexUtils.toHexString(TEST01_BYTES)));
}
@Test
public void testRoundTrip02() {
Assert.assertArrayEquals(TEST02_STRING, TEST02_BYTES,
HexUtils.fromHexString(HexUtils.toHexString(TEST02_BYTES)));
}
@Test(expected=IllegalArgumentException.class)
public void testFromHex01() {
HexUtils.fromHexString("Not a hex string");
}
@Test(expected=IllegalArgumentException.class)
public void testFromHex02() {
// Odd number of hex characters
HexUtils.fromHexString("aaa");
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.tomcat.util.buf;
import org.junit.Test;
public class TestMessageBytes {
@Test
public void testToStringFromNull() {
MessageBytes mb = MessageBytes.newInstance();
mb.toString();
}
@Test
public void testToBytesFromNull() {
MessageBytes mb = MessageBytes.newInstance();
mb.toBytes();
}
@Test
public void testToCharsFromNull() {
MessageBytes mb = MessageBytes.newInstance();
mb.toChars();
}
@Test
public void testToStringAfterRecycle() {
MessageBytes mb = MessageBytes.newInstance();
mb.setString("foo");
mb.recycle();
mb.toString();
}
@Test
public void testToBytesAfterRecycle() {
MessageBytes mb = MessageBytes.newInstance();
mb.setString("foo");
mb.recycle();
mb.toBytes();
}
@Test
public void testToCharsAfterRecycle() {
MessageBytes mb = MessageBytes.newInstance();
mb.setString("foo");
mb.recycle();
mb.toChars();
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.tomcat.util.buf;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
/*
* None of these tests should throw a NPE.
*/
public class TestStringUtils {
@Test
public void testNullArray() {
Assert.assertEquals("", StringUtils.join((String[]) null));
}
@Test
public void testNullArrayCharStringBuilder() {
StringBuilder sb = new StringBuilder();
StringUtils.join((String[]) null, ',', sb);
Assert.assertEquals("", sb.toString());
}
@Test
public void testNullCollection() {
Assert.assertEquals("", StringUtils.join((Collection<String>) null));
}
@Test
public void testNullCollectionChar() {
Assert.assertEquals("", StringUtils.join(null, ','));
}
@Test
public void testNullIterableCharStringBuilder() {
StringBuilder sb = new StringBuilder();
StringUtils.join((Iterable<String>) null, ',', sb);
Assert.assertEquals("", sb.toString());
}
@Test
public void testNullArrayCharFunctionStringBuilder() {
StringBuilder sb = new StringBuilder();
StringUtils.join((String[]) null, ',', null, sb);
Assert.assertEquals("", sb.toString());
}
@Test
public void testNullIterableCharFunctionStringBuilder() {
StringBuilder sb = new StringBuilder();
StringUtils.join((Iterable<String>) null, ',', null, sb);
Assert.assertEquals("", sb.toString());
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.tomcat.util.buf;
import java.nio.charset.StandardCharsets;
import org.junit.Assert;
import org.junit.Test;
public class TestUDecoder {
@Test
public void testURLDecodeStringInvalid() {
// %n rather than %nn should throw an IAE according to the Javadoc
Exception exception = null;
try {
UDecoder.URLDecode("%5xxxxx");
} catch (Exception e) {
exception = e;
}
Assert.assertTrue(exception instanceof IllegalArgumentException);
// Edge case trying to trigger ArrayIndexOutOfBoundsException
exception = null;
try {
UDecoder.URLDecode("%5");
} catch (Exception e) {
exception = e;
}
Assert.assertTrue(exception instanceof IllegalArgumentException);
}
@Test
public void testURLDecodeStringValidIso88591Start() {
String result = UDecoder.URLDecode("%41xxxx", StandardCharsets.ISO_8859_1);
Assert.assertEquals("Axxxx", result);
}
@Test
public void testURLDecodeStringValidIso88591Middle() {
String result = UDecoder.URLDecode("xx%41xx", StandardCharsets.ISO_8859_1);
Assert.assertEquals("xxAxx", result);
}
@Test
public void testURLDecodeStringValidIso88591End() {
String result = UDecoder.URLDecode("xxxx%41", StandardCharsets.ISO_8859_1);
Assert.assertEquals("xxxxA", result);
}
@Test
public void testURLDecodeStringValidUtf8Start() {
String result = UDecoder.URLDecode("%c3%aaxxxx", StandardCharsets.UTF_8);
Assert.assertEquals("\u00eaxxxx", result);
}
@Test
public void testURLDecodeStringValidUtf8Middle() {
String result = UDecoder.URLDecode("xx%c3%aaxx", StandardCharsets.UTF_8);
Assert.assertEquals("xx\u00eaxx", result);
}
@Test
public void testURLDecodeStringValidUtf8End() {
String result = UDecoder.URLDecode("xxxx%c3%aa", StandardCharsets.UTF_8);
Assert.assertEquals("xxxx\u00ea", result);
}
@Test
public void testURLDecodeStringNonAsciiValidNone() {
String result = UDecoder.URLDecode("\u00eaxxxx", StandardCharsets.UTF_8);
Assert.assertEquals("\u00eaxxxx", result);
}
@Test
public void testURLDecodeStringNonAsciiValidUtf8() {
String result = UDecoder.URLDecode("\u00ea%c3%aa", StandardCharsets.UTF_8);
Assert.assertEquals("\u00ea\u00ea", result);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.tomcat.util.buf;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.buf.UEncoder.SafeCharsSet;
/**
* Test cases for {@link UEncoder}.
*/
public class TestUEncoder {
@Test
public void testEncodeURLWithSlashInit() throws IOException {
UEncoder urlEncoder = new UEncoder(SafeCharsSet.WITH_SLASH);
String s = "a+b/c/d+e.class";
Assert.assertTrue(urlEncoder.encodeURL(s, 0, s.length()).equals(
"a%2bb/c/d%2be.class"));
Assert.assertTrue(urlEncoder.encodeURL(s, 2, s.length() - 2).equals(
"b/c/d%2be.cla"));
s = new String(new char[] { 0xD801, 0xDC01 });
Assert.assertTrue(urlEncoder.encodeURL(s, 0, s.length())
.equals("%f0%90%90%81"));
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.tomcat.util.buf;
public class TestUriUtil24 extends TesterUriUtilBase {
public TestUriUtil24() {
super("$");
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.tomcat.util.buf;
public class TestUriUtil26 extends TesterUriUtilBase {
public TestUriUtil26() {
super("&");
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.tomcat.util.buf;
public class TestUriUtil2A extends TesterUriUtilBase {
public TestUriUtil2A() {
super("*");
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.tomcat.util.buf;
public class TestUriUtil40 extends TesterUriUtilBase {
public TestUriUtil40() {
super("@");
}
}

View File

@@ -0,0 +1,678 @@
/*
* 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.tomcat.util.buf;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
/**
* These tests have been written with reference to
* <a href="http://www.unicode.org/versions/Unicode6.2.0/ch03.pdf">unicode 6.2,
* chapter 3, section 3.9</a>.
*/
public class TestUtf8 {
// Indicates that at invalid sequence is detected one character later than
// the earliest possible moment
private static final int ERROR_POS_PLUS1 = 1;
// Indicates that at invalid sequence is detected two characters later than
// the earliest possible moment
private static final int ERROR_POS_PLUS2 = 2;
// Indicates that at invalid sequence is detected four characters later
// than the earliest possible moment
private static final int ERROR_POS_PLUS4 = 4;
// Indicates that the trailing valid byte is included in replacement of the
// previous error
private static final int REPLACE_SWALLOWS_TRAILER = 8;
// Indicates that one replacement character is missing
private static final int REPLACE_MISSING1 = 16;
// Indicates that two replacement characters are missing
private static final int REPLACE_MISSING2 = 32;
// Indicates that three replacement characters are missing
private static final int REPLACE_MISSING4 = 64;
public static final List<Utf8TestCase> TEST_CASES;
private static int workAroundCount = 0;
static {
// All known issues have been fixed in Java 8
// https://bugs.openjdk.java.net/browse/JDK-8039751
// Base assumption in Java 7
int javaVersion = 7;
try {
Class.forName("java.util.stream.Collector");
javaVersion = 8;
} catch (Exception e) {
// Ignore
}
Utf8TestCase testCase = null;
ArrayList<Utf8TestCase> testCases = new ArrayList<>();
testCases.add(new Utf8TestCase(
"Zero length input",
new int[] {},
-1,
""));
testCases.add(new Utf8TestCase(
"Valid one byte sequence",
new int[] {0x41},
-1,
"A"));
testCases.add(new Utf8TestCase(
"Valid two byte sequence",
new int[] {0xC2, 0xA9},
-1,
"\u00A9"));
testCases.add(new Utf8TestCase(
"Valid three byte sequence",
new int[] {0xE0, 0xA4, 0x87},
-1,
"\u0907"));
testCases.add(new Utf8TestCase(
"Valid four byte sequence",
new int[] {0xF0, 0x90, 0x90, 0x80},
-1,
"\uD801\uDC00"));
// Java 7 JVM decoder does not report error until all 4 bytes are
// available
testCase = new Utf8TestCase(
"Invalid code point - out of range",
new int[] {0x41, 0xF4, 0x90, 0x80, 0x80, 0x41},
2,
"A\uFFFD\uFFFD\uFFFD\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
// Java 7 JVM decoder does not report error until all 2 bytes are available
testCase = new Utf8TestCase(
"Valid sequence padded from one byte to two",
new int[] {0x41, 0xC0, 0xC1, 0x41},
1,
"A\uFFFD\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
// Java 7 JVM decoder does not report error until all 3 bytes are available
testCase = new Utf8TestCase(
"Valid sequence padded from one byte to three",
new int[] {0x41, 0xE0, 0x80, 0xC1, 0x41},
2,
"A\uFFFD\uFFFD\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
// Java 7 JVM decoder does not report error until all 4 bytes are
// available
testCase = new Utf8TestCase(
"Valid sequence padded from one byte to four",
new int[] {0x41, 0xF0, 0x80, 0x80, 0xC1, 0x41},
2,
"A\uFFFD\uFFFD\uFFFD\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Invalid one byte 1111 1111",
new int[] {0x41, 0xFF, 0x41},
1,
"A\uFFFDA"));
testCase = new Utf8TestCase(
"Invalid one byte 1111 0000",
new int[] {0x41, 0xF0, 0x41},
2,
"A\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Invalid one byte 1110 0000",
new int[] {0x41, 0xE0, 0x41},
2,
"A\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Invalid one byte 1100 0000",
new int[] {0x41, 0xC0, 0x41},
1,
"A\uFFFDA");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Invalid one byte 1000 000",
new int[] {0x41, 0x80, 0x41},
1,
"A\uFFFDA"));
testCases.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(
"Valid 4-byte sequence truncated to 3 bytes",
new int[] {0x61, 0xF0, 0x90, 0x90},
3,
"a\uFFFD"));
testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 2 bytes",
new int[] {0x61, 0xF0, 0x90},
2,
"a\uFFFD"));
testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 1 byte",
new int[] {0x61, 0xF0},
1,
"a\uFFFD"));
testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 3 bytes with trailer",
new int[] {0x61, 0xF0, 0x90, 0x90, 0x61},
4,
"a\uFFFDa"));
testCase = new Utf8TestCase(
"Valid 4-byte sequence truncated to 2 bytes with trailer",
new int[] {0x61, 0xF0, 0x90, 0x61},
3,
"a\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Valid 4-byte sequence truncated to 1 byte with trailer",
new int[] {0x61, 0xF0, 0x61},
2,
"a\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"U+0000 zero-padded to two bytes",
new int[] {0x61, 0xC0, 0x80, 0x61},
1,
"a\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"U+007F zero-padded to two bytes",
new int[] {0x61, 0xC1, 0xBF, 0x61},
1,
"a\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Two bytes, all 1's",
new int[] {0x61, 0xFF, 0xFF, 0x61},
1,
"a\uFFFD\uFFFDa"));
testCase = new Utf8TestCase(
"Two bytes, 1110 first byte first nibble",
new int[] {0x61, 0xE0, 0x80, 0x61},
2,
"a\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Two bytes, 101x first byte first nibble",
new int[] {0x61, 0xA0, 0x80, 0x61},
1,
"a\uFFFD\uFFFDa"));
testCases.add(new Utf8TestCase(
"Two bytes, invalid second byte",
new int[] {0x61, 0xC2, 0x00, 0x61},
2,
"a\uFFFD\u0000a"));
testCases.add(new Utf8TestCase(
"Two bytes, invalid second byte",
new int[] {0x61, 0xC2, 0xC0, 0x61},
2,
"a\uFFFD\uFFFDa"));
testCase = new Utf8TestCase(
"Three bytes, U+0000 zero-padded",
new int[] {0x61, 0xE0, 0x80, 0x80, 0x61},
2,
"a\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Three bytes, U+007F zero-padded",
new int[] {0x61, 0xE0, 0x81, 0xBF, 0x61},
2,
"a\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Three bytes, U+07FF zero-padded",
new int[] {0x61, 0xE0, 0x9F, 0xBF, 0x61},
2,
"a\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Three bytes, all 1's",
new int[] {0x61, 0xFF, 0xFF, 0xFF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFDa"));
testCase = new Utf8TestCase(
"Three bytes, invalid first byte",
new int[] {0x61, 0xF8, 0x80, 0x80, 0x61},
1,
"a\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_MISSING2).addForJvm(
REPLACE_SWALLOWS_TRAILER);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Three bytes, invalid second byte",
new int[] {0x61, 0xE0, 0xC0, 0x80, 0x61},
2,
"a\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Three bytes, invalid third byte",
new int[] {0x61, 0xE1, 0x80, 0xC0, 0x61},
3,
"a\uFFFD\uFFFDa"));
testCase = new Utf8TestCase(
"Four bytes, U+0000 zero-padded",
new int[] {0x61, 0xF0, 0x80, 0x80, 0x80, 0x61},
2,
"a\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, U+007F zero-padded",
new int[] {0x61, 0xF0, 0x80, 0x81, 0xBF, 0x61},
2,
"a\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, U+07FF zero-padded",
new int[] {0x61, 0xF0, 0x80, 0x9F, 0xBF, 0x61},
2,
"a\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, U+FFFF zero-padded",
new int[] {0x61, 0xF0, 0x8F, 0xBF, 0xBF, 0x61},
2,
"a\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Four bytes, all 1's",
new int[] {0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFDa"));
testCase = new Utf8TestCase(
"Four bytes, invalid first byte",
new int[] {0x61, 0xF8, 0x80, 0x80, 0x80, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(
REPLACE_MISSING2).addForJvm(REPLACE_MISSING1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, invalid second byte",
new int[] {0x61, 0xF1, 0xC0, 0x80, 0x80, 0x61},
2,
"a\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, invalid third byte",
new int[] {0x61, 0xF1, 0x80, 0xC0, 0x80, 0x61},
3,
"a\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
testCases.add(new Utf8TestCase(
"Four bytes, invalid fourth byte",
new int[] {0x61, 0xF1, 0x80, 0x80, 0xC0, 0x61},
4,
"a\uFFFD\uFFFDa"));
testCase = new Utf8TestCase(
"Five bytes, U+0000 zero padded",
new int[] {0x61, 0xF8, 0x80, 0x80, 0x80, 0x80, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Five bytes, U+007F zero padded",
new int[] {0x61, 0xF8, 0x80, 0x80, 0x81, 0xBF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Five bytes, U+07FF zero padded",
new int[] {0x61, 0xF8, 0x80, 0x80, 0x9F, 0xBF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Five bytes, U+FFFF zero padded",
new int[] {0x61, 0xF8, 0x80, 0x8F, 0xBF, 0xBF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+0000 zero padded",
new int[] {0x61, 0xFC, 0x80, 0x80, 0x80, 0x80, 0x80, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+007F zero padded",
new int[] {0x61, 0xFC, 0x80, 0x80, 0x80, 0x81, 0xBF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+07FF zero padded",
new int[] {0x61, 0xFC, 0x80, 0x80, 0x80, 0x9F, 0xBF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+FFFF zero padded",
new int[] {0x61, 0xFC, 0x80, 0x80, 0x8F, 0xBF, 0xBF, 0x61},
1,
"a\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFDa");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
testCases.add(testCase);
testCase = new Utf8TestCase(
"Original test case - derived from Autobahn?",
new int[] {0xCE, 0xBA, 0xE1, 0xDB, 0xB9, 0xCF, 0x83, 0xCE,
0xBC, 0xCE, 0xB5, 0xED, 0x80, 0x65, 0x64, 0x69,
0x74, 0x65, 0x64},
3,
"\u03BA\uFFFD\u06F9\u03C3\u03BC\u03B5\uFFFDedited");
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
testCases.add(testCase);
TEST_CASES = Collections.unmodifiableList(testCases);
}
@Test
public void testHarmonyDecoder() {
CharsetDecoder decoder = new Utf8Decoder();
for (Utf8TestCase testCase : TEST_CASES) {
doTest(decoder, testCase, 0);
}
}
@Test
public void testJvmDecoder() {
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
int testCount = 0;
try {
for (Utf8TestCase testCase : TEST_CASES) {
doTest(decoder, testCase, testCase.flagsJvm);
testCount++;
}
} finally {
System.err.println("Workarounds added to " + workAroundCount +
" tests to account for known JVM bugs");
if (testCount < TEST_CASES.size()) {
System.err.println("Executed " + testCount + " of " +
TEST_CASES.size() + " UTF-8 tests before " +
"encountering a failure");
}
}
}
private void doTest(CharsetDecoder decoder, Utf8TestCase testCase,
int flags) {
int len = testCase.input.length;
ByteBuffer bb = ByteBuffer.allocate(len);
CharBuffer cb = CharBuffer.allocate(len);
// Configure decoder to fail on an error
decoder.reset();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
// Add each byte one at a time. The decoder should fail as soon as
// an invalid sequence has been provided
for (int i = 0; i < len; i++) {
bb.put((byte) testCase.input[i]);
bb.flip();
CoderResult cr = decoder.decode(bb, cb, false);
if (cr.isError()) {
int expected = testCase.invalidIndex;
if ((flags & ERROR_POS_PLUS1) != 0) {
expected += 1;
}
if ((flags & ERROR_POS_PLUS2) != 0) {
expected += 2;
}
if ((flags & ERROR_POS_PLUS4) != 0) {
expected += 4;
}
Assert.assertEquals(testCase.description, expected, i);
break;
}
bb.compact();
}
// Configure decoder to replace on an error
decoder.reset();
decoder.onMalformedInput(CodingErrorAction.REPLACE);
decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
// Add each byte one at a time.
bb.clear();
cb.clear();
for (int i = 0; i < len; i++) {
bb.put((byte) testCase.input[i]);
bb.flip();
CoderResult cr = decoder.decode(bb, cb, false);
if (cr.isError()) {
Assert.fail(testCase.description);
}
bb.compact();
}
// For incomplete sequences at the end of the input need to tell
// the decoder the input has ended
bb.flip();
CoderResult cr = decoder.decode(bb, cb, true);
if (cr.isError()) {
Assert.fail(testCase.description);
}
cb.flip();
String expected = testCase.outputReplaced;
if ((flags & REPLACE_SWALLOWS_TRAILER) != 0) {
expected = expected.substring(0, expected.length() - 1);
}
if ((flags & REPLACE_MISSING1) != 0) {
expected = expected.substring(0, 1) +
expected.substring(2, expected.length());
}
if ((flags & REPLACE_MISSING2) != 0) {
expected = expected.substring(0, 1) +
expected.substring(3, expected.length());
}
if ((flags & REPLACE_MISSING4) != 0) {
expected = expected.substring(0, 1) +
expected.substring(5, expected.length());
}
Assert.assertEquals(testCase.description, expected, cb.toString());
}
/**
* Encapsulates a single UTF-8 test case
*/
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) {
this.description = description;
this.input = input;
this.invalidIndex = invalidIndex;
this.outputReplaced = outputReplaced;
}
public Utf8TestCase addForJvm(int flag) {
if (this.flagsJvm == 0) {
TestUtf8.workAroundCount++;
}
this.flagsJvm = this.flagsJvm | flag;
return this;
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.tomcat.util.buf;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
public abstract class TesterUriUtilBase {
private final String separator;
protected TesterUriUtilBase(String separator) {
this.separator = separator;
TomcatURLStreamHandlerFactory.register();
System.setProperty("org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR", separator);
}
@Test
public void testBuildJarUrl01() throws MalformedURLException {
File jarFile = new File("/patha/pathb!/pathc");
String result = UriUtil.buildJarUrl(jarFile).toString();
int index = result.indexOf("!/");
Assert.assertEquals(result, result.length() - 2, index);
}
@Test
public void testBuildJarUrl02() throws MalformedURLException {
File jarFile = new File("/patha/pathb*/pathc");
String result = UriUtil.buildJarUrl(jarFile).toString();
int index = result.indexOf("!/");
Assert.assertEquals(result, result.length() - 2, index);
index = result.indexOf("*/");
Assert.assertEquals(result, -1, index);
}
@Test
public void testBuildJarUrl03() throws MalformedURLException {
File jarFile = new File("/patha/pathb^/pathc");
String result = UriUtil.buildJarUrl(jarFile).toString();
int index = result.indexOf("!/");
Assert.assertEquals(result, result.length() - 2, index);
index = result.indexOf("^/");
Assert.assertEquals(result, -1, index);
}
@Test
public void testBuildJarUrl04() throws MalformedURLException {
File jarFile = new File("/patha/pathb" + separator + "/pathc");
String result = UriUtil.buildJarUrl(jarFile).toString();
int index = result.indexOf("!/");
Assert.assertEquals(result, result.length() - 2, index);
index = result.indexOf(separator + "/");
Assert.assertEquals(result, -1, index);
}
@Test
public void testWarToJar01() throws MalformedURLException {
doTestWarToJar("^");
}
@Test
public void testWarToJar02() throws MalformedURLException {
doTestWarToJar("*");
}
@Test
public void testWarToJar03() throws MalformedURLException {
doTestWarToJar(separator);
}
private void doTestWarToJar(String separator) throws MalformedURLException {
URL warUrl = new URL("war:file:/external/path" + separator + "/internal/path");
URL jarUrl = UriUtil.warToJar(warUrl);
Assert.assertEquals("jar:file:/external/path!/internal/path", jarUrl.toString());
}
// @Test /* Uncomment to test performance for different implementations. */
public void performanceTestBuildJarUrl() throws MalformedURLException {
File jarFile = new File("/patha/pathb^/pathc");
URL url = null;
int count = 1000000;
// Warm up
for (int i = 0; i < count / 10; i++) {
url = UriUtil.buildJarUrl(jarFile);
}
// Test
long start = System.nanoTime();
for (int i = 0; i < count / 10; i++) {
url = UriUtil.buildJarUrl(jarFile);
}
long duration = System.nanoTime() - start;
System.out.println("[" + count + "] iterations took [" +
duration + "] ns for [" + url + "]");
}
}