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,112 @@
/*
* 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;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.core.StandardContext;
public class TestIntrospectionUtils {
// Test for all the classes and interfaces in StandardContext's type hierarchy
@Test
public void testIsInstanceStandardContext01() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.catalina.core.StandardContext"));
}
@Test
public void testIsInstanceStandardContext02() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.catalina.util.LifecycleMBeanBase"));
}
@Test
public void testIsInstanceStandardContext03() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.catalina.util.LifecycleBase"));
}
@Test
public void testIsInstanceStandardContext04() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "java.lang.Object"));
}
@Test
public void testIsInstanceStandardContext05() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.catalina.Lifecycle"));
}
@Test
public void testIsInstanceStandardContext06() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.catalina.JmxEnabled"));
}
@Test
public void testIsInstanceStandardContext07() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "javax.management.MBeanRegistration"));
}
@Test
public void testIsInstanceStandardContext08() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.catalina.Container"));
}
@Test
public void testIsInstanceStandardContext09() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "org.apache.tomcat.ContextBind"));
}
@Test
public void testIsInstanceStandardContext10() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "javax.management.NotificationEmitter"));
}
@Test
public void testIsInstanceStandardContext11() {
Assert.assertTrue(IntrospectionUtils.isInstance(
StandardContext.class, "javax.management.NotificationBroadcaster"));
}
// And one to check that non-matches return false
@Test
public void testIsInstanceStandardContext12() {
Assert.assertFalse(IntrospectionUtils.isInstance(
StandardContext.class, "com.example.Other"));
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.bcel;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.Jar;
import org.apache.tomcat.util.bcel.classfile.ClassParser;
import org.apache.tomcat.util.scan.JarFactory;
public class TesterPerformance {
private static final String JAR_LOCATION = "/tmp/jira-libs";
@Test
public void testClassParserPerformance() throws IOException {
File libDir = new File(JAR_LOCATION);
String[] libs = libDir.list();
Assert.assertNotNull(libs);
Set<URL> jarURLs = new HashSet<>();
for (String lib : libs) {
if (!lib.toLowerCase(Locale.ENGLISH).endsWith(".jar")) {
continue;
}
jarURLs.add(new URL("jar:" + new File (libDir, lib).toURI().toURL().toExternalForm() + "!/"));
}
long duration = 0;
for (URL jarURL : jarURLs) {
try (Jar jar = JarFactory.newInstance(jarURL)) {
jar.nextEntry();
String jarEntryName = jar.getEntryName();
while (jarEntryName != null) {
if (jarEntryName.endsWith(".class")) {
InputStream is = jar.getEntryInputStream();
long start = System.nanoTime();
ClassParser cp = new ClassParser(is);
cp.parse();
duration += System.nanoTime() - start;
}
jar.nextEntry();
jarEntryName = jar.getEntryName();
}
}
}
System.out.println("ClassParser performance test took: " + duration + " ns");
}
}

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 + "]");
}
}

View File

@@ -0,0 +1,215 @@
/*
* 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.collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
public class TestCaseInsensitiveKeyMap {
@Test
public void testPut() {
Object o1 = new Object();
Object o2 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Object o = map.put("A", o2);
Assert.assertEquals(o1, o);
Assert.assertEquals(o2, map.get("a"));
Assert.assertEquals(o2, map.get("A"));
}
@Test(expected=NullPointerException.class)
public void testPutNullKey() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put(null, o1);
}
@Test
public void testGet() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertEquals(o1, map.get("a"));
Assert.assertEquals(o1, map.get("A"));
}
@Test
public void testGetNullKey() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertNull(map.get(null));
}
@Test
public void testContainsKey() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertTrue(map.containsKey("a"));
Assert.assertTrue(map.containsKey("A"));
}
@Test
public void testContainsKeyNonString() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertFalse(map.containsKey(o1));
}
@Test
public void testContainsKeyNull() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertFalse(map.containsKey(null));
}
@Test
public void testContainsValue() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertTrue(map.containsValue(o1));
}
@Test
public void testRemove() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Assert.assertFalse(map.isEmpty());
map.remove("A");
Assert.assertTrue(map.isEmpty());
map.put("A", o1);
Assert.assertFalse(map.isEmpty());
map.remove("a");
Assert.assertTrue(map.isEmpty());
}
@Test
public void testClear() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
for (int i = 0; i < 10; i++) {
map.put(Integer.toString(i), o1);
}
Assert.assertEquals(10, map.size());
map.clear();
Assert.assertEquals(0, map.size());
}
@Test
public void testPutAll() {
Object o1 = new Object();
Object o2 = new Object();
Map<String,Object> source = new HashMap<>();
source.put("a", o1);
source.put("A", o2);
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.putAll(source);
Assert.assertEquals(1, map.size());
Assert.assertTrue(map.containsValue(o1) != map.containsValue(o2));
}
@Test
public void testKeySetContains() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Set<String> keys = map.keySet();
Assert.assertTrue(keys.contains("a"));
Assert.assertTrue(keys.contains("A"));
}
@Test
public void testKeySetRemove() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Iterator<String> iter = map.keySet().iterator();
Assert.assertTrue(iter.hasNext());
iter.next();
iter.remove();
Assert.assertTrue(map.isEmpty());
}
@Test
public void testEntrySetRemove() {
Object o1 = new Object();
CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
map.put("a", o1);
Iterator<Entry<String,Object>> iter = map.entrySet().iterator();
Assert.assertTrue(iter.hasNext());
Entry<String,Object> entry = iter.next();
Assert.assertEquals("a", entry.getKey());
Assert.assertEquals(o1, entry.getValue());
iter.remove();
Assert.assertTrue(map.isEmpty());
}
}

View File

@@ -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.tomcat.util.collections;
import org.junit.Assert;
import org.junit.Test;
public class TestSynchronizedQueue {
public void testPollEmpty() {
SynchronizedQueue<Object> queue = new SynchronizedQueue<>();
Assert.assertNull(queue.poll());
}
@Test
public void testOfferPollOrder() {
SynchronizedQueue<Object> queue = new SynchronizedQueue<>();
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
queue.offer(o1);
queue.offer(o2);
queue.offer(o3);
queue.offer(o4);
Assert.assertSame(queue.poll(), o1);
Assert.assertSame(queue.poll(), o2);
Assert.assertSame(queue.poll(), o3);
Assert.assertSame(queue.poll(), o4);
Assert.assertNull(queue.poll());
}
@Test
public void testExpandOfferPollOrder() {
SynchronizedQueue<Object> queue = new SynchronizedQueue<>();
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
for (int i = 0; i < 300; i++) {
queue.offer(o1);
queue.offer(o2);
queue.offer(o3);
queue.offer(o4);
}
for (int i = 0; i < 300; i++) {
Assert.assertSame(queue.poll(), o1);
Assert.assertSame(queue.poll(), o2);
Assert.assertSame(queue.poll(), o3);
Assert.assertSame(queue.poll(), o4);
}
Assert.assertNull(queue.poll());
}
@Test
public void testExpandOfferPollOrder2() {
SynchronizedQueue<Object> queue = new SynchronizedQueue<>();
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
for (int i = 0; i < 100; i++) {
queue.offer(o1);
queue.offer(o2);
queue.offer(o3);
queue.offer(o4);
}
for (int i = 0; i < 50; i++) {
Assert.assertSame(queue.poll(), o1);
Assert.assertSame(queue.poll(), o2);
Assert.assertSame(queue.poll(), o3);
Assert.assertSame(queue.poll(), o4);
}
for (int i = 0; i < 200; i++) {
queue.offer(o1);
queue.offer(o2);
queue.offer(o3);
queue.offer(o4);
}
for (int i = 0; i < 250; i++) {
Assert.assertSame(queue.poll(), o1);
Assert.assertSame(queue.poll(), o2);
Assert.assertSame(queue.poll(), o3);
Assert.assertSame(queue.poll(), o4);
}
Assert.assertNull(queue.poll());
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.collections;
import org.junit.Assert;
import org.junit.Test;
public class TestSynchronizedStack {
@Test
public void testPopEmpty() {
SynchronizedStack<Object> stack = new SynchronizedStack<>();
Assert.assertNull(stack.pop());
}
@Test
public void testPushPopOrder() {
SynchronizedStack<Object> stack = new SynchronizedStack<>();
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
stack.push(o1);
stack.push(o2);
stack.push(o3);
stack.push(o4);
Assert.assertSame(stack.pop(), o4);
Assert.assertSame(stack.pop(), o3);
Assert.assertSame(stack.pop(), o2);
Assert.assertSame(stack.pop(), o1);
Assert.assertNull(stack.pop());
}
@Test
public void testExpandPushPopOrder() {
SynchronizedStack<Object> stack = new SynchronizedStack<>();
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
for (int i = 0; i < 300; i++) {
stack.push(o1);
stack.push(o2);
stack.push(o3);
stack.push(o4);
}
for (int i = 0; i < 300; i++) {
Assert.assertSame(stack.pop(), o4);
Assert.assertSame(stack.pop(), o3);
Assert.assertSame(stack.pop(), o2);
Assert.assertSame(stack.pop(), o1);
}
Assert.assertNull(stack.pop());
}
@Test
public void testLimit() {
SynchronizedStack<Object> stack = new SynchronizedStack<>(2,2);
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
stack.push(o1);
stack.push(o2);
stack.push(o3);
stack.push(o4);
Assert.assertSame(stack.pop(), o2);
Assert.assertSame(stack.pop(), o1);
Assert.assertNull(stack.pop());
}
@Test
public void testLimitExpand() {
SynchronizedStack<Object> stack = new SynchronizedStack<>(1,3);
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
stack.push(o1);
stack.push(o2);
stack.push(o3);
stack.push(o4);
Assert.assertSame(stack.pop(), o3);
Assert.assertSame(stack.pop(), o2);
Assert.assertSame(stack.pop(), o1);
Assert.assertNull(stack.pop());
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.collections;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.junit.Test;
public class TesterPerformanceSynchronizedQueue {
private static final int THREAD_COUNT = 4;
private static final int ITERATIONS = 1000000;
private static final SynchronizedQueue<Object> S_QUEUE =
new SynchronizedQueue<>();
private static final Queue<Object> QUEUE = new ConcurrentLinkedQueue<>();
@Test
public void testSynchronizedQueue() throws InterruptedException {
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new StackThread();
}
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].start();
}
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println("SynchronizedQueue: " + (end - start) + "ms");
}
public static class StackThread extends Thread {
@Override
public void run() {
for(int i = 0; i < ITERATIONS; i++) {
Object obj = S_QUEUE.poll();
if (obj == null) {
obj = new Object();
}
S_QUEUE.offer(obj);
}
super.run();
}
}
@Test
public void testConcurrentQueue() throws InterruptedException {
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new QueueThread();
}
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].start();
}
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println("ConcurrentLinkedQueue: " + (end - start) + "ms");
}
public static class QueueThread extends Thread {
@Override
public void run() {
for(int i = 0; i < ITERATIONS; i++) {
Object obj = QUEUE.poll();
if (obj == null) {
obj = new Object();
}
QUEUE.offer(obj);
}
super.run();
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.collections;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.junit.Test;
public class TesterPerformanceSynchronizedStack {
private static final int THREAD_COUNT = 4;
private static final int ITERATIONS = 1000000;
private static final SynchronizedStack<Object> STACK =
new SynchronizedStack<>();
private static final Queue<Object> QUEUE = new ConcurrentLinkedQueue<>();
@Test
public void testSynchronizedStack() throws InterruptedException {
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new StackThread();
}
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].start();
}
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println("SynchronizedStack: " + (end - start) + "ms");
}
public static class StackThread extends Thread {
@Override
public void run() {
for(int i = 0; i < ITERATIONS; i++) {
Object obj = STACK.pop();
if (obj == null) {
obj = new Object();
}
STACK.push(obj);
}
super.run();
}
}
@Test
public void testConcurrentQueue() throws InterruptedException {
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new QueueThread();
}
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].start();
}
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println("ConcurrentLinkedQueue: " + (end - start) + "ms");
}
public static class QueueThread extends Thread {
@Override
public void run() {
for(int i = 0; i < ITERATIONS; i++) {
Object obj = QUEUE.poll();
if (obj == null) {
obj = new Object();
}
QUEUE.offer(obj);
}
super.run();
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.descriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class TestLocalResolver {
private final Map<String, String> publicIds = new HashMap<>();
private final Map<String, String> systemIds = new HashMap<>();
private LocalResolver resolver = new LocalResolver(publicIds, systemIds, true);
private String WEB_22_LOCAL;
private String WEB_31_LOCAL;
private String WEBCOMMON_31_LOCAL;
@Before
public void init() {
WEB_22_LOCAL = urlFor("resources/web-app_2_2.dtd");
WEB_31_LOCAL = urlFor("resources/web-app_3_1.xsd");
WEBCOMMON_31_LOCAL = urlFor("resources/web-common_3_1.xsd");
publicIds.put(XmlIdentifiers.WEB_22_PUBLIC, WEB_22_LOCAL);
systemIds.put(XmlIdentifiers.WEB_31_XSD, WEB_31_LOCAL);
systemIds.put(WEBCOMMON_31_LOCAL, WEBCOMMON_31_LOCAL);
}
public String urlFor(String id) {
return ServletContext.class.getResource(id).toExternalForm();
}
@Test(expected = FileNotFoundException.class)
public void unknownNullId() throws IOException, SAXException {
Assert.assertNull(resolver.resolveEntity(null, null));
}
@Test(expected = FileNotFoundException.class)
public void unknownPublicId() throws IOException, SAXException {
Assert.assertNull(resolver.resolveEntity("unknown", null));
}
@Test(expected = FileNotFoundException.class)
public void unknownSystemId() throws IOException, SAXException {
InputSource source = resolver.resolveEntity(null, "unknown");
Assert.assertEquals(null, source.getPublicId());
Assert.assertEquals("unknown", source.getSystemId());
}
@Test(expected = FileNotFoundException.class)
public void unknownRelativeSystemId()
throws IOException, SAXException {
InputSource source = resolver.resolveEntity(
null, null, "http://example.com/home.html", "unknown");
Assert.assertEquals(null, source.getPublicId());
Assert.assertEquals("http://example.com/unknown", source.getSystemId());
}
@Test
public void publicIdIsResolved() throws IOException, SAXException {
InputSource source = resolver.resolveEntity(
XmlIdentifiers.WEB_22_PUBLIC, XmlIdentifiers.WEB_22_SYSTEM);
Assert.assertEquals(XmlIdentifiers.WEB_22_PUBLIC, source.getPublicId());
Assert.assertEquals(WEB_22_LOCAL, source.getSystemId());
}
@Test
public void systemIdIsIgnoredWhenPublicIdIsResolved()
throws IOException, SAXException {
InputSource source = resolver.resolveEntity(
XmlIdentifiers.WEB_22_PUBLIC, "unknown");
Assert.assertEquals(XmlIdentifiers.WEB_22_PUBLIC, source.getPublicId());
Assert.assertEquals(WEB_22_LOCAL, source.getSystemId());
}
@Test
public void systemIdIsResolved() throws IOException, SAXException {
InputSource source =
resolver.resolveEntity(null, XmlIdentifiers.WEB_31_XSD);
Assert.assertEquals(null, source.getPublicId());
Assert.assertEquals(WEB_31_LOCAL, source.getSystemId());
}
@Test
public void relativeSystemIdIsResolvedAgainstBaseURI()
throws IOException, SAXException {
InputSource source = resolver.resolveEntity(
null, null, WEB_31_LOCAL, "web-common_3_1.xsd");
Assert.assertEquals(null, source.getPublicId());
Assert.assertEquals(WEBCOMMON_31_LOCAL, source.getSystemId());
}
@Test
public void absoluteSystemIdOverridesBaseURI()
throws IOException, SAXException {
InputSource source = resolver.resolveEntity(null, null,
"http://example.com/home.html", XmlIdentifiers.WEB_31_XSD);
Assert.assertEquals(null, source.getPublicId());
Assert.assertEquals(WEB_31_LOCAL, source.getSystemId());
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.descriptor.tld;
import java.io.File;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class TestImplicitTldParser {
private TldParser parser;
@Before
public void init() {
parser = new TldParser(true, true, new ImplicitTldRuleSet(), true);
}
@Test
public void testImplicitTldGood() throws Exception {
TaglibXml xml = parse("test/tld/implicit-good.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("2.1", xml.getJspVersion());
Assert.assertEquals("Ignored", xml.getShortName());
}
@Test(expected=SAXParseException.class)
public void testImplicitTldBad() throws Exception {
TaglibXml xml = parse("test/tld/implicit-bad.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("2.1", xml.getJspVersion());
Assert.assertEquals("Ignored", xml.getShortName());
}
private TaglibXml parse(String pathname) throws IOException, SAXException {
File file = new File(pathname);
TldResourcePath path = new TldResourcePath(file.toURI().toURL(), null);
return parser.parse(path);
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.descriptor.tld;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.servlet.jsp.tagext.FunctionInfo;
import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TagVariableInfo;
import javax.servlet.jsp.tagext.VariableInfo;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.xml.sax.SAXException;
public class TestTldParser {
private TldParser parser;
@Before
public void init() {
parser = new TldParser(true, true, new TldRuleSet(), true);
}
@Test
public void testTld() throws Exception {
TaglibXml xml = parse("test/tld/test.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("2.1", xml.getJspVersion());
Assert.assertEquals("test", xml.getShortName());
Assert.assertEquals("http://tomcat.apache.org/TldTests", xml.getUri());
Assert.assertEquals(1, xml.getFunctions().size());
ValidatorXml validator = xml.getValidator();
Assert.assertEquals("com.example.Validator", validator.getValidatorClass());
Assert.assertEquals(1, validator.getInitParams().size());
Assert.assertEquals("value", validator.getInitParams().get("name"));
Assert.assertEquals(1, xml.getTags().size());
TagXml tag = xml.getTags().get(0);
Assert.assertEquals("org.apache.jasper.compiler.TestValidator$Echo", tag.getTagClass());
Assert.assertEquals("empty", tag.getBodyContent());
Assert.assertTrue(tag.hasDynamicAttributes());
Assert.assertEquals(1, tag.getVariables().size());
TagVariableInfo variableInfo = tag.getVariables().get(0);
Assert.assertEquals("var", variableInfo.getNameGiven());
Assert.assertEquals("java.lang.Object", variableInfo.getClassName());
Assert.assertTrue(variableInfo.getDeclare());
Assert.assertEquals(VariableInfo.AT_END, variableInfo.getScope());
Assert.assertEquals(4, tag.getAttributes().size());
TagAttributeInfo attributeInfo = tag.getAttributes().get(0);
Assert.assertEquals("Echo Tag", tag.getInfo());
Assert.assertEquals("Echo", tag.getDisplayName());
Assert.assertEquals("small", tag.getSmallIcon());
Assert.assertEquals("large", tag.getLargeIcon());
Assert.assertEquals("echo", attributeInfo.getName());
Assert.assertTrue(attributeInfo.isRequired());
Assert.assertTrue(attributeInfo.canBeRequestTime());
attributeInfo = tag.getAttributes().get(1);
Assert.assertEquals("fragment", attributeInfo.getName());
Assert.assertTrue(attributeInfo.isFragment());
Assert.assertTrue(attributeInfo.canBeRequestTime());
Assert.assertEquals("javax.servlet.jsp.tagext.JspFragment", attributeInfo.getTypeName());
attributeInfo = tag.getAttributes().get(2);
Assert.assertEquals("deferredValue", attributeInfo.getName());
Assert.assertEquals("javax.el.ValueExpression", attributeInfo.getTypeName());
Assert.assertEquals("java.util.Date", attributeInfo.getExpectedTypeName());
attributeInfo = tag.getAttributes().get(3);
Assert.assertEquals("deferredMethod", attributeInfo.getName());
Assert.assertEquals("javax.el.MethodExpression", attributeInfo.getTypeName());
Assert.assertEquals("java.util.Date getDate()", attributeInfo.getMethodSignature());
Assert.assertEquals(1, xml.getTagFiles().size());
TagFileXml tagFile = xml.getTagFiles().get(0);
Assert.assertEquals("Echo", tag.getDisplayName());
Assert.assertEquals("small", tag.getSmallIcon());
Assert.assertEquals("large", tag.getLargeIcon());
Assert.assertEquals("Echo2", tagFile.getName());
Assert.assertEquals("/echo.tag", tagFile.getPath());
Assert.assertEquals(1, xml.getFunctions().size());
FunctionInfo fn = xml.getFunctions().get(0);
Assert.assertEquals("trim", fn.getName());
Assert.assertEquals("org.apache.el.TesterFunctions", fn.getFunctionClass());
Assert.assertEquals("java.lang.String trim(java.lang.String)", fn.getFunctionSignature());
}
@Test
public void testParseTld21() throws Exception {
TaglibXml xml = parse("test/tld/tags21.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("2.1", xml.getJspVersion());
Assert.assertEquals("Tags21", xml.getShortName());
Assert.assertEquals("http://tomcat.apache.org/tags21", xml.getUri());
verifyTags(xml.getTags());
}
@Test
public void testParseTld20() throws Exception {
TaglibXml xml = parse("test/tld/tags20.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("2.0", xml.getJspVersion());
Assert.assertEquals("Tags20", xml.getShortName());
Assert.assertEquals("http://tomcat.apache.org/tags20", xml.getUri());
verifyTags(xml.getTags());
}
@Test
public void testParseTld12() throws Exception {
TaglibXml xml = parse("test/tld/tags12.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("1.2", xml.getJspVersion());
Assert.assertEquals("Tags12", xml.getShortName());
Assert.assertEquals("http://tomcat.apache.org/tags12", xml.getUri());
verifyTags(xml.getTags());
}
@Test
public void testParseTld11() throws Exception {
TaglibXml xml = parse("test/tld/tags11.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
Assert.assertEquals("1.1", xml.getJspVersion());
Assert.assertEquals("Tags11", xml.getShortName());
Assert.assertEquals("http://tomcat.apache.org/tags11", xml.getUri());
verifyTags(xml.getTags());
}
private void verifyTags(List<TagXml> tags) {
Assert.assertEquals(1, tags.size());
TagXml tag = tags.get(0);
Assert.assertEquals("Echo", tag.getName());
Assert.assertEquals("org.apache.jasper.compiler.TestValidator$Echo", tag.getTagClass());
Assert.assertEquals("empty", tag.getBodyContent());
}
@Test
public void testListener() throws Exception {
TaglibXml xml = parse("test/tld/listener.tld");
Assert.assertEquals("1.0", xml.getTlibVersion());
List<String> listeners = xml.getListeners();
Assert.assertEquals(1, listeners.size());
Assert.assertEquals("org.apache.catalina.core.TesterTldListener", listeners.get(0));
}
private TaglibXml parse(String pathname) throws IOException, SAXException {
File file = new File(pathname);
TldResourcePath path = new TldResourcePath(file.toURI().toURL(), null);
return parser.parse(path);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.descriptor.web;
import org.junit.Assert;
import org.junit.Test;
/**
* Test case for {@link FilterDef}.
*/
public class TestFilterDef {
@Test(expected = IllegalArgumentException.class)
public void testSetFilterNameNull() {
new FilterDef().setFilterName(null);
}
@Test(expected = IllegalArgumentException.class)
public void testSetFilterNameEmptyString() {
new FilterDef().setFilterName("");
}
@Test
public void testSetFilterName() {
FilterDef filterDef = new FilterDef();
filterDef.setFilterName("test");
Assert.assertEquals("'test' is expected as filter name",
"test", filterDef.getFilterName());
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.descriptor.web;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import javax.servlet.descriptor.TaglibDescriptor;
import org.junit.Assert;
import org.junit.Test;
public class TestJspConfigDescriptorImpl {
@Test
public void testTaglibsAreIsolate() {
List<TaglibDescriptor> taglibs = new ArrayList<>();
taglibs.add(new TaglibDescriptorImpl("location", "uri"));
List<JspPropertyGroupDescriptor> propertyGroups = Collections.emptyList();
JspConfigDescriptor descriptor = new JspConfigDescriptorImpl(propertyGroups, taglibs);
descriptor.getTaglibs().clear();
Assert.assertEquals(taglibs, descriptor.getTaglibs());
}
@Test
public void testPropertyGroupsAreIsolate() {
List<TaglibDescriptor> taglibs = Collections.emptyList();
List<JspPropertyGroupDescriptor> propertyGroups = new ArrayList<>();
propertyGroups.add(new JspPropertyGroupDescriptorImpl(new JspPropertyGroup()));
JspConfigDescriptor descriptor = new JspConfigDescriptorImpl(propertyGroups, taglibs);
descriptor.getJspPropertyGroups().clear();
Assert.assertEquals(propertyGroups, descriptor.getJspPropertyGroups());
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.descriptor.web;
import org.junit.Assert;
import org.junit.Test;
public class TestJspPropertyGroup {
private JspPropertyGroup group = new JspPropertyGroup();
@Test
public void testBug55262() {
group.addIncludePrelude("/prelude");
group.addIncludePrelude("/prelude");
group.addIncludeCoda("/coda");
group.addIncludeCoda("/coda");
Assert.assertEquals(2, group.getIncludePreludes().size());
Assert.assertEquals(2, group.getIncludeCodas().size());
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.descriptor.web;
import org.junit.Assert;
import org.junit.Test;
public class TestJspPropertyGroupDescriptorImpl {
@Test
public void testPreludesAreIsolated() {
JspPropertyGroup jpg = new JspPropertyGroup();
jpg.addIncludePrelude("prelude");
JspPropertyGroupDescriptorImpl descriptor = new JspPropertyGroupDescriptorImpl(jpg);
descriptor.getIncludePreludes().clear();
Assert.assertEquals(1, descriptor.getIncludePreludes().size());
}
@Test
public void testCodasAreIsolated() {
JspPropertyGroup jpg = new JspPropertyGroup();
jpg.addIncludeCoda("coda");
JspPropertyGroupDescriptorImpl descriptor = new JspPropertyGroupDescriptorImpl(jpg);
descriptor.getIncludeCodas().clear();
Assert.assertEquals(1, descriptor.getIncludeCodas().size());
}
@Test
public void testUrlPatternsAreIsolated() {
JspPropertyGroup jpg = new JspPropertyGroup();
jpg.addUrlPatternDecoded("pattern");
JspPropertyGroupDescriptorImpl descriptor = new JspPropertyGroupDescriptorImpl(jpg);
descriptor.getUrlPatterns().clear();
Assert.assertEquals(1, descriptor.getUrlPatterns().size());
}
}

View File

@@ -0,0 +1,453 @@
/*
* 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.descriptor.web;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.HttpConstraintElement;
import javax.servlet.HttpMethodConstraintElement;
import javax.servlet.ServletSecurityElement;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import org.junit.Assert;
import org.junit.Test;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
public class TestSecurityConstraint {
private static final String URL_PATTERN = "/test";
private static final String ROLE1 = "R1";
private static final Log DUMMY_LOG = LogFactory.getLog("DUMMY");
private static final SecurityConstraint GET_ONLY;
private static final SecurityConstraint POST_ONLY;
private static final SecurityConstraint GET_OMIT;
private static final SecurityConstraint POST_OMIT;
static {
// Configure the constraints to use in the tests
GET_ONLY = new SecurityConstraint();
GET_ONLY.addAuthRole(ROLE1);
SecurityCollection scGetOnly = new SecurityCollection();
scGetOnly.addMethod("GET");
scGetOnly.addPatternDecoded(URL_PATTERN);
scGetOnly.setName("GET-ONLY");
GET_ONLY.addCollection(scGetOnly);
POST_ONLY = new SecurityConstraint();
POST_ONLY.addAuthRole(ROLE1);
SecurityCollection scPostOnly = new SecurityCollection();
scPostOnly.addMethod("POST");
scPostOnly.addPatternDecoded(URL_PATTERN);
scPostOnly.setName("POST_ONLY");
POST_ONLY.addCollection(scPostOnly);
GET_OMIT = new SecurityConstraint();
GET_OMIT.addAuthRole(ROLE1);
SecurityCollection scGetOmit = new SecurityCollection();
scGetOmit.addOmittedMethod("GET");
scGetOmit.addPatternDecoded(URL_PATTERN);
scGetOmit.setName("GET_OMIT");
GET_OMIT.addCollection(scGetOmit);
POST_OMIT = new SecurityConstraint();
POST_OMIT.addAuthRole(ROLE1);
SecurityCollection scPostOmit = new SecurityCollection();
scPostOmit.addOmittedMethod("POST");
scPostOmit.addPatternDecoded(URL_PATTERN);
scPostOmit.setName("POST_OMIT");
POST_OMIT.addCollection(scPostOmit);
}
/**
* Uses the examples in SRV.13.4 as the basis for these tests
*/
@Test
public void testCreateConstraints() {
ServletSecurityElement element;
SecurityConstraint[] result;
Set<HttpMethodConstraintElement> hmces = new HashSet<>();
// Example 13-1
// @ServletSecurity
element = new ServletSecurityElement();
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(0, result.length);
// Example 13-2
// @ServletSecurity(
// @HttpConstraint(
// transportGuarantee = TransportGuarantee.CONFIDENTIAL))
element = new ServletSecurityElement(
new HttpConstraintElement(
ServletSecurity.TransportGuarantee.CONFIDENTIAL));
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(1, result.length);
Assert.assertFalse(result[0].getAuthConstraint());
Assert.assertTrue(result[0].findCollections()[0].findPattern(URL_PATTERN));
Assert.assertEquals(0, result[0].findCollections()[0].findMethods().length);
Assert.assertEquals(ServletSecurity.TransportGuarantee.CONFIDENTIAL.name(),
result[0].getUserConstraint());
// Example 13-3
// @ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
element = new ServletSecurityElement(
new HttpConstraintElement(EmptyRoleSemantic.DENY));
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(1, result.length);
Assert.assertTrue(result[0].getAuthConstraint());
Assert.assertTrue(result[0].findCollections()[0].findPattern(URL_PATTERN));
Assert.assertEquals(0, result[0].findCollections()[0].findMethods().length);
Assert.assertEquals(ServletSecurity.TransportGuarantee.NONE.name(),
result[0].getUserConstraint());
// Example 13-4
// @ServletSecurity(@HttpConstraint(rolesAllowed = "R1"))
element = new ServletSecurityElement(new HttpConstraintElement(
ServletSecurity.TransportGuarantee.NONE, ROLE1));
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(1, result.length);
Assert.assertTrue(result[0].getAuthConstraint());
Assert.assertEquals(1, result[0].findAuthRoles().length);
Assert.assertTrue(result[0].findAuthRole(ROLE1));
Assert.assertTrue(result[0].findCollections()[0].findPattern(URL_PATTERN));
Assert.assertEquals(0, result[0].findCollections()[0].findMethods().length);
Assert.assertEquals(ServletSecurity.TransportGuarantee.NONE.name(),
result[0].getUserConstraint());
// Example 13-5
// @ServletSecurity((httpMethodConstraints = {
// @HttpMethodConstraint(value = "GET", rolesAllowed = "R1"),
// @HttpMethodConstraint(value = "POST", rolesAllowed = "R1",
// transportGuarantee = TransportGuarantee.CONFIDENTIAL)
// })
hmces.clear();
hmces.add(new HttpMethodConstraintElement("GET",
new HttpConstraintElement(
ServletSecurity.TransportGuarantee.NONE, ROLE1)));
hmces.add(new HttpMethodConstraintElement("POST",
new HttpConstraintElement(
ServletSecurity.TransportGuarantee.CONFIDENTIAL,
ROLE1)));
element = new ServletSecurityElement(hmces);
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(2, result.length);
for (int i = 0; i < 2; i++) {
Assert.assertTrue(result[i].getAuthConstraint());
Assert.assertEquals(1, result[i].findAuthRoles().length);
Assert.assertTrue(result[i].findAuthRole(ROLE1));
Assert.assertTrue(result[i].findCollections()[0].findPattern(URL_PATTERN));
Assert.assertEquals(1, result[i].findCollections()[0].findMethods().length);
String method = result[i].findCollections()[0].findMethods()[0];
if ("GET".equals(method)) {
Assert.assertEquals(ServletSecurity.TransportGuarantee.NONE.name(),
result[i].getUserConstraint());
} else if ("POST".equals(method)) {
Assert.assertEquals(ServletSecurity.TransportGuarantee.CONFIDENTIAL.name(),
result[i].getUserConstraint());
} else {
Assert.fail("Unexpected method :[" + method + "]");
}
}
// Example 13-6
// @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),
// httpMethodConstraints = @HttpMethodConstraint("GET"))
hmces.clear();
hmces.add(new HttpMethodConstraintElement("GET"));
element = new ServletSecurityElement(
new HttpConstraintElement(
ServletSecurity.TransportGuarantee.NONE,
ROLE1),
hmces);
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(2, result.length);
for (int i = 0; i < 2; i++) {
Assert.assertTrue(result[i].findCollections()[0].findPattern(URL_PATTERN));
if (result[i].findCollections()[0].findMethods().length == 1) {
Assert.assertEquals("GET",
result[i].findCollections()[0].findMethods()[0]);
Assert.assertFalse(result[i].getAuthConstraint());
} else if (result[i].findCollections()[0].findOmittedMethods().length == 1) {
Assert.assertEquals("GET",
result[i].findCollections()[0].findOmittedMethods()[0]);
Assert.assertTrue(result[i].getAuthConstraint());
Assert.assertEquals(1, result[i].findAuthRoles().length);
Assert.assertEquals(ROLE1, result[i].findAuthRoles()[0]);
} else {
Assert.fail("Unexpected number of methods defined");
}
Assert.assertEquals(ServletSecurity.TransportGuarantee.NONE.name(),
result[i].getUserConstraint());
}
// Example 13-7
// @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),
// httpMethodConstraints = @HttpMethodConstraint(value="TRACE",
// emptyRoleSemantic = EmptyRoleSemantic.DENY))
hmces.clear();
hmces.add(new HttpMethodConstraintElement("TRACE",
new HttpConstraintElement(EmptyRoleSemantic.DENY)));
element = new ServletSecurityElement(
new HttpConstraintElement(
ServletSecurity.TransportGuarantee.NONE,
ROLE1),
hmces);
result = SecurityConstraint.createConstraints(element, URL_PATTERN);
Assert.assertEquals(2, result.length);
for (int i = 0; i < 2; i++) {
Assert.assertTrue(result[i].findCollections()[0].findPattern(URL_PATTERN));
if (result[i].findCollections()[0].findMethods().length == 1) {
Assert.assertEquals("TRACE",
result[i].findCollections()[0].findMethods()[0]);
Assert.assertTrue(result[i].getAuthConstraint());
Assert.assertEquals(0, result[i].findAuthRoles().length);
} else if (result[i].findCollections()[0].findOmittedMethods().length == 1) {
Assert.assertEquals("TRACE",
result[i].findCollections()[0].findOmittedMethods()[0]);
Assert.assertTrue(result[i].getAuthConstraint());
Assert.assertEquals(1, result[i].findAuthRoles().length);
Assert.assertEquals(ROLE1, result[i].findAuthRoles()[0]);
} else {
Assert.fail("Unexpected number of methods defined");
}
Assert.assertEquals(ServletSecurity.TransportGuarantee.NONE.name(),
result[i].getUserConstraint());
}
// Example 13-8 is the same as 13-4
// Example 13-9 is the same as 13-7
}
@Test
public void testFindUncoveredHttpMethods01() {
// No new constraints if denyUncoveredHttpMethods is false
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_ONLY}, false, DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods02() {
// No new constraints if denyUncoveredHttpMethods is false
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_OMIT}, false, DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods03() {
// No new constraints if denyUncoveredHttpMethods is false
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {POST_ONLY}, false, DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods04() {
// No new constraints if denyUncoveredHttpMethods is false
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {POST_OMIT}, false, DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods05() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_ONLY}, true, DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list GET as an omitted method
Assert.assertEquals(0, sc.findMethods().length);
Assert.assertEquals(1, sc.findOmittedMethods().length);
Assert.assertEquals("GET", sc.findOmittedMethods()[0]);
}
@Test
public void testFindUncoveredHttpMethods06() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {POST_ONLY}, true, DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list POST as an omitted method
Assert.assertEquals(0, sc.findMethods().length);
Assert.assertEquals(1, sc.findOmittedMethods().length);
Assert.assertEquals("POST", sc.findOmittedMethods()[0]);
}
@Test
public void testFindUncoveredHttpMethods07() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_OMIT}, true, DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list GET as an method
Assert.assertEquals(0, sc.findOmittedMethods().length);
Assert.assertEquals(1, sc.findMethods().length);
Assert.assertEquals("GET", sc.findMethods()[0]);
}
@Test
public void testFindUncoveredHttpMethods08() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {POST_OMIT}, true, DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list POST as an method
Assert.assertEquals(0, sc.findOmittedMethods().length);
Assert.assertEquals(1, sc.findMethods().length);
Assert.assertEquals("POST", sc.findMethods()[0]);
}
@Test
public void testFindUncoveredHttpMethods09() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_ONLY, GET_OMIT}, true,
DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods10() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {POST_ONLY, POST_OMIT}, true,
DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods11() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_ONLY, POST_ONLY}, true,
DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list GET and POST as omitted methods
Assert.assertEquals(0, sc.findMethods().length);
Assert.assertEquals(2, sc.findOmittedMethods().length);
HashSet<String> omittedMethods = new HashSet<>();
for (String omittedMethod : sc.findOmittedMethods()) {
omittedMethods.add(omittedMethod);
}
Assert.assertTrue(omittedMethods.remove("GET"));
Assert.assertTrue(omittedMethods.remove("POST"));
}
@Test
public void testFindUncoveredHttpMethods12() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_OMIT, POST_OMIT}, true,
DUMMY_LOG);
Assert.assertEquals(0, result.length);
}
@Test
public void testFindUncoveredHttpMethods13() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_ONLY, POST_OMIT}, true,
DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list POST as a method
Assert.assertEquals(1, sc.findMethods().length);
Assert.assertEquals(0, sc.findOmittedMethods().length);
Assert.assertEquals("POST", sc.findMethods()[0]);
}
@Test
public void testFindUncoveredHttpMethods14() {
SecurityConstraint[] result =
SecurityConstraint.findUncoveredHttpMethods(
new SecurityConstraint[] {GET_OMIT, POST_ONLY}, true,
DUMMY_LOG);
Assert.assertEquals(1, result.length);
// Should be a deny constraint
Assert.assertTrue(result[0].getAuthConstraint());
// Should have a single collection
Assert.assertEquals(1, result[0].findCollections().length);
SecurityCollection sc = result[0].findCollections()[0];
// Should list GET as a method
Assert.assertEquals(1, sc.findMethods().length);
Assert.assertEquals(0, sc.findOmittedMethods().length);
Assert.assertEquals("GET", sc.findMethods()[0]);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.descriptor.web;
import org.junit.Assert;
import org.junit.Test;
/**
* Test case for {@link ServletDef}
*/
public class TestServletDef {
@Test(expected = IllegalArgumentException.class)
public void testSetServletNameNull() {
new ServletDef().setServletName(null);
}
@Test(expected = IllegalArgumentException.class)
public void testSetServletNameEmptyString() {
new ServletDef().setServletName("");
}
@Test
public void testSetServletName() {
ServletDef servletDef = new ServletDef();
servletDef.setServletName("test");
Assert.assertEquals("'test' is expected as servlet name",
"test", servletDef.getServletName());
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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.descriptor.web;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.digester.Digester;
public class TestWebRuleSet {
private Digester fragmentDigester = new Digester();
private WebRuleSet fragmentRuleSet = new WebRuleSet(true);
private Digester webDigester = new Digester();
private WebRuleSet webRuleSet = new WebRuleSet(false);
public TestWebRuleSet() {
fragmentDigester.addRuleSet(fragmentRuleSet);
webDigester.addRuleSet(webRuleSet);
}
@Test
public void testSingleNameInWebFragmentXml() throws Exception {
WebXml webXml = new WebXml();
parse(webXml, "web-fragment-1name.xml", true, true);
Assert.assertEquals("name1", webXml.getName());
}
@Test
public void testMultipleNameInWebFragmentXml() throws Exception {
parse(new WebXml(), "web-fragment-2name.xml", true, false);
}
@Test
public void testSingleOrderingInWebFragmentXml() throws Exception {
WebXml webXml = new WebXml();
parse(webXml, "web-fragment-1ordering.xml", true, true);
Assert.assertEquals(1, webXml.getBeforeOrdering().size());
Assert.assertTrue(webXml.getBeforeOrdering().contains("bar"));
}
@Test
public void testMultipleOrderingInWebFragmentXml() throws Exception {
parse(new WebXml(), "web-fragment-2ordering.xml", true, false);
}
@Test
public void testSingleOrderingInWebXml() throws Exception {
WebXml webXml = new WebXml();
parse(webXml, "web-1ordering.xml", false, true);
Assert.assertEquals(1, webXml.getAbsoluteOrdering().size());
Assert.assertTrue(webXml.getAbsoluteOrdering().contains("bar"));
}
@Test
public void testMultipleOrderingInWebXml() throws Exception {
parse(new WebXml(), "web-2ordering.xml", false, false);
}
@Test
public void testRecycle() throws Exception {
// Name
parse(new WebXml(), "web-fragment-2name.xml", true, false);
parse(new WebXml(), "web-fragment-1name.xml", true, true);
parse(new WebXml(), "web-fragment-2name.xml", true, false);
parse(new WebXml(), "web-fragment-1name.xml", true, true);
// Relative ordering
parse(new WebXml(), "web-fragment-2ordering.xml", true, false);
parse(new WebXml(), "web-fragment-1ordering.xml", true, true);
parse(new WebXml(), "web-fragment-2ordering.xml", true, false);
parse(new WebXml(), "web-fragment-1ordering.xml", true, true);
// Absolute ordering
parse(new WebXml(), "web-2ordering.xml", false, false);
parse(new WebXml(), "web-1ordering.xml", false, true);
parse(new WebXml(), "web-2ordering.xml", false, false);
parse(new WebXml(), "web-1ordering.xml", false, true);
}
@Test
public void testLifecycleMethodsDefinitions() throws Exception {
// post-construct and pre-destroy
parse(new WebXml(), "web-1lifecyclecallback.xml", false, true);
// conflicting post-construct definitions
parse(new WebXml(), "web-2lifecyclecallback.xml", false, false);
}
private synchronized void parse(WebXml webXml, String target,
boolean fragment, boolean expected) {
Digester d;
if (fragment) {
d = fragmentDigester;
fragmentRuleSet.recycle();
} else {
d = webDigester;
webRuleSet.recycle();
}
d.push(webXml);
File f = new File("test/org/apache/catalina/startup/" + target);
boolean result = true;
try (InputStream is = new FileInputStream(f);) {
d.parse(is);
} catch (Exception e) {
if (expected) {
// Didn't expect an exception
e.printStackTrace();
}
result = false;
}
if (expected) {
Assert.assertTrue(result);
} else {
Assert.assertFalse(result);
}
}
}

View File

@@ -0,0 +1,473 @@
/*
* 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.descriptor.web;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.descriptor.DigesterFactory;
import org.apache.tomcat.util.descriptor.XmlErrorHandler;
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
import org.apache.tomcat.util.digester.Digester;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Test case for {@link WebXml}.
*/
public class TestWebXml {
@Test
public void testParseVersion() {
WebXml webxml = new WebXml();
// Defaults
Assert.assertEquals(3, webxml.getMajorVersion());
Assert.assertEquals(1, webxml.getMinorVersion());
// Both get changed
webxml.setVersion("2.5");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
// unknown input should be ignored
webxml.setVersion("0.0");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
// null input should be ignored
webxml.setVersion(null);
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
}
@Test
public void testParsePublicIdVersion22() {
WebXml webxml = new WebXml();
webxml.setPublicId(XmlIdentifiers.WEB_22_PUBLIC);
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(2, webxml.getMinorVersion());
Assert.assertEquals("2.2", webxml.getVersion());
}
@Test
public void testParsePublicIdVersion23() {
WebXml webxml = new WebXml();
webxml.setPublicId(XmlIdentifiers.WEB_23_PUBLIC);
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(3, webxml.getMinorVersion());
Assert.assertEquals("2.3", webxml.getVersion());
}
@Test
public void testParseVersion24() {
WebXml webxml = new WebXml();
webxml.setVersion("2.4");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(4, webxml.getMinorVersion());
Assert.assertEquals("2.4", webxml.getVersion());
}
@Test
public void testParseVersion25() {
WebXml webxml = new WebXml();
webxml.setVersion("2.5");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
Assert.assertEquals("2.5", webxml.getVersion());
}
@Test
public void testParseVersion30() {
WebXml webxml = new WebXml();
webxml.setVersion("3.0");
Assert.assertEquals(3, webxml.getMajorVersion());
Assert.assertEquals(0, webxml.getMinorVersion());
Assert.assertEquals("3.0", webxml.getVersion());
}
@Test
public void testParseVersion31() {
WebXml webxml = new WebXml();
webxml.setVersion("3.1");
Assert.assertEquals(3, webxml.getMajorVersion());
Assert.assertEquals(1, webxml.getMinorVersion());
Assert.assertEquals("3.1", webxml.getVersion());
}
@Test
public void testValidateVersion22() throws IOException, SAXException {
doTestValidateVersion("2.2");
}
@Test
public void testValidateVersion23() throws IOException, SAXException {
doTestValidateVersion("2.3");
}
@Test
public void testValidateVersion24() throws IOException, SAXException {
doTestValidateVersion("2.4");
}
@Test
public void testValidateVersion25() throws IOException, SAXException {
doTestValidateVersion("2.5");
}
@Test
public void testValidateVersion30() throws IOException, SAXException {
doTestValidateVersion("3.0");
}
@Test
public void testValidateVersion31() throws IOException, SAXException {
doTestValidateVersion("3.1");
}
private void doTestValidateVersion(String version) throws IOException, SAXException {
WebXml webxml = new WebXml();
// Special cases
if ("2.2".equals(version)) {
webxml.setPublicId(XmlIdentifiers.WEB_22_PUBLIC);
} else if ("2.3".equals(version)) {
webxml.setPublicId(XmlIdentifiers.WEB_23_PUBLIC);
} else {
webxml.setVersion(version);
}
// Merged web.xml that is published as MERGED_WEB_XML context attribute
// in the simplest case consists of webapp's web.xml file
// plus the default conf/web.xml one.
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment());
webxml.merge(defaults);
Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(), true);
XmlErrorHandler handler = new XmlErrorHandler();
digester.setErrorHandler(handler);
InputSource is = new InputSource(new StringReader(webxml.toXml()));
WebXml webxmlResult = new WebXml();
digester.push(webxmlResult);
digester.parse(is);
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
Assert.assertEquals(version, webxml.getVersion());
Assert.assertEquals(version, webxmlResult.getVersion());
}
// A simplified copy of ContextConfig.getDefaultWebXmlFragment().
// Assuming that global web.xml exists, host-specific web.xml does not exist.
private WebXml getDefaultWebXmlFragment() throws IOException, SAXException {
InputSource globalWebXml = new InputSource(new File("conf/web.xml")
.getAbsoluteFile().toURI().toString());
WebXml webXmlDefaultFragment = new WebXml();
webXmlDefaultFragment.setOverridable(true);
webXmlDefaultFragment.setDistributable(true);
webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(), true);
XmlErrorHandler handler = new XmlErrorHandler();
digester.setErrorHandler(handler);
digester.push(webXmlDefaultFragment);
digester.parse(globalWebXml);
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
webXmlDefaultFragment.setReplaceWelcomeFiles(true);
// Assert that web.xml was parsed and is not empty. Default servlet is known to be there.
Assert.assertNotNull(webXmlDefaultFragment.getServlets().get("default"));
// Manually add some version specific features to ensure that these do
// not cause problems for the merged web.xml
// Filters were added in 2.3 so should be excluded in 2.2
FilterDef filterDef = new FilterDef();
filterDef.setFilterClass("org.apache.tomcat.DummyFilter");
filterDef.setFilterName("Dummy");
webXmlDefaultFragment.addFilter(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("Dummy");
filterMap.addURLPatternDecoded("/*");
webXmlDefaultFragment.addFilterMapping(filterMap);
// Listeners were added in 2.3 so should be excluded in 2.2
webXmlDefaultFragment.addListener("org.apache.tomcat.DummyListener");
// resource-env-ref was added in 2.3 so should be excluded in 2.2
ContextResourceEnvRef resourceEnvRef = new ContextResourceEnvRef();
resourceEnvRef.setName("dummy");
resourceEnvRef.setType("dummy");
webXmlDefaultFragment.addResourceEnvRef(resourceEnvRef);
// ejb-local-ref was added in 2.3 so should be excluded in 2.2
ContextLocalEjb ejbLocalRef = new ContextLocalEjb();
ejbLocalRef.setName("dummy");
ejbLocalRef.setType("Session");
ejbLocalRef.setLocal("dummy");
ejbLocalRef.setHome("dummy");
webXmlDefaultFragment.addEjbLocalRef(ejbLocalRef);
// Servlet/run-as was added in 2.3 so should be excluded in 2.2
ServletDef servletDef = new ServletDef();
servletDef.setServletName("Dummy");
servletDef.setServletClass("org.apache.tomcat.DummyServlet");
servletDef.setRunAs("dummy");
webXmlDefaultFragment.addServlet(servletDef);
webXmlDefaultFragment.addServletMapping("/dummy", "Dummy");
// resource-ref/res-sharing-scope was added in 2.3 so should be excluded
// in 2.2
ContextResource contextResource = new ContextResource();
contextResource.setName("dummy");
contextResource.setType("dummy");
contextResource.setAuth("Container");
contextResource.setScope("Shareable");
webXmlDefaultFragment.addResourceRef(contextResource);
// security-constraint/display-name was added in 2.3 so should be
// excluded in 2.2
SecurityConstraint sc = new SecurityConstraint();
sc.setDisplayName("dummy");
SecurityCollection collection = new SecurityCollection();
collection.setName("dummy");
collection.addPatternDecoded("/*");
collection.addMethod("DELETE");
sc.addCollection(collection);
webXmlDefaultFragment.addSecurityConstraint(sc);
// service-ref was added in 2.4 so should be excluded in 2.3 and earlier
ContextService serviceRef = new ContextService();
serviceRef.setName("dummy");
serviceRef.setInterface("dummy");
webXmlDefaultFragment.addServiceRef(serviceRef);
// message-destination-ref was added in 2.4 so should be excluded in 2.3
// and earlier
MessageDestinationRef mdRef = new MessageDestinationRef();
mdRef.setName("dummy");
mdRef.setType("dummy");
mdRef.setUsage("Consumes");
webXmlDefaultFragment.addMessageDestinationRef(mdRef);
// message-destination was added in 2.4 so should be excluded in 2.3
// and earlier
MessageDestination md = new MessageDestination();
md.setName("dummy");
webXmlDefaultFragment.addMessageDestination(md);
// local-encoding-mapping-list was added in 2.4 so should be excluded in
// 2.3 and earlier
webXmlDefaultFragment.addLocaleEncodingMapping("en", "UTF-8");
// jsp-config was added in Servlet 2.4
webXmlDefaultFragment.addTaglib("dummy", "dummy");
// filter-mapping/dispatcher added in Servlet 2.4
filterMap.setDispatcher("REQUEST");
// listener-[description|display-name|icon] added in Servlet 2.4
// None of these are supported in WebXml
// filter-mapping/dispatcher/ASYNC added in Servlet 3.0
filterMap.setDispatcher("ASYNC");
// error-page with just location allowed in Servlet 3.0+
ErrorPage errorPage = new ErrorPage();
errorPage.setLocation("/dummy");
webXmlDefaultFragment.addErrorPage(errorPage);
// async-supported added to Servlet and Filter in 3.0
filterDef.setAsyncSupported("false");
servletDef.setAsyncSupported("false");
// session-cookie-config added in 3.0
SessionConfig sessionConfig = new SessionConfig();
sessionConfig.setCookieDomain("dummy");
webXmlDefaultFragment.setSessionConfig(sessionConfig);
// http-method-omission added in Servlet 3.0
// Let this trigger a validation error as dropping it silently could
// be a security concern
// multi-part-config added in Servlet 3.0
MultipartDef multiPart = new MultipartDef();
servletDef.setMultipartDef(multiPart);
// deny-uncovered-http-methods added in Servlet 3.1
webXmlDefaultFragment.setDenyUncoveredHttpMethods(true);
return webXmlDefaultFragment;
}
@Test
public void testLifecycleMethodsWebXml() {
WebXml webxml = new WebXml();
webxml.addPostConstructMethods("a", "a");
webxml.addPreDestroyMethods("b", "b");
WebXml fragment = new WebXml();
fragment.addPostConstructMethods("c", "c");
fragment.addPreDestroyMethods("d", "d");
Set<WebXml> fragments = new HashSet<>();
fragments.add(fragment);
webxml.merge(fragments);
Map<String, String> postConstructMethods = webxml.getPostConstructMethods();
Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
Assert.assertEquals(1, postConstructMethods.size());
Assert.assertEquals(1, preDestroyMethods.size());
Assert.assertEquals("a", postConstructMethods.get("a"));
Assert.assertEquals("b", preDestroyMethods.get("b"));
}
@Test
public void testLifecycleMethodsWebFragments() {
WebXml webxml = new WebXml();
WebXml fragment1 = new WebXml();
fragment1.addPostConstructMethods("a", "a");
fragment1.addPreDestroyMethods("b", "b");
WebXml fragment2 = new WebXml();
fragment2.addPostConstructMethods("c", "c");
fragment2.addPreDestroyMethods("d", "d");
Set<WebXml> fragments = new HashSet<>();
fragments.add(fragment1);
fragments.add(fragment2);
webxml.merge(fragments);
Map<String, String> postConstructMethods = webxml.getPostConstructMethods();
Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
Assert.assertEquals(2, postConstructMethods.size());
Assert.assertEquals(2, preDestroyMethods.size());
Assert.assertEquals("a", postConstructMethods.get("a"));
Assert.assertEquals("c", postConstructMethods.get("c"));
Assert.assertEquals("b", preDestroyMethods.get("b"));
Assert.assertEquals("d", preDestroyMethods.get("d"));
}
@Test
public void testLifecycleMethodsWebFragmentsWithConflicts() {
WebXml webxml = new WebXml();
WebXml fragment1 = new WebXml();
fragment1.addPostConstructMethods("a", "a");
fragment1.addPreDestroyMethods("b", "a");
WebXml fragment2 = new WebXml();
fragment2.addPostConstructMethods("a", "b");
Set<WebXml> fragments = new HashSet<>();
fragments.add(fragment1);
fragments.add(fragment2);
Assert.assertFalse(webxml.merge(fragments));
Assert.assertEquals(0, webxml.getPostConstructMethods().size());
WebXml fragment3 = new WebXml();
fragment3.addPreDestroyMethods("b", "b");
fragments.remove(fragment2);
fragments.add(fragment3);
Assert.assertFalse(webxml.merge(fragments));
Assert.assertEquals(0, webxml.getPreDestroyMethods().size());
}
@Test(expected=IllegalArgumentException.class)
public void testBug54387a() {
// Multiple servlets may not be mapped to the same url-pattern
WebXml webxml = new WebXml();
webxml.addServletMapping("/foo", "a");
webxml.addServletMapping("/foo", "b");
}
@Test(expected=IllegalArgumentException.class)
public void testBug54387b() {
// Multiple servlets may not be mapped to the same url-pattern
WebXml webxml = new WebXml();
WebXml f1 = new WebXml();
WebXml f2 = new WebXml();
HashSet<WebXml> fragments = new HashSet<>();
fragments.add(f1);
fragments.add(f2);
f1.addServletMapping("/foo", "a");
f2.addServletMapping("/foo", "b");
webxml.merge(fragments);
}
@Test
public void testBug54387c() {
// Multiple servlets may not be mapped to the same url-pattern but main
// web.xml takes priority
WebXml webxml = new WebXml();
WebXml f1 = new WebXml();
WebXml f2 = new WebXml();
HashSet<WebXml> fragments = new HashSet<>();
fragments.add(f1);
fragments.add(f2);
f1.addServletMapping("/foo", "a");
f2.addServletMapping("/foo", "b");
webxml.addServletMapping("/foo", "main");
webxml.merge(fragments);
}
}

View File

@@ -0,0 +1,696 @@
/*
* 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.descriptor.web;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Test case for {@link WebXml} fragment ordering.
*/
public class TestWebXmlOrdering {
private WebXml app;
private WebXml a;
private WebXml b;
private WebXml c;
private WebXml d;
private WebXml e;
private WebXml f;
private Map<String,WebXml> fragments;
private int posA;
private int posB;
private int posC;
private int posD;
private int posE;
private int posF;
@Before
public void setUp() {
app = new WebXml();
a = new WebXml();
a.setName("a");
b = new WebXml();
b.setName("b");
c = new WebXml();
c.setName("c");
d = new WebXml();
d.setName("d");
e = new WebXml();
e.setName("e");
f = new WebXml();
f.setName("f");
// Control the input order
fragments = new LinkedHashMap<>();
fragments.put("a",a);
fragments.put("b",b);
fragments.put("c",c);
fragments.put("d",d);
fragments.put("e",e);
fragments.put("f",f);
}
@Test
public void testOrderWebFragmentsAbsolute() {
app.addAbsoluteOrdering("c");
app.addAbsoluteOrdering("a");
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering("e");
app.addAbsoluteOrdering("d");
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
Assert.assertEquals(c,iter.next());
Assert.assertEquals(a,iter.next());
Assert.assertEquals(b,iter.next());
Assert.assertEquals(e,iter.next());
Assert.assertEquals(d,iter.next());
Assert.assertFalse(iter.hasNext());
}
@Test
public void testOrderWebFragmentsAbsolutePartial() {
app.addAbsoluteOrdering("c");
app.addAbsoluteOrdering("a");
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
Assert.assertEquals(c,iter.next());
Assert.assertEquals(a,iter.next());
Assert.assertFalse(iter.hasNext());
}
@Test
public void testOrderWebFragmentsAbsoluteOthersStart() {
app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering("d");
Set<WebXml> others = new HashSet<>();
others.add(a);
others.add(c);
others.add(e);
others.add(f);
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
while (others.size() > 0) {
WebXml o = iter.next();
Assert.assertTrue(others.contains(o));
others.remove(o);
}
Assert.assertEquals(b,iter.next());
Assert.assertEquals(d,iter.next());
Assert.assertFalse(iter.hasNext());
}
@Test
public void testOrderWebFragmentsAbsoluteOthersMiddle() {
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
app.addAbsoluteOrdering("d");
Set<WebXml> others = new HashSet<>();
others.add(a);
others.add(c);
others.add(e);
others.add(f);
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
Assert.assertEquals(b,iter.next());
while (others.size() > 0) {
WebXml o = iter.next();
Assert.assertTrue(others.contains(o));
others.remove(o);
}
Assert.assertEquals(d,iter.next());
Assert.assertFalse(iter.hasNext());
}
@Test
public void testWebFragmentsAbsoluteWrongFragmentName() {
app.addAbsoluteOrdering("a");
app.addAbsoluteOrdering("z");
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Assert.assertEquals(1,ordered.size());
Assert.assertEquals(fragments.get("a"),ordered.toArray()[0]);
}
@Test
public void testOrderWebFragmentsAbsoluteOthersEnd() {
app.addAbsoluteOrdering("b");
app.addAbsoluteOrdering("d");
app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
Set<WebXml> others = new HashSet<>();
others.add(a);
others.add(c);
others.add(e);
others.add(f);
Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments, null);
Iterator<WebXml> iter = ordered.iterator();
Assert.assertEquals(b,iter.next());
Assert.assertEquals(d,iter.next());
while (others.size() > 0) {
WebXml o = iter.next();
Assert.assertTrue(others.contains(o));
others.remove(o);
}
Assert.assertFalse(iter.hasNext());
}
private void doRelativeOrderingTest(RelativeOrderingTestRunner runner) {
// Confirm we have all 720 possible input orders
// Set<String> orders = new HashSet<>();
// Test all possible input orders since some bugs were discovered that
// depended on input order
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 4; k++) {
for (int l = 0; l < 3; l++) {
for (int m = 0; m < 2; m++) {
setUp();
runner.init();
ArrayList<WebXml> source = new ArrayList<>();
source.addAll(fragments.values());
Map<String,WebXml> input =
new LinkedHashMap<>();
WebXml one = source.remove(i);
input.put(one.getName(), one);
WebXml two = source.remove(j);
input.put(two.getName(), two);
WebXml three = source.remove(k);
input.put(three.getName(), three);
WebXml four = source.remove(l);
input.put(four.getName(), four);
WebXml five = source.remove(m);
input.put(five.getName(), five);
WebXml six = source.remove(0);
input.put(six.getName(), six);
/*
String order = one.getName() + two.getName() +
three.getName() + four.getName() +
five.getName() + six.getName();
orders.add(order);
*/
Set<WebXml> ordered =
WebXml.orderWebFragments(app, input, null);
populatePositions(ordered);
runner.validate(getOrder(ordered));
}
}
}
}
}
// System.out.println(orders.size());
}
private String getOrder(Set<WebXml> ordered) {
StringBuilder sb = new StringBuilder(ordered.size());
for (WebXml webXml : ordered) {
sb.append(webXml.getName());
}
return sb.toString();
}
private void populatePositions(Set<WebXml> ordered) {
List<WebXml> indexed = new ArrayList<>();
indexed.addAll(ordered);
posA = indexed.indexOf(a);
posB = indexed.indexOf(b);
posC = indexed.indexOf(c);
posD = indexed.indexOf(d);
posE = indexed.indexOf(e);
posF = indexed.indexOf(f);
}
@Test
public void testOrderWebFragmentsRelative1() {
// First example from servlet spec
doRelativeOrderingTest(new RelativeTestRunner1());
}
@Test
public void testOrderWebFragmentsRelative2() {
// Second example - use fragment a for no-id fragment
doRelativeOrderingTest(new RelativeTestRunner2());
}
@Test
public void testOrderWebFragmentsRelative3() {
// Third example from spec with e & f added
doRelativeOrderingTest(new RelativeTestRunner3());
}
@Test
public void testOrderWebFragmentsRelative4Bug54068() {
// Simple sequence that failed for some inputs
doRelativeOrderingTest(new RelativeTestRunner4());
}
@Test
public void testOrderWebFragmentsRelative5Bug54068() {
// Simple sequence that failed for some inputs
doRelativeOrderingTest(new RelativeTestRunner5());
}
@Test
public void testOrderWebFragmentsRelative6Bug54068() {
// Simple sequence that failed for some inputs
doRelativeOrderingTest(new RelativeTestRunner6());
}
@Test
public void testOrderWebFragmentsRelative7() {
// Reference loop (but not circular dependencies)
doRelativeOrderingTest(new RelativeTestRunner7());
}
@Test
public void testOrderWebFragmentsRelative8() {
// More complex, trying to break the algorithm
doRelativeOrderingTest(new RelativeTestRunner8());
}
@Test
public void testOrderWebFragmentsRelative9() {
// Variation on bug 54068
doRelativeOrderingTest(new RelativeTestRunner9());
}
@Test
public void testOrderWebFragmentsRelative10() {
// Variation on bug 54068
doRelativeOrderingTest(new RelativeTestRunner10());
}
@Test
public void testOrderWebFragmentsRelative11() {
// Test references to non-existent fragments
doRelativeOrderingTest(new RelativeTestRunner11());
}
@Test(expected=IllegalArgumentException.class)
public void testOrderWebFragmentsrelativeCircular1() {
a.addBeforeOrdering("b");
b.addBeforeOrdering("a");
WebXml.orderWebFragments(app, fragments, null);
}
@Test(expected=IllegalArgumentException.class)
public void testOrderWebFragmentsrelativeCircular2() {
a.addBeforeOrderingOthers();
b.addAfterOrderingOthers();
c.addBeforeOrdering("a");
c.addAfterOrdering("b");
WebXml.orderWebFragments(app, fragments, null);
}
private interface RelativeOrderingTestRunner {
void init();
void validate(String order);
}
private class RelativeTestRunner1 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrderingOthers();
a.addAfterOrdering("c");
b.addBeforeOrderingOthers();
c.addAfterOrderingOthers();
f.addBeforeOrderingOthers();
f.addBeforeOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
//a.addAfterOrderingOthers();
Assert.assertTrue(order, posA > posB);
Assert.assertTrue(order, posA > posC);
Assert.assertTrue(order, posA > posD);
Assert.assertTrue(order, posA > posE);
Assert.assertTrue(order, posA > posF);
// a.addAfterOrdering("c");
Assert.assertTrue(order, posA > posC);
// b.addBeforeOrderingOthers();
Assert.assertTrue(order, posB < posC);
// c.addAfterOrderingOthers();
Assert.assertTrue(order, posC > posB);
Assert.assertTrue(order, posC > posD);
Assert.assertTrue(order, posC > posE);
Assert.assertTrue(order, posC > posF);
// f.addBeforeOrderingOthers();
Assert.assertTrue(order, posF < posA);
Assert.assertTrue(order, posF < posB);
Assert.assertTrue(order, posF < posC);
Assert.assertTrue(order, posF < posD);
Assert.assertTrue(order, posF < posE);
// f.addBeforeOrdering("b");
Assert.assertTrue(order, posF < posB);
}
}
private class RelativeTestRunner2 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrderingOthers();
a.addBeforeOrdering("c");
b.addBeforeOrderingOthers();
d.addAfterOrderingOthers();
e.addBeforeOrderingOthers();
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrderingOthers();
Assert.assertTrue(order, posA > posB);
Assert.assertTrue(order, posA > posE);
Assert.assertTrue(order, posA > posF);
// a.addBeforeOrdering("c");
Assert.assertTrue(order, posC > posA);
Assert.assertTrue(order, posC > posB);
Assert.assertTrue(order, posC > posE);
Assert.assertTrue(order, posC > posF);
// b.addBeforeOrderingOthers();
Assert.assertTrue(order, posB < posA);
Assert.assertTrue(order, posB < posC);
Assert.assertTrue(order, posB < posD);
Assert.assertTrue(order, posB < posF);
// d.addAfterOrderingOthers();
Assert.assertTrue(order, posD > posB);
Assert.assertTrue(order, posD > posE);
Assert.assertTrue(order, posD > posF);
// e.addBeforeOrderingOthers();
Assert.assertTrue(order, posE < posA);
Assert.assertTrue(order, posE < posC);
Assert.assertTrue(order, posE < posD);
Assert.assertTrue(order, posE < posF);
}
}
private class RelativeTestRunner3 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrdering("b");
c.addBeforeOrderingOthers();
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrdering("b");
Assert.assertTrue(order, posA > posB);
// c.addBeforeOrderingOthers();
Assert.assertTrue(order, posC < posA);
Assert.assertTrue(order, posC < posB);
Assert.assertTrue(order, posC < posD);
Assert.assertTrue(order, posC < posE);
Assert.assertTrue(order, posC < posF);
}
}
private class RelativeTestRunner4 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addAfterOrdering("a");
c.addAfterOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addAfterOrdering("a");
Assert.assertTrue(order, posB > posA);
// c.addAfterOrdering("b");
Assert.assertTrue(order, posC > posB);
}
}
private class RelativeTestRunner5 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addBeforeOrdering("a");
c.addBeforeOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addBeforeOrdering("a");
Assert.assertTrue(order, posB < posA);
// c.addBeforeOrdering("b");
Assert.assertTrue(order, posC < posB);
}
}
private class RelativeTestRunner6 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addBeforeOrdering("a");
b.addAfterOrdering("c");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addBeforeOrdering("a");
Assert.assertTrue(order, posB < posA);
//b.addAfterOrdering("c");
Assert.assertTrue(order, posB > posC);
}
}
private class RelativeTestRunner7 implements RelativeOrderingTestRunner {
@Override
public void init() {
b.addBeforeOrdering("a");
c.addBeforeOrdering("b");
a.addAfterOrdering("c");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// b.addBeforeOrdering("a");
Assert.assertTrue(order, posB < posA);
// c.addBeforeOrdering("b");
Assert.assertTrue(order, posC < posB);
// a.addAfterOrdering("c");
Assert.assertTrue(order, posA > posC);
}
}
private class RelativeTestRunner8 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addBeforeOrderingOthers();
a.addBeforeOrdering("b");
b.addBeforeOrderingOthers();
c.addAfterOrdering("b");
d.addAfterOrdering("c");
e.addAfterOrderingOthers();
f.addAfterOrderingOthers();
f.addAfterOrdering("e");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addBeforeOrderingOthers();
Assert.assertTrue(order, posA < posB);
Assert.assertTrue(order, posA < posC);
Assert.assertTrue(order, posA < posD);
Assert.assertTrue(order, posA < posE);
Assert.assertTrue(order, posA < posF);
// a.addBeforeOrdering("b");
Assert.assertTrue(order, posA < posB);
// b.addBeforeOrderingOthers();
Assert.assertTrue(order, posB < posC);
Assert.assertTrue(order, posB < posD);
Assert.assertTrue(order, posB < posE);
Assert.assertTrue(order, posB < posF);
// c.addAfterOrdering("b");
Assert.assertTrue(order, posC > posB);
// d.addAfterOrdering("c");
Assert.assertTrue(order, posD > posC);
// e.addAfterOrderingOthers();
Assert.assertTrue(order, posE > posA);
Assert.assertTrue(order, posE > posB);
Assert.assertTrue(order, posE > posC);
Assert.assertTrue(order, posE > posD);
// f.addAfterOrderingOthers();
Assert.assertTrue(order, posF > posA);
Assert.assertTrue(order, posF > posB);
Assert.assertTrue(order, posF > posC);
Assert.assertTrue(order, posF > posD);
Assert.assertTrue(order, posF > posE);
// f.addAfterOrdering("e");
Assert.assertTrue(order, posF > posE);
}
}
private class RelativeTestRunner9 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addBeforeOrderingOthers();
b.addBeforeOrdering("a");
c.addBeforeOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addBeforeOrderingOthers();
Assert.assertTrue(order, posA < posD);
Assert.assertTrue(order, posA < posE);
Assert.assertTrue(order, posA < posF);
// b.addBeforeOrdering("a");
Assert.assertTrue(order, posB < posA);
// c.addBeforeOrdering("b");
Assert.assertTrue(order, posC < posB);
}
}
private class RelativeTestRunner10 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrderingOthers();
b.addAfterOrdering("a");
c.addAfterOrdering("b");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrderingOthers();
Assert.assertTrue(order, posA > posD);
Assert.assertTrue(order, posA > posE);
Assert.assertTrue(order, posA > posF);
// b.addAfterOrdering("a");
Assert.assertTrue(order, posB > posA);
// c.addAfterOrdering("b");
Assert.assertTrue(order, posC > posB);
}
}
private class RelativeTestRunner11 implements RelativeOrderingTestRunner {
@Override
public void init() {
a.addAfterOrdering("b");
b.addAfterOrdering("z");
b.addBeforeOrdering("y");
}
@Override
public void validate(String order) {
// There is some duplication in the tests below - it is easier to
// check the tests are complete this way.
// a.addAfterOrdering("b");
Assert.assertTrue(order, posA > posB);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.file;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
public class TestConfigFileLoader {
@BeforeClass
public static void setup() {
TomcatURLStreamHandlerFactory.getInstance();
System.setProperty("catalina.base", "");
}
@Test
public void test01() throws IOException {
doTest("classpath:org/apache/catalina/mbeans-descriptors.xml");
}
@Test(expected=FileNotFoundException.class)
public void test02() throws IOException {
doTest("classpath:org/apache/catalina/foo");
}
@Test
public void test03() throws IOException {
doTest("test/webresources/dir1");
}
@Test(expected=FileNotFoundException.class)
public void test04() throws IOException {
doTest("test/webresources/unknown");
}
private void doTest(String path) throws IOException {
try (InputStream is = ConfigFileLoader.getInputStream(path)) {
Assert.assertNotNull(is);
}
}
}

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.http;
import java.io.IOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
/**
* Base Test case for {@link LegacyCookieProcessor}. <b>Note</b> because of the
* use of <code>static final</code> constants in {@link LegacyCookieProcessor},
* each of these tests must be executed in a new JVM instance. The tests have
* been place in separate classes to facilitate this when running the unit tests
* via Ant.
*/
public abstract class CookiesBaseTest extends TomcatBaseTest {
/**
* Servlet for cookie naming test.
*/
public static class CookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final String cookieName;
private final String cookieValue;
public CookieServlet(String cookieName, String cookieValue) {
this.cookieName = cookieName;
this.cookieValue = cookieValue;
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
try {
Cookie cookie = new Cookie(cookieName, cookieValue);
res.addCookie(cookie);
res.getWriter().write("Cookie name ok");
} catch (IllegalArgumentException iae) {
res.getWriter().write("Cookie name fail");
}
}
}
public static void addServlets(Tomcat tomcat) {
// No file system docBase required
Context ctx = tomcat.addContext("", null);
ctx.setCookieProcessor(new LegacyCookieProcessor());
Tomcat.addServlet(ctx, "invalid", new CookieServlet("na;me", "value"));
ctx.addServletMappingDecoded("/invalid", "invalid");
Tomcat.addServlet(ctx, "null", new CookieServlet(null, "value"));
ctx.addServletMappingDecoded("/null", "null");
Tomcat.addServlet(ctx, "blank", new CookieServlet("", "value"));
ctx.addServletMappingDecoded("/blank", "blank");
Tomcat.addServlet(ctx, "invalidFwd",
new CookieServlet("na/me", "value"));
ctx.addServletMappingDecoded("/invalidFwd", "invalidFwd");
Tomcat.addServlet(ctx, "invalidStrict",
new CookieServlet("$name", "value"));
ctx.addServletMappingDecoded("/invalidStrict", "invalidStrict");
Tomcat.addServlet(ctx, "valid", new CookieServlet("name", "value"));
ctx.addServletMappingDecoded("/valid", "valid");
Tomcat.addServlet(ctx, "switch", new CookieServlet("name", "val?ue"));
ctx.addServletMappingDecoded("/switch", "switch");
}
public abstract void testCookiesInstance() throws Exception;
}

View File

@@ -0,0 +1,84 @@
/*
* 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.http;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Test case for {@link LegacyCookieProcessor}. <b>Note</b> because of the use of <code>final
* static</code> constants in {@link LegacyCookieProcessor}, each of these tests must be
* executed in a new JVM instance. The tests have been place in separate classes
* to facilitate this when running the unit tests via Ant.
*/
public class TestBug49158 extends CookiesBaseTest {
public static final String path = "49158";
@Override
@Test
public void testCookiesInstance() throws Exception {
Tomcat tomcat = getTomcatInstance();
addServlets(tomcat);
tomcat.start();
Map<String,List<String>> headers = new HashMap<>();
ByteChunk res = new ByteChunk();
getUrl("http://localhost:" + getPort() + "/"+path, res, headers);
List<String> cookieHeaders = headers.get("Set-Cookie");
Assert.assertEquals("There should only be one Set-Cookie header in this test",
1, cookieHeaders.size());
}
public static void addServlets(Tomcat tomcat) {
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, path, new TestBug49158Servlet());
ctx.addServletMappingDecoded("/"+path, path);
}
public static class TestBug49158Servlet extends HttpServlet {
private static final long serialVersionUID = 2725990508758127399L;
@Override
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = req.getSession();
session.invalidate();
session = req.getSession();
session.invalidate();
req.getSession();
}
}
}

View File

@@ -0,0 +1,344 @@
/*
* 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.http;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
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.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestCookieParsing extends TomcatBaseTest {
private static final String[] COOKIES_WITH_EQUALS = new String[] {
"name=equals=middle", "name==equalsstart", "name=equalsend=" };
private static final String COOKIES_WITH_EQUALS_TRUNC = "name=equalsname=name=equalsend";
private static final String[] COOKIES_WITH_NAME_ONLY = new String[] {
"bob", "bob=" };
private static final String COOKIES_WITH_NAME_ONLY_CONCAT = "bob=bob=";
private static final String[] COOKIES_WITH_SEPS = new String[] {
"name=val/ue" };
private static final String COOKIES_WITH_SEPS_TRUNC = "name=val";
private static final String[] COOKIES_WITH_QUOTES = new String[] {
"name=\"val\\\"ue\"", "name=\"value\"" };
private static final String[] COOKIES_V0 = new String[] {
"$Version=0;name=\"val ue\"", "$Version=0;name=\"val\tue\""};
private static final String COOKIES_V0_CONCAT = "name=\"val ue\"name=\"val\tue\"";
private static final String[] COOKIES_V1 = new String[] {
"$Version=1;name=\"val ue\"", "$Version=1;name=\"val\tue\""};
private static final String COOKIES_V1_CONCAT = "name=\"val ue\"name=\"val\tue\"";
@Test
public void testLegacyWithEquals() throws Exception {
doTestLegacyEquals(true);
}
@Test
public void testLegacyWithoutEquals() throws Exception {
doTestLegacyEquals(false);
}
private void doTestLegacyEquals(boolean allowEquals) throws Exception {
LegacyCookieProcessor legacyCookieProcessor = new LegacyCookieProcessor();
legacyCookieProcessor.setAllowEqualsInValue(allowEquals);
// Need to allow name only cookies to handle equals at the start of
// the value
legacyCookieProcessor.setAllowNameOnly(true);
String expected;
if (allowEquals) {
expected = concat(COOKIES_WITH_EQUALS);
} else {
expected = COOKIES_WITH_EQUALS_TRUNC;
}
TestCookieParsingClient client = new TestCookieParsingClient(
legacyCookieProcessor, COOKIES_WITH_EQUALS, expected);
client.doRequest();
}
@Test
public void testRfc6265Equals() throws Exception {
// Always allows equals
TestCookieParsingClient client = new TestCookieParsingClient(
new Rfc6265CookieProcessor(), COOKIES_WITH_EQUALS, concat(COOKIES_WITH_EQUALS));
client.doRequest();
}
@Test
public void testLegacyWithNameOnly() throws Exception {
doTestLegacyNameOnly(true);
}
@Test
public void testLegacyWithoutNameOnly() throws Exception {
doTestLegacyNameOnly(false);
}
private void doTestLegacyNameOnly(boolean nameOnly) throws Exception {
LegacyCookieProcessor legacyCookieProcessor = new LegacyCookieProcessor();
legacyCookieProcessor.setAllowNameOnly(nameOnly);
String expected;
if (nameOnly) {
expected = COOKIES_WITH_NAME_ONLY_CONCAT;
} else {
expected = "";
}
TestCookieParsingClient client = new TestCookieParsingClient(
legacyCookieProcessor, COOKIES_WITH_NAME_ONLY, expected);
client.doRequest();
}
@Test
public void testRfc6265NameOnly() throws Exception {
// Always allows equals
TestCookieParsingClient client = new TestCookieParsingClient(
new Rfc6265CookieProcessor(), COOKIES_WITH_NAME_ONLY,
COOKIES_WITH_NAME_ONLY_CONCAT);
client.doRequest();
}
@Test
public void testRfc6265V0() throws Exception {
TestCookieParsingClient client = new TestCookieParsingClient(
new Rfc6265CookieProcessor(), COOKIES_V0, COOKIES_V0_CONCAT);
client.doRequest();
}
@Test
public void testRfc6265V1() throws Exception {
TestCookieParsingClient client = new TestCookieParsingClient(
new Rfc6265CookieProcessor(), COOKIES_V1, COOKIES_V1_CONCAT);
client.doRequest();
}
@Test
public void testLegacyWithSeps() throws Exception {
doTestLegacySeps(true, true);
}
@Test
public void testLegacyWithoutSeps() throws Exception {
doTestLegacySeps(false, true);
}
@Test
public void testLegacyWithFwdSlash() throws Exception {
doTestLegacySeps(true, false);
}
@Test
public void testLegacyWithoutFwdSlash() throws Exception {
doTestLegacySeps(false, false);
}
private void doTestLegacySeps(boolean seps, boolean fwdSlash) throws Exception {
LegacyCookieProcessor legacyCookieProcessor = new LegacyCookieProcessor();
legacyCookieProcessor.setAllowHttpSepsInV0(seps);
legacyCookieProcessor.setForwardSlashIsSeparator(fwdSlash);
String expected;
if (!seps && fwdSlash) {
expected = COOKIES_WITH_SEPS_TRUNC;
} else {
expected = concat(COOKIES_WITH_SEPS);
}
TestCookieParsingClient client = new TestCookieParsingClient(
legacyCookieProcessor, COOKIES_WITH_SEPS, expected);
client.doRequest();
}
@Test
public void testRfc6265Seps() throws Exception {
// Always allows equals
TestCookieParsingClient client = new TestCookieParsingClient(
new Rfc6265CookieProcessor(), COOKIES_WITH_SEPS, concat(COOKIES_WITH_SEPS));
client.doRequest();
}
@Test
public void testLegacyPreserveHeader() throws Exception {
LegacyCookieProcessor legacyCookieProcessor = new LegacyCookieProcessor();
String expected;
expected = concat(COOKIES_WITH_QUOTES);
TestCookieParsingClient client = new TestCookieParsingClient(
legacyCookieProcessor, true, COOKIES_WITH_QUOTES, expected);
client.doRequest();
}
@Test
public void testRfc6265PreserveHeader() throws Exception {
// Always allows equals
TestCookieParsingClient client = new TestCookieParsingClient(new Rfc6265CookieProcessor(),
true, COOKIES_WITH_QUOTES, concat(COOKIES_WITH_QUOTES));
client.doRequest();
}
private static String concat(String[] input) {
StringBuilder result = new StringBuilder();
for (String s : input) {
result.append(s);
}
return result.toString();
}
private class TestCookieParsingClient extends SimpleHttpClient {
private final CookieProcessor cookieProcessor;
private final String[] cookies;
private final String expected;
private final boolean echoHeader;
public TestCookieParsingClient(CookieProcessor cookieProcessor,
String[] cookies, String expected) {
this(cookieProcessor, false, cookies, expected);
}
public TestCookieParsingClient(CookieProcessor cookieProcessor,
boolean echoHeader, String[] cookies, String expected) {
this.cookieProcessor = cookieProcessor;
this.echoHeader = echoHeader;
this.cookies = cookies;
this.expected = expected;
}
private void doRequest() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
root.setCookieProcessor(cookieProcessor);
if (echoHeader) {
Tomcat.addServlet(root, "Cookies", new EchoCookieHeader());
} else {
Tomcat.addServlet(root, "Cookies", new EchoCookies());
}
root.addServletMappingDecoded("/test", "Cookies");
tomcat.start();
// Open connection
setPort(tomcat.getConnector().getLocalPort());
connect();
StringBuilder request = new StringBuilder();
request.append("GET /test HTTP/1.0");
request.append(CRLF);
for (String cookie : cookies) {
request.append("Cookie: ");
request.append(cookie);
request.append(CRLF);
}
request.append(CRLF);
setRequest(new String[] {request.toString()});
processRequest(true); // blocks until response has been read
String response = getResponseBody();
// Close the connection
disconnect();
reset();
tomcat.stop();
Assert.assertEquals(expected, response);
}
@Override
public boolean isResponseBodyOK() {
return true;
}
}
private static class EchoCookies extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Cookie cookies[] = req.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
resp.getWriter().write(cookie.getName() + "=" +
cookie.getValue());
}
}
resp.flushBuffer();
}
}
private static class EchoCookieHeader extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getCookies();
// Never do this in production code. It triggers an XSS.
Enumeration<String> cookieHeaders = req.getHeaders("Cookie");
while (cookieHeaders.hasMoreElements()) {
String cookieHeader = cookieHeaders.nextElement();
resp.getWriter().write(cookieHeader);
}
resp.flushBuffer();
}
}
}

View File

@@ -0,0 +1,407 @@
/*
* 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.http;
import javax.servlet.http.Cookie;
import org.junit.Assert;
import org.junit.Test;
public class TestCookieProcessorGeneration {
@Test
public void v0SimpleCookie() {
doTest(new Cookie("foo", "bar"), "foo=bar");
}
@Test
public void v0NullValue() {
doTest(new Cookie("foo", null), "foo=\"\"", "foo=");
}
@Test
public void v0QuotedValue() {
doTest(new Cookie("foo", "\"bar\""), "foo=\"bar\"");
}
@Test
public void v0ValueContainsSemicolon() {
doTest(new Cookie("foo", "a;b"), "foo=\"a;b\"; Version=1", null);
}
@Test
public void v0ValueContainsComma() {
doTest(new Cookie("foo", "a,b"), "foo=\"a,b\"; Version=1", null);
}
@Test
public void v0ValueContainsSpace() {
doTest(new Cookie("foo", "a b"), "foo=\"a b\"; Version=1", null);
}
@Test
public void v0ValueContainsEquals() {
Cookie cookie = new Cookie("foo", "a=b");
doTestDefaults(cookie, "foo=\"a=b\"; Version=1", "foo=a=b");
doTestAllowSeparators(cookie, "foo=a=b", "foo=a=b");
}
@Test
public void v0ValueContainsQuote() {
Cookie cookie = new Cookie("foo", "a\"b");
doTestDefaults(cookie,"foo=\"a\\\"b\"; Version=1", null);
doTestAllowSeparators(cookie,"foo=a\"b", null);
}
@Test
public void v0ValueContainsNonV0Separator() {
Cookie cookie = new Cookie("foo", "a()<>@:\\\"/[]?={}b");
doTestDefaults(cookie,"foo=\"a()<>@:\\\\\\\"/[]?={}b\"; Version=1", null);
doTestAllowSeparators(cookie,"foo=a()<>@:\\\"/[]?={}b", null);
}
@Test
public void v0ValueContainsBackslash() {
Cookie cookie = new Cookie("foo", "a\\b");
doTestDefaults(cookie, "foo=\"a\\\\b\"; Version=1", null);
doTestAllowSeparators(cookie, "foo=a\\b", null);
}
@Test
public void v0ValueContainsBackslashAtEnd() {
Cookie cookie = new Cookie("foo", "a\\");
doTestDefaults(cookie, "foo=\"a\\\\\"; Version=1", null);
doTestAllowSeparators(cookie, "foo=a\\", null);
}
@Test
public void v0ValueContainsBackslashAndQuote() {
Cookie cookie = new Cookie("foo", "a\"b\\c");
doTestDefaults(cookie, "foo=\"a\\\"b\\\\c\"; Version=1", null);
doTestAllowSeparators(cookie, "foo=a\"b\\c", null);
}
@Test
public void v1simpleCookie() {
Cookie cookie = new Cookie("foo", "bar");
cookie.setVersion(1);
doTest(cookie, "foo=bar; Version=1", "foo=bar");
}
@Test
public void v1NullValue() {
Cookie cookie = new Cookie("foo", null);
cookie.setVersion(1);
doTest(cookie, "foo=\"\"; Version=1", "foo=");
}
@Test
public void v1QuotedValue() {
Cookie cookie = new Cookie("foo", "\"bar\"");
cookie.setVersion(1);
doTest(cookie, "foo=\"bar\"; Version=1", "foo=\"bar\"");
}
@Test
public void v1ValueContainsSemicolon() {
Cookie cookie = new Cookie("foo", "a;b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a;b\"; Version=1", null);
}
@Test
public void v1ValueContainsComma() {
Cookie cookie = new Cookie("foo", "a,b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a,b\"; Version=1", null);
}
@Test
public void v1ValueContainsSpace() {
Cookie cookie = new Cookie("foo", "a b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a b\"; Version=1", null);
}
@Test
public void v1ValueContainsEquals() {
Cookie cookie = new Cookie("foo", "a=b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a=b\"; Version=1", "foo=a=b");
}
@Test
public void v1ValueContainsQuote() {
Cookie cookie = new Cookie("foo", "a\"b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a\\\"b\"; Version=1", null);
}
@Test
public void v1ValueContainsNonV0Separator() {
Cookie cookie = new Cookie("foo", "a()<>@,;:\\\"/[]?={}b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a()<>@,;:\\\\\\\"/[]?={}b\"; Version=1", null);
}
@Test
public void v1ValueContainsBackslash() {
Cookie cookie = new Cookie("foo", "a\\b");
cookie.setVersion(1);
doTest(cookie, "foo=\"a\\\\b\"; Version=1", null);
}
@Test
public void v1ValueContainsBackslashAndQuote() {
Cookie cookie = new Cookie("foo", "a\"b\\c");
cookie.setVersion(1);
doTest(cookie, "foo=\"a\\\"b\\\\c\"; Version=1", null);
}
@Test
public void v1ValueUTF8() {
String value = "\u2300";
Cookie cookie = new Cookie("foo", value);
cookie.setVersion(1);
doTest(cookie, (String) null, "foo=" + value);
}
@Test
public void v1TestMaxAgePositive() {
doV1TestMaxAge(100, "foo=bar; Version=1; Max-Age=100", "foo=bar; Max-Age=100");
}
@Test
public void v1TestMaxAgeZero() {
doV1TestMaxAge(0, "foo=bar; Version=1; Max-Age=0",
"foo=bar; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT");
}
@Test
public void v1TestMaxAgeNegative() {
doV1TestMaxAge(-100, "foo=bar; Version=1", "foo=bar");
}
@Test
public void v1TestDomainValid01() {
doV1TestDomain("example.com", "foo=bar; Version=1; Domain=example.com",
"foo=bar; Domain=example.com");
}
@Test
public void v1TestDomainValid02() {
doV1TestDomain("exa-mple.com", "foo=bar; Version=1; Domain=exa-mple.com",
"foo=bar; Domain=exa-mple.com");
}
@Test
public void v1TestDomainInvalid01() {
doV1TestDomain("example.com.", "foo=bar; Version=1; Domain=example.com.", null);
}
@Test
public void v1TestDomainInvalid02() {
doV1TestDomain("example.com-", "foo=bar; Version=1; Domain=example.com-", null);
}
@Test
public void v1TestDomainInvalid03() {
doV1TestDomain(".example.com.", "foo=bar; Version=1; Domain=.example.com.", null);
}
@Test
public void v1TestDomainInvalid04() {
doV1TestDomain("-example.com.", "foo=bar; Version=1; Domain=-example.com.", null);
}
@Test
public void v1TestDomainInvalid05() {
doV1TestDomain("example..com.", "foo=bar; Version=1; Domain=example..com.", null);
}
@Test
public void v1TestDomainInvalid06() {
doV1TestDomain("example-.com.", "foo=bar; Version=1; Domain=example-.com.", null);
}
@Test
public void v1TestDomainInvalid07() {
doV1TestDomain("exam$ple.com.", "foo=bar; Version=1; Domain=exam$ple.com.", null);
}
@Test
public void v1TestPathValid() {
doV1TestPath("/example", "foo=bar; Version=1; Path=/example",
"foo=bar; Path=/example");
}
@Test
public void v1TestPathInvalid01() {
doV1TestPath("exa\tmple", "foo=bar; Version=1; Path=\"exa\tmple\"", null);
}
@Test
public void testSameSiteCookies() {
LegacyCookieProcessor legacy = new LegacyCookieProcessor();
Rfc6265CookieProcessor rfc6265 = new Rfc6265CookieProcessor();
Cookie cookie = new Cookie("foo", "bar");
Assert.assertEquals("foo=bar", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("unset");
rfc6265.setSameSiteCookies("unset");
Assert.assertEquals("foo=bar", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("none");
rfc6265.setSameSiteCookies("none");
Assert.assertEquals("foo=bar; SameSite=None", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; SameSite=None", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("lax");
rfc6265.setSameSiteCookies("lax");
Assert.assertEquals("foo=bar; SameSite=Lax", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; SameSite=Lax", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("strict");
rfc6265.setSameSiteCookies("strict");
Assert.assertEquals("foo=bar; SameSite=Strict", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; SameSite=Strict", rfc6265.generateHeader(cookie));
cookie.setSecure(true);
cookie.setHttpOnly(true);
legacy.setSameSiteCookies("unset");
rfc6265.setSameSiteCookies("unset");
Assert.assertEquals("foo=bar; Secure; HttpOnly", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; Secure; HttpOnly", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("none");
rfc6265.setSameSiteCookies("none");
Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=None", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=None", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("lax");
rfc6265.setSameSiteCookies("lax");
Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Lax", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Lax", rfc6265.generateHeader(cookie));
legacy.setSameSiteCookies("strict");
rfc6265.setSameSiteCookies("strict");
Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Strict", legacy.generateHeader(cookie));
Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Strict", rfc6265.generateHeader(cookie));
}
private void doTest(Cookie cookie, String expected) {
doTest(cookie, expected, expected);
}
private void doTest(Cookie cookie,
String expectedLegacy, String expectedRfc6265) {
doTestDefaults(cookie, expectedLegacy, expectedRfc6265);
doTestAllowSeparators(cookie, expectedLegacy, expectedRfc6265);
}
private void doTestDefaults(Cookie cookie,
String expectedLegacy, String expectedRfc6265) {
CookieProcessor legacy = new LegacyCookieProcessor();
CookieProcessor rfc6265 = new Rfc6265CookieProcessor();
doTest(cookie, legacy, expectedLegacy, rfc6265, expectedRfc6265);
}
private void doTestAllowSeparators(Cookie cookie,
String expectedLegacy, String expectedRfc6265) {
LegacyCookieProcessor legacy = new LegacyCookieProcessor();
legacy.setAllowHttpSepsInV0(true);
legacy.setForwardSlashIsSeparator(true);
CookieProcessor rfc6265 = new Rfc6265CookieProcessor();
doTest(cookie, legacy, expectedLegacy, rfc6265, expectedRfc6265);
}
private void doTest(Cookie cookie,
CookieProcessor legacy, String expectedLegacy,
CookieProcessor rfc6265, String expectedRfc6265) {
doTest(cookie, legacy, expectedLegacy);
doTest(cookie, rfc6265, expectedRfc6265);
}
private void doTest(Cookie cookie, CookieProcessor cookieProcessor, String expected) {
if (expected == null) {
IllegalArgumentException e = null;
try {
cookieProcessor.generateHeader(cookie);
} catch (IllegalArgumentException iae) {
e = iae;
}
Assert.assertNotNull("Failed to throw IAE", e);
} else {
if (cookieProcessor instanceof Rfc6265CookieProcessor &&
cookie.getMaxAge() > 0) {
// Expires attribute will depend on time cookie is generated so
// use a modified test
Assert.assertTrue(cookieProcessor.generateHeader(cookie).startsWith(expected));
} else {
Assert.assertEquals(expected, cookieProcessor.generateHeader(cookie));
}
}
}
private void doV1TestMaxAge(int age, String expectedLegacy, String expectedRfc6265) {
LegacyCookieProcessor legacy = new LegacyCookieProcessor();
legacy.setAlwaysAddExpires(false);
Cookie cookie = new Cookie("foo", "bar");
cookie.setVersion(1);
cookie.setMaxAge(age);
doTest(cookie, legacy, expectedLegacy, new Rfc6265CookieProcessor(), expectedRfc6265);
}
private void doV1TestDomain(String domain, String expectedLegacy, String expectedRfc6265) {
LegacyCookieProcessor legacy = new LegacyCookieProcessor();
legacy.setAlwaysAddExpires(false);
Cookie cookie = new Cookie("foo", "bar");
cookie.setVersion(1);
cookie.setDomain(domain);
doTest(cookie, legacy, expectedLegacy, new Rfc6265CookieProcessor(), expectedRfc6265);
}
private void doV1TestPath(String path, String expectedLegacy, String expectedRfc6265) {
LegacyCookieProcessor legacy = new LegacyCookieProcessor();
legacy.setAlwaysAddExpires(false);
Cookie cookie = new Cookie("foo", "bar");
cookie.setVersion(1);
cookie.setPath(path);
doTest(cookie, legacy, expectedLegacy, new Rfc6265CookieProcessor(), expectedRfc6265);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.http;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
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.ByteChunk;
public class TestCookieProcessorGenerationHttp extends TomcatBaseTest {
@Test
public void testUtf8CookieValue() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
ctx.setCookieProcessor(new Rfc6265CookieProcessor());
Tomcat.addServlet(ctx, "test", new CookieServlet("\u0120"));
ctx.addServletMappingDecoded("/test", "test");
tomcat.start();
Map<String,List<String>> headers = new HashMap<>();
ByteChunk res = new ByteChunk();
getUrl("http://localhost:" + getPort() + "/test", res, headers);
List<String> cookieHeaders = headers.get("Set-Cookie");
Assert.assertEquals("There should only be one Set-Cookie header in this test",
1, cookieHeaders.size());
// Client is assuming header is ISO-8859-1 encoding which it isn't. Turn
// the header value back into the received bytes (this isn't guaranteed
// to work with all values but it will for this test value)
byte[] headerBytes = cookieHeaders.get(0).getBytes(StandardCharsets.ISO_8859_1);
// Now convert those bytes to a String using UTF-8
String utf8Header = new String(headerBytes, StandardCharsets.UTF_8);
Assert.assertEquals("Test=\u0120", utf8Header);
}
private static class CookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final String cookieValue;
public CookieServlet(String cookieValue) {
this.cookieValue = cookieValue;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Cookie cookie = new Cookie("Test", cookieValue);
resp.addCookie(cookie);
resp.setContentType("text/plain");
resp.getWriter().print("OK");
}
}
}

View File

@@ -0,0 +1,530 @@
/*
* 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.http;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.Cookie;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.buf.MessageBytes;
public class TestCookies {
private final Cookie FOO = new Cookie("foo", "bar");
private final Cookie FOO_EMPTY = new Cookie("foo", "");
private final Cookie FOO_CONTROL = new Cookie("foo", "b\u00e1r");
private final Cookie BAR = new Cookie("bar", "rab");
private final Cookie BAR_EMPTY = new Cookie("bar", "");
private final Cookie A = new Cookie("a", "b");
private final Cookie HASH_EMPTY = new Cookie("#", "");
private final Cookie $PORT = new Cookie("$Port", "8080");
@Test
public void testBasicCookieOld() {
doTestBasicCookie(false);
}
@Test
public void testBasicCookieRfc6265() {
doTestBasicCookie(true);
}
private void doTestBasicCookie(boolean useRfc6265) {
test(useRfc6265, "foo=bar; a=b", FOO, A);
test(useRfc6265, "foo=bar;a=b", FOO, A);
test(useRfc6265, "foo=bar;a=b;", FOO, A);
test(useRfc6265, "foo=bar;a=b; ", FOO, A);
test(useRfc6265, "foo=bar;a=b; ;", FOO, A);
}
@Test
public void testNameOnlyAreDroppedOld() {
test(false, "foo=;a=b; ;", A);
test(false, "foo;a=b; ;", A);
test(false, "foo;a=b;bar", A);
test(false, "foo;a=b;bar;", A);
test(false, "foo;a=b;bar ", A);
test(false, "foo;a=b;bar ;", A);
// Bug 49000
Cookie fred = new Cookie("fred", "1");
Cookie jim = new Cookie("jim", "2");
Cookie george = new Cookie("george", "3");
test(false, "fred=1; jim=2; bob", fred, jim);
test(false, "fred=1; jim=2; bob; george=3", fred, jim, george);
test(false, "fred=1; jim=2; bob=; george=3", fred, jim, george);
test(false, "fred=1; jim=2; bob=", fred, jim);
}
@Test
public void testNameOnlyAreDroppedRfc6265() {
// Name only cookies are not dropped in RFC6265
test(true, "foo=;a=b; ;", FOO_EMPTY, A);
test(true, "foo;a=b; ;", FOO_EMPTY, A);
test(true, "foo;a=b;bar", FOO_EMPTY, A, BAR_EMPTY);
test(true, "foo;a=b;bar;", FOO_EMPTY, A, BAR_EMPTY);
test(true, "foo;a=b;bar ", FOO_EMPTY, A, BAR_EMPTY);
test(true, "foo;a=b;bar ;", FOO_EMPTY, A, BAR_EMPTY);
// Bug 49000
Cookie fred = new Cookie("fred", "1");
Cookie jim = new Cookie("jim", "2");
Cookie bobEmpty = new Cookie("bob", "");
Cookie george = new Cookie("george", "3");
test(true, "fred=1; jim=2; bob", fred, jim, bobEmpty);
test(true, "fred=1; jim=2; bob; george=3", fred, jim, bobEmpty, george);
test(true, "fred=1; jim=2; bob=; george=3", fred, jim, bobEmpty, george);
test(true, "fred=1; jim=2; bob=", fred, jim, bobEmpty);
}
@Test
public void testQuotedValueOld() {
doTestQuotedValue(false);
}
@Test
public void testQuotedValueRfc6265() {
doTestQuotedValue(true);
}
private void doTestQuotedValue(boolean useRfc6265) {
test(useRfc6265, "foo=bar;a=\"b\"", FOO, A);
test(useRfc6265, "foo=bar;a=\"b\";", FOO, A);
}
@Test
public void testEmptyPairsOld() {
test(false, "foo;a=b; ;bar", A);
test(false, "foo;a=b;;bar", A);
test(false, "foo;a=b; ;;bar=rab", A, BAR);
test(false, "foo;a=b;; ;bar=rab", A, BAR);
test(false, "foo;a=b;;#;bar=rab", A, BAR);
test(false, "foo;a=b;;\\;bar=rab", A, BAR);
}
@Test
public void testEmptyPairsRfc6265() {
test(true, "foo;a=b; ;bar", FOO_EMPTY, A, BAR_EMPTY);
test(true, "foo;a=b;;bar", FOO_EMPTY, A, BAR_EMPTY);
test(true, "foo;a=b; ;;bar=rab", FOO_EMPTY, A, BAR);
test(true, "foo;a=b;; ;bar=rab", FOO_EMPTY, A, BAR);
test(true, "foo;a=b;;#;bar=rab", FOO_EMPTY, A, HASH_EMPTY, BAR);
test(true, "foo;a=b;;\\;bar=rab", FOO_EMPTY, A, BAR);
}
@Test
public void testSeparatorsInValueOld() {
doTestSeparatorsInValue(false);
}
@Test
public void testSeparatorsInValueRfc6265() {
doTestSeparatorsInValue(true);
}
private void doTestSeparatorsInValue(boolean useRfc6265) {
test(useRfc6265, "a=()<>@:\\\"/[]?={}\t; foo=bar", FOO);
}
@Test
public void v1TokenValueOld() {
doV1TokenValue(false);
}
@Test
public void v1TokenValueRfc6265() {
doV1TokenValue(true);
}
private void doV1TokenValue(boolean useRfc6265) {
FOO.setVersion(1);
A.setVersion(1);
test(useRfc6265, "$Version=1; foo=bar;a=b", FOO, A);
test(useRfc6265, "$Version=1;foo=bar;a=b; ; ", FOO, A);
}
@Test
public void v1NameOnlyIsDroppedOld() {
doV1NameOnlyIsDropped(false);
}
@Test
public void v1NameOnlyIsDroppedRfc6265() {
doV1NameOnlyIsDropped(true);
}
private void doV1NameOnlyIsDropped(boolean useRfc6265) {
A.setVersion(1);
test(useRfc6265, "$Version=1;foo=;a=b; ; ", A);
test(useRfc6265, "$Version=1;foo= ;a=b; ; ", A);
test(useRfc6265, "$Version=1;foo;a=b; ; ", A);
}
@Test
public void v1QuotedValueOld() {
doV1QuotedValue(false);
}
@Test
public void v1QuotedValueRfc6265() {
doV1QuotedValue(true);
}
private void doV1QuotedValue(boolean useRfc6265) {
FOO.setVersion(1);
A.setVersion(1);
// presumes quotes are removed
test(useRfc6265, "$Version=1;foo=\"bar\";a=b; ; ", FOO, A);
}
@Test
public void v1DQuoteInValueOld() {
FOO.setValue("b");
FOO.setVersion(1);
A.setVersion(1);
test(false, "$Version=1;foo=\"b\"ar\";a=b", FOO, A); // Incorrectly escaped.
}
@Test
public void v1DQuoteInValueRfc6265() {
A.setVersion(1);
test(true, "$Version=1;foo=\"b\"ar\";a=b", A); // Incorrectly escaped.
}
@Test
public void v1QuoteInValueOld() {
doV1QuoteInValue(false);
}
@Test
public void v1QuoteInValueRfc6265() {
doV1QuoteInValue(true);
}
private void doV1QuoteInValue(boolean useRfc6265) {
FOO.setValue("b'ar");
FOO.setVersion(1);
A.setVersion(1);
test(useRfc6265, "$Version=1;foo=b'ar;a=b", FOO, A);
}
@Test
public void v1QuoteInQuotedValueOld() {
doV1QuoteInQuotedValue(false);
}
@Test
public void v1QuoteInQuotedValueRfc6265() {
doV1QuoteInQuotedValue(true);
}
private void doV1QuoteInQuotedValue(boolean useRfc6265) {
FOO.setValue("b'ar");
FOO.setVersion(1);
A.setVersion(1);
test(useRfc6265, "$Version=1;foo=\"b'ar\";a=b", FOO, A);
}
@Test
public void v1EscapedDQuoteInValueOld() {
doV1EscapedDQuoteInValue(false);
}
@Test
public void v1EscapedDQuoteInValueRfc6265() {
doV1EscapedDQuoteInValue(true);
}
private void doV1EscapedDQuoteInValue(boolean useRfc6265) {
FOO.setValue("b\"ar");
FOO.setVersion(1);
A.setVersion(1);
test(useRfc6265, "$Version=1;foo=\"b\\\"ar\";a=b", FOO, A); // correctly escaped.
}
@Test
public void v1QuotedValueEndsInBackslashOld() {
doV1QuotedValueEndsInBackslash(false);
}
@Test
public void v1QuotedValueEndsInBackslashRfc6265() {
doV1QuotedValueEndsInBackslash(true);
}
private void doV1QuotedValueEndsInBackslash(boolean useRfc6265) {
FOO.setVersion(1);
test(useRfc6265, "$Version=1;foo=bar;a=\"b\\\"", FOO);
}
@Test
public void v1MismatchedQuotesOld() {
doV1MismatchedQuotes(false);
}
@Test
public void v1MismatchedQuotesRfc6265() {
doV1MismatchedQuotes(true);
}
private void doV1MismatchedQuotes(boolean useRfc6265) {
FOO.setVersion(1);
test(useRfc6265, "$Version=1;foo=bar;a=\"b\\", FOO);
}
@Test
public void v1SingleQuotesAreValidTokenCharactersOld() {
doV1SingleQuotesAreValidTokenCharacters(false);
}
@Test
public void v1SingleQuotesAreValidTokenCharactersRfc6265() {
doV1SingleQuotesAreValidTokenCharacters(true);
}
private void doV1SingleQuotesAreValidTokenCharacters(boolean useRfc6265) {
FOO.setVersion(1);
FOO.setValue("'bar'");
test(useRfc6265, "$Version=1; foo='bar'", FOO);
}
@Test
public void v1DomainIsParsedOld() {
doV1DomainIsParsed(false);
}
@Test
public void v1DomainIsParsedRfc6265() {
doV1DomainIsParsed(true);
}
private void doV1DomainIsParsed(boolean useRfc6265) {
FOO.setVersion(1);
FOO.setDomain("apache.org");
A.setVersion(1);
A.setDomain("yahoo.com");
test(useRfc6265, "$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com", FOO, A);
}
@Test
public void v1DomainOnlyAffectsPrecedingCookieOld() {
doV1DomainOnlyAffectsPrecedingCookie(false);
}
@Test
public void v1DomainOnlyAffectsPrecedingCookieRfc6265() {
doV1DomainOnlyAffectsPrecedingCookie(true);
}
private void doV1DomainOnlyAffectsPrecedingCookie(boolean useRfc6265) {
FOO.setVersion(1);
FOO.setDomain("apache.org");
A.setVersion(1);
test(useRfc6265, "$Version=1;foo=\"bar\";$Domain=apache.org;a=b", FOO, A);
}
@Test
public void v1PortIsIgnoredOld() {
FOO.setVersion(1);
FOO.setDomain("apache.org");
A.setVersion(1);
test(false, "$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, A);
}
@Test
public void v1PortIsIgnoredRfc6265() {
FOO.setVersion(1);
FOO.setDomain("apache.org");
$PORT.setVersion(1);
A.setVersion(1);
test(true, "$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, $PORT, A);
}
@Test
public void v1PathAffectsPrecedingCookieOld() {
doV1PathAffectsPrecedingCookie(false);
}
@Test
public void v1PathAffectsPrecedingCookieRfc6265() {
doV1PathAffectsPrecedingCookie(true);
}
private void doV1PathAffectsPrecedingCookie(boolean useRfc6265) {
FOO.setVersion(1);
FOO.setPath("/examples");
A.setVersion(1);
test(useRfc6265, "$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ", FOO, A);
}
@Test
public void rfc2109Version0Old() {
// rfc2109 semantically does not allow $Version to be 0 but it is valid syntax
test(false, "$Version=0;foo=bar", FOO);
}
@Test
public void rfc2109Version0Rfc6265() {
// RFC6265 will parse explicit version 0 using RFC2109
test(true, "$Version=0;foo=bar", FOO);
}
@Test
public void disallow8bitInName() {
// Bug 55917
test(true, "f\u00f6o=bar");
}
@Test
public void disallowControlInName() {
// Bug 55917
test(true, "f\010o=bar");
}
@Test
public void disallow8BitControlInName() {
// Bug 55917
test(true, "f\210o=bar");
}
@Test
public void allow8BitInV0Value() {
// Bug 55917
test(true, "foo=b\u00e1r", FOO_CONTROL);
}
@Test
public void disallow8bitInV1UnquotedValue() {
// Bug 55917
test(true, "$Version=1; foo=b\u00e1r");
}
@Test
public void allow8bitInV1QuotedValue() {
// Bug 55917
FOO_CONTROL.setVersion(1);
test(true, "$Version=1; foo=\"b\u00e1r\"", FOO_CONTROL);
}
@Test
public void disallowControlInV0Value() {
// Bug 55917
test(true, "foo=b\010r");
}
@Test
public void disallowControlInV1UnquotedValue() {
// Bug 55917
test(true, "$Version=1; foo=b\010r");
}
@Test
public void disallowControlInV1QuotedValue() {
// Bug 55917 / Bug 55918
test(true, "$Version=1; foo=\"b\010r\"");
}
@Test
public void disallow8BitControlInV1UnquotedValue() {
// Bug 55917
test(true, "$Version=1; foo=b\210r");
}
@Test
public void testJsonInV0() {
// Bug 55921
test(true, "{\"a\":true, \"b\":false};a=b", A);
}
@Test
public void testJsonInV1() {
// Bug 55921
A.setVersion(1);
test(true, "$Version=1;{\"a\":true, \"b\":false};a=b", A);
}
@Test
public void testSkipSemicolonOrComma() {
// V1 cookies can also use commas to separate cookies
FOO.setVersion(1);
A.setVersion(1);
test(true, "$Version=1;x\tx=yyy,foo=bar;a=b", FOO, A);
}
@Test
public void testBug60788Rfc6265() {
doTestBug60788(true);
}
@Test
public void testBug60788Rfc2109() {
doTestBug60788(false);
}
private void doTestBug60788(boolean useRfc6265) {
Cookie expected = new Cookie("userId", "foo");
expected.setVersion(1);
if (useRfc6265) {
expected.setDomain("\"www.example.org\"");
expected.setPath("\"/\"");
} else {
// The legacy processor removes the quotes for domain and path
expected.setDomain("www.example.org");
expected.setPath("/");
}
test(useRfc6265, "$Version=\"1\"; userId=\"foo\";$Path=\"/\";$Domain=\"www.example.org\"",
expected);
}
private void test(boolean useRfc6265, String header, Cookie... expected) {
MimeHeaders mimeHeaders = new MimeHeaders();
ServerCookies serverCookies = new ServerCookies(4);
CookieProcessor cookieProcessor;
if (useRfc6265) {
cookieProcessor = new Rfc6265CookieProcessor();
} else {
cookieProcessor = new LegacyCookieProcessor();
}
MessageBytes cookieHeaderValue = mimeHeaders.addValue("Cookie");
byte[] bytes = header.getBytes(StandardCharsets.UTF_8);
cookieHeaderValue.setBytes(bytes, 0, bytes.length);
cookieProcessor.parseCookieHeader(mimeHeaders, serverCookies);
Assert.assertEquals(expected.length, serverCookies.getCookieCount());
for (int i = 0; i < expected.length; i++) {
Cookie cookie = expected[i];
ServerCookie actual = serverCookies.getCookie(i);
Assert.assertEquals(cookie.getVersion(), actual.getVersion());
Assert.assertEquals(cookie.getName(), actual.getName().toString());
actual.getValue().getByteChunk().setCharset(StandardCharsets.UTF_8);
Assert.assertEquals(cookie.getValue(),
org.apache.tomcat.util.http.parser.Cookie.unescapeCookieValueRfc2109(
actual.getValue().toString()));
if (cookie.getVersion() == 1) {
Assert.assertEquals(cookie.getDomain(), actual.getDomain().toString());
Assert.assertEquals(cookie.getPath(), actual.getPath().toString());
}
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.http;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Test case for {@link LegacyCookieProcessor}. <b>Note</b> because of the use
* of <code>final static</code> constants in the cookie processing code, each of
* these tests must be executed in a new JVM instance. The tests have been place
* in separate classes to facilitate this when running the unit tests via Ant.
*/
public class TestCookiesDefaultSysProps extends CookiesBaseTest {
@Override
@Test
public void testCookiesInstance() throws Exception {
Tomcat tomcat = getTomcatInstance();
addServlets(tomcat);
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/invalid");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/null");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/blank");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidFwd");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidStrict");
Assert.assertEquals("Cookie name ok", res.toString());
res = getUrl("http://localhost:" + getPort() + "/valid");
Assert.assertEquals("Cookie name ok", res.toString());
// Need to read response headers to test version switching
Map<String,List<String>> headers = new HashMap<>();
getUrl("http://localhost:" + getPort() + "/switch", res, headers);
List<String> cookieHeaders = headers.get("Set-Cookie");
for (String cookieHeader : cookieHeaders) {
Assert.assertEquals("name=\"val?ue\"; Version=1", cookieHeader);
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.http;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Test case for {@link LegacyCookieProcessor}. <b>Note</b> because of the use
* of <code>final static</code> constants in the cookie processing code, each of
* these tests must be executed in a new JVM instance. The tests have been place
* in separate classes to facilitate this when running the unit tests via Ant.
*/
public class TestCookiesNoFwdStrictSysProps extends CookiesBaseTest {
@Override
@Test
public void testCookiesInstance() throws Exception {
System.setProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
"true");
System.setProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR",
"false");
Tomcat tomcat = getTomcatInstance();
addServlets(tomcat);
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/invalid");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/null");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/blank");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidFwd");
Assert.assertEquals("Cookie name ok", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidStrict");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/valid");
Assert.assertEquals("Cookie name ok", res.toString());
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.http;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Test case for {@link LegacyCookieProcessor}. <b>Note</b> because of the use
* of <code>final static</code> constants in the cookie processing code, each of
* these tests must be executed in a new JVM instance. The tests have been place
* in separate classes to facilitate this when running the unit tests via Ant.
*/
public class TestCookiesNoStrictNamingSysProps extends CookiesBaseTest {
@Override
@Test
public void testCookiesInstance() throws Exception {
System.setProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
"true");
System.setProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING",
"false");
Tomcat tomcat = getTomcatInstance();
addServlets(tomcat);
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/invalid");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/null");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/blank");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidFwd");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidStrict");
Assert.assertEquals("Cookie name ok", res.toString());
res = getUrl("http://localhost:" + getPort() + "/valid");
Assert.assertEquals("Cookie name ok", res.toString());
}
}

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.http;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Test case for {@link LegacyCookieProcessor}. <b>Note</b> because of the use
* of <code>final static</code> constants in the cookie processing code, each of
* these tests must be executed in a new JVM instance. The tests have been place
* in separate classes to facilitate this when running the unit tests via Ant.
*/
public class TestCookiesStrictSysProps extends CookiesBaseTest {
@Override
@Test
public void testCookiesInstance() throws Exception {
System.setProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
"true");
Tomcat tomcat = getTomcatInstance();
addServlets(tomcat);
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/invalid");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/null");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/blank");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidFwd");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidStrict");
Assert.assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/valid");
Assert.assertEquals("Cookie name ok", res.toString());
// Need to read response headers to test version switching
Map<String,List<String>> headers = new HashMap<>();
getUrl("http://localhost:" + getPort() + "/switch", res, headers);
List<String> cookieHeaders = headers.get("Set-Cookie");
for (String cookieHeader : cookieHeaders) {
Assert.assertEquals("name=\"val?ue\"; Version=1", cookieHeader);
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.http;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
@RunWith(Parameterized.class)
public class TestHeaderUtiltoPrintableString {
@Parameterized.Parameters(name = "{index}: expected[{1}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
parameterSets.add(new String[] { "", "" });
parameterSets.add(new String[] { "abcd", "abcd" });
parameterSets.add(new String[] { "\u0000abcd", "0x00abcd" });
parameterSets.add(new String[] { "ab\u0000cd", "ab0x00cd" });
parameterSets.add(new String[] { "abcd\u0000", "abcd0x00" });
parameterSets.add(new String[] { "\tabcd", "0x09abcd" });
parameterSets.add(new String[] { "ab\tcd", "ab0x09cd" });
parameterSets.add(new String[] { "abcd\t", "abcd0x09" });
parameterSets.add(new String[] { " abcd", " abcd" });
parameterSets.add(new String[] { "ab cd", "ab cd" });
parameterSets.add(new String[] { "abcd ", "abcd " });
parameterSets.add(new String[] { "~abcd", "~abcd" });
parameterSets.add(new String[] { "ab~cd", "ab~cd" });
parameterSets.add(new String[] { "abcd~", "abcd~" });
parameterSets.add(new String[] { "\u007fabcd", "0x7fabcd" });
parameterSets.add(new String[] { "ab\u007fcd", "ab0x7fcd" });
parameterSets.add(new String[] { "abcd\u007f", "abcd0x7f" });
parameterSets.add(new String[] { "\u00a3abcd", "0xa3abcd" });
parameterSets.add(new String[] { "ab\u00a3cd", "ab0xa3cd" });
parameterSets.add(new String[] { "abcd\u00a3", "abcd0xa3" });
return parameterSets;
}
@Parameter(0)
public String input;
@Parameter(1)
public String expected;
@Test
public void doTest() {
byte[] bytes = input.getBytes(StandardCharsets.ISO_8859_1);
String result = HeaderUtil.toPrintableString(bytes, 0, bytes.length);
Assert.assertEquals(expected, result);
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.http;
import java.nio.charset.StandardCharsets;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.buf.MessageBytes;
public class TestLegacyCookieProcessor {
/*
* https://bz.apache.org/bugzilla/show_bug.cgi?id=59925
*/
@Test
public void testV0WithPath() {
LegacyCookieProcessor cp = new LegacyCookieProcessor();
cp.setAllowHttpSepsInV0(true);
cp.setForwardSlashIsSeparator(true);
MimeHeaders mimeHeaders = new MimeHeaders();
ServerCookies serverCookies = new ServerCookies(4);
MessageBytes cookieHeaderValue = mimeHeaders.addValue("Cookie");
byte[] bytes = "$Version=0;cname=cvalue;$Path=/example".getBytes(StandardCharsets.UTF_8);
cookieHeaderValue.setBytes(bytes, 0, bytes.length);
cp.parseCookieHeader(mimeHeaders, serverCookies);
Assert.assertEquals(1, serverCookies.getCookieCount());
for (int i = 0; i < 1; i++) {
ServerCookie actual = serverCookies.getCookie(i);
Assert.assertEquals(0, actual.getVersion());
Assert.assertEquals("cname", actual.getName().toString());
actual.getValue().getByteChunk().setCharset(StandardCharsets.UTF_8);
Assert.assertEquals("cvalue",
org.apache.tomcat.util.http.parser.Cookie.unescapeCookieValueRfc2109(
actual.getValue().toString()));
Assert.assertEquals("/example", actual.getPath().toString());
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.http;
import org.junit.Assert;
import org.junit.Test;
public class TestMimeHeaders {
public static final String HEADER_NAME_LC_STRING = "test";
public static final String HEADER_NAME_UC_STRING = "TEST";
public static final String HEADER_NAME_MIXED_STRING = "tEsT";
@Test
public void testSetValueStringIgnoresCase01() {
MimeHeaders mh = new MimeHeaders();
mh.setValue(HEADER_NAME_LC_STRING).setString(HEADER_NAME_LC_STRING);
mh.setValue(HEADER_NAME_UC_STRING).setString(HEADER_NAME_UC_STRING);
Assert.assertEquals(HEADER_NAME_UC_STRING, mh.getValue(HEADER_NAME_UC_STRING).toString());
Assert.assertEquals(HEADER_NAME_UC_STRING, mh.getValue(HEADER_NAME_LC_STRING).toString());
Assert.assertEquals(HEADER_NAME_UC_STRING, mh.getValue(HEADER_NAME_MIXED_STRING).toString());
}
@Test
public void testSetValueStringIgnoresCase02() {
MimeHeaders mh = new MimeHeaders();
mh.setValue(HEADER_NAME_UC_STRING).setString(HEADER_NAME_UC_STRING);
mh.setValue(HEADER_NAME_LC_STRING).setString(HEADER_NAME_LC_STRING);
Assert.assertEquals(HEADER_NAME_LC_STRING, mh.getValue(HEADER_NAME_LC_STRING).toString());
Assert.assertEquals(HEADER_NAME_LC_STRING, mh.getValue(HEADER_NAME_UC_STRING).toString());
Assert.assertEquals(HEADER_NAME_LC_STRING, mh.getValue(HEADER_NAME_MIXED_STRING).toString());
}
@Test
public void testSetValueStringIgnoresCase03() {
MimeHeaders mh = new MimeHeaders();
mh.setValue(HEADER_NAME_UC_STRING).setString(HEADER_NAME_UC_STRING);
mh.setValue(HEADER_NAME_MIXED_STRING).setString(HEADER_NAME_MIXED_STRING);
Assert.assertEquals(HEADER_NAME_MIXED_STRING, mh.getValue(HEADER_NAME_LC_STRING).toString());
Assert.assertEquals(HEADER_NAME_MIXED_STRING, mh.getValue(HEADER_NAME_UC_STRING).toString());
Assert.assertEquals(HEADER_NAME_MIXED_STRING, mh.getValue(HEADER_NAME_MIXED_STRING).toString());
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.http;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.SocketException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.valves.TesterAccessLogValve;
public class TestMimeHeadersIntegration extends TomcatBaseTest {
private HeaderCountLogValve alv;
private void setupHeadersTest(Tomcat tomcat) {
Context ctx = tomcat.addContext("", getTemporaryDirectory()
.getAbsolutePath());
Tomcat.addServlet(ctx, "servlet", new HttpServlet() {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.setContentType("text/plain; charset=ISO-8859-1");
res.getWriter().write("OK");
}
});
ctx.addServletMappingDecoded("/", "servlet");
alv = new HeaderCountLogValve();
tomcat.getHost().getPipeline().addValve(alv);
}
private void runHeadersTest(final boolean successExpected,
final Tomcat tomcat, final int count,
final int expectedMaxHeaderCount) throws Exception {
tomcat.start();
String header = "A:B" + SimpleHttpClient.CRLF;
StringBuilder request = new StringBuilder();
request.append("GET / HTTP/1.0" + SimpleHttpClient.CRLF);
for (int i = 0; i < count; i++) {
request.append(header);
}
request.append(SimpleHttpClient.CRLF);
Client client = new Client(tomcat);
client.setRequest(new String[] { request.toString() });
try {
client.connect();
client.processRequest();
client.disconnect();
} catch (SocketException ex) {
// Connection was closed by Tomcat
if (successExpected) {
// unexpected
log.error(ex.getMessage(), ex);
} else {
log.warn(ex.getMessage(), ex);
}
}
if (successExpected) {
alv.validateAccessLog(1, 200, 0, 3000);
// Response 200
Assert.assertTrue("Response line is: " + client.getResponseLine(),
client.getResponseLine() != null && client.isResponse200());
Assert.assertEquals("OK", client.getResponseBody());
} else {
alv.validateAccessLog(1, 400, 0, 3000);
// Connection aborted or response 400
Assert.assertTrue("Response line is: " + client.getResponseLine(),
client.getResponseLine() == null || client.isResponse400());
}
int maxHeaderCount =
((Integer) tomcat.getConnector().getProperty("maxHeaderCount")).intValue();
Assert.assertEquals(expectedMaxHeaderCount, maxHeaderCount);
if (maxHeaderCount > 0) {
Assert.assertEquals(maxHeaderCount, alv.arraySize);
} else if (maxHeaderCount < 0) {
int maxHttpHeaderSize = ((Integer) tomcat.getConnector()
.getAttribute("maxHttpHeaderSize")).intValue();
int headerCount = Math.min(count,
maxHttpHeaderSize / header.length() + 1);
int arraySize = 1;
while (arraySize < headerCount) {
arraySize <<= 1;
}
Assert.assertEquals(arraySize, alv.arraySize);
}
}
@Test
public void testHeaderLimits1() throws Exception {
// Bumping into maxHttpHeaderSize
Tomcat tomcat = getTomcatInstance();
setupHeadersTest(tomcat);
Assert.assertTrue(tomcat.getConnector().setProperty("maxHeaderCount", "-1"));
runHeadersTest(false, tomcat, 8 * 1024, -1);
}
@Test
public void testHeaderLimits2() throws Exception {
// Can process 100 headers
Tomcat tomcat = getTomcatInstance();
setupHeadersTest(tomcat);
runHeadersTest(true, tomcat, 100, 100);
}
@Test
public void testHeaderLimits3() throws Exception {
// Cannot process 101 header
Tomcat tomcat = getTomcatInstance();
setupHeadersTest(tomcat);
runHeadersTest(false, tomcat, 101, 100);
}
@Test
public void testHeaderLimits4() throws Exception {
// Can change maxHeaderCount
Tomcat tomcat = getTomcatInstance();
setupHeadersTest(tomcat);
Assert.assertTrue(tomcat.getConnector().setProperty("maxHeaderCount", "-1"));
runHeadersTest(true, tomcat, 300, -1);
}
private static final class HeaderCountLogValve extends TesterAccessLogValve {
public volatile int arraySize = -1;
@Override
public void log(Request request, Response response, long time) {
super.log(request, response, time);
try {
MimeHeaders mh = request.getCoyoteRequest().getMimeHeaders();
Field headersArrayField = MimeHeaders.class
.getDeclaredField("headers");
headersArrayField.setAccessible(true);
arraySize = ((Object[]) headersArrayField.get(mh)).length;
} catch (Exception ex) {
Assert.assertNull(ex.getMessage(), ex);
}
}
}
private static final class Client extends SimpleHttpClient {
public Client(Tomcat tomcat) {
setPort(tomcat.getConnector().getLocalPort());
}
@Override
public boolean isResponseBodyOK() {
return true;
}
}
}

View File

@@ -0,0 +1,318 @@
/*
* 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.http;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import org.junit.Assert;
import org.junit.Test;
public class TestParameters {
private static final Parameter SIMPLE =
new Parameter("foo1", "bar1");
private static final Parameter SIMPLE_MULTIPLE =
new Parameter("foo2", "bar1", "bar2", "hello world", "?%@");
private static final Parameter NO_VALUE =
new Parameter("foo3");
private static final Parameter EMPTY_VALUE =
new Parameter("foo4", "");
private static final Parameter EMPTY =
new Parameter("");
private static final Parameter UTF8 =
new Parameter("\ufb6b\ufb6a\ufb72", "\uffee\uffeb\uffe2");
@Test
public void testProcessParametersByteArrayIntInt() {
doTestProcessParametersByteArrayIntInt(-1, SIMPLE);
doTestProcessParametersByteArrayIntInt(-1, SIMPLE_MULTIPLE);
doTestProcessParametersByteArrayIntInt(-1, NO_VALUE);
doTestProcessParametersByteArrayIntInt(-1, EMPTY_VALUE);
doTestProcessParametersByteArrayIntInt(-1, EMPTY);
doTestProcessParametersByteArrayIntInt(-1, UTF8);
doTestProcessParametersByteArrayIntInt(-1,
SIMPLE, SIMPLE_MULTIPLE, NO_VALUE, EMPTY_VALUE, EMPTY, UTF8);
doTestProcessParametersByteArrayIntInt(-1,
SIMPLE_MULTIPLE, NO_VALUE, EMPTY_VALUE, EMPTY, UTF8, SIMPLE);
doTestProcessParametersByteArrayIntInt(-1,
NO_VALUE, EMPTY_VALUE, EMPTY, UTF8, SIMPLE, SIMPLE_MULTIPLE);
doTestProcessParametersByteArrayIntInt(-1,
EMPTY_VALUE, EMPTY, UTF8, SIMPLE, SIMPLE_MULTIPLE, NO_VALUE);
doTestProcessParametersByteArrayIntInt(-1,
EMPTY, UTF8, SIMPLE, SIMPLE_MULTIPLE, NO_VALUE, EMPTY_VALUE);
doTestProcessParametersByteArrayIntInt(-1,
UTF8, SIMPLE, SIMPLE_MULTIPLE, NO_VALUE, EMPTY_VALUE, EMPTY);
doTestProcessParametersByteArrayIntInt(1,
SIMPLE, NO_VALUE, EMPTY_VALUE, UTF8);
doTestProcessParametersByteArrayIntInt(2,
SIMPLE, NO_VALUE, EMPTY_VALUE, UTF8);
doTestProcessParametersByteArrayIntInt(3,
SIMPLE, NO_VALUE, EMPTY_VALUE, UTF8);
doTestProcessParametersByteArrayIntInt(4,
SIMPLE, NO_VALUE, EMPTY_VALUE, UTF8);
}
// Make sure the inner Parameter class behaves correctly
@Test
public void testInternal() {
Assert.assertEquals("foo1=bar1", SIMPLE.toString());
// Note: testing requires that ' ' is encoded as '+',
// because that is what browsers will send us.
Assert.assertEquals("foo2=bar1&foo2=bar2&foo2=hello+world&foo2=%3F%25%40",
SIMPLE_MULTIPLE.toString());
Assert.assertEquals("foo3", NO_VALUE.toString());
Assert.assertEquals("foo4=", EMPTY_VALUE.toString());
}
private long doTestProcessParametersByteArrayIntInt(int limit,
Parameter... parameters) {
// Build the byte array
StringBuilder input = new StringBuilder();
boolean first = true;
for (Parameter parameter : parameters) {
if (first) {
first = false;
} else {
input.append('&');
}
input.append(parameter.toString());
}
byte[] data = input.toString().getBytes();
Parameters p = new Parameters();
p.setCharset(StandardCharsets.UTF_8);
p.setLimit(limit);
long start = System.nanoTime();
p.processParameters(data, 0, data.length);
long end = System.nanoTime();
if (limit == -1) {
validateParameters(parameters, p);
} else {
Parameter[] limitParameters = new Parameter[limit];
System.arraycopy(parameters, 0, limitParameters, 0, limit);
validateParameters(limitParameters, p);
}
return end - start;
}
@Test
public void testNonExistantParameter() {
Parameters p = new Parameters();
String value = p.getParameter("foo");
Assert.assertNull(value);
Enumeration<String> names = p.getParameterNames();
Assert.assertFalse(names.hasMoreElements());
String[] values = p.getParameterValues("foo");
Assert.assertNull(values);
}
@Test
public void testAddParameters() {
Parameters p = new Parameters();
// Empty at this point
Enumeration<String> names = p.getParameterNames();
Assert.assertFalse(names.hasMoreElements());
String[] values = p.getParameterValues("foo");
Assert.assertNull(values);
// Add a parameter with two values
p.addParameter("foo", "value1");
p.addParameter("foo", "value2");
names = p.getParameterNames();
Assert.assertTrue(names.hasMoreElements());
Assert.assertEquals("foo", names.nextElement());
Assert.assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo");
Assert.assertEquals(2, values.length);
Assert.assertEquals("value1", values[0]);
Assert.assertEquals("value2", values[1]);
// Add two more values
p.addParameter("foo", "value3");
p.addParameter("foo", "value4");
names = p.getParameterNames();
Assert.assertTrue(names.hasMoreElements());
Assert.assertEquals("foo", names.nextElement());
Assert.assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo");
Assert.assertEquals(4, values.length);
Assert.assertEquals("value1", values[0]);
Assert.assertEquals("value2", values[1]);
Assert.assertEquals("value3", values[2]);
Assert.assertEquals("value4", values[3]);
}
@Test
public void testAddParametersLimit() {
Parameters p = new Parameters();
p.setLimit(2);
// Empty at this point
Enumeration<String> names = p.getParameterNames();
Assert.assertFalse(names.hasMoreElements());
String[] values = p.getParameterValues("foo1");
Assert.assertNull(values);
// Add a parameter
p.addParameter("foo1", "value1");
names = p.getParameterNames();
Assert.assertTrue(names.hasMoreElements());
Assert.assertEquals("foo1", names.nextElement());
Assert.assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo1");
Assert.assertEquals(1, values.length);
Assert.assertEquals("value1", values[0]);
// Add another parameter
p.addParameter("foo2", "value2");
names = p.getParameterNames();
Assert.assertTrue(names.hasMoreElements());
Assert.assertEquals("foo1", names.nextElement());
Assert.assertEquals("foo2", names.nextElement());
Assert.assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo1");
Assert.assertEquals(1, values.length);
Assert.assertEquals("value1", values[0]);
values = p.getParameterValues("foo2");
Assert.assertEquals(1, values.length);
Assert.assertEquals("value2", values[0]);
// Add another parameter
IllegalStateException e = null;
try {
p.addParameter("foo3", "value3");
} catch (IllegalStateException ise) {
e = ise;
}
Assert.assertNotNull(e);
// Check current parameters remain unaffected
names = p.getParameterNames();
Assert.assertTrue(names.hasMoreElements());
Assert.assertEquals("foo1", names.nextElement());
Assert.assertEquals("foo2", names.nextElement());
Assert.assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo1");
Assert.assertEquals(1, values.length);
Assert.assertEquals("value1", values[0]);
values = p.getParameterValues("foo2");
Assert.assertEquals(1, values.length);
Assert.assertEquals("value2", values[0]);
}
private void validateParameters(Parameter[] parameters, Parameters p) {
Enumeration<String> names = p.getParameterNames();
int i = 0;
while (names.hasMoreElements()) {
while (parameters[i].getName() == null) {
i++;
}
String name = names.nextElement();
String[] values = p.getParameterValues(name);
boolean match = false;
for (Parameter parameter : parameters) {
if (name.equals(parameter.getName())) {
match = true;
if (parameter.values.length == 0) {
// Special case
Assert.assertArrayEquals(new String[] {""}, values);
} else {
Assert.assertArrayEquals(parameter.getValues(), values);
}
break;
}
}
Assert.assertTrue(match);
}
}
private static class Parameter {
private final String name;
private final String[] values;
public Parameter(String name, String... values) {
this.name = name;
this.values = values;
}
public String getName() {
return name;
}
public String[] getValues() {
return values;
}
@Override
public String toString() {
try {
StringBuilder result = new StringBuilder();
boolean first = true;
if (values.length == 0) {
return URLEncoder.encode(name, "UTF-8");
}
for (String value : values) {
if (first) {
first = false;
} else {
result.append('&');
}
if (name != null) {
result.append(URLEncoder.encode(name, "UTF-8"));
}
if (value != null) {
result.append('=');
result.append(URLEncoder.encode(value, "UTF-8"));
}
}
return result.toString();
} catch (UnsupportedEncodingException ex) {
return ex.toString();
}
}
}
}

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.http;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
@RunWith(Parameterized.class)
public class TestRequestUtilNormalize {
@Parameterized.Parameters(name = "{index}: input[{0}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
parameterSets.add(new String[] { "//something", "/something" });
parameterSets.add(new String[] { "some//thing", "/some/thing" });
parameterSets.add(new String[] { "something//", "/something/" });
parameterSets.add(new String[] { "//", "/" });
parameterSets.add(new String[] { "///", "/" });
parameterSets.add(new String[] { "////", "/" });
parameterSets.add(new String[] { "/.", "/" });
parameterSets.add(new String[] { "/./", "/" });
parameterSets.add(new String[] { ".", "/" });
parameterSets.add(new String[] { "/..", null });
parameterSets.add(new String[] { "/../", null });
parameterSets.add(new String[] { "..", null });
parameterSets.add(new String[] { "//..", null });
parameterSets.add(new String[] { "//../", null });
parameterSets.add(new String[] { "/./..", null });
parameterSets.add(new String[] { "/./../", null });
parameterSets.add(new String[] { "/a/../..", null });
parameterSets.add(new String[] { "/a/../../", null });
parameterSets.add(new String[] { "/a/..", "/" });
parameterSets.add(new String[] { "/a/.", "/a" });
parameterSets.add(new String[] { "/a/../", "/" });
parameterSets.add(new String[] { "/a/./", "/a/" });
parameterSets.add(new String[] { "/a/b/..", "/a" });
parameterSets.add(new String[] { "/a/b/.", "/a/b" });
parameterSets.add(new String[] { "/a/b/../", "/a/" });
parameterSets.add(new String[] { "/a/b/./", "/a/b/" });
return parameterSets;
}
@Parameter(0)
public String input;
@Parameter(1)
public String expected;
@Test
public void testNormalize() {
Assert.assertEquals(expected,RequestUtil.normalize(input));
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.http;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.connector.Request;
@RunWith(Parameterized.class)
public class TestRequestUtilSameOrigin {
@Parameterized.Parameters(name = "{index}: request[{0}], origin[{1}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
TesterRequest request1 = new TesterRequest("http", "example.com", 80);
TesterRequest request2 = new TesterRequest("ws", "example.com", 80);
TesterRequest request3 = new TesterRequest("http", "example.com", 443);
TesterRequest request4 = new TesterRequest("http", "example.com", 8080);
parameterSets.add(new Object[] { request1, "http://example.com", Boolean.TRUE });
parameterSets.add(new Object[] { request1, "http://example.com:80", Boolean.TRUE });
parameterSets.add(new Object[] { request1, "http://example.com:8080", Boolean.FALSE});
parameterSets.add(new Object[] { request2, "ws://example.com", Boolean.TRUE });
parameterSets.add(new Object[] { request2, "ws://example.com:80", Boolean.TRUE });
parameterSets.add(new Object[] { request2, "ws://example.com:8080", Boolean.FALSE});
parameterSets.add(new Object[] { request3, "http://example.com", Boolean.FALSE });
parameterSets.add(new Object[] { request3, "http://example.com:80", Boolean.FALSE });
parameterSets.add(new Object[] { request3, "http://example.com:443", Boolean.TRUE});
parameterSets.add(new Object[] { request4, "http://example.com", Boolean.FALSE });
parameterSets.add(new Object[] { request4, "http://example.com:80", Boolean.FALSE });
parameterSets.add(new Object[] { request4, "http://example.com:8080", Boolean.TRUE});
return parameterSets;
}
@Parameter(0)
public HttpServletRequest request;
@Parameter(1)
public String origin;
@Parameter(2)
public Boolean same;
@Test
public void testSameOrigin() {
Assert.assertEquals(same, Boolean.valueOf(RequestUtil.isSameOrigin(request, origin)));
}
private static class TesterRequest extends HttpServletRequestWrapper {
private final String scheme;
private final String host;
private final int port;
public TesterRequest(String scheme, String host, int port) {
super(new Request());
this.scheme = scheme;
this.host = host;
this.port = port;
}
@Override
public String getScheme() {
return scheme;
}
@Override
public String getServerName() {
return host;
}
@Override
public int getServerPort() {
return port;
}
@Override
public String toString() {
return scheme + "://" + host + ":" + port;
}
}
}

View File

@@ -0,0 +1,217 @@
/*
* 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.http;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.unittest.TesterResponse;
public class TestResponseUtil {
@Test
public void testAddValidWithAll() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "host");
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(response, "*", expected);
}
@Test
public void testAddAllWithAll() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "*");
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(response, "*", expected);
}
@Test
public void testAddAllWithNone() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(response, "*", expected);
}
@Test
public void testAddValidWithValidSingleHeader() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "foo, bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("too");
doTestAddVaryFieldName(response, "too", expected);
}
@Test
public void testAddValidWithValidSingleHeaderIncludingAll() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "foo, *");
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(response, "too", expected);
}
@Test
public void testAddValidWithValidSingleHeaderAlreadyPresent() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "foo, bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
doTestAddVaryFieldName(response, "foo", expected);
}
@Test
public void testAddValidWithValidHeaders() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "foo");
response.addHeader("vary", "bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("too");
doTestAddVaryFieldName(response, "too", expected);
}
@Test
public void testAddValidWithValidHeadersIncludingAll() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "foo");
response.addHeader("vary", "*");
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(response, "too", expected);
}
@Test
public void testAddValidWithValidHeadersAlreadyPresent() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "foo");
response.addHeader("vary", "bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
doTestAddVaryFieldName(response, "foo", expected);
}
@Test
public void testAddValidWithPartiallyValidSingleHeader() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "{{{, bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("too");
doTestAddVaryFieldName(response, "too", expected);
}
@Test
public void testAddValidWithPartiallyValidSingleHeaderIncludingAll() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "{{{, *");
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(response, "too", expected);
}
@Test
public void testAddValidWithPartiallyValidSingleHeaderAlreadyPresent() {
TesterResponse response = new TesterResponse();
response.getCoyoteResponse();
response.addHeader("vary", "{{{, bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
doTestAddVaryFieldName(response, "bar", expected);
}
private void doTestAddVaryFieldName(TesterResponse response, String fieldName,
Set<String> expected) {
ResponseUtil.addVaryFieldName(response, fieldName);
// There will now only be one Vary header
String resultHeader = response.getHeader("vary");
Set<String> result = new HashSet<>();
// Deliberately do not use Vary.parseVary as it will skip invalid values.
for (String value : resultHeader.split(",")) {
result.add(value.trim());
}
Assert.assertEquals(expected, result);
}
@Test
public void testMimeHeadersAddAllWithNone() {
MimeHeaders mh = new MimeHeaders();
Set<String> expected = new HashSet<>();
expected.add("*");
doTestAddVaryFieldName(mh, "*", expected);
}
@Test
public void testMimeHeadersAddValidWithValidHeaders() {
MimeHeaders mh = new MimeHeaders();
mh.addValue("vary").setString("foo");
mh.addValue("vary").setString("bar");
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("too");
doTestAddVaryFieldName(mh, "too", expected);
}
private void doTestAddVaryFieldName(MimeHeaders mh, String fieldName,
Set<String> expected) {
ResponseUtil.addVaryFieldName(mh, fieldName);
// There will now only be one Vary header
String resultHeader = mh.getHeader("vary");
Set<String> result = new HashSet<>();
// Deliberately do not use Vary.parseVary as it will skip invalid values.
for (String value : resultHeader.split(",")) {
result.add(value.trim());
}
Assert.assertEquals(expected, result);
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.http;
import org.junit.Assert;
import org.junit.Test;
public class TestSameSiteCookies {
@Test
public void testUnset() {
SameSiteCookies attribute = SameSiteCookies.UNSET;
Assert.assertEquals("Unset", attribute.getValue());
Assert.assertEquals(SameSiteCookies.UNSET, attribute);
Assert.assertNotEquals(SameSiteCookies.NONE, attribute);
Assert.assertNotEquals(SameSiteCookies.LAX, attribute);
Assert.assertNotEquals(SameSiteCookies.STRICT, attribute);
}
@Test
public void testNone() {
SameSiteCookies attribute = SameSiteCookies.NONE;
Assert.assertEquals("None", attribute.getValue());
Assert.assertEquals(SameSiteCookies.NONE, attribute);
Assert.assertNotEquals(SameSiteCookies.UNSET, attribute);
Assert.assertNotEquals(SameSiteCookies.LAX, attribute);
Assert.assertNotEquals(SameSiteCookies.STRICT, attribute);
}
@Test
public void testLax() {
SameSiteCookies attribute = SameSiteCookies.LAX;
Assert.assertEquals("Lax", attribute.getValue());
Assert.assertEquals(SameSiteCookies.LAX, attribute);
Assert.assertNotEquals(SameSiteCookies.UNSET, attribute);
Assert.assertNotEquals(SameSiteCookies.NONE, attribute);
Assert.assertNotEquals(SameSiteCookies.STRICT, attribute);
}
@Test
public void testStrict() {
SameSiteCookies attribute = SameSiteCookies.STRICT;
Assert.assertEquals("Strict", attribute.getValue());
Assert.assertEquals(SameSiteCookies.STRICT, attribute);
Assert.assertNotEquals(SameSiteCookies.UNSET, attribute);
Assert.assertNotEquals(SameSiteCookies.NONE, attribute);
Assert.assertNotEquals(SameSiteCookies.LAX, attribute);
}
@Test
public void testToValidAttribute() {
Assert.assertEquals(SameSiteCookies.fromString("unset"), SameSiteCookies.UNSET);
Assert.assertEquals(SameSiteCookies.fromString("Unset"), SameSiteCookies.UNSET);
Assert.assertEquals(SameSiteCookies.fromString("UNSET"), SameSiteCookies.UNSET);
Assert.assertEquals(SameSiteCookies.fromString("none"), SameSiteCookies.NONE);
Assert.assertEquals(SameSiteCookies.fromString("None"), SameSiteCookies.NONE);
Assert.assertEquals(SameSiteCookies.fromString("NONE"), SameSiteCookies.NONE);
Assert.assertEquals(SameSiteCookies.fromString("lax"), SameSiteCookies.LAX);
Assert.assertEquals(SameSiteCookies.fromString("Lax"), SameSiteCookies.LAX);
Assert.assertEquals(SameSiteCookies.fromString("LAX"), SameSiteCookies.LAX);
Assert.assertEquals(SameSiteCookies.fromString("strict"), SameSiteCookies.STRICT);
Assert.assertEquals(SameSiteCookies.fromString("Strict"), SameSiteCookies.STRICT);
Assert.assertEquals(SameSiteCookies.fromString("STRICT"), SameSiteCookies.STRICT);
}
@Test(expected = IllegalStateException.class)
public void testToInvalidAttribute01() {
SameSiteCookies.fromString("");
}
@Test(expected = IllegalStateException.class)
public void testToInvalidAttribute02() {
SameSiteCookies.fromString(" ");
}
@Test(expected = IllegalStateException.class)
public void testToInvalidAttribute03() {
SameSiteCookies.fromString("Strict1");
}
@Test(expected = IllegalStateException.class)
public void testToInvalidAttribute04() {
SameSiteCookies.fromString("foo");
}
@Test(expected = IllegalStateException.class)
public void testToInvalidAttribute05() {
SameSiteCookies.fromString("Lax ");
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.http;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.buf.MessageBytes;
public class TesterCookiesPerformance {
@Test
public void testPerformance01() throws Exception {
final int cookieCount = 100;
final int parsingLoops = 200000;
MimeHeaders mimeHeaders = new MimeHeaders();
StringBuilder cookieHeader = new StringBuilder();
// Create cookies
for (int i = 0; i < cookieCount; i++) {
cookieHeader.append("name");
cookieHeader.append(i);
cookieHeader.append('=');
cookieHeader.append("value");
cookieHeader.append(i);
cookieHeader.append(';');
}
byte[] cookieHeaderBytes = cookieHeader.toString().getBytes("UTF-8");
MessageBytes headerValue = mimeHeaders.addValue("Cookie");
headerValue.setBytes(cookieHeaderBytes, 0, cookieHeaderBytes.length);
ServerCookies serverCookies = new ServerCookies(4);
LegacyCookieProcessor originalCookieProcessor = new LegacyCookieProcessor();
Rfc6265CookieProcessor rfc6265CookieProcessor = new Rfc6265CookieProcessor();
// warm up
for (int i = 0; i < parsingLoops; i++) {
originalCookieProcessor.parseCookieHeader(mimeHeaders, serverCookies);
Assert.assertEquals(cookieCount, serverCookies.getCookieCount());
serverCookies.recycle();
}
long oldStart = System.nanoTime();
for (int i = 0; i < parsingLoops; i++) {
originalCookieProcessor.parseCookieHeader(mimeHeaders, serverCookies);
Assert.assertEquals(cookieCount, serverCookies.getCookieCount());
serverCookies.recycle();
}
long oldDuration = System.nanoTime() - oldStart;
long newStart = System.nanoTime();
for (int i = 0; i < parsingLoops; i++) {
rfc6265CookieProcessor.parseCookieHeader(mimeHeaders, serverCookies);
Assert.assertEquals(cookieCount, serverCookies.getCookieCount());
serverCookies.recycle();
}
long newDuration = System.nanoTime() - newStart;
System.out.println("Original duration: " + oldDuration);
System.out.println("RFC6265 duration: " + newDuration);
}
}

View File

@@ -0,0 +1,128 @@
/*
* 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.http;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import java.util.logging.LogManager;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.buf.B2CConverter;
public class TesterParametersPerformance {
@Test
public void testProcessParametersByteArrayIntInt() {
LogManager.getLogManager().getLogger("").setLevel(Level.OFF);
doTestProcessParametersMultiple("foo".getBytes());
}
private void doTestProcessParametersMultiple(byte[] input) {
System.out.println(doTestProcessParameters(input, 10000));
System.out.println(doTestProcessParameters(input, 20000));
System.out.println(doTestProcessParameters(input, 40000));
System.out.println(doTestProcessParameters(input, 80000));
System.out.println(doTestProcessParameters(input, 160000));
System.out.println(doTestProcessParameters(input, 320000));
System.out.println(doTestProcessParameters(input, 640000));
System.out.println(doTestProcessParameters(input, 1280000));
}
private long doTestProcessParameters(byte[] input, int size) {
Assert.assertEquals(input.length, 3);
Parameters p = new Parameters();
byte[] params = createParams(input, size);
//byte[] input = createParams(8);
p.setCharset(StandardCharsets.ISO_8859_1);
long start = System.nanoTime();
p.processParameters(params, 0, params.length);
return System.nanoTime() - start;
}
private byte[] createParams(byte[] input, int len) {
byte[] result = new byte[len * 4 - 1];
for (int i = 0; i < len; i++) {
result[i * 4] = input[0];
result[i * 4 + 1] = input[1];
result[i * 4 + 2] = input[2];
if (i < len -1) {
result[i * 4 + 3] = 38;
}
}
return result;
}
@Test
public void testCreateString() throws UnsupportedEncodingException {
B2CConverter.getCharset("ISO-8859-1");
doCreateStringMultiple("foo");
}
private void doCreateStringMultiple(String input) {
System.out.println(doCreateString(input, 10, true));
System.out.println(doCreateString(input, 100, true));
System.out.println(doCreateString(input, 1000, true));
System.out.println(doCreateString(input, 10000, true));
System.out.println(doCreateString(input, 100000, true));
System.out.println(doCreateString(input, 1000000, true));
System.out.println(doCreateString(input, 2000000, true));
//System.out.println(doCreateString(input, 4000000, true));
//System.out.println(doCreateString(input, 8000000, true));
System.out.println(doCreateString(input, 10, false));
System.out.println(doCreateString(input, 100, false));
System.out.println(doCreateString(input, 1000, false));
System.out.println(doCreateString(input, 10000, false));
System.out.println(doCreateString(input, 100000, false));
System.out.println(doCreateString(input, 1000000, false));
System.out.println(doCreateString(input, 2000000, false));
//System.out.println(doCreateString(input, 4000000, false));
//System.out.println(doCreateString(input, 8000000, false));
}
private long doCreateString(String input, int size,
boolean defensiveCopyWorkAround) {
int loops = 10000;
byte[] inputBytes = input.getBytes();
byte[] bytes = new byte[size];
int inputLength = inputBytes.length;
System.arraycopy(inputBytes, 0, bytes, 0, inputLength);
String[] result = new String[loops];
Charset charset = StandardCharsets.ISO_8859_1;
long start = System.nanoTime();
for (int i = 0; i < loops; i++) {
if (defensiveCopyWorkAround) {
byte[] tmp = new byte[inputLength];
System.arraycopy(bytes, 0, tmp, 0, inputLength);
result[i] = new String(tmp, 0, inputLength, charset);
} else {
result[i] = new String(bytes, 0, inputLength, charset);
}
}
return System.nanoTime() - start;
}
}

View File

@@ -0,0 +1,358 @@
/*
* 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.http.parser;
import java.io.StringReader;
import java.util.List;
import java.util.Locale;
import org.junit.Assert;
import org.junit.Test;
public class TestAcceptLanguage {
private static final Locale L_EN = Locale.forLanguageTag("en");
private static final Locale L_EN_GB = Locale.forLanguageTag("en-gb");
private static final Locale L_FR = Locale.forLanguageTag("fr");
private static final double Q1_000 = 1;
private static final double Q0_500 = 0.5;
private static final double Q0_050 = 0.05;
@Test
public void testSingle01() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle02() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle03() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle04() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; "));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle05() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=1"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle06() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q=1"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle07() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q= 1"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle08() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q = 1"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle09() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q = 1 "));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle10() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q =\t1"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle11() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q =1\t"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle12() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb; q =\t1\t"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle13() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle14() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.50"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle15() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.500"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle16() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.5009"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testSingle17() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;,"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed01() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;x=1,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed02() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=a,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed03() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.5a,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed04() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.05a,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed05() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.005a,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed06() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=0.00005a,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed07() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en,,"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed08() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader(",en,"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed09() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader(",,en"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMalformed10() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en;q"));
Assert.assertEquals(0, actual.size());
}
@Test
public void testMalformed11() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=1a0"));
Assert.assertEquals(0, actual.size());
}
@Test
public void testMalformed12() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=1.a0"));
Assert.assertEquals(0, actual.size());
}
@Test
public void testMalformed13() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=1.0a0"));
Assert.assertEquals(0, actual.size());
}
@Test
public void testMalformed14() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=1.1"));
Assert.assertEquals(0, actual.size());
}
@Test
public void testMalformed15() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en-gb;q=1a0,en-gb;q=0.5"));
Assert.assertEquals(1, actual.size());
Assert.assertEquals(L_EN_GB, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
}
@Test
public void testMultiple01() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en,fr"));
Assert.assertEquals(2, actual.size());
Assert.assertEquals(L_EN, actual.get(0).getLocale());
Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001);
Assert.assertEquals(L_FR, actual.get(1).getLocale());
Assert.assertEquals(Q1_000, actual.get(1).getQuality(), 0.0001);
}
@Test
public void testMultiple02() throws Exception {
List<AcceptLanguage> actual = AcceptLanguage.parse(new StringReader("en; q= 0.05,fr;q=0.5"));
Assert.assertEquals(2, actual.size());
Assert.assertEquals(L_EN, actual.get(0).getLocale());
Assert.assertEquals(Q0_050, actual.get(0).getQuality(), 0.0001);
Assert.assertEquals(L_FR, actual.get(1).getLocale());
Assert.assertEquals(Q0_500, actual.get(1).getQuality(), 0.0001);
}
@Test
public void bug56848() throws Exception {
List<AcceptLanguage> actual =
AcceptLanguage.parse(new StringReader("zh-hant-CN;q=0.5,zh-hans-TW;q=0.05"));
Assert.assertEquals(2, actual.size());
Locale.Builder b = new Locale.Builder();
b.setLanguage("zh").setRegion("CN").setScript("hant");
Locale l1 = b.build();
b.clear().setLanguage("zh").setRegion("TW").setScript("hans");
Locale l2 = b.build();
Assert.assertEquals(l1, actual.get(0).getLocale());
Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001);
Assert.assertEquals(l2, actual.get(1).getLocale());
Assert.assertEquals(Q0_050, actual.get(1).getQuality(), 0.0001);
}
}

View File

@@ -0,0 +1,463 @@
/*
* 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.http.parser;
import java.io.StringReader;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
public class TestAuthorizationDigest {
@Test
public void testBug54060a() throws Exception {
String header = "Digest username=\"mthornton\", " +
"realm=\"optrak.com\", " +
"nonce=\"1351427243671:c1d6360150712149bae931a3ed7cb498\", " +
"uri=\"/files/junk.txt\", " +
"response=\"c5c2410bfc46753e83a8f007888b0d2e\", " +
"opaque=\"DB85C1A73933A7EB586D10E4BF2924EF\", " +
"qop=auth, " +
"nc=00000001, " +
"cnonce=\"9926cb3c334ede11\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("mthornton", result.get("username"));
Assert.assertEquals("optrak.com", result.get("realm"));
Assert.assertEquals("1351427243671:c1d6360150712149bae931a3ed7cb498",
result.get("nonce"));
Assert.assertEquals("/files/junk.txt", result.get("uri"));
Assert.assertEquals("c5c2410bfc46753e83a8f007888b0d2e",
result.get("response"));
Assert.assertEquals("DB85C1A73933A7EB586D10E4BF2924EF",
result.get("opaque"));
Assert.assertEquals("auth", result.get("qop"));
Assert.assertEquals("00000001", result.get("nc"));
Assert.assertEquals("9926cb3c334ede11", result.get("cnonce"));
}
@Test
public void testBug54060b() throws Exception {
String header = "Digest username=\"mthornton\", " +
"realm=\"optrak.com\", " +
"nonce=\"1351427480964:a01c16fed5168d72a2b5267395a2022e\", " +
"uri=\"/files\", " +
"algorithm=MD5, " +
"response=\"f310c44b87efc0bc0a7aab7096fd36b6\", " +
"opaque=\"DB85C1A73933A7EB586D10E4BF2924EF\", " +
"cnonce=\"MHg3ZjA3ZGMwMTUwMTA6NzI2OToxMzUxNDI3NDgw\", " +
"nc=00000001, " +
"qop=auth";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("mthornton", result.get("username"));
Assert.assertEquals("optrak.com", result.get("realm"));
Assert.assertEquals("1351427480964:a01c16fed5168d72a2b5267395a2022e",
result.get("nonce"));
Assert.assertEquals("/files", result.get("uri"));
Assert.assertEquals("MD5", result.get("algorithm"));
Assert.assertEquals("f310c44b87efc0bc0a7aab7096fd36b6",
result.get("response"));
Assert.assertEquals("DB85C1A73933A7EB586D10E4BF2924EF",
result.get("opaque"));
Assert.assertEquals("MHg3ZjA3ZGMwMTUwMTA6NzI2OToxMzUxNDI3NDgw",
result.get("cnonce"));
Assert.assertEquals("00000001", result.get("nc"));
Assert.assertEquals("auth", result.get("qop"));
}
@Test
public void testBug54060c() throws Exception {
String header = "Digest username=\"mthornton\", qop=auth";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("mthornton", result.get("username"));
Assert.assertEquals("auth", result.get("qop"));
}
@Test
public void testBug54060d() throws Exception {
String header = "Digest username=\"mthornton\"," +
"qop=auth," +
"cnonce=\"9926cb3c334ede11\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("mthornton", result.get("username"));
Assert.assertEquals("auth", result.get("qop"));
Assert.assertEquals("9926cb3c334ede11", result.get("cnonce"));
}
@Test
public void testEndWithLhex() throws Exception {
String header = "Digest nc=00000001";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("00000001", result.get("nc"));
}
@Test
public void testEndWithLhexReverse() throws Exception {
String header = "Digest nc=10000000";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("10000000", result.get("nc"));
}
@Test
public void testQuotedLhex() throws Exception {
String header = "Digest nc=\"09abcdef\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("09abcdef", result.get("nc"));
}
@Test
public void testQuotedLhexReverse() throws Exception {
String header = "Digest nc=\"fedcba90\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("fedcba90", result.get("nc"));
}
@Test
public void testLhex() throws Exception {
String header = "Digest nc=09abcdef";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("09abcdef", result.get("nc"));
}
@Test
public void testLhexReverse() throws Exception {
String header = "Digest nc=fedcba90";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("fedcba90", result.get("nc"));
}
@Test
public void testQuotedLhexUppercase() throws Exception {
String header = "Digest nc=\"00ABCDEF\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("00abcdef", result.get("nc"));
}
@Test
public void testQuotedLhexUppercaseReverse() throws Exception {
String header = "Digest nc=\"FEDCBA00\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("fedcba00", result.get("nc"));
}
@Test
public void testLhexUppercase() throws Exception {
String header = "Digest nc=00ABCDEF";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("00abcdef", result.get("nc"));
}
@Test
public void testLhexUppercaseReverse() throws Exception {
String header = "Digest nc=FEDCBA00";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("fedcba00", result.get("nc"));
}
@Test
public void testUnclosedQuotedLhex() throws Exception {
String header = "Digest nc=\"00000001";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testEmptyLhex() throws Exception {
String header = "Digest nc=";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testQuotedEmptyLhex() throws Exception {
String header = "Digest nc=\"\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testUnclosedQuotedString1() throws Exception {
String header = "Digest username=\"test";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testUnclosedQuotedString2() throws Exception {
String header = "Digest username=\"test\\";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testNonTokenDirective() throws Exception {
String header = "Digest user{name=\"test\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testTokenQop() throws Exception {
String header = "Digest qop=auth";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("auth", result.get("qop"));
}
@Test
public void testQuotedTokenQop() throws Exception {
String header = "Digest qop=\"auth\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("auth", result.get("qop"));
}
@Test
public void testEmptyQuotedTokenQop() throws Exception {
String header = "Digest qop=\"\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testNonTokenQop01() throws Exception {
String header = "Digest qop=au{th";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testNonTokenQop02() throws Exception {
String header = "Digest qop=auth{";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testQuotedNonTokenQop() throws Exception {
String header = "Digest qop=\"au{th\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testQuotedNonTokenQop2() throws Exception {
String header = "Digest qop=\"{auth\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testUnclosedQuotedTokenQop() throws Exception {
String header = "Digest qop=\"auth";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInToken() throws Exception {
String header = "Digest \u044f";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInToken2() throws Exception {
String header = "Digest qop=\u044f";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInQuotedToken() throws Exception {
String header = "Digest qop=\"\u044f\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInHex01() throws Exception {
String header = "Digest nc=\u044f";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInHex02() throws Exception {
String header = "Digest nc=aaa\u044f";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInHex03() throws Exception {
String header = "Digest nc=\u044faaa";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testWrongCharacterInQuotedHex() throws Exception {
String header = "Digest nc=\"\u044f\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertNull(result);
}
@Test
public void testParseAuthParamA() throws Exception {
// Test for HttpParser.readTokenOrQuotedString()
// auth-param = token "=" ( token | quoted-string )
String header = "Digest a=b";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("b", result.get("a"));
}
@Test
public void testParseAuthParamB() throws Exception {
// Test for HttpParser.readTokenOrQuotedString()
// auth-param = token "=" ( token | quoted-string )
String header = "Digest a=\"b\"";
StringReader input = new StringReader(header);
Map<String,String> result = Authorization.parseAuthorizationDigest(input);
Assert.assertEquals("b", result.get("a"));
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.http.parser;
import org.junit.Assert;
import org.junit.Test;
public class TestHttpParser {
@Test
public void testTokenDel() {
Assert.assertFalse("DEL is not a token", HttpParser.isToken(127));
}
}

View File

@@ -0,0 +1,255 @@
/*
* 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.http.parser;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class TestHttpParserHost {
private static final Class<? extends Exception> IAE = IllegalArgumentException.class;
@Parameter(0)
public TestType testType;
@Parameter(1)
public String input;
@Parameter(2)
public Integer expectedResult;
@Parameter(3)
public Class<? extends Exception> expectedException;
@Parameters(name="{index}: host {1}")
public static Collection<Object[]> inputs() {
List<Object[]> result = new ArrayList<>();
// IPv4 - valid
result.add(new Object[] { TestType.IPv4, "127.0.0.1", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "127.0.0.1:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.0:8080", Integer.valueOf(7), null} );
// IPv4 - invalid
result.add(new Object[] { TestType.IPv4, ".0.0.0", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "0..0.0", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "0]", Integer.valueOf(-1), IAE} );
// Domain Name - valid
result.add(new Object[] { TestType.IPv4, "0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0:8080", Integer.valueOf(3), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0:8080", Integer.valueOf(5), null} );
result.add(new Object[] { TestType.IPv4, "0.00.0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.00.0.0:8080", Integer.valueOf(8), null} );
result.add(new Object[] { TestType.IPv4, "256.0.0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "256.0.0.0:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "0.256.0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.256.0.0:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "0.0.256.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.256.0:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.256", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.256:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "0.a.0.0", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.a.0.0:8080", Integer.valueOf(7), null} );
result.add(new Object[] { TestType.IPv4, "localhost", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "localhost:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "tomcat.apache.org", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "tomcat.apache.org:8080", Integer.valueOf(17), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.com:8080", Integer.valueOf(9), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.0.com:8080", Integer.valueOf(11), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.com:8080", Integer.valueOf(11), null} );
result.add(new Object[] { TestType.IPv4, "1foo.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "1foo.0.0.com:8080", Integer.valueOf(12), null} );
result.add(new Object[] { TestType.IPv4, "foo1.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo1.0.0.com:8080", Integer.valueOf(12), null} );
result.add(new Object[] { TestType.IPv4, "1foo1.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "1foo1.0.0.com:8080", Integer.valueOf(13), null} );
result.add(new Object[] { TestType.IPv4, "1-foo.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "1-foo.0.0.com:8080", Integer.valueOf(13), null} );
result.add(new Object[] { TestType.IPv4, "1--foo.0.0.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "1--foo.0.0.com:8080", Integer.valueOf(14), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1com:8080", Integer.valueOf(12), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.com1", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.com1:8080", Integer.valueOf(12), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1com1", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1com1:8080", Integer.valueOf(13), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1-com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1-com:8080", Integer.valueOf(13), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1--com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.0.0.1--com:8080", Integer.valueOf(14), null} );
result.add(new Object[] { TestType.IPv4, "com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "com:8080", Integer.valueOf(3), null} );
result.add(new Object[] { TestType.IPv4, "0com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0com:8080", Integer.valueOf(4), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0com:8080", Integer.valueOf(8), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.0com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "0.0.0.0com:8080", Integer.valueOf(10), null} );
result.add(new Object[] { TestType.IPv4, "123", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "123:8080", Integer.valueOf(3), null} );
result.add(new Object[] { TestType.IPv4, "foo.bar.0com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.bar.0com:8080", Integer.valueOf(12), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.mydomain.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.mydomain.com:8080", Integer.valueOf(20), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.my-domain.com", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.my-domain.com:8080", Integer.valueOf(21), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.my-domain.c-om", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.my-domain.c-om:8080", Integer.valueOf(22), null} );
// Domain Name with trailing dot - valid
result.add(new Object[] { TestType.IPv4, "0.0.0.", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.mydomain.com.", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "myapp-t.mydomain.com.:8080", Integer.valueOf(21), null} );
result.add(new Object[] { TestType.IPv4, "foo.bar.", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv4, "foo.bar.:8080", Integer.valueOf(8), null} );
// Domain Name - invalid
result.add(new Object[] { TestType.IPv4, ".", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, ".:8080", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, ".foo.bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "-foo.bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo.bar-", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo.bar-:8080", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "^foo.bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo-.bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "f*oo.bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo..bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo.-bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo.^bar", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv4, "foo.b*ar", Integer.valueOf(-1), IAE} );
// IPv6 - valid
result.add(new Object[] { TestType.IPv6, "[::1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[::1]:8080", Integer.valueOf(5), null} );
result.add(new Object[] { TestType.IPv6, "[1::1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[1::1]:8080", Integer.valueOf(6), null} );
result.add(new Object[] { TestType.IPv6, "[A::A]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[A::A]:8080", Integer.valueOf(6), null} );
result.add(new Object[] { TestType.IPv6, "[A:0::A]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[A:0::A]:8080", Integer.valueOf(8), null} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:CDEF]",
Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:CDEF]:8080",
Integer.valueOf(41), null} );
result.add(new Object[] { TestType.IPv6, "[::5678:90AB:CDEF:1234:5678:90AB:CDEF]:8080",
Integer.valueOf(38), null} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB::]:8080",
Integer.valueOf(38), null} );
result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:0:0]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:0:0]:8080",
Integer.valueOf(17), null} );
result.add(new Object[] { TestType.IPv6, "[::127.0.0.1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[::127.0.0.1]:8080", Integer.valueOf(13), null} );
result.add(new Object[] { TestType.IPv6, "[1::127.0.0.1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[1::127.0.0.1]:8080", Integer.valueOf(14), null} );
result.add(new Object[] { TestType.IPv6, "[A::127.0.0.1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[A::127.0.0.1]:8080", Integer.valueOf(14), null} );
result.add(new Object[] { TestType.IPv6, "[A:0::127.0.0.1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[A:0::127.0.0.1]:8080", Integer.valueOf(16), null} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:127.0.0.1]",
Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:127.0.0.1]:8080",
Integer.valueOf(41), null} );
result.add(new Object[] { TestType.IPv6, "[::5678:90AB:CDEF:1234:5678:127.0.0.1]:8080",
Integer.valueOf(38), null} );
result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:127.0.0.1]", Integer.valueOf(-1), null} );
result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:127.0.0.1]:8080",
Integer.valueOf(23), null} );
result.add(new Object[] { TestType.IPv6, "[::1.2.3.4]", Integer.valueOf(-1), null} );
// IPv6 - invalid
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:127.0.0.1]",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:127.0.0.1",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[0::0::0]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[0:0:G:0:0:0:0:0]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[00000:0:0:0:0:0:0:0]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:]",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:CDEF",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[::127.00.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[0::0::127.0.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[0:0:G:0:0:0:127.0.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[00000:0:0:0:0:0:127.0.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1::127..0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1::127..0.1]:8080", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1::127.a.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1::127.a.0.1]:8080", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1::127.-.0.1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1::127.-.0.1]:8080", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[::1]'", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[:2222:3333:4444:5555:6666:7777:8888]",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1111:::3333:4444:5555:6666:7777:8888]",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "::1]", Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1111:2222:3333:4444:5555:6666:7777:8888:9999]",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1111:2222:3333:4444:5555:6666:7777:1.2.3.4]",
Integer.valueOf(-1), IAE} );
result.add(new Object[] { TestType.IPv6, "[1111:2222:3333]",
Integer.valueOf(-1), IAE} );
return result;
}
@Test
public void testHost() {
Class<? extends Exception> exceptionClass = null;
int result = -1;
try {
StringReader sr = new StringReader(input);
switch(testType) {
case IPv4:
result = HttpParser.readHostIPv4(sr, false);
break;
case IPv6:
result = HttpParser.readHostIPv6(sr);
break;
}
} catch (Exception e) {
exceptionClass = e.getClass();
}
Assert.assertEquals(input, expectedResult.intValue(), result);
if (expectedException == null) {
Assert.assertNull(input, exceptionClass);
} else {
Assert.assertNotNull(exceptionClass);
Assert.assertTrue(input, expectedException.isAssignableFrom(exceptionClass));
}
}
private static enum TestType {
IPv4,
IPv6
}
}

View File

@@ -0,0 +1,309 @@
/*
* 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.http.parser;
import java.io.IOException;
import java.io.StringReader;
import org.junit.Assert;
import org.junit.Test;
/**
* Unit tests for {@link HttpParser} focusing on media-type as defined in
* section 3.7 of RFC 2616.
*/
public class TestMediaType {
// Include whitespace to ensure Parser handles it correctly (it should be
// skipped).
private static final String TYPE = " foo ";
private static final String SUBTYPE = " bar ";
private static final String TYPES = TYPE + "/" + SUBTYPE;
private static final Parameter PARAM_TOKEN =
new Parameter("a", "b");
private static final Parameter PARAM_QUOTED =
new Parameter("x", "\"y\"");
private static final Parameter PARAM_EMPTY_QUOTED =
new Parameter("z", "\"\"");
private static final Parameter PARAM_COMPLEX_QUOTED =
new Parameter("w", "\"foo'bar,a=b;x=y\"");
private static final String CHARSET = "UTF-8";
private static final String WS_CHARSET = " \tUTF-8";
private static final String CHARSET_WS = "UTF-8 \t";
// Since this is quoted, it should retain the space at the end
private static final String CHARSET_QUOTED = "\"" + CHARSET_WS + "\"";
private static final Parameter PARAM_CHARSET =
new Parameter("charset", CHARSET);
private static final Parameter PARAM_WS_CHARSET =
new Parameter("charset", WS_CHARSET);
private static final Parameter PARAM_CHARSET_WS =
new Parameter("charset", CHARSET_WS);
private static final Parameter PARAM_CHARSET_QUOTED =
new Parameter("charset", CHARSET_QUOTED);
private static final String[] LWS_VALUES = new String[] {
"", " ", "\t", "\r", "\n", "\r\n", " \r", " \n", " \r\n",
"\r ", "\n ", "\r\n ", " \r ", " \n ", " \r\n " };
@Test
public void testSimple() throws IOException {
doTest();
}
@Test
public void testSimpleWithToken() throws IOException {
doTest(PARAM_TOKEN);
}
@Test
public void testSimpleWithQuotedString() throws IOException {
doTest(PARAM_QUOTED);
}
@Test
public void testSimpleWithEmptyQuotedString() throws IOException {
doTest(PARAM_EMPTY_QUOTED);
}
@Test
public void testSimpleWithComplexQuotedString() throws IOException {
doTest(PARAM_COMPLEX_QUOTED);
}
@Test
public void testSimpleWithCharset() throws IOException {
doTest(PARAM_CHARSET);
}
@Test
public void testSimpleWithCharsetWhitespaceBefore() throws IOException {
doTest(PARAM_WS_CHARSET);
}
@Test
public void testSimpleWithCharsetWhitespaceAfter() throws IOException {
doTest(PARAM_CHARSET_WS);
}
@Test
public void testSimpleWithCharsetQuoted() throws IOException {
doTest(PARAM_CHARSET_QUOTED);
}
@Test
public void testSimpleWithAll() throws IOException {
doTest(PARAM_COMPLEX_QUOTED, PARAM_EMPTY_QUOTED, PARAM_QUOTED,
PARAM_TOKEN, PARAM_CHARSET);
}
@Test
public void testCharset() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(TYPES);
sb.append(PARAM_CHARSET);
sb.append(PARAM_TOKEN);
StringReader sr = new StringReader(sb.toString());
MediaType m = MediaType.parseMediaType(sr);
Assert.assertEquals("foo/bar; charset=UTF-8; a=b", m.toString());
Assert.assertEquals(CHARSET, m.getCharset());
Assert.assertEquals("foo/bar; a=b", m.toStringNoCharset());
}
@Test
public void testCharsetQuoted() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(TYPES);
sb.append(PARAM_CHARSET_QUOTED);
StringReader sr = new StringReader(sb.toString());
MediaType m = MediaType.parseMediaType(sr);
Assert.assertEquals(CHARSET_WS, m.getCharset());
Assert.assertEquals(TYPES.replaceAll(" ", ""),
m.toStringNoCharset());
}
@Test
public void testBug52811() throws IOException {
String input = "multipart/related;boundary=1_4F50BD36_CDF8C28;" +
"Start=\"<31671603.smil>\";" +
"Type=\"application/smil;charset=UTF-8\"";
StringReader sr = new StringReader(input);
MediaType m = MediaType.parseMediaType(sr);
// Check the types
Assert.assertEquals("multipart", m.getType());
Assert.assertEquals("related", m.getSubtype());
// Check the parameters
Assert.assertTrue(m.getParameterCount() == 3);
Assert.assertEquals("1_4F50BD36_CDF8C28", m.getParameterValue("boundary"));
Assert.assertEquals("\"<31671603.smil>\"", m.getParameterValue("Start"));
Assert.assertEquals("\"application/smil;charset=UTF-8\"",
m.getParameterValue("Type"));
String expected = "multipart/related; boundary=1_4F50BD36_CDF8C28; " +
"start=\"<31671603.smil>\"; " +
"type=\"application/smil;charset=UTF-8\"";
Assert.assertEquals(expected, m.toString());
Assert.assertEquals(expected, m.toStringNoCharset());
Assert.assertNull(m.getCharset());
}
@Test
public void testBug53353() throws IOException {
String input = "text/html; UTF-8;charset=UTF-8";
StringReader sr = new StringReader(input);
MediaType m = MediaType.parseMediaType(sr);
// Check the types
Assert.assertEquals("text", m.getType());
Assert.assertEquals("html", m.getSubtype());
// Check the parameters
Assert.assertTrue(m.getParameterCount() == 2);
Assert.assertEquals("", m.getParameterValue("UTF-8"));
Assert.assertEquals("UTF-8", m.getCharset());
// Note: Invalid input is filtered out
Assert.assertEquals("text/html; charset=UTF-8", m.toString());
Assert.assertEquals("UTF-8", m.getCharset());
}
@Test
public void testBug55454() throws IOException {
String input = "text/html;;charset=UTF-8";
StringReader sr = new StringReader(input);
MediaType m = MediaType.parseMediaType(sr);
Assert.assertEquals("text", m.getType());
Assert.assertEquals("html", m.getSubtype());
Assert.assertTrue(m.getParameterCount() == 1);
Assert.assertEquals("UTF-8", m.getParameterValue("charset"));
Assert.assertEquals("UTF-8", m.getCharset());
Assert.assertEquals("text/html; charset=UTF-8", m.toString());
}
private void doTest(Parameter... parameters) throws IOException {
for (String lws : LWS_VALUES) {
doTest(lws, parameters);
}
}
private void doTest(String lws, Parameter... parameters)
throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(TYPES);
for (Parameter p : parameters) {
sb.append(p.toString(lws));
}
StringReader sr = new StringReader(sb.toString());
MediaType m = MediaType.parseMediaType(sr);
// Check all expected parameters are present
Assert.assertTrue(m.getParameterCount() == parameters.length);
// Check the types
Assert.assertEquals(TYPE.trim(), m.getType());
Assert.assertEquals(SUBTYPE.trim(), m.getSubtype());
// Check the parameters
for (int i = 0; i < parameters.length; i++) {
Assert.assertEquals(parameters[i].getValue().trim(),
m.getParameterValue(parameters[i].getName().trim()));
}
}
private static class Parameter {
private final String name;
private final String value;
public Parameter(String name,String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return toString("");
}
public String toString(String lws) {
StringBuilder sb = new StringBuilder();
sb.append(lws);
sb.append(";");
sb.append(lws);
sb.append(name);
sb.append(lws);
sb.append("=");
sb.append(lws);
sb.append(value);
sb.append(lws);
return sb.toString();
}
}
@Test
public void testCase() throws Exception {
StringReader sr = new StringReader("type/sub-type;a=1;B=2");
MediaType m = MediaType.parseMediaType(sr);
Assert.assertEquals("1", m.getParameterValue("A"));
Assert.assertEquals("1", m.getParameterValue("a"));
Assert.assertEquals("2", m.getParameterValue("B"));
Assert.assertEquals("2", m.getParameterValue("b"));
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.http.parser;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
public class TestTokenList {
@Test
public void testAll() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("*");
doTestVary("*", expected, true);
}
@Test
public void testSingle() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("host");
doTestVary("Host", expected, true);
}
@Test
public void testMultiple() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("Host, Foo, Bar", expected, true);
}
@Test
public void testEmptyString() throws IOException {
Set<String> s = Collections.emptySet();
doTestVary("", s, false);
}
@Test
public void testSingleInvalid() throws IOException {
Set<String> s = Collections.emptySet();
doTestVary("{{{", s, false);
}
@Test
public void testMultipleWithInvalidStart() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("{{{, Host, Foo, Bar", expected, false);
}
@Test
public void testMultipleWithInvalidMiddle() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("Host, {{{, Foo, Bar", expected, false);
}
@Test
public void testMultipleWithInvalidEnd() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("Host, Foo, Bar, {{{", expected, false);
}
@Test
public void testMultipleWithInvalidStart2() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("OK {{{, Host, Foo, Bar", expected, false);
}
@Test
public void testMultipleWithInvalidMiddle2() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("Host, OK {{{, Foo, Bar", expected, false);
}
@Test
public void testMultipleWithInvalidEnd2() throws IOException {
Set<String> expected = new HashSet<>();
expected.add("bar");
expected.add("foo");
expected.add("host");
doTestVary("Host, Foo, Bar, OK {{{", expected, false);
}
@SuppressWarnings("deprecation")
private void doTestVary(String input, Set<String> expectedTokens, boolean expectedResult) throws IOException {
StringReader reader = new StringReader(input);
Set<String> tokens = new HashSet<>();
Vary.parseVary(reader, tokens);
Assert.assertEquals(expectedTokens, tokens);
// Can't use reset(). Parser uses marks.
reader = new StringReader(input);
tokens.clear();
boolean result = TokenList.parseTokenList(reader, tokens);
Assert.assertEquals(expectedTokens, tokens);
Assert.assertEquals(Boolean.valueOf(expectedResult), Boolean.valueOf(result));
}
@Test
public void testMultipleHeadersValidWithoutNull() throws IOException {
doTestMultipleHeadersValid(false);
}
@Test
public void testMultipleHeadersValidWithNull() throws IOException {
doTestMultipleHeadersValid(true);
}
private void doTestMultipleHeadersValid(boolean withNull) throws IOException {
Set<String> expectedTokens = new HashSet<>();
expectedTokens.add("bar");
expectedTokens.add("foo");
expectedTokens.add("foo2");
Set<String> inputs = new HashSet<>();
inputs.add("foo");
if (withNull) {
inputs.add(null);
}
inputs.add("bar, foo2");
Set<String> tokens = new HashSet<>();
boolean result = TokenList.parseTokenList(Collections.enumeration(inputs), tokens);
Assert.assertEquals(expectedTokens, tokens);
Assert.assertTrue(result);
}
@Test
public void doTestMultipleHeadersInvalid() throws IOException {
Set<String> expectedTokens = new HashSet<>();
expectedTokens.add("bar");
expectedTokens.add("bar2");
expectedTokens.add("foo");
expectedTokens.add("foo2");
expectedTokens.add("foo3");
Set<String> inputs = new HashSet<>();
inputs.add("foo");
inputs.add("bar2, }}}, foo3");
inputs.add("bar, foo2");
Set<String> tokens = new HashSet<>();
boolean result = TokenList.parseTokenList(Collections.enumeration(inputs), tokens);
Assert.assertEquals(expectedTokens, tokens);
Assert.assertFalse(result);
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.http.parser;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.apache.tomcat.util.buf.MessageBytes;
@RunWith(Parameterized.class)
public class TesterHostPerformance {
@Parameters
public static Collection<Object[]> inputs() {
List<Object[]> result = new ArrayList<>();
result.add(new Object[] { "localhost" });
result.add(new Object[] { "tomcat.apache.org" });
result.add(new Object[] { "tomcat.apache.org." });
result.add(new Object[] { "127.0.0.1" });
result.add(new Object[] { "255.255.255.255" });
result.add(new Object[] { "[::1]" });
result.add(new Object[] { "[0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]" });
return result;
}
@Parameter(0)
public String hostname;
private static final int ITERATIONS = 100000000;
@Test
public void testParseHost() throws Exception {
long start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
Host.parse(hostname);
}
long time = System.nanoTime() - start;
System.out.println("St " + hostname + ": " + ITERATIONS + " iterations in " + time + "ns");
System.out.println("St " + hostname + ": " + ITERATIONS * 1000000000.0/time + " iterations per second");
MessageBytes mb = MessageBytes.newInstance();
mb.setString(hostname);
mb.toBytes();
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
Host.parse(mb);
}
time = System.nanoTime() - start;
System.out.println("MB " + hostname + ": " + ITERATIONS + " iterations in " + time + "ns");
System.out.println("MB " + hostname + ": " + ITERATIONS * 1000000000.0/time + " iterations per second");
}
}

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.net;
import org.junit.Assert;
import org.junit.Test;
/**
* Mostly examples from RFC 5952
*/
public class IPv6UtilsTest {
@Test
public void testMayBeIPv6Address() {
Assert.assertFalse(IPv6Utils.mayBeIPv6Address(null));
Assert.assertTrue(IPv6Utils.mayBeIPv6Address("::1"));
Assert.assertTrue(IPv6Utils.mayBeIPv6Address("::"));
Assert.assertTrue(IPv6Utils.mayBeIPv6Address("2001:db8:0:0:1:0:0:1"));
Assert.assertFalse(IPv6Utils.mayBeIPv6Address(""));
Assert.assertFalse(IPv6Utils.mayBeIPv6Address(":1"));
Assert.assertFalse(IPv6Utils.mayBeIPv6Address("123.123.123.123"));
Assert.assertFalse(IPv6Utils.mayBeIPv6Address("tomcat.eu.apache.org:443"));
}
@Test
public void testCanonize() {
Assert.assertNull(IPv6Utils.canonize(null));
Assert.assertEquals("", IPv6Utils.canonize(""));
// IPv4-safe
Assert.assertEquals("123.123.123.123", IPv6Utils.canonize("123.123.123.123"));
Assert.assertEquals("123.1.2.23", IPv6Utils.canonize("123.1.2.23"));
// Introductory RFC 5952 examples
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:db8:0:0:1:0:0:1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:0db8:0:0:1:0:0:1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:db8::1:0:0:1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:db8::0:1:0:0:1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:0db8::1:0:0:1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:db8:0:0:1::1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:db8:0000:0:1::1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:DB8:0:0:1::1"));
// Strip leading zeros (2.1)
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:0001"));
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:001"));
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:01"));
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:1"));
// Zero compression (2.2)
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:0:1", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd::1"));
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:0:1", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:0:1"));
Assert.assertEquals("2001:db8::1", IPv6Utils.canonize("2001:db8:0:0:0::1"));
Assert.assertEquals("2001:db8::1", IPv6Utils.canonize("2001:db8:0:0::1"));
Assert.assertEquals("2001:db8::1", IPv6Utils.canonize("2001:db8:0::1"));
Assert.assertEquals("2001:db8::1", IPv6Utils.canonize("2001:db8::1"));
Assert.assertEquals("2001:db8::aaaa:0:0:1", IPv6Utils.canonize("2001:db8::aaaa:0:0:1"));
Assert.assertEquals("2001:db8::aaaa:0:0:1", IPv6Utils.canonize("2001:db8:0:0:aaaa::1"));
// Uppercase or lowercase (2.3)
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa"));
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:AAAA"));
Assert.assertEquals("2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", IPv6Utils.canonize("2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa"));
// Some more zero compression for localhost addresses
Assert.assertEquals("::1", IPv6Utils.canonize("0:0:0:0:0:0:0:1"));
Assert.assertEquals("::1", IPv6Utils.canonize("0000:0:0:0:0:0:0:0001"));
Assert.assertEquals("::1", IPv6Utils.canonize("00:00:0:0:00:00:0:01"));
Assert.assertEquals("::1", IPv6Utils.canonize("::0001"));
Assert.assertEquals("::1", IPv6Utils.canonize("::1"));
// IPv6 unspecified address
Assert.assertEquals("::", IPv6Utils.canonize("0:0:0:0:0:0:0:0"));
Assert.assertEquals("::", IPv6Utils.canonize("0000:0:0:0:0:0:0:0000"));
Assert.assertEquals("::", IPv6Utils.canonize("00:00:0:0:00:00:0:00"));
Assert.assertEquals("::", IPv6Utils.canonize("::0000"));
Assert.assertEquals("::", IPv6Utils.canonize("::0"));
Assert.assertEquals("::", IPv6Utils.canonize("::"));
// Leading zeros (4.1)
Assert.assertEquals("2001:db8::1", IPv6Utils.canonize("2001:0db8::0001"));
// Shorten as much as possible (4.2.1)
Assert.assertEquals("2001:db8::2:1", IPv6Utils.canonize("2001:db8:0:0:0:0:2:1"));
Assert.assertEquals("2001:db8::", IPv6Utils.canonize("2001:db8:0:0:0:0:0:0"));
// Handling One 16-Bit 0 Field (4.2.2)
Assert.assertEquals("2001:db8:0:1:1:1:1:1", IPv6Utils.canonize("2001:db8:0:1:1:1:1:1"));
Assert.assertEquals("2001:db8:0:1:1:1:1:1", IPv6Utils.canonize("2001:db8::1:1:1:1:1"));
// Choice in Placement of "::" (4.2.3)
Assert.assertEquals("2001:0:0:1::1", IPv6Utils.canonize("2001:0:0:1:0:0:0:1"));
Assert.assertEquals("2001:db8::1:0:0:1", IPv6Utils.canonize("2001:db8:0:0:1:0:0:1"));
// IPv4 inside IPv6
Assert.assertEquals("::ffff:192.0.2.1", IPv6Utils.canonize("::ffff:192.0.2.1"));
Assert.assertEquals("::ffff:192.0.2.1", IPv6Utils.canonize("0:0:0:0:0:ffff:192.0.2.1"));
Assert.assertEquals("::192.0.2.1", IPv6Utils.canonize("::192.0.2.1"));
Assert.assertEquals("::192.0.2.1", IPv6Utils.canonize("0:0:0:0:0:0:192.0.2.1"));
// Zone ID
Assert.assertEquals("fe80::f0f0:c0c0:1919:1234%4", IPv6Utils.canonize("fe80::f0f0:c0c0:1919:1234%4"));
Assert.assertEquals("fe80::f0f0:c0c0:1919:1234%4", IPv6Utils.canonize("fe80:0:0:0:f0f0:c0c0:1919:1234%4"));
Assert.assertEquals("::%4", IPv6Utils.canonize("::%4"));
Assert.assertEquals("::%4", IPv6Utils.canonize("::0%4"));
Assert.assertEquals("::%4", IPv6Utils.canonize("0:0::0%4"));
Assert.assertEquals("::%4", IPv6Utils.canonize("0:0:0:0:0:0:0:0%4"));
Assert.assertEquals("::1%4", IPv6Utils.canonize("::1%4"));
Assert.assertEquals("::1%4", IPv6Utils.canonize("0:0::1%4"));
Assert.assertEquals("::1%4", IPv6Utils.canonize("0:0:0:0:0:0:0:1%4"));
Assert.assertEquals("::1%eth0", IPv6Utils.canonize("::1%eth0"));
Assert.assertEquals("::1%eth0", IPv6Utils.canonize("0:0::1%eth0"));
Assert.assertEquals("::1%eth0", IPv6Utils.canonize("0:0:0:0:0:0:0:1%eth0"));
// Hostname safety
Assert.assertEquals("www.apache.org", IPv6Utils.canonize("www.apache.org"));
Assert.assertEquals("ipv6.google.com", IPv6Utils.canonize("ipv6.google.com"));
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.net;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Assume;
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.ByteChunk;
/**
* The keys and certificates used in this file are all available in svn and were
* generated using a test CA the files for which are in the Tomcat PMC private
* repository since not all of them are AL2 licensed.
*/
public class TestClientCert extends TomcatBaseTest {
@Test
public void testClientCertGetWithoutPreemptive() throws Exception {
doTestClientCertGet(false);
}
@Test
public void testClientCertGetWithPreemptive() throws Exception {
doTestClientCertGet(true);
}
private void doTestClientCertGet(boolean preemptive) throws Exception {
Assume.assumeTrue("SSL renegotiation has to be supported for this test",
TesterSupport.isRenegotiationSupported(getTomcatInstance()));
if (preemptive) {
Tomcat tomcat = getTomcatInstance();
// Only one context deployed
Context c = (Context) tomcat.getHost().findChildren()[0];
// Enable pre-emptive auth
c.setPreemptiveAuthentication(true);
}
getTomcatInstance().start();
// Unprotected resource
ByteChunk res = getUrl("https://localhost:" + getPort() + "/unprotected");
int count = TesterSupport.getLastClientAuthRequestedIssuerCount();
if (log.isDebugEnabled()) {
log.debug("Last client KeyManager usage: " + TesterSupport.getLastClientAuthKeyManagerUsage() +
", " + count + " requested Issuers, first one: " +
(count > 0 ? TesterSupport.getLastClientAuthRequestedIssuer(0).getName() : "NONE"));
log.debug("Expected requested Issuer: " +
(preemptive ? TesterSupport.getClientAuthExpectedIssuer() : "NONE"));
}
if (preemptive) {
Assert.assertTrue("Checking requested client issuer against " +
TesterSupport.getClientAuthExpectedIssuer(),
TesterSupport.checkLastClientAuthRequestedIssuers());
Assert.assertEquals("OK-" + TesterSupport.ROLE, res.toString());
} else {
Assert.assertEquals(0, count);
Assert.assertEquals("OK", res.toString());
}
// Protected resource
res = getUrl("https://localhost:" + getPort() + "/protected");
if (log.isDebugEnabled()) {
count = TesterSupport.getLastClientAuthRequestedIssuerCount();
log.debug("Last client KeyManager usage: " + TesterSupport.getLastClientAuthKeyManagerUsage() +
", " + count + " requested Issuers, first one: " +
(count > 0 ? TesterSupport.getLastClientAuthRequestedIssuer(0).getName() : "NONE"));
log.debug("Expected requested Issuer: " + TesterSupport.getClientAuthExpectedIssuer());
}
Assert.assertTrue("Checking requested client issuer against " +
TesterSupport.getClientAuthExpectedIssuer(),
TesterSupport.checkLastClientAuthRequestedIssuers());
Assert.assertEquals("OK-" + TesterSupport.ROLE, res.toString());
}
@Test
public void testClientCertPostSmaller() throws Exception {
Tomcat tomcat = getTomcatInstance();
int bodySize = tomcat.getConnector().getMaxSavePostSize() / 2;
doTestClientCertPost(bodySize, false);
}
@Test
public void testClientCertPostSame() throws Exception {
Tomcat tomcat = getTomcatInstance();
int bodySize = tomcat.getConnector().getMaxSavePostSize();
doTestClientCertPost(bodySize, false);
}
@Test
public void testClientCertPostLarger() throws Exception {
Tomcat tomcat = getTomcatInstance();
int bodySize = tomcat.getConnector().getMaxSavePostSize() * 2;
doTestClientCertPost(bodySize, true);
}
private void doTestClientCertPost(int bodySize, boolean expectProtectedFail)
throws Exception {
Assume.assumeTrue("SSL renegotiation has to be supported for this test",
TesterSupport.isRenegotiationSupported(getTomcatInstance()));
getTomcatInstance().start();
byte[] body = new byte[bodySize];
Arrays.fill(body, TesterSupport.DATA);
// Unprotected resource
ByteChunk res = postUrl(body, "https://localhost:" + getPort() + "/unprotected");
int count = TesterSupport.getLastClientAuthRequestedIssuerCount();
if (log.isDebugEnabled()) {
log.debug("Last client KeyManager usage: " + TesterSupport.getLastClientAuthKeyManagerUsage() +
", " + count + " requested Issuers, first one: " +
(count > 0 ? TesterSupport.getLastClientAuthRequestedIssuer(0).getName() : "NONE"));
log.debug("Expected requested Issuer: NONE");
}
// Unprotected resource with no preemptive authentication
Assert.assertEquals(0, count);
// No authentication no need to buffer POST body during TLS handshake so
// no possibility of hitting buffer limit
Assert.assertEquals("OK-" + bodySize, res.toString());
// Protected resource
res.recycle();
int rc = postUrl(body, "https://localhost:" + getPort() + "/protected", res, null);
count = TesterSupport.getLastClientAuthRequestedIssuerCount();
if (log.isDebugEnabled()) {
log.debug("Last client KeyManager usage: " + TesterSupport.getLastClientAuthKeyManagerUsage() +
", " + count + " requested Issuers, first one: " +
(count > 0 ? TesterSupport.getLastClientAuthRequestedIssuer(0).getName() : "NONE"));
log.debug("Expected requested Issuer: " + TesterSupport.getClientAuthExpectedIssuer());
}
if (expectProtectedFail) {
Assert.assertEquals(401, rc);
// POST body buffer fails so TLS handshake never happens
Assert.assertEquals(0, count);
} else {
Assert.assertTrue("Checking requested client issuer against " +
TesterSupport.getClientAuthExpectedIssuer(),
TesterSupport.checkLastClientAuthRequestedIssuers());
Assert.assertEquals("OK-" + bodySize, res.toString());
}
}
@Override
public void setUp() throws Exception {
super.setUp();
Tomcat tomcat = getTomcatInstance();
TesterSupport.configureClientCertContext(tomcat);
TesterSupport.configureClientSsl();
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.net;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* The keys and certificates used in this file are all available in svn and were
* generated using a test CA the files for which are in the Tomcat PMC private
* repository since not all of them are AL2 licensed.
*
* The JSSE implementation of TLSv1.3 only supports authentication during the
* initial handshake. This test requires TLSv1.3 on client and server so it is
* skipped unless running on a Java version that supports TLSv1.3.
*/
public class TestClientCertTls13 extends TomcatBaseTest {
@Test
public void testClientCertGet() throws Exception {
Tomcat tomcat = getTomcatInstance();
tomcat.start();
ByteChunk res = getUrl("https://localhost:" + getPort() + "/protected");
Assert.assertEquals("OK-" + TesterSupport.ROLE, res.toString());
}
@Test
public void testClientCertPost() throws Exception {
Tomcat tomcat = getTomcatInstance();
tomcat.start();
int size = 32 * 1024;
byte[] body = new byte[size];
Arrays.fill(body, TesterSupport.DATA);
// Protected resource
ByteChunk res = new ByteChunk();
int rc = postUrl(body, "https://localhost:" + getPort() + "/protected", res, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK-" + size, res.toString());
}
@Override
public void setUp() throws Exception {
super.setUp();
Tomcat tomcat = getTomcatInstance();
Connector connector = tomcat.getConnector();
Assume.assumeTrue(TesterSupport.isDefaultTLSProtocolForTesting13(connector));
TesterSupport.configureClientCertContext(tomcat);
// Need to override some of the previous settings
Assert.assertTrue(tomcat.getConnector().setProperty("sslEnabledProtocols", Constants.SSL_PROTO_TLSv1_3));
// And add force authentication to occur on the initial handshake
Assert.assertTrue(tomcat.getConnector().setProperty("clientAuth", "required"));
TesterSupport.configureClientSsl();
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.net;
import java.io.File;
import java.net.SocketException;
import javax.net.ssl.SSLException;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
import org.apache.tomcat.util.net.jsse.TesterBug50640SslImpl;
import org.apache.tomcat.websocket.server.WsContextListener;
/**
* The keys and certificates used in this file are all available in svn and were
* generated using a test CA the files for which are in the Tomcat PMC private
* repository since not all of them are AL2 licensed.
*/
public class TestCustomSsl extends TomcatBaseTest {
private static enum TrustType {
ALL,
CA,
NONE
}
@Test
public void testCustomSslImplementation() throws Exception {
TesterSupport.configureClientSsl();
Tomcat tomcat = getTomcatInstance();
Connector connector = tomcat.getConnector();
Assume.assumeFalse("This test is only for JSSE based SSL connectors",
connector.getProtocolHandlerClassName().contains("Apr"));
SSLHostConfig sslHostConfig = new SSLHostConfig();
SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, Type.UNDEFINED);
sslHostConfig.addCertificate(certificate);
connector.addSslHostConfig(sslHostConfig);
Assert.assertTrue(connector.setProperty(
"sslImplementationName", "org.apache.tomcat.util.net.jsse.TesterBug50640SslImpl"));
// This setting will break ssl configuration unless the custom
// implementation is used.
sslHostConfig.setProtocols(TesterBug50640SslImpl.PROPERTY_VALUE);
sslHostConfig.setSslProtocol("tls");
File keystoreFile = new File(TesterSupport.LOCALHOST_RSA_JKS);
certificate.setCertificateKeystoreFile(keystoreFile.getAbsolutePath());
connector.setSecure(true);
Assert.assertTrue(connector.setProperty("SSLEnabled", "true"));
File appDir = new File(getBuildDirectory(), "webapps/examples");
Context ctxt = tomcat.addWebapp(
null, "/examples", appDir.getAbsolutePath());
ctxt.addApplicationListener(WsContextListener.class.getName());
tomcat.start();
ByteChunk res = getUrl("https://localhost:" + getPort() +
"/examples/servlets/servlet/HelloWorldExample");
Assert.assertTrue(res.toString().indexOf("<a href=\"../helloworld.html\">") > 0);
}
@Test
public void testCustomTrustManagerAll() throws Exception {
doTestCustomTrustManager(TrustType.ALL);
}
@Test
public void testCustomTrustManagerCA() throws Exception {
doTestCustomTrustManager(TrustType.CA);
}
@Test
public void testCustomTrustManagerNone() throws Exception {
doTestCustomTrustManager(TrustType.NONE);
}
private void doTestCustomTrustManager(TrustType trustType)
throws Exception {
Tomcat tomcat = getTomcatInstance();
Assume.assumeTrue("SSL renegotiation has to be supported for this test",
TesterSupport.isRenegotiationSupported(tomcat));
TesterSupport.configureClientCertContext(tomcat);
Connector connector = tomcat.getConnector();
// Override the defaults
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11JsseProtocol) {
connector.findSslHostConfigs()[0].setTruststoreFile(null);
} else {
// Unexpected
Assert.fail("Unexpected handler type");
}
if (trustType.equals(TrustType.ALL)) {
connector.findSslHostConfigs()[0].setTrustManagerClassName(
"org.apache.tomcat.util.net.TesterSupport$TrustAllCerts");
} else if (trustType.equals(TrustType.CA)) {
connector.findSslHostConfigs()[0].setTrustManagerClassName(
"org.apache.tomcat.util.net.TesterSupport$SequentialTrustManager");
}
// Start Tomcat
tomcat.start();
TesterSupport.configureClientSsl();
// Unprotected resource
ByteChunk res = getUrl("https://localhost:" + getPort() + "/unprotected");
Assert.assertEquals("OK", res.toString());
// Protected resource
res.recycle();
int rc = -1;
try {
rc = getUrl("https://localhost:" + getPort() + "/protected", res, null, null);
} catch (SocketException se) {
if (!trustType.equals(TrustType.NONE)) {
Assert.fail(se.getMessage());
se.printStackTrace();
}
} catch (SSLException he) {
if (!trustType.equals(TrustType.NONE)) {
Assert.fail(he.getMessage());
he.printStackTrace();
}
}
if (trustType.equals(TrustType.CA)) {
if (log.isDebugEnabled()) {
int count = TesterSupport.getLastClientAuthRequestedIssuerCount();
log.debug("Last client KeyManager usage: " + TesterSupport.getLastClientAuthKeyManagerUsage() +
", " + count + " requested Issuers, first one: " +
(count > 0 ? TesterSupport.getLastClientAuthRequestedIssuer(0).getName() : "NONE"));
log.debug("Expected requested Issuer: " + TesterSupport.getClientAuthExpectedIssuer());
}
Assert.assertTrue("Checking requested client issuer against " +
TesterSupport.getClientAuthExpectedIssuer(),
TesterSupport.checkLastClientAuthRequestedIssuers());
}
if (trustType.equals(TrustType.NONE)) {
Assert.assertTrue(rc != 200);
Assert.assertEquals("", res.toString());
} else {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK-" + TesterSupport.ROLE, res.toString());
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.net;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.net.openssl.OpenSSLConf;
import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
public class TestSSLHostConfig {
@Test
public void testCipher01() {
SSLHostConfig hc = new SSLHostConfig();
Cipher c = Cipher.TLS_RSA_WITH_NULL_MD5;
// Single JSSE name
hc.setCiphers(c.getJsseNames().iterator().next());
Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
}
@Test
public void testCipher02() {
SSLHostConfig hc = new SSLHostConfig();
Cipher c1 = Cipher.TLS_RSA_WITH_NULL_MD5;
Cipher c2 = Cipher.TLS_RSA_WITH_NULL_SHA;
// Two JSSE names
hc.setCiphers(c1.getJsseNames().iterator().next() + "," +
c2.getJsseNames().iterator().next());
Assert.assertEquals(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias(), hc.getCiphers());
}
@Test
public void testCipher03() {
SSLHostConfig hc = new SSLHostConfig();
// Single OpenSSL alias
hc.setCiphers("ALL");
Assert.assertEquals("ALL", hc.getCiphers());
}
@Test
public void testCipher04() {
SSLHostConfig hc = new SSLHostConfig();
Cipher c = Cipher.TLS_RSA_WITH_NULL_MD5;
// Single OpenSSLName name
hc.setCiphers(c.getOpenSSLAlias());
Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
}
@Test
public void testSerialization() throws IOException, ClassNotFoundException {
// Dummy OpenSSL command name/value pair
String name = "foo";
String value = "bar";
// Set up the object
SSLHostConfig sslHostConfig = new SSLHostConfig();
OpenSSLConf openSSLConf = new OpenSSLConf();
OpenSSLConfCmd openSSLConfCmd = new OpenSSLConfCmd();
openSSLConfCmd.setName(name);
openSSLConfCmd.setValue(value);
openSSLConf.addCmd(openSSLConfCmd);
sslHostConfig.setOpenSslConf(openSSLConf);
// Serialize
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(sslHostConfig);
oos.close();
// Deserialize
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
SSLHostConfig output = (SSLHostConfig) ois.readObject();
// Check values
List<OpenSSLConfCmd> commands = output.getOpenSslConf().getCommands();
Assert.assertEquals(1, commands.size());
OpenSSLConfCmd command = commands.get(0);
Assert.assertEquals(name, command.getName());
Assert.assertEquals(value, command.getValue());
}
}

View File

@@ -0,0 +1,349 @@
/*
* 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.net;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.net.SSLHostConfigCertificate.StoreType;
import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
import org.apache.tomcat.util.net.TesterSupport.ClientSSLSocketFactory;
/*
* Tests compatibility of JSSE and OpenSSL settings.
*/
@RunWith(Parameterized.class)
public class TestSSLHostConfigCompat extends TomcatBaseTest {
@Parameterized.Parameters(name = "{0}-{3}")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
for (StoreType storeType : new StoreType[] { StoreType.KEYSTORE, StoreType.PEM } ) {
parameterSets.add(new Object[] {"NIO-JSSE", "org.apache.coyote.http11.Http11NioProtocol",
"org.apache.tomcat.util.net.jsse.JSSEImplementation", storeType});
parameterSets.add(new Object[] {"NIO-OpenSSL", "org.apache.coyote.http11.Http11NioProtocol",
"org.apache.tomcat.util.net.openssl.OpenSSLImplementation", storeType});
parameterSets.add(new Object[] { "APR/Native", "org.apache.coyote.http11.Http11AprProtocol",
"org.apache.tomcat.util.net.openssl.OpenSSLImplementation", storeType});
}
return parameterSets;
}
@Parameter(0)
public String connectorName;
@Parameter(1)
public String protocolName;
@Parameter(2)
public String sslImplementationName;
@Parameter(3)
public StoreType storeType;
private SSLHostConfig sslHostConfig = new SSLHostConfig();
@Test
public void testHostEC() throws Exception {
configureHostEC();
doTest();
}
@Test
public void testHostRSA() throws Exception {
configureHostRSA();
doTest();
}
@Test
public void testHostRSAandECwithDefaultClient() throws Exception {
configureHostRSA();
configureHostEC();
doTest();
}
/*
* This test and the next just swap the order in which the server certs are
* configured to ensure correct operation isn't dependent on order.
*/
@Test
public void testHostRSAandECwithRSAClient() throws Exception {
configureHostRSA();
configureHostEC();
// Configure cipher suite that requires an RSA certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
/*
* This test and the previous just swap the order in which the server certs
* are configured to ensure correct operation isn't dependent on order.
*/
@Test
public void testHostECandRSAwithRSAClient() throws Exception {
configureHostEC();
configureHostRSA();
// Configure cipher suite that requires an RSA certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
/*
* This test and the next just swap the order in which the server certs are
* configured to ensure correct operation isn't dependent on order.
*/
@Test
public void testHostRSAandECwithECClient() throws Exception {
configureHostRSA();
configureHostEC();
// Configure cipher suite that requires an EC certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
/*
* This test and the previous just swap the order in which the server certs
* are configured to ensure correct operation isn't dependent on order.
*/
@Test
public void testHostECandRSAwithECClient() throws Exception {
configureHostEC();
configureHostRSA();
// Configure cipher suite that requires an EC certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
@Test
public void testHostRSAwithRSAClient() throws Exception {
configureHostRSA();
// Configure cipher suite that requires an RSA certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
@Test(expected=javax.net.ssl.SSLHandshakeException.class)
public void testHostRSAwithECClient() throws Exception {
configureHostRSA();
// Configure cipher suite that requires an EC certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
@Test
public void testHostRSAwithRSAandECClient() throws Exception {
configureHostRSA();
// Configure cipher suite that requires an EC certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
@Test(expected=javax.net.ssl.SSLHandshakeException.class)
public void testHostECwithRSAClient() throws Exception {
configureHostEC();
// Configure cipher suite that requires an RSA certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
@Test
public void testHostECwithECClient() throws Exception {
configureHostEC();
// Configure cipher suite that requires an EC certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
@Test
public void testHostECwithRSAandECClient() throws Exception {
configureHostEC();
// Configure cipher suite that requires an RSA certificate on the server
ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl();
clientSSLSocketFactory.setCipher(new String[] {
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"});
doTest(false);
}
private void configureHostRSA() {
switch (storeType) {
case KEYSTORE: {
SSLHostConfigCertificate sslHostConfigCertificateRsa = new SSLHostConfigCertificate(sslHostConfig, Type.RSA);
sslHostConfigCertificateRsa.setCertificateKeystoreFile(getPath(TesterSupport.LOCALHOST_RSA_JKS));
sslHostConfig.addCertificate(sslHostConfigCertificateRsa);
break;
}
case PEM: {
SSLHostConfigCertificate sslHostConfigCertificateRsa = new SSLHostConfigCertificate(sslHostConfig, Type.RSA);
sslHostConfigCertificateRsa.setCertificateFile(getPath(TesterSupport.LOCALHOST_RSA_CERT_PEM));
sslHostConfigCertificateRsa.setCertificateKeyFile(getPath(TesterSupport.LOCALHOST_RSA_KEY_PEM));
sslHostConfig.addCertificate(sslHostConfigCertificateRsa);
break;
}
}
}
private void configureHostEC() {
switch (storeType) {
case KEYSTORE: {
SSLHostConfigCertificate sslHostConfigCertificateEc = new SSLHostConfigCertificate(sslHostConfig, Type.EC);
sslHostConfigCertificateEc.setCertificateKeystoreFile(getPath(TesterSupport.LOCALHOST_EC_JKS));
sslHostConfig.addCertificate(sslHostConfigCertificateEc);
break;
}
case PEM: {
SSLHostConfigCertificate sslHostConfigCertificateEc = new SSLHostConfigCertificate(sslHostConfig, Type.EC);
sslHostConfigCertificateEc.setCertificateFile(getPath(TesterSupport.LOCALHOST_EC_CERT_PEM));
sslHostConfigCertificateEc.setCertificateKeyFile(getPath(TesterSupport.LOCALHOST_EC_KEY_PEM));
sslHostConfig.addCertificate(sslHostConfigCertificateEc);
break;
}
}
}
private void doTest() throws Exception {
// Use the default client TLS config
doTest(true);
}
private void doTest(boolean configureClientSsl) throws Exception {
if (configureClientSsl) {
TesterSupport.configureClientSsl();
}
Tomcat tomcat = getTomcatInstance();
tomcat.start();
// Check a request can be made
ByteChunk res = getUrl("https://localhost:" + getPort() + "/");
Assert.assertEquals("OK", res.toString());
}
@Override
protected String getProtocol() {
return protocolName;
}
@Override
public void setUp() throws Exception {
super.setUp();
AprLifecycleListener listener = new AprLifecycleListener();
Assume.assumeTrue(AprLifecycleListener.isAprAvailable());
Assume.assumeTrue(JreCompat.isJre8Available());
Tomcat tomcat = getTomcatInstance();
Connector connector = tomcat.getConnector();
connector.setPort(0);
connector.setScheme("https");
connector.setSecure(true);
Assert.assertTrue(connector.setProperty("SSLEnabled", "true"));
if (!connector.getProtocolHandlerClassName().contains("Apr")) {
// Skip this for APR. It is not supported.
Assert.assertTrue(connector.setProperty("sslImplementationName", sslImplementationName));
}
sslHostConfig.setProtocols("TLSv1.2");
connector.addSslHostConfig(sslHostConfig);
StandardServer server = (StandardServer) tomcat.getServer();
server.addLifecycleListener(listener);
// Simple webapp
Context ctxt = tomcat.addContext("", null);
Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet());
ctxt.addServletMappingDecoded("/*", "TesterServlet");
}
private static String getPath(String relativePath) {
File f = new File(relativePath);
return f.getAbsolutePath();
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.net;
import java.io.File;
import java.io.ObjectOutputStream;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.apache.tomcat.websocket.server.WsContextListener;
public class TestSSLHostConfigIntegration extends TomcatBaseTest {
@Test
public void testSslHostConfigIsSerializable() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
org.apache.catalina.Context ctxt = tomcat.addWebapp(
null, "/examples", appDir.getAbsolutePath());
ctxt.addApplicationListener(WsContextListener.class.getName());
TesterSupport.initSsl(tomcat);
tomcat.start();
SSLHostConfig[] sslHostConfigs =
tomcat.getConnector().getProtocolHandler().findSslHostConfigs();
boolean written = false;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
for (SSLHostConfig sslHostConfig : sslHostConfigs) {
oos.writeObject(sslHostConfig);
written = true;
}
}
Assert.assertTrue(written);
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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.net;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.websocket.server.WsContextListener;
/**
* The keys and certificates used in this file are all available in svn and were
* generated using a test CA the files for which are in the Tomcat PMC private
* repository since not all of them are AL2 licensed.
*/
public class TestSsl extends TomcatBaseTest {
@Test
public void testSimpleSsl() throws Exception {
TesterSupport.configureClientSsl();
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
org.apache.catalina.Context ctxt = tomcat.addWebapp(
null, "/examples", appDir.getAbsolutePath());
ctxt.addApplicationListener(WsContextListener.class.getName());
TesterSupport.initSsl(tomcat);
tomcat.start();
ByteChunk res = getUrl("https://localhost:" + getPort() +
"/examples/servlets/servlet/HelloWorldExample");
Assert.assertTrue(res.toString().indexOf("<a href=\"../helloworld.html\">") > 0);
Assert.assertTrue("Checking no client issuer has been requested",
TesterSupport.getLastClientAuthRequestedIssuerCount() == 0);
}
@Test
public void testKeyPass() throws Exception {
TesterSupport.configureClientSsl();
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
org.apache.catalina.Context ctxt = tomcat.addWebapp(
null, "/examples", appDir.getAbsolutePath());
ctxt.addApplicationListener(WsContextListener.class.getName());
TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS,
TesterSupport.JKS_PASS, TesterSupport.JKS_KEY_PASS);
tomcat.start();
ByteChunk res = getUrl("https://localhost:" + getPort() +
"/examples/servlets/servlet/HelloWorldExample");
Assert.assertTrue(res.toString().indexOf("<a href=\"../helloworld.html\">") > 0);
Assert.assertTrue("Checking no client issuer has been requested",
TesterSupport.getLastClientAuthRequestedIssuerCount() == 0);
}
@Test
public void testRenegotiateWorks() throws Exception {
Tomcat tomcat = getTomcatInstance();
Assume.assumeTrue("SSL renegotiation has to be supported for this test",
TesterSupport.isClientRenegotiationSupported(getTomcatInstance()));
Context root = tomcat.addContext("", TEMP_DIR);
Wrapper w =
Tomcat.addServlet(root, "tester", new TesterServlet());
w.setAsyncSupported(true);
root.addServletMappingDecoded("/", "tester");
TesterSupport.initSsl(tomcat);
tomcat.start();
SSLContext sslCtx;
if (TesterSupport.isDefaultTLSProtocolForTesting13(tomcat.getConnector())) {
// Force TLS 1.2 if TLS 1.3 is available as JSSE's TLS 1.3
// implementation doesn't support Post Handshake Authentication
// which is required for this test to pass.
sslCtx = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_2);
} else {
sslCtx = SSLContext.getInstance(Constants.SSL_PROTO_TLS);
}
sslCtx.init(null, TesterSupport.getTrustManagers(), null);
SSLSocketFactory socketFactory = sslCtx.getSocketFactory();
SSLSocket socket = (SSLSocket) socketFactory.createSocket("localhost",
getPort());
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
Reader r = new InputStreamReader(is);
doRequest(os, r);
Assert.assertTrue("Checking no client issuer has been requested",
TesterSupport.getLastClientAuthRequestedIssuerCount() == 0);
TesterHandshakeListener listener = new TesterHandshakeListener();
socket.addHandshakeCompletedListener(listener);
socket.startHandshake();
doRequest(os, r);
// Handshake complete appears to be called asynchronously
int wait = 0;
while (wait < 5000 && !listener.isComplete()) {
wait += 50;
Thread.sleep(50);
}
Assert.assertTrue("Checking no client issuer has been requested",
TesterSupport.getLastClientAuthRequestedIssuerCount() == 0);
Assert.assertTrue(listener.isComplete());
System.out.println("Renegotiation completed after " + wait + " ms");
}
private void doRequest(OutputStream os, Reader r) throws IOException {
char[] expectedResponseLine = "HTTP/1.1 200 \r\n".toCharArray();
os.write("GET /tester HTTP/1.1\r\n".getBytes());
os.write("Host: localhost\r\n".getBytes());
os.write("Connection: Keep-Alive\r\n\r\n".getBytes());
os.flush();
// First check we get the expected response line
for (char c : expectedResponseLine) {
int read = r.read();
Assert.assertEquals(c, read);
}
// Skip to the end of the headers
char[] endOfHeaders ="\r\n\r\n".toCharArray();
int found = 0;
while (found != endOfHeaders.length) {
if (r.read() == endOfHeaders[found]) {
found++;
} else {
found = 0;
}
}
// Read the body
char[] expectedBody = "OK".toCharArray();
for (char c : expectedBody) {
int read = r.read();
Assert.assertEquals(c, read);
}
}
private static class TesterHandshakeListener implements HandshakeCompletedListener {
private volatile boolean complete = false;
@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
complete = true;
}
public boolean isComplete() {
return complete;
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.net.TLSClientHelloExtractor.ExtractorResult;
public class TestTLSClientHelloExtractor {
@Test
public void testInputNeedRead01() throws IOException {
ByteBuffer testInput = ByteBuffer.allocate(1024);
doTestInputNeedRead(testInput);
}
@Test(expected=IOException.class)
public void testInputMalformed01() throws IOException {
ByteBuffer testInput = ByteBuffer.allocate(1024);
// TLS handshake
testInput.put((byte) 22);
// TLS 1.0
testInput.put((byte) 3);
testInput.put((byte) 1);
// Record length 0 (correct, but not legal)
testInput.put((byte) 0);
testInput.put((byte) 0);
doTestInputNeedRead(testInput);
}
@Test(expected=IOException.class)
public void testInputMalformed02() throws IOException {
ByteBuffer testInput = ByteBuffer.allocate(1024);
// TLS handshake
testInput.put((byte) 22);
// TLS 1.0
testInput.put((byte) 3);
testInput.put((byte) 1);
// Record length 4
testInput.put((byte) 0);
testInput.put((byte) 4);
// Type 1 (client hello)
testInput.put((byte) 1);
// Client hello size 0 (correct, but not legal)
testInput.put((byte) 0);
testInput.put((byte) 0);
testInput.put((byte) 0);
doTestInputNeedRead(testInput);
}
public void doTestInputMalformed(ByteBuffer input) throws IOException {
TLSClientHelloExtractor extractor = new TLSClientHelloExtractor(input);
// Expect this to fail
extractor.getResult();
}
public void doTestInputNeedRead(ByteBuffer input) throws IOException {
TLSClientHelloExtractor extractor = new TLSClientHelloExtractor(input);
// Expect this to fail
ExtractorResult result = extractor.getResult();
Assert.assertEquals(ExtractorResult.NEED_READ, result);
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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.net;
import java.io.File;
import java.net.InetAddress;
import java.net.ServerSocket;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.jni.Address;
import org.apache.tomcat.jni.Error;
import org.apache.tomcat.jni.Library;
import org.apache.tomcat.jni.OS;
import org.apache.tomcat.jni.Pool;
import org.apache.tomcat.jni.Socket;
/**
* Test case for the Endpoint implementations. The testing framework will ensure
* that each implementation is tested.
*/
public class TestXxxEndpoint extends TomcatBaseTest {
private long createAprPool() {
// Create the pool for the server socket
try {
return Pool.create(0);
} catch (UnsatisfiedLinkError e) {
log.error("Could not create socket pool", e);
return 0;
}
}
private long createAprSocket(int port, long pool)
throws Exception {
/**
* Server socket "pointer".
*/
long serverSock = 0;
String address = InetAddress.getByName("localhost").getHostAddress();
// Create the APR address that will be bound
int family = Socket.APR_INET;
if (Library.APR_HAVE_IPV6) {
if (!OS.IS_BSD && !OS.IS_WIN32 && !OS.IS_WIN64)
family = Socket.APR_UNSPEC;
}
long inetAddress = 0;
try {
inetAddress = Address.info(address, family,
port, 0, pool);
// Create the APR server socket
serverSock = Socket.create(Address.getInfo(inetAddress).family,
Socket.SOCK_STREAM,
Socket.APR_PROTO_TCP, pool);
} catch (Exception ex) {
log.error("Could not create socket for address '" + address + "'");
return 0;
}
if (OS.IS_UNIX) {
Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1);
}
// Deal with the firewalls that tend to drop the inactive sockets
Socket.optSet(serverSock, Socket.APR_SO_KEEPALIVE, 1);
// Bind the server socket
int ret = Socket.bind(serverSock, inetAddress);
if (ret != 0) {
log.error("Could not bind: " + Error.strerror(ret));
throw (new Exception(Error.strerror(ret)));
}
return serverSock;
}
private void destroyAprSocket(long serverSock, long pool) {
if (serverSock != 0) {
Socket.shutdown(serverSock, Socket.APR_SHUTDOWN_READWRITE);
Socket.close(serverSock);
Socket.destroy(serverSock);
}
if (pool != 0) {
Pool.destroy(pool);
pool = 0;
}
}
@Test
public void testStartStopBindOnInit() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
tomcat.start();
int port = getPort();
tomcat.getConnector().stop();
Exception e = null;
ServerSocket s = null;
long pool = 0;
long nativeSocket = 0;
boolean isApr = tomcat.getConnector().getProtocolHandlerClassName().contains("Apr");
try {
// This should throw an Exception
if (isApr) {
pool = createAprPool();
Assert.assertTrue(pool != 0);
nativeSocket = createAprSocket(port, pool);
Assert.assertTrue(nativeSocket != 0);
} else {
s = new ServerSocket(port, 100,
InetAddress.getByName("localhost"));
}
} catch (Exception e1) {
e = e1;
} finally {
try {
if (isApr) {
destroyAprSocket(nativeSocket, pool);
} else if (s != null) {
s.close();
}
} catch (Exception e2) { /* Ignore */ }
}
if (e != null) {
log.info("Exception was", e);
}
Assert.assertNotNull(e);
tomcat.getConnector().start();
}
@Test
public void testStartStopBindOnStart() throws Exception {
Tomcat tomcat = getTomcatInstance();
Connector c = tomcat.getConnector();
Assert.assertTrue(c.setProperty("bindOnInit", "false"));
File appDir = new File(getBuildDirectory(), "webapps/examples");
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
tomcat.start();
int port = getPort();
tomcat.getConnector().stop();
Exception e = null;
ServerSocket s = null;
long pool = 0;
long nativeSocket = 0;
boolean isApr = tomcat.getConnector().getProtocolHandlerClassName().contains("Apr");
try {
// This should not throw an Exception
if (isApr) {
pool = createAprPool();
Assert.assertTrue(pool != 0);
nativeSocket = createAprSocket(port, pool);
Assert.assertTrue(nativeSocket != 0);
} else {
s = new ServerSocket(port, 100,
InetAddress.getByName("localhost"));
}
} catch (Exception e1) {
e = e1;
} finally {
try {
if (isApr) {
destroyAprSocket(nativeSocket, pool);
} else if (s != null) {
s.close();
}
} catch (Exception e2) { /* Ignore */ }
}
Assert.assertNull(e);
tomcat.getConnector().start();
}
}

View File

@@ -0,0 +1,698 @@
/*
* 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.net;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Locale;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
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.apache.catalina.Context;
import org.apache.catalina.authenticator.SSLAuthenticator;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.jni.Library;
import org.apache.tomcat.jni.LibraryNotFoundError;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
public final class TesterSupport {
public static final String SSL_DIR = "test/org/apache/tomcat/util/net/";
public static final String CA_ALIAS = "ca";
public static final String CA_JKS = SSL_DIR + CA_ALIAS + ".jks";
public static final String CLIENT_ALIAS = "user1";
public static final String CLIENT_JKS = SSL_DIR + CLIENT_ALIAS + ".jks";
public static final String LOCALHOST_EC_JKS = SSL_DIR + "localhost-ec.jks";
public static final String LOCALHOST_RSA_JKS = SSL_DIR + "localhost-rsa.jks";
public static final String LOCALHOST_KEYPASS_JKS = SSL_DIR + "localhost-rsa-copy1.jks";
public static final String JKS_PASS = "changeit";
public static final String JKS_KEY_PASS = "tomcatpass";
public static final String CA_CERT_PEM = SSL_DIR + CA_ALIAS + "-cert.pem";
public static final String LOCALHOST_EC_CERT_PEM = SSL_DIR + "localhost-ec-cert.pem";
public static final String LOCALHOST_EC_KEY_PEM = SSL_DIR + "localhost-ec-key.pem";
public static final String LOCALHOST_RSA_CERT_PEM = SSL_DIR + "localhost-rsa-cert.pem";
public static final String LOCALHOST_RSA_KEY_PEM = SSL_DIR + "localhost-rsa-key.pem";
public static final boolean OPENSSL_AVAILABLE;
public static final int OPENSSL_VERSION;
public static final boolean TLSV13_AVAILABLE;
public static final String ROLE = "testrole";
private static String clientAuthExpectedIssuer;
private static String lastUsage = "NONE";
private static Principal[] lastRequestedIssuers = new Principal[0];
static {
boolean available = false;
int version = 0;
try {
Library.initialize(null);
available = true;
version = SSL.version();
Library.terminate();
} catch (Exception | LibraryNotFoundError ex) {
// Ignore
}
OPENSSL_AVAILABLE = available;
OPENSSL_VERSION = version;
available = false;
try {
SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3);
available = true;
} catch (NoSuchAlgorithmException ex) {
}
TLSV13_AVAILABLE = available;
}
public static boolean isOpensslAvailable() {
return OPENSSL_AVAILABLE;
}
public static int getOpensslVersion() {
return OPENSSL_VERSION;
}
public static boolean isTlsv13Available() {
return TLSV13_AVAILABLE;
}
public static void initSsl(Tomcat tomcat) {
initSsl(tomcat, LOCALHOST_RSA_JKS, null, null);
}
protected static void initSsl(Tomcat tomcat, String keystore,
String keystorePass, String keyPass) {
Connector connector = tomcat.getConnector();
connector.setSecure(true);
Assert.assertTrue(connector.setProperty("SSLEnabled", "true"));
SSLHostConfig sslHostConfig = new SSLHostConfig();
SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, Type.UNDEFINED);
sslHostConfig.addCertificate(certificate);
connector.addSslHostConfig(sslHostConfig);
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (!protocol.contains("Apr")) {
String sslImplementation = System.getProperty("tomcat.test.sslImplementation");
if (sslImplementation != null && !"${test.sslImplementation}".equals(sslImplementation)) {
StandardServer server = (StandardServer) tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
listener.setSSLRandomSeed("/dev/urandom");
server.addLifecycleListener(listener);
connector.setAttribute("sslImplementationName", sslImplementation);
}
sslHostConfig.setSslProtocol("tls");
certificate.setCertificateKeystoreFile(new File(keystore).getAbsolutePath());
sslHostConfig.setTruststoreFile(new File(CA_JKS).getAbsolutePath());
if (keystorePass != null) {
certificate.setCertificateKeystorePassword(keystorePass);
}
if (keyPass != null) {
certificate.setCertificateKeyPassword(keyPass);
}
} else {
certificate.setCertificateFile(new File(LOCALHOST_RSA_CERT_PEM).getAbsolutePath());
certificate.setCertificateKeyFile(new File(LOCALHOST_RSA_KEY_PEM).getAbsolutePath());
sslHostConfig.setCaCertificateFile(new File(CA_CERT_PEM).getAbsolutePath());
}
}
protected static KeyManager[] getUser1KeyManagers() throws Exception {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(getKeyStore(CLIENT_JKS), JKS_PASS.toCharArray());
KeyManager[] managers = kmf.getKeyManagers();
KeyManager manager;
for (int i=0; i < managers.length; i++) {
manager = managers[i];
if (manager instanceof X509ExtendedKeyManager) {
managers[i] = new TrackingExtendedKeyManager((X509ExtendedKeyManager)manager);
} else if (manager instanceof X509KeyManager) {
managers[i] = new TrackingKeyManager((X509KeyManager)manager);
}
}
return managers;
}
protected static TrustManager[] getTrustManagers() throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(getKeyStore(CA_JKS));
return tmf.getTrustManagers();
}
protected static ClientSSLSocketFactory configureClientSsl() {
ClientSSLSocketFactory clientSSLSocketFactory = null;
try {
SSLContext sc = SSLContext.getInstance(Constants.SSL_PROTO_TLS);
sc.init(TesterSupport.getUser1KeyManagers(),
TesterSupport.getTrustManagers(),
null);
clientSSLSocketFactory = new ClientSSLSocketFactory(sc.getSocketFactory());
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(clientSSLSocketFactory);
} catch (Exception e) {
e.printStackTrace();
}
return clientSSLSocketFactory;
}
private static KeyStore getKeyStore(String keystore) throws Exception {
File keystoreFile = new File(keystore);
KeyStore ks = KeyStore.getInstance("JKS");
try (InputStream is = new FileInputStream(keystoreFile)) {
ks.load(is, JKS_PASS.toCharArray());
}
return ks;
}
protected static boolean isMacOs() {
return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("mac os x");
}
protected static boolean isRenegotiationSupported(Tomcat tomcat) {
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (protocol.contains("Apr")) {
// Disabled by default in 1.1.20 windows binary (2010-07-27)
return false;
}
return true;
}
protected static boolean isClientRenegotiationSupported(Tomcat tomcat) {
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (protocol.contains("Apr")) {
// Disabled by default in 1.1.20 windows binary (2010-07-27)
return false;
}
if (protocol.contains("NioProtocol") || (protocol.contains("Nio2Protocol") && isMacOs())) {
// Doesn't work on all platforms - see BZ 56448.
return false;
}
String sslImplementation = System.getProperty("tomcat.test.sslImplementation");
if (sslImplementation != null && !"${test.sslImplementation}".equals(sslImplementation)) {
// Assume custom SSL is not supporting this
return false;
}
return true;
}
protected static void configureClientCertContext(Tomcat tomcat) {
TesterSupport.initSsl(tomcat);
/* When running on Java 11, TLSv1.3 is enabled by default. The JSSE
* implementation of TLSv1.3 does not support
* certificateVerification="optional", a setting on which these tests
* depend.
* Java 7 does not enable TLSv1.1 or TLS1.2 by default
*
* Ensure these tests pass with all JREs from Java 7 onwards.
*/
if (JreCompat.isJre8Available()) {
tomcat.getConnector().findSslHostConfigs()[0].setProtocols(Constants.SSL_PROTO_TLSv1_2);
} else {
tomcat.getConnector().findSslHostConfigs()[0].setProtocols(Constants.SSL_PROTO_TLSv1);
}
// Need a web application with a protected and unprotected URL
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "simple", new SimpleServlet());
ctx.addServletMappingDecoded("/unprotected", "simple");
ctx.addServletMappingDecoded("/protected", "simple");
// Security constraints
SecurityCollection collection = new SecurityCollection();
collection.addPatternDecoded("/protected");
SecurityConstraint sc = new SecurityConstraint();
sc.addAuthRole(ROLE);
sc.addCollection(collection);
ctx.addConstraint(sc);
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
// Get the CA subject the server should send us for client cert selection
try {
KeyStore ks = getKeyStore(CA_JKS);
X509Certificate cert = (X509Certificate)ks.getCertificate(CA_ALIAS);
clientAuthExpectedIssuer = cert.getSubjectDN().getName();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
String cn = "NOTFOUND";
try {
KeyStore ks = getKeyStore(CLIENT_JKS);
X509Certificate cert = (X509Certificate)ks.getCertificate(CLIENT_ALIAS);
cn = cert.getSubjectDN().getName();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
realm.addUser(cn, "not used");
realm.addUserRole(cn, ROLE);
ctx.setRealm(realm);
// Configure the authenticator
LoginConfig lc = new LoginConfig();
lc.setAuthMethod("CLIENT-CERT");
ctx.setLoginConfig(lc);
ctx.getPipeline().addValve(new SSLAuthenticator());
// Clear the tracking data
lastUsage = "NONE";
lastRequestedIssuers = new Principal[0];
}
protected static String getClientAuthExpectedIssuer() {
return clientAuthExpectedIssuer;
}
protected static void trackTrackingKeyManagers(@SuppressWarnings("unused") KeyManager wrapper,
@SuppressWarnings("unused") KeyManager wrapped, String usage, Principal[] issuers) {
lastUsage = usage;
lastRequestedIssuers = issuers;
}
protected static String getLastClientAuthKeyManagerUsage() {
return lastUsage;
}
protected static int getLastClientAuthRequestedIssuerCount() {
return lastRequestedIssuers == null ? 0 : lastRequestedIssuers.length;
}
protected static Principal getLastClientAuthRequestedIssuer(int index) {
return lastRequestedIssuers[index];
}
protected static boolean checkLastClientAuthRequestedIssuers() {
if (lastRequestedIssuers == null || lastRequestedIssuers.length != 1)
return false;
return (new X500Principal(clientAuthExpectedIssuer)).equals(
new X500Principal(lastRequestedIssuers[0].getName()));
}
public static final byte DATA = (byte)33;
public static class SimpleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.getWriter().print("OK");
if (req.isUserInRole(ROLE)) {
resp.getWriter().print("-" + ROLE);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Swallow any request body
int read = 0;
int len = 0;
byte[] buffer = new byte[4096];
InputStream is = req.getInputStream();
boolean contentOK = true;
while (len > -1) {
len = is.read(buffer);
read = read + len;
for (int i=0; i<len && contentOK; i++) {
contentOK = (buffer[i] == DATA);
}
}
// len will have been -1 on last iteration
read++;
// Report the number of bytes read
resp.setContentType("text/plain");
if (contentOK)
resp.getWriter().print("OK-" + read);
else
resp.getWriter().print("CONTENT-MISMATCH-" + read);
}
}
public static class TrackingKeyManager implements X509KeyManager {
private X509KeyManager manager = null;
public TrackingKeyManager(X509KeyManager manager) {
this.manager = manager;
}
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
trackTrackingKeyManagers(this, manager, "chooseClientAlias", issuers);
return manager.chooseClientAlias(keyType, issuers, socket);
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
trackTrackingKeyManagers(this, manager, "chooseServerAlias", issuers);
return manager.chooseServerAlias(keyType, issuers, socket);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return manager.getCertificateChain(alias);
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
trackTrackingKeyManagers(this, manager, "getClientAliases", issuers);
return manager.getClientAliases(keyType, issuers);
}
@Override
public PrivateKey getPrivateKey(String alias) {
return manager.getPrivateKey(alias);
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
trackTrackingKeyManagers(this, manager, "getServerAliases", issuers);
return manager.getServerAliases(keyType, issuers);
}
}
public static class TrackingExtendedKeyManager extends X509ExtendedKeyManager {
private X509ExtendedKeyManager manager = null;
public TrackingExtendedKeyManager(X509ExtendedKeyManager manager) {
this.manager = manager;
}
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
trackTrackingKeyManagers(this, manager, "chooseClientAlias", issuers);
return manager.chooseClientAlias(keyType, issuers, socket);
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
trackTrackingKeyManagers(this, manager, "chooseServerAlias", issuers);
return manager.chooseServerAlias(keyType, issuers, socket);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return manager.getCertificateChain(alias);
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
trackTrackingKeyManagers(this, manager, "getClientAliases", issuers);
return manager.getClientAliases(keyType, issuers);
}
@Override
public PrivateKey getPrivateKey(String alias) {
return manager.getPrivateKey(alias);
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
trackTrackingKeyManagers(this, manager, "getServerAliases", issuers);
return manager.getServerAliases(keyType, issuers);
}
@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
trackTrackingKeyManagers(this, manager, "chooseEngineClientAlias", issuers);
return manager.chooseEngineClientAlias(keyType, issuers, engine);
}
@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
trackTrackingKeyManagers(this, manager, "chooseEngineServerAlias", issuers);
return manager.chooseEngineServerAlias(keyType, issuers, engine);
}
}
public static class TrustAllCerts implements X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
// NOOP - Trust everything
}
@Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
// NOOP - Trust everything
}
}
public static class SequentialTrustManager implements X509TrustManager {
private static X509TrustManager[] tms;
private static X509Certificate[] certs;
static {
try {
TrustManager[] managers = getTrustManagers();
int mcount = 0;
int ccount = 0;
for (TrustManager tm : managers) {
if (tm instanceof X509TrustManager) {
mcount++;
ccount += ((X509TrustManager)tm).getAcceptedIssuers().length;
}
}
tms = new X509TrustManager[mcount];
certs = new X509Certificate[ccount];
mcount = 0;
ccount = 0;
for (TrustManager tm : managers) {
if (tm instanceof X509TrustManager) {
tms[mcount] = (X509TrustManager)tm;
mcount++;
for (X509Certificate cert : ((X509TrustManager)tm).getAcceptedIssuers()) {
certs[ccount] = cert;
ccount++;
}
}
}
} catch (Exception ex) {
tms = new X509TrustManager[1];
tms[0] = new TrustAllCerts();
certs = new X509Certificate[0];
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return certs;
}
@Override
public void checkClientTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
boolean trust = false;
for (X509TrustManager tm : tms) {
try {
tm.checkClientTrusted(certs, authType);
trust = true;
} catch (CertificateException ex) {
// Ignore
}
}
if (!trust) {
throw new CertificateException();
}
}
@Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
boolean trust = false;
for (X509TrustManager tm : tms) {
try {
tm.checkServerTrusted(certs, authType);
trust = true;
} catch (CertificateException ex) {
// Ignore
}
}
if (!trust) {
throw new CertificateException();
}
}
}
public static class ClientSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
private String[] ciphers = null;
public ClientSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
/**
* Forces the use of the specified cipher.
*
* @param ciphers Array of standard JSSE names of ciphers to use
*/
public void setCipher(String[] ciphers) {
this.ciphers = ciphers;
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
Socket result = delegate.createSocket(s, host, port, autoClose);
reconfigureSocket(result);
return result;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
Socket result = delegate.createSocket(host, port);
reconfigureSocket(result);
return result;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket result = delegate.createSocket(host, port);
reconfigureSocket(result);
return result;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
Socket result = delegate.createSocket(host, port, localHost, localPort);
reconfigureSocket(result);
return result;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
Socket result = delegate.createSocket(address, port, localAddress, localPort);
reconfigureSocket(result);
return result;
}
private Socket reconfigureSocket(Socket socket) {
if (ciphers != null) {
((SSLSocket) socket).setEnabledCipherSuites(ciphers);
}
return socket;
}
}
/*
* We want to use TLS 1.3 where we can but this requires TLS 1.3 to be
* supported on the client and the server.
*/
public static String getDefaultTLSProtocolForTesting(Connector connector) {
// Clients always use JSSE
if (!TLSV13_AVAILABLE) {
// Client doesn't support TLS 1.3 so we have to use TLS 1.2
return Constants.SSL_PROTO_TLSv1_2;
}
if (connector.getProtocolHandlerClassName().contains("Apr")) {
// APR connector so OpenSSL is used for TLS.
if (SSL.version() >= 0x1010100f) {
return Constants.SSL_PROTO_TLSv1_3;
} else {
return Constants.SSL_PROTO_TLSv1_2;
}
} else {
// NIO or NIO2. Tests do not use JSSE+OpenSSL so JSSE will be used.
// Due to check above, it is known that TLS 1.3 is available
return Constants.SSL_PROTO_TLSv1_3;
}
}
public static boolean isDefaultTLSProtocolForTesting13(Connector connector) {
return Constants.SSL_PROTO_TLSv1_3.equals(
TesterSupport.getDefaultTLSProtocolForTesting(connector));
}
}

View File

@@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIGpzCCBI+gAwIBAgIJAL51xu6EZW62MA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCTUExEjAQBgNVBAcTCVdha2VmaWVsZDEnMCUGA1UE
ChMeVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFj
aGUgVG9tY2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4X
DTE3MDgwODEwMzYzNloXDTI3MDgwNjEwMzYzNlowgZMxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUgQXBh
Y2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21jYXQg
UE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwggIiMA0GCSqGSIb3
DQEBAQUAA4ICDwAwggIKAoICAQCuokXuzdQ8HToRVcL07AdxHW9WmaMpcPWb/vyQ
dcuha+JXJ9Wu+d7fpxppeuxnjDmDCZNo0kimI7nYIEDMd3WVen75aoMZnQ7+vN/G
ZQXxzSPz2vzTZyEETAqs7DsGwO5CK2y5sWKl57+QCz/N+xM7EwOyNkmt+7xI1eQ+
z2sUNLRMK7abom8nm/wVftGAXIiribmTqukoxjr8dpEDg77VCy9eqe6kcil6Fvnr
mYrJqmrwzGldUlw4jqHl1IJnJ5z281vzzQ0U5ULeiuBpDGXcOHoaH8zYxilBVpPu
RDRBOcX17e5NouZtDTFemkJq5ns3PDt+WjJvuYNSELLBbnP+S1V6mt+MU9PsF6Td
lVZZxxFD9hPYqAzymwJGzTKbE8juZruQswL4iftyELmLPjIsetVtXifsUNay6CfD
r5sN6r+KLrhJWUqhii2mH1jx4cLmlf308TOc80TldvvI9cfrb596954cEE+7dlaU
vnRbBAeVNHNHl5e68fvwpKgtvQhtg1rZ2w1foSkAyyNRkYrUZKe4ztUx9E2w9qIm
3OkZyMcPTKYkBVahR6K1bCo69uaUrxY4NaYlPfKdJmGfio/J2WGdqLq9na4iHRyY
pb5zKvYmH9cNpmn5V42yhmX7tjMJzUyWw8KxXpE/qEVB2wl11wNguEL8CaZy+3u0
iaCqbQIDAQABo4H7MIH4MB0GA1UdDgQWBBQA8phNISwAPECbhPTeKvAm7jIOnzCB
yAYDVR0jBIHAMIG9gBQA8phNISwAPECbhPTeKvAm7jIOn6GBmaSBljCBkzELMAkG
A1UEBhMCVVMxCzAJBgNVBAgTAk1BMRIwEAYDVQQHEwlXYWtlZmllbGQxJzAlBgNV
BAoTHlRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbjEaMBgGA1UECxMRQXBh
Y2hlIFRvbWNhdCBQTUMxHjAcBgNVBAMTFUFwYWNoZSBUb21jYXQgVGVzdCBDQYIJ
AL51xu6EZW62MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAAJx4rzn
rtrcic/F0keS2BSazERFmRlSOlHUsREk+6fq35h7ktnijBHsPXyLEPa5w9x5qJZf
a2zFGiiiHqRBiX1E8JhKR6jjcX3D3VODfLomXWInHgEDcwNNcnvspG0RUX2jUh7m
p1i1c32r1s51P0AEB4zmT6KZ7gAZThqwtkpf6FQXmKdVXQFbf8EP0+6HFpHhV4lc
Ee4tDGJnc8X59Yzhu2rg8tF8OmNwcccTXthCH4I/4wymbw6YLg/B/V7AXH1/lui3
B15MKabYgZOU3TeOmQ9sqFPztekEKe+sE3Mvdf90Fh4EBZCENWULGUJE9uVJuT8S
2WVGOMmIkDlMP0t8Wnb3gMwUzhGyWp2FjzixVg8vS85ZE5wX4kGPD6nx+cAPDKrd
j3TCdr0VHoxVoGkzvijDjf6+aNhHp87VYSOZDQh1ToNgDFHum362iXt7n+ppu3u4
LDG3c1ztmUjgGrki+bQvnVyeYSprNWO1houo7xvZ61gWtzo1jwvcOwU0NxWtQMAg
NLZeketZSAL2834Xhkj1tjP2HT5HffkYbg6QRWKPYk/vBUKU40VilDCXf2ieOR9A
UtbcjjB5dRbR0CTnbwu33XeuhqobhaaAbp9gGt71WnOZpKIrkvVG3Z+YLpotRiYd
cl3dVVqvg/CTCpwd/VOOAmW1ynLpflLR8rH/
-----END CERTIFICATE-----

Binary file not shown.

View File

@@ -0,0 +1,45 @@
/*
* 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.net.jsse;
import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.net.Constants;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.SSLUtil;
public class TesterBug50640SslImpl extends JSSEImplementation {
public static final String PROPERTY_VALUE = "magic";
@Override
public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
SSLHostConfig sslHostConfig = certificate.getSSLHostConfig();
if (sslHostConfig.getProtocols().size() == 1 &&
sslHostConfig.getProtocols().contains(PROPERTY_VALUE)) {
if (JreCompat.isJre8Available()) {
sslHostConfig.setProtocols(Constants.SSL_PROTO_TLSv1_2);
} else {
sslHostConfig.setProtocols(Constants.SSL_PROTO_TLSv1);
}
return super.getSSLUtil(certificate);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,28 @@
================================================================================
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.
================================================================================
ca.jks (changeit)
ca CN=Apache Tomcat Test CA
localhost-rsa.jks (changeit)
tomcat CN=localhost
localhost-rsa-copy1.jks (changeit)
tomcat CN=localhost (tomcatpass)
user1.jks (changeit)
user1 CN=user1

View File

@@ -0,0 +1,86 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4098 (0x1002)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA
Validity
Not Before: Feb 15 19:32:18 2019 GMT
Not After : Feb 14 19:32:18 2021 GMT
Subject: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:10:cc:24:b7:0c:2a:fe:a6:af:ea:b2:dc:26:f1:
81:06:ae:0b:eb:f0:c0:5f:a3:ee:5a:e3:d3:7c:02:
b0:58:6c:47:0e:6e:08:ac:30:e1:76:e5:9c:06:80:
af:42:ce:a7:6f:49:b5:ec:95:08:b1:a9:e3:7a:f7:
84:4f:e2:05:60
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
F8:98:B3:3A:75:F3:09:EB:FF:CC:6E:26:39:F0:B5:FF:1F:0F:FB:01
X509v3 Authority Key Identifier:
keyid:00:F2:98:4D:21:2C:00:3C:40:9B:84:F4:DE:2A:F0:26:EE:32:0E:9F
Signature Algorithm: sha256WithRSAEncryption
66:3f:a4:8e:4b:e0:3c:a2:54:d3:8d:6a:6d:83:fe:02:13:a8:
79:41:55:68:33:7a:13:84:2f:92:db:aa:06:ab:4c:69:a7:fe:
47:2f:31:a0:16:e8:cb:df:a8:d7:b3:21:27:2b:51:e2:77:05:
65:40:17:40:ff:9c:b8:3c:9f:c7:bf:65:8e:00:6f:ce:01:6d:
30:37:84:96:bd:78:11:26:be:27:22:53:67:c8:ac:cb:04:cb:
e2:96:a3:9e:a3:16:af:bf:97:be:c6:3d:0a:0f:1d:e9:45:0b:
ea:77:47:a7:d5:79:b2:5a:bc:83:4c:8c:2a:ca:b7:4c:0c:d4:
17:d5:24:b1:b1:5b:2c:6e:59:5d:30:40:b5:72:6f:3a:b1:f4:
f9:0d:7e:b9:aa:99:26:19:21:b0:07:4d:49:c3:e7:c2:3d:c8:
98:62:cd:b6:d5:9a:21:f8:c7:b0:1a:72:59:02:80:0f:83:af:
d7:3b:8a:7e:53:38:8c:0d:e9:03:9d:c8:f9:1d:5c:82:7f:49:
8d:87:d3:89:69:a1:39:d3:fd:04:17:e5:63:af:55:02:ef:60:
d7:70:1d:60:6c:aa:53:43:13:f1:82:f6:b6:41:71:7b:38:ff:
82:78:73:73:11:e7:48:2f:f8:e8:77:27:7a:0f:a3:14:b0:33:
f9:aa:65:0c:8f:69:3b:2f:ee:b3:51:d6:5d:8a:67:80:47:1e:
a3:bd:d2:03:c3:62:45:1a:ac:dd:79:2e:84:a7:3d:8a:27:89:
c4:31:cc:1c:0b:37:a6:9d:a4:e4:65:03:8b:a3:5a:63:60:fb:
b9:7b:44:7f:8d:6a:74:9f:52:0e:b8:e7:12:52:98:5f:e9:34:
20:5a:f6:b7:15:a1:81:5e:f4:18:6c:18:c7:e8:dc:64:f8:d1:
a2:6f:98:a6:fd:36:e8:be:e7:a8:3f:a5:cb:de:1f:8f:ef:4a:
29:ee:69:f3:81:cd:ce:ec:5f:d7:b8:61:c1:41:4b:b0:49:5c:
29:eb:dd:e8:a6:54:4c:61:72:af:9c:50:da:16:1d:da:14:c9:
5f:8a:ae:2a:41:3b:9d:1e:72:7d:c8:eb:28:f2:a5:49:9b:ca:
0c:38:88:09:b3:5f:a9:83:13:6a:93:03:f9:3c:92:22:b8:cb:
ad:ba:dc:9b:6d:a6:9e:b0:d5:5a:57:ea:ae:f7:e9:8f:03:c2:
24:80:f8:50:21:94:7c:58:ac:b0:86:58:13:f2:d4:ef:f3:c1:
53:96:88:f9:dd:19:a7:83:fe:a9:d1:0a:1c:d0:10:23:6e:24:
47:41:3b:d4:dd:a1:06:2d:8a:ba:51:ef:34:e7:81:f0:94:51:
28:3a:44:8e:de:25:fa:e3
-----BEGIN CERTIFICATE-----
MIIESDCCAjCgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgZMxCzAJBgNVBAYTAlVT
MQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUg
QXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21j
YXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwHhcNMTkwMjE1
MTkzMjE4WhcNMjEwMjE0MTkzMjE4WjCBhzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
Ak1BMRIwEAYDVQQHDAlXYWtlZmllbGQxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29m
dHdhcmUgRm91bmRhdGlvbjEaMBgGA1UECwwRQXBhY2hlIFRvbWNhdCBQTUMxEjAQ
BgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBDMJLcM
Kv6mr+qy3CbxgQauC+vwwF+j7lrj03wCsFhsRw5uCKww4XblnAaAr0LOp29JteyV
CLGp43r3hE/iBWCjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5T
U0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBT4mLM6dfMJ6//MbiY5
8LX/Hw/7ATAfBgNVHSMEGDAWgBQA8phNISwAPECbhPTeKvAm7jIOnzANBgkqhkiG
9w0BAQsFAAOCAgEAZj+kjkvgPKJU041qbYP+AhOoeUFVaDN6E4QvktuqBqtMaaf+
Ry8xoBboy9+o17MhJytR4ncFZUAXQP+cuDyfx79ljgBvzgFtMDeElr14ESa+JyJT
Z8isywTL4pajnqMWr7+XvsY9Cg8d6UUL6ndHp9V5slq8g0yMKsq3TAzUF9UksbFb
LG5ZXTBAtXJvOrH0+Q1+uaqZJhkhsAdNScPnwj3ImGLNttWaIfjHsBpyWQKAD4Ov
1zuKflM4jA3pA53I+R1cgn9JjYfTiWmhOdP9BBflY69VAu9g13AdYGyqU0MT8YL2
tkFxezj/gnhzcxHnSC/46Hcneg+jFLAz+aplDI9pOy/us1HWXYpngEceo73SA8Ni
RRqs3XkuhKc9iieJxDHMHAs3pp2k5GUDi6NaY2D7uXtEf41qdJ9SDrjnElKYX+k0
IFr2txWhgV70GGwYx+jcZPjRom+Ypv026L7nqD+ly94fj+9KKe5p84HNzuxf17hh
wUFLsElcKevd6KZUTGFyr5xQ2hYd2hTJX4quKkE7nR5yfcjrKPKlSZvKDDiICbNf
qYMTapMD+TySIrjLrbrcm22mnrDVWlfqrvfpjwPCJID4UCGUfFissIZYE/LU7/PB
U5aI+d0Zp4P+qdEKHNAQI24kR0E71N2hBi2KulHvNOeB8JRRKDpEjt4l+uM=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0U7ZRpeTEzVDXCCP
oKwgWnN0tf7CMaE9dJmLIPpNgnChRANCAAQQzCS3DCr+pq/qstwm8YEGrgvr8MBf
o+5a49N8ArBYbEcObgisMOF25ZwGgK9CzqdvSbXslQixqeN694RP4gVg
-----END PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,109 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4102 (0x1006)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA
Validity
Not Before: Aug 7 20:30:28 2019 GMT
Not After : Aug 6 20:30:28 2021 GMT
Subject: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:cf:e2:56:a6:67:a6:e8:e7:f3:94:86:6e:f9:06:
46:cf:20:66:b5:cd:b1:c7:d6:50:ea:4d:46:44:ed:
45:65:ea:b6:9b:2e:49:a5:25:c1:8e:36:f6:2c:bc:
8e:09:35:0b:2f:43:70:73:07:47:1d:78:a1:12:e9:
56:5d:ab:84:15:16:0e:38:01:bb:81:87:2d:c4:3b:
dc:2e:4a:e1:d4:66:1b:ce:87:2c:a9:b8:e3:aa:80:
75:79:b1:98:f3:dd:df:66:d0:0d:e1:06:d8:6c:6c:
50:f0:00:80:32:70:55:7b:dd:eb:ae:f2:6a:bf:93:
3d:15:e1:25:f8:75:ce:d8:46:dc:c4:6b:ee:f9:f5:
93:39:ad:90:47:15:4b:fa:ca:5b:fe:ca:1b:29:8a:
74:19:2a:cb:1e:4f:20:d9:74:75:24:a0:06:d1:3a:
ed:9b:88:87:f3:1b:0f:a6:14:67:e9:ed:47:2e:a1:
25:6a:c2:97:04:13:f4:9f:62:38:cd:5a:e7:ad:c2:
64:2c:8f:9c:3d:04:58:12:42:e5:0c:8e:8c:ce:78:
3d:60:38:ce:06:ff:9c:ea:9c:c9:0f:73:90:b2:1a:
4a:16:99:c9:fe:95:88:7b:3c:7f:19:d0:26:27:11:
78:f9:92:5c:b4:f5:d4:cb:b0:84:0c:74:37:3d:87:
1a:0b
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
0D:86:88:1D:07:59:CE:14:B4:89:81:58:C6:0B:FF:4C:CA:25:52:80
X509v3 Authority Key Identifier:
keyid:00:F2:98:4D:21:2C:00:3C:40:9B:84:F4:DE:2A:F0:26:EE:32:0E:9F
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1
Signature Algorithm: sha256WithRSAEncryption
7d:dc:b1:0f:dd:34:df:26:63:73:02:8a:d6:39:64:73:c3:fc:
40:75:26:b6:9b:42:72:af:c9:63:41:68:d0:78:c7:47:ef:c2:
44:5a:b3:58:95:a3:2c:f3:b1:f4:a3:3d:0b:94:ff:b4:97:6a:
e9:4b:4b:c2:3a:f6:36:43:af:ee:2f:39:3e:f2:5f:2c:a2:b7:
43:3c:13:42:d8:4e:e0:36:bc:23:c5:43:88:46:92:f7:77:14:
67:73:14:5b:43:0e:3d:b5:1a:69:e9:ca:84:08:20:27:9f:23:
4d:60:db:cb:98:4a:b3:3e:71:e6:e8:a1:11:1c:7e:7e:43:fb:
6d:a5:41:c0:7e:3f:84:ed:06:28:dc:aa:80:17:76:ec:8a:e6:
65:45:21:85:13:48:e0:5b:87:c8:2a:1a:0f:37:0f:2a:64:53:
a8:e3:49:04:84:88:fe:8b:a2:3c:cc:41:c7:c0:ad:26:d6:e1:
67:69:9a:50:c7:eb:3d:1c:7f:da:88:08:24:14:6e:a1:ab:3e:
77:3f:88:12:55:98:97:9f:db:ad:09:e2:20:fe:8d:1f:ea:4f:
46:7e:d8:aa:ba:14:bd:a8:c2:6f:1b:47:62:d9:05:ca:c7:30:
7b:1e:95:2e:55:10:1d:b1:e3:44:95:07:25:6e:8c:9d:69:5b:
5c:ad:5f:56:27:e8:60:9f:d2:f4:64:7f:f7:8f:dc:bb:ee:bf:
be:0b:ea:34:9b:37:de:f0:5c:e0:64:c2:52:42:a6:0d:20:7d:
78:34:42:c1:1c:43:a1:98:e8:48:7b:92:49:2b:d9:63:91:6a:
70:02:d0:1b:a5:2a:ee:e5:1b:12:4f:cb:c9:e7:18:ae:66:f5:
04:d9:d2:68:95:c1:31:fe:57:9d:51:f5:fc:ed:43:3b:79:bf:
c3:9d:85:68:d8:98:a5:3c:a2:bb:fb:5b:19:5b:de:f0:7e:c8:
5e:47:ba:5d:8a:5b:44:f1:44:54:64:c0:da:95:a6:f0:bf:a9:
3f:5d:4c:72:97:86:ae:1e:0d:cd:20:4b:85:e0:4e:26:4d:29:
4e:96:43:b0:fd:30:5f:53:24:97:bc:35:d8:31:4b:6c:ea:a7:
f9:64:f9:cb:a0:14:c4:fc:54:78:13:52:b5:06:8f:7a:c2:00:
14:97:18:06:ef:bc:2f:2a:31:fc:11:25:7f:47:e3:3b:54:e7:
46:62:78:ba:52:07:32:41:48:9d:47:bd:1c:f4:eb:49:11:42:
40:9c:36:5a:e0:84:bd:09:44:91:bb:5c:d1:c4:28:6a:68:34:
f9:2c:22:b7:fc:43:bb:c4:96:02:ce:73:43:be:de:02:9c:e1:
d2:2a:4a:76:19:d6:3f:b0
-----BEGIN CERTIFICATE-----
MIIFZDCCA0ygAwIBAgICEAYwDQYJKoZIhvcNAQELBQAwgZMxCzAJBgNVBAYTAlVT
MQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUg
QXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21j
YXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwHhcNMTkwODA3
MjAzMDI4WhcNMjEwODA2MjAzMDI4WjCBhzELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
Ak1BMRIwEAYDVQQHEwlXYWtlZmllbGQxJzAlBgNVBAoTHlRoZSBBcGFjaGUgU29m
dHdhcmUgRm91bmRhdGlvbjEaMBgGA1UECxMRQXBhY2hlIFRvbWNhdCBQTUMxEjAQ
BgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AM/iVqZnpujn85SGbvkGRs8gZrXNscfWUOpNRkTtRWXqtpsuSaUlwY429iy8jgk1
Cy9DcHMHRx14oRLpVl2rhBUWDjgBu4GHLcQ73C5K4dRmG86HLKm446qAdXmxmPPd
32bQDeEG2GxsUPAAgDJwVXvd667yar+TPRXhJfh1zthG3MRr7vn1kzmtkEcVS/rK
W/7KGymKdBkqyx5PINl0dSSgBtE67ZuIh/MbD6YUZ+ntRy6hJWrClwQT9J9iOM1a
563CZCyPnD0EWBJC5QyOjM54PWA4zgb/nOqcyQ9zkLIaShaZyf6ViHs8fxnQJicR
ePmSXLT11MuwhAx0Nz2HGgsCAwEAAaOByzCByDAJBgNVHRMEAjAAMCwGCWCGSAGG
+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU
DYaIHQdZzhS0iYFYxgv/TMolUoAwHwYDVR0jBBgwFoAUAPKYTSEsADxAm4T03irw
Ju4yDp8wMQYIKwYBBQUHAQEEJTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAu
MC4xOjg4ODgwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEB
CwUAA4ICAQB93LEP3TTfJmNzAorWOWRzw/xAdSa2m0Jyr8ljQWjQeMdH78JEWrNY
laMs87H0oz0LlP+0l2rpS0vCOvY2Q6/uLzk+8l8sordDPBNC2E7gNrwjxUOIRpL3
dxRncxRbQw49tRpp6cqECCAnnyNNYNvLmEqzPnHm6KERHH5+Q/ttpUHAfj+E7QYo
3KqAF3bsiuZlRSGFE0jgW4fIKhoPNw8qZFOo40kEhIj+i6I8zEHHwK0m1uFnaZpQ
x+s9HH/aiAgkFG6hqz53P4gSVZiXn9utCeIg/o0f6k9GftiquhS9qMJvG0di2QXK
xzB7HpUuVRAdseNElQclboydaVtcrV9WJ+hgn9L0ZH/3j9y77r++C+o0mzfe8Fzg
ZMJSQqYNIH14NELBHEOhmOhIe5JJK9ljkWpwAtAbpSru5RsST8vJ5xiuZvUE2dJo
lcEx/ledUfX87UM7eb/DnYVo2JilPKK7+1sZW97wfsheR7pdiltE8URUZMDalabw
v6k/XUxyl4auHg3NIEuF4E4mTSlOlkOw/TBfUySXvDXYMUts6qf5ZPnLoBTE/FR4
E1K1Bo96wgAUlxgG77wvKjH8ESV/R+M7VOdGYni6UgcyQUidR70c9OtJEUJAnDZa
4IS9CUSRu1zRxChqaDT5LCK3/EO7xJYCznNDvt4CnOHSKkp2GdY/sA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDP4lamZ6bo5/OU
hm75BkbPIGa1zbHH1lDqTUZE7UVl6rabLkmlJcGONvYsvI4JNQsvQ3BzB0cdeKES
6VZdq4QVFg44AbuBhy3EO9wuSuHUZhvOhyypuOOqgHV5sZjz3d9m0A3hBthsbFDw
AIAycFV73euu8mq/kz0V4SX4dc7YRtzEa+759ZM5rZBHFUv6ylv+yhspinQZKsse
TyDZdHUkoAbROu2biIfzGw+mFGfp7UcuoSVqwpcEE/SfYjjNWuetwmQsj5w9BFgS
QuUMjozOeD1gOM4G/5zqnMkPc5CyGkoWmcn+lYh7PH8Z0CYnEXj5kly09dTLsIQM
dDc9hxoLAgMBAAECggEAXfOqO6yux6ZE9MRJFSzcBbJcGSBsj6dxjGL+NhqR+by5
aKrjx8qnjpGScqeI/epGMsck5CfO4SfqjDR+vvjMSgdcx70otCKW8ZAoM5fONoMr
YAzBh7cy1ZUXArfcK6MD22B+VUwVtfLCJaXkSmdwivnCEaAn1ItD2UaXNZJwuFeF
pT9Veif4MwfRhrVvHeEK+hEsrUePOOZ7bvfvIyd2pKtBuniiaoP9LIE2E/s+G458
xFZUHDxSgumEekZLv1mt03rcNDW9B9kLELaDuVbO4mQBn3edolvmL7N2iZpPIhM/
UDMqs7HfZ70bLcENa26UAZqIenbkueg7UkZgCF5RWQKBgQDxVTizo8vF+f7BhY8o
VSvGEhyRsJsyt9wb5AkVUCSM6Q+fKTimRBoDBQijDVc20uV5HpYIy35THphmXYX8
qIYNQiaGyLwBk6YWJDfqcfCjKBm6P7vtSFWjroRj2c0GjmXBZPk4sGYj16P4Qy00
ZssPsa+ENYgc4mox6szTp5Zp1wKBgQDchLRPaz1tWoLIlIl35a5GLHZwf6GGDeDI
2bxTMhBAohbjW9NSWf7GeZgeigRd7p5s6m2EriEJwsn61W0IWUzPLJ7iTHKOrVxU
tGxHd+SV3KhOkGn1EJ8zFiNIma/nAZraGW9zH/lhq09G8ygf1y3lSCQIABG8pAbK
xHmn3BoS7QKBgQCi0hSHXqNE1v4CItILLCt0XxPXV4feGB3w01Eth/yg9T0M7QrD
Yn8KOoMxPvbwjik0JmajWGfKPIIlzkNvy2Nl3pOPrC7sAWm01orDKkxoR83T0tw/
ouXkoQHBPFkPa1NLv4xFqv2+gOanwOrmx9OIqyD32gYTNs7fDsNSqWbZ0QKBgCup
WsoewZrVQO/V+SH0J/1c8FZ17tVMCiW6dr9COlWRwlZh6AV2LCvAB46EZTjz9go6
oFSU5ZW5K6SufVgZ1ktu2kaUPFpjmNRspMPByVCiz/A+R7xt/hdvWq0VQO7MMozc
XGS+//GGqbuyiU9Em6G6Fug+m0RudanQHQZPXhpBAoGBAIuWHrYCOWJRHDw8WdOE
811QFYHpMbYc0G4/50+O/1qKADWKbqAZpnbIW8NpHrcfggkgJw6E9kmtt8HbBu/3
NuCWK1K/0aLwQQMXqgrwuNYvk1QRXbAx86fbC1XVrY2KwmuCg6snXjJZqsTI91xm
jO0LxqN3mDyK11I9/XuearPH
-----END PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,137 @@
/*
* 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.net.openssl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.jni.SSLContext;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.TesterSupport;
public class TestOpenSSLConf extends TomcatBaseTest {
private static final String ENABLED_CIPHER = "AES256-SHA256";
private static final String[] EXPECTED_CIPHERS = {ENABLED_CIPHER};
private static final String[] ENABLED_PROTOCOLS = {"TLSv1.1"};
private static final String[] DISABLED_PROTOCOLS = {"SSLv3", "TLSv1", "TLSv1.2"};
private static final String[] DISABLED_PROTOCOLS_TLS13 = {"TLSv1.3"};
// Test behavior needs to adjust for OpenSSL 1.1.1-pre3 and above
private static final int OPENSSL_TLS13_SUPPORT_MIN_VERSION = 0x10101003;
private static int OPENSSL_VERSION = TesterSupport.getOpensslVersion();
private static boolean hasTLS13() {
return OPENSSL_VERSION >= OPENSSL_TLS13_SUPPORT_MIN_VERSION;
}
public SSLHostConfig initOpenSSLConfCmd(String... commands) throws Exception {
Assert.assertNotNull(commands);
Assert.assertTrue("Invalid length", commands.length % 2 == 0);
Tomcat tomcat = getTomcatInstance();
TesterSupport.initSsl(tomcat);
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
// The tests are only supported for APR and OpenSSL
if (!protocol.contains("Apr")) {
String sslImplementation = String.valueOf(
tomcat.getConnector().getProperty("sslImplementationName"));
Assume.assumeTrue("This test is only for OpenSSL based SSL connectors",
sslImplementation.contains("openssl"));
}
OpenSSLConf conf = new OpenSSLConf();
for (int i = 0; i < commands.length;) {
OpenSSLConfCmd cmd = new OpenSSLConfCmd();
cmd.setName(commands[i++]);
cmd.setValue(commands[i++]);
conf.addCmd(cmd);
}
SSLHostConfig[] sslHostConfigs = tomcat.getConnector().
getProtocolHandler().findSslHostConfigs();
Assert.assertEquals("Wrong SSLHostConfigCount", 1, sslHostConfigs.length);
sslHostConfigs[0].setOpenSslConf(conf);
tomcat.start();
sslHostConfigs = tomcat.getConnector().getProtocolHandler().findSslHostConfigs();
Assert.assertEquals("Wrong SSLHostConfigCount", 1, sslHostConfigs.length);
return sslHostConfigs[0];
}
@Test
public void testOpenSSLConfCmdCipher() throws Exception {
log.info("Found OpenSSL version 0x" + Integer.toHexString(OPENSSL_VERSION));
SSLHostConfig sslHostConfig;
if (hasTLS13()) {
// Ensure TLSv1.3 ciphers aren't returned
sslHostConfig = initOpenSSLConfCmd("CipherString", ENABLED_CIPHER,
"CipherSuites", "");
} else {
sslHostConfig = initOpenSSLConfCmd("CipherString", ENABLED_CIPHER);
}
String[] ciphers = sslHostConfig.getEnabledCiphers();
Assert.assertThat("Wrong HostConfig ciphers", ciphers,
CoreMatchers.is(EXPECTED_CIPHERS));
ciphers = SSLContext.getCiphers(sslHostConfig.getOpenSslContext().longValue());
Assert.assertThat("Wrong native SSL context ciphers", ciphers,
CoreMatchers.is(EXPECTED_CIPHERS));
}
@Test
public void testOpenSSLConfCmdProtocol() throws Exception {
log.info("Found OpenSSL version 0x" + Integer.toHexString(OPENSSL_VERSION));
Set<String> disabledProtocols = new HashSet<>(Arrays.asList(DISABLED_PROTOCOLS));
StringBuilder sb = new StringBuilder();
for (String protocol : DISABLED_PROTOCOLS) {
sb.append(",").append("-").append(protocol);
}
if (hasTLS13()) {
// Also disable TLSv1.3
for (String protocol : DISABLED_PROTOCOLS_TLS13) {
sb.append(",").append("-").append(protocol);
disabledProtocols.add(protocol);
}
}
for (String protocol : ENABLED_PROTOCOLS) {
sb.append(",").append(protocol);
}
SSLHostConfig sslHostConfig = initOpenSSLConfCmd("Protocol", sb.substring(1));
String[] protocols = sslHostConfig.getEnabledProtocols();
for (String protocol : protocols) {
Assert.assertFalse("Protocol " + protocol + " is not allowed",
disabledProtocols.contains(protocol));
}
Set<String> enabledProtocols = new HashSet<>(Arrays.asList(protocols));
for (String protocol : ENABLED_PROTOCOLS) {
Assert.assertTrue("Protocol " + protocol + " is not enabled",
enabledProtocols.contains(protocol));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,620 @@
/*
* 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.net.openssl.ciphers;
import java.util.List;
import java.util.TreeSet;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
public class TestOpenSSLCipherConfigurationParser {
@Test
public void testDEFAULT() throws Exception {
if (TesterOpenSSL.VERSION < 10100) {
// Account for classes of ciphers removed from DEFAULT in 1.1.0
testSpecification("DEFAULT:!RC4:!DSS:!SEED:!IDEA:!CAMELLIA:!AESCCM:!3DES");
} else {
testSpecification("DEFAULT");
}
}
@Test
public void testCOMPLEMENTOFDEFAULT() throws Exception {
if (TesterOpenSSL.VERSION < 10100) {
// Account for classes of ciphers removed from DEFAULT in 1.1.0
testSpecification("COMPLEMENTOFDEFAULT:RC4:DSS:SEED:IDEA:CAMELLIA:AESCCM:aNULL:3DES");
} else {
testSpecification("COMPLEMENTOFDEFAULT");
}
}
@Test
public void testALL() throws Exception {
testSpecification("ALL");
}
@Test
public void testCOMPLEMENTOFALL() throws Exception {
testSpecification("COMPLEMENTOFALL");
}
@Test
public void testaNULL() throws Exception {
testSpecification("aNULL");
}
@Test
public void testeNULL() throws Exception {
testSpecification("eNULL");
}
@Test
public void testHIGH() throws Exception {
testSpecification("HIGH");
}
@Test
public void testMEDIUM() throws Exception {
testSpecification("MEDIUM");
}
@Test
public void testLOW() throws Exception {
testSpecification("LOW");
}
@Test
public void testEXPORT40() throws Exception {
testSpecification("EXPORT40");
}
@Test
public void testEXPORT() throws Exception {
testSpecification("EXPORT");
}
@Test
public void testRSA() throws Exception {
testSpecification("RSA");
}
@Test
public void testaRSA() throws Exception {
testSpecification("aRSA");
}
@Test
public void testkRSA() throws Exception {
testSpecification("kRSA");
}
@Test
public void testkEDH() throws Exception {
testSpecification("kEDH");
}
@Test
public void testkDHE() throws Exception {
// This alias was introduced in 1.0.2
if (TesterOpenSSL.VERSION >= 10002) {
testSpecification("kDHE");
}
}
@Test
public void testEDH() throws Exception {
testSpecification("EDH");
}
@Test
public void testDHE() throws Exception {
// This alias was introduced in 1.0.2
if (TesterOpenSSL.VERSION >= 10002) {
testSpecification("DHE");
}
}
@Test
public void testkDHr() throws Exception {
testSpecification("kDHr");
}
@Test
public void testkDHd() throws Exception {
testSpecification("kDHd");
}
@Test
public void testkDH() throws Exception {
testSpecification("kDH");
}
@Test
public void testkECDHr() throws Exception {
testSpecification("kECDHr");
}
@Test
public void testkECDHe() throws Exception {
testSpecification("kECDHe");
}
@Test
public void testkECDH() throws Exception {
testSpecification("kECDH");
}
@Test
public void testkEECDH() throws Exception {
testSpecification("kEECDH");
}
@Test
public void testECDH() throws Exception {
testSpecification("ECDH");
}
@Test
public void testkECDHE() throws Exception {
testSpecification("kECDHE");
}
@Test
public void testECDHE() throws Exception {
testSpecification("ECDHE");
}
@Test
@Ignore("Contrary to the docs, OpenSSL does not recognise EECDHE")
public void testEECDHE() throws Exception {
testSpecification("EECDHE");
}
@Test
public void testAECDH() throws Exception {
testSpecification("AECDH");
}
@Test
public void testDSS() throws Exception {
testSpecification("DSS");
}
@Test
public void testaDSS() throws Exception {
testSpecification("aDSS");
}
@Test
public void testaDH() throws Exception {
testSpecification("aDH");
}
@Test
public void testaECDH() throws Exception {
testSpecification("aECDH");
}
@Test
public void testaECDSA() throws Exception {
testSpecification("aECDSA");
}
@Test
public void testECDSA() throws Exception {
testSpecification("ECDSA");
}
@Test
public void testkFZA() throws Exception {
testSpecification("kFZA");
}
@Test
public void testaFZA() throws Exception {
testSpecification("aFZA");
}
@Test
public void testeFZA() throws Exception {
testSpecification("eFZA");
}
@Test
public void testFZA() throws Exception {
testSpecification("FZA");
}
@Test
public void testTLSv1_2() throws Exception {
testSpecification("TLSv1.2");
}
@Test
public void testTLSv1() throws Exception {
// In OpenSSL 1.1.0-dev, TLSv1 refers to those ciphers that require
// TLSv1 rather than being an alias for SSLv3
if (TesterOpenSSL.VERSION >= 10100) {
testSpecification("TLSv1");
}
}
@Test
public void testSSLv2() throws Exception {
testSpecification("SSLv2");
}
@Test
public void testSSLv3() throws Exception {
// In OpenSSL 1.1.0-dev, TLSv1 refers to those ciphers that require
// TLSv1 rather than being an alias for SSLv3
if (TesterOpenSSL.VERSION < 10100) {
testSpecification("SSLv3:TLSv1");
}
}
@Test
public void testDH() throws Exception {
testSpecification("DH");
}
@Test
public void testADH() throws Exception {
testSpecification("ADH");
}
@Test
public void testAES128() throws Exception {
testSpecification("AES128");
}
@Test
public void testAES256() throws Exception {
testSpecification("AES256");
}
@Test
public void testAES() throws Exception {
testSpecification("AES");
}
@Test
public void testAESGCM() throws Exception {
testSpecification("AESGCM");
}
@Test
public void testAESCCM() throws Exception {
testSpecification("AESCCM");
}
@Test
public void testAESCCM8() throws Exception {
testSpecification("AESCCM8");
}
@Test
public void testCAMELLIA128() throws Exception {
testSpecification("CAMELLIA128");
}
@Test
public void testCAMELLIA256() throws Exception {
testSpecification("CAMELLIA256");
}
@Test
public void testCAMELLIA() throws Exception {
testSpecification("CAMELLIA");
}
@Test
public void testCHACHA20() throws Exception {
testSpecification("CHACHA20");
}
@Test
public void test3DES() throws Exception {
testSpecification("3DES");
}
@Test
public void testDES() throws Exception {
testSpecification("DES");
}
@Test
public void testRC4() throws Exception {
testSpecification("RC4");
}
@Test
public void testRC2() throws Exception {
testSpecification("RC2");
}
@Test
public void testIDEA() throws Exception {
testSpecification("IDEA");
}
@Test
public void testSEED() throws Exception {
testSpecification("SEED");
}
@Test
public void testMD5() throws Exception {
testSpecification("MD5");
}
@Test
public void testSHA1() throws Exception {
testSpecification("SHA1");
}
@Test
public void testSHA() throws Exception {
testSpecification("SHA");
}
@Test
public void testSHA256() throws Exception {
testSpecification("SHA256");
}
@Test
public void testSHA384() throws Exception {
testSpecification("SHA384");
}
@Test
public void testKRB5() throws Exception {
testSpecification("KRB5");
}
@Test
public void testaGOST() throws Exception {
testSpecification("aGOST");
}
@Test
public void testaGOST01() throws Exception {
testSpecification("aGOST01");
}
@Test
public void testaGOST94() throws Exception {
testSpecification("aGOST94");
}
@Test
public void testkGOST() throws Exception {
testSpecification("kGOST");
}
@Test
public void testGOST94() throws Exception {
testSpecification("GOST94");
}
@Test
public void testGOST89MAC() throws Exception {
testSpecification("GOST89MAC");
}
@Test
public void testaPSK() throws Exception {
testSpecification("aPSK");
}
@Test
public void testkPSK() throws Exception {
testSpecification("kPSK");
}
@Test
public void testkRSAPSK() throws Exception {
testSpecification("kRSAPSK");
}
@Test
public void testkECDHEPSK() throws Exception {
testSpecification("kECDHEPSK");
}
@Test
public void testkDHEPSK() throws Exception {
testSpecification("kDHEPSK");
}
@Test
public void testPSK() throws Exception {
testSpecification("PSK");
}
@Test
public void testARIA() throws Exception {
testSpecification("ARIA");
}
@Test
public void testARIA128() throws Exception {
testSpecification("ARIA128");
}
@Test
public void testARIA256() throws Exception {
testSpecification("ARIA256");
}
// TODO: Add tests for the individual operators
@Test
public void testSpecification01() throws Exception {
// Tomcat 8 default as of 2014-08-04
// This gets an A- from https://www.ssllabs.com/ssltest with no FS for
// a number of the reference browsers
testSpecification("HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5");
}
@Test
public void testSpecification02() throws Exception {
// Suggestion from dev list (s/ECDHE/kEECDH/, s/DHE/EDH/
testSpecification("!aNULL:!eNULL:!EXPORT:!DSS:!DES:!SSLv2:kEECDH:ECDH:EDH:AES256-GCM-SHA384:AES128-GCM-SHA256:+RC4:HIGH:aRSA:kECDHr:MEDIUM");
}
@Test
public void testSpecification03() throws Exception {
// Reported as failing during 8.0.11 release vote by Ognjen Blagojevic
// EDH was introduced in 1.0.0
testSpecification("EECDH+aRSA+SHA384:EECDH:EDH+aRSA:RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS");
}
private void testSpecification(String specification) throws Exception {
// Filter out cipher suites that OpenSSL does not implement
String openSSLCipherList = TesterOpenSSL.getOpenSSLCiphersAsExpression(specification);
List<String> jsseCipherListFromOpenSSL =
OpenSSLCipherConfigurationParser.parseExpression(openSSLCipherList);
List<String> jsseCipherListFromParser =
OpenSSLCipherConfigurationParser.parseExpression(specification);
TesterOpenSSL.removeUnimplementedCiphersJsse(jsseCipherListFromParser);
// First check the lists have the same entries
// Order is NOT important at this point. It is checked below.
Assert.assertEquals(
"Expected " + jsseCipherListFromParser.size() + " ciphers but got "
+ jsseCipherListFromOpenSSL.size() + " for the specification '"
+ specification + "'",
new TreeSet<>(jsseCipherListFromParser), new TreeSet<>(jsseCipherListFromOpenSSL));
// OpenSSL treats many ciphers as having equal preference. The order
// returned depends on the order they are requested. The following code
// checks that the Parser produces a cipher list that is consistent with
// OpenSSL's preference order by confirming that running through OpenSSL
// does not change the order.
String parserOrderedExpression = listToString(jsseCipherListFromParser, ',');
Assert.assertEquals(
listToString(OpenSSLCipherConfigurationParser.parseExpression(
parserOrderedExpression), ','),
parserOrderedExpression);
}
private String listToString(List<String> list, char separator) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String entry : list) {
if (first) {
first = false;
} else {
sb.append(separator);
}
sb.append(entry);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.net.openssl.ciphers;
import java.util.LinkedHashSet;
import org.junit.Assert;
import org.junit.Test;
/*
* The unit test is independent of OpenSSL version and does not require OpenSSL
* to be present.
*/
public class TestOpenSSLCipherConfigurationParserOnly {
@Test
public void testDefaultSort01() throws Exception {
// Reproducing a failure observed on Gump with OpenSSL 1.1.x
// Everything else being equal, AES is preferred
LinkedHashSet<Cipher> input = new LinkedHashSet<>();
input.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
input.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
input.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
input.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
expected.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
Assert.assertEquals(expected.toString(), result.toString());
}
@Test
public void testDefaultSort02() throws Exception {
// Reproducing a failure observed on Gump with OpenSSL 1.1.x
// ECHDE should beat AES
LinkedHashSet<Cipher> input = new LinkedHashSet<>();
input.add(Cipher.TLS_RSA_WITH_AES_256_CBC_SHA);
input.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
expected.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
expected.add(Cipher.TLS_RSA_WITH_AES_256_CBC_SHA);
Assert.assertEquals(expected.toString(), result.toString());
}
@Test
public void testDefaultSort03() throws Exception {
// Reproducing a failure observed on Gump with OpenSSL 1.1.x
// AES should beat CAMELLIA
LinkedHashSet<Cipher> input = new LinkedHashSet<>();
input.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
input.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
expected.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
Assert.assertEquals(expected.toString(), result.toString());
}
@Test
public void testRename01() throws Exception {
// EDH -> DHE
LinkedHashSet<Cipher> result =
OpenSSLCipherConfigurationParser.parse("EXP-EDH-DSS-DES-CBC-SHA");
LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
expected.add(Cipher.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
Assert.assertEquals(expected, result);
}
@Test
public void testCustomOrdering() throws Exception {
// https://bz.apache.org/bugzilla/show_bug.cgi?id=59081
LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.parse(
"ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" +
"DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DES-CBC3-SHA");
LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
expected.add(Cipher.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
expected.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
expected.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
expected.add(Cipher.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
Assert.assertEquals(expected.toString(), result.toString());
}
}

View File

@@ -0,0 +1,527 @@
/*
* 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.net.openssl.ciphers;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.catalina.util.IOTools;
public class TesterOpenSSL {
public static final int VERSION;
public static final Set<Cipher> OPENSSL_UNIMPLEMENTED_CIPHERS;
public static final Map<String,String> OPENSSL_RENAMED_CIPHERS;
static {
// Note: The following lists are intended to be aligned with the most
// recent release of each OpenSSL release branch. Running the unit
// tests with earlier releases is likely to result in failures.
String versionString = null;
try {
versionString = executeOpenSSLCommand("version");
} catch (IOException e) {
versionString = "";
}
if (versionString.startsWith("OpenSSL 3.0.0")) {
// Note: Gump currently tests 9.0.x with OpenSSL master
// (a.k.a 3.0.0-dev)
VERSION = 30000;
} else if (versionString.startsWith("OpenSSL 1.1.1")) {
// LTS
// Supported until at least 2023-09-11
VERSION = 10101;
} else if (versionString.startsWith("OpenSSL 1.1.0")) {
// Support ends 2019-09-11
VERSION = 10100;
} else if (versionString.startsWith("OpenSSL 1.0.2")) {
// LTS
// Support ends 2019-12-31
// Note: Gump current tests 8.5.x with OpenSSL 1.0.2
VERSION = 10002;
// Note: Release branches 1.0.1 and earlier are no longer supported by
// the OpenSSL team so these tests don't support them either.
} else {
VERSION = -1;
}
HashSet<Cipher> unimplemented = new HashSet<>();
// These have been removed from all supported versions.
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5);
unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA);
unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC4_56_SHA);
unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC4_56_MD5);
unimplemented.add(Cipher.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_anon_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_WITH_DES_CBC_SHA);
unimplemented.add(Cipher.SSL2_DES_64_CBC_WITH_MD5);
unimplemented.add(Cipher.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5);
unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_RC4_40_MD5);
unimplemented.add(Cipher.SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5);
unimplemented.add(Cipher.SSL_CK_RC2_128_CBC_WITH_MD5);
unimplemented.add(Cipher.SSL_CK_RC4_128_WITH_MD5);
unimplemented.add(Cipher.SSL2_RC4_128_EXPORT40_WITH_MD5);
unimplemented.add(Cipher.SSL2_IDEA_128_CBC_WITH_MD5);
unimplemented.add(Cipher.SSL2_DES_192_EDE3_CBC_WITH_MD5);
// These are TLS v1.3 cipher suites
// Java does not currently support these so they are excluded from the
// testing.
// Note: If OpenSSL is used then some of these may be available
// depending on the OpenSSL version used and the defaults for that
// version
unimplemented.add(Cipher.TLS_AES_128_CCM_8_SHA256);
unimplemented.add(Cipher.TLS_AES_128_CCM_SHA256);
unimplemented.add(Cipher.TLS_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_CHACHA20_POLY1305_SHA256);
if (VERSION < 10100) {
// These were implemented in 1.1.0 so won't be available in any
// earlier version
unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA384);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA256);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA384);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA256);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA384);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA384);
unimplemented.add(Cipher.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_RSA_WITH_AES_128_CCM);
unimplemented.add(Cipher.TLS_RSA_WITH_AES_256_CCM);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CCM);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CCM);
unimplemented.add(Cipher.TLS_RSA_WITH_AES_128_CCM_8);
unimplemented.add(Cipher.TLS_RSA_WITH_AES_256_CCM_8);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CCM_8);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CCM_8);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CCM);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CCM);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CCM);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CCM);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CCM_8);
unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CCM_8);
unimplemented.add(Cipher.TLS_PSK_DHE_WITH_AES_128_CCM_8);
unimplemented.add(Cipher.TLS_PSK_DHE_WITH_AES_256_CCM_8);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_128_CCM);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CCM);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_CHACHA20_POLY1305_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256);
} else {
// These were removed in 1.1.0 so won't be available from that
// version onwards.
unimplemented.add(Cipher.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_DSS_WITH_SEED_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA256);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_RSA_WITH_SEED_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_NULL_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_NULL_SHA);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384);
unimplemented.add(Cipher.TLS_RSA_WITH_RC4_128_MD5);
unimplemented.add(Cipher.TLS_DH_anon_WITH_RC4_128_MD5);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_RSA_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_RC4_128_SHA);
unimplemented.add(Cipher.TLS_ECDH_anon_WITH_RC4_128_SHA);
// 3DES requires a compile time switch to enable. Treat as removed.
unimplemented.add(Cipher.TLS_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
unimplemented.add(Cipher.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA);
}
if (VERSION < 10101) {
// These were implemented in 1.1.1 so won't be available in any
// earlier version
unimplemented.add(Cipher.TLS_RSA_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_RSA_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_PSK_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_PSK_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256);
unimplemented.add(Cipher.TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384);
}
String skipCiphers = System.getProperty("tomcat.test.openssl.unimplemented", "");
if (!skipCiphers.isEmpty()) {
String[] skip = skipCiphers.split(",");
for (Cipher c : Cipher.values()) {
for (String s : skip) {
if (c.toString().contains(s)) {
unimplemented.add(c);
}
}
}
}
OPENSSL_UNIMPLEMENTED_CIPHERS = Collections.unmodifiableSet(unimplemented);
Map<String,String> renamed = new HashMap<>();
renamed.put("ECDH-ECDSA-RC4-SHA+SSLv3", "ECDH-ECDSA-RC4-SHA+TLSv1");
renamed.put("ECDHE-ECDSA-NULL-SHA+SSLv3", "ECDHE-ECDSA-NULL-SHA+TLSv1");
renamed.put("ECDHE-ECDSA-DES-CBC3-SHA+SSLv3", "ECDHE-ECDSA-DES-CBC3-SHA+TLSv1");
renamed.put("ECDHE-ECDSA-AES128-SHA+SSLv3", "ECDHE-ECDSA-AES128-SHA+TLSv1");
renamed.put("ECDHE-ECDSA-AES256-SHA+SSLv3", "ECDHE-ECDSA-AES256-SHA+TLSv1");
renamed.put("ECDHE-RSA-NULL-SHA+SSLv3", "ECDHE-RSA-NULL-SHA+TLSv1");
renamed.put("ECDHE-RSA-RC4-SHA+SSLv3", "ECDHE-RSA-RC4-SHA+TLSv1");
renamed.put("ECDHE-RSA-DES-CBC3-SHA+SSLv3", "ECDHE-RSA-DES-CBC3-SHA+TLSv1");
renamed.put("ECDHE-RSA-AES128-SHA+SSLv3", "ECDHE-RSA-AES128-SHA+TLSv1");
renamed.put("ECDHE-RSA-AES256-SHA+SSLv3", "ECDHE-RSA-AES256-SHA+TLSv1");
renamed.put("AECDH-NULL-SHA+SSLv3", "AECDH-NULL-SHA+TLSv1");
renamed.put("AECDH-RC4-SHA+SSLv3", "AECDH-RC4-SHA+TLSv1");
renamed.put("AECDH-DES-CBC3-SHA+SSLv3", "AECDH-DES-CBC3-SHA+TLSv1");
renamed.put("AECDH-AES128-SHA+SSLv3", "AECDH-AES128-SHA+TLSv1");
renamed.put("AECDH-AES256-SHA+SSLv3", "AECDH-AES256-SHA+TLSv1");
renamed.put("ECDHE-PSK-RC4-SHA+SSLv3", "ECDHE-PSK-RC4-SHA+TLSv1");
renamed.put("ECDHE-PSK-3DES-EDE-CBC-SHA+SSLv3", "ECDHE-PSK-3DES-EDE-CBC-SHA+TLSv1");
renamed.put("ECDHE-PSK-AES128-CBC-SHA+SSLv3", "ECDHE-PSK-AES128-CBC-SHA+TLSv1");
renamed.put("ECDHE-PSK-AES256-CBC-SHA+SSLv3", "ECDHE-PSK-AES256-CBC-SHA+TLSv1");
renamed.put("ECDHE-PSK-NULL-SHA+SSLv3", "ECDHE-PSK-NULL-SHA+TLSv1");
OPENSSL_RENAMED_CIPHERS = Collections.unmodifiableMap(renamed);
}
private TesterOpenSSL() {
// Utility class. Hide default constructor.
}
public static Set<String> getOpenSSLCiphersAsSet(String specification) throws Exception {
String[] ciphers = getOpenSSLCiphersAsExpression(specification).trim().split(":");
Set<String> result = new HashSet<>(ciphers.length);
for (String cipher : ciphers) {
result.add(cipher);
}
return result;
}
public static String getOpenSSLCiphersAsExpression(String specification) throws Exception {
List<String> args = new ArrayList<>();
// Standard command to list the ciphers
args.add("ciphers");
args.add("-v");
if (VERSION < 10100) {
// Need to exclude the GOST ciphers
if (specification == null) {
specification = "DEFAULT:!aGOST";
} else {
specification = "!aGOST:" + specification;
}
}
if (VERSION >= 10101) {
// Need to exclude the TLSv1.3 ciphers
args.add("-ciphersuites");
args.add("");
}
// Include the specification if provided
if (specification != null) {
args.add(specification);
}
String stdout = executeOpenSSLCommand(args.toArray(new String[args.size()]));
if (stdout.length() == 0) {
return stdout;
}
StringBuilder output = new StringBuilder();
boolean first = true;
// OpenSSL should have returned one cipher per line
String ciphers[] = stdout.split("\n");
for (String cipher : ciphers) {
// Handle rename for 1.1.0 onwards
cipher = cipher.replaceAll("EDH", "DHE");
if (first) {
first = false;
} else {
output.append(':');
}
StringBuilder name = new StringBuilder();
// Name is first part
int i = cipher.indexOf(' ');
name.append(cipher.substring(0, i));
// Advance i past the space
while (Character.isWhitespace(cipher.charAt(i))) {
i++;
}
// Protocol is the second
int j = cipher.indexOf(' ', i);
name.append('+');
name.append(cipher.substring(i, j));
// More renames
if (OPENSSL_RENAMED_CIPHERS.containsKey(name.toString())) {
output.append(OPENSSL_RENAMED_CIPHERS.get(name.toString()));
} else {
output.append(name.toString());
}
}
return output.toString();
}
/*
* Use this method to filter parser results when comparing them to OpenSSL
* results to take account of unimplemented cipher suites.
*/
public static void removeUnimplementedCiphersJsse(List<String> list) {
for (Cipher cipher : OPENSSL_UNIMPLEMENTED_CIPHERS) {
for (String jsseName : cipher.getJsseNames()) {
list.remove(jsseName);
}
}
}
private static String executeOpenSSLCommand(String... args) throws IOException {
String openSSLPath = System.getProperty("tomcat.test.openssl.path");
String openSSLLibPath = null;
if (openSSLPath == null || openSSLPath.length() == 0) {
openSSLPath = "openssl";
} else {
// Explicit OpenSSL path may also need explicit lib path
// (e.g. Gump needs this)
openSSLLibPath = openSSLPath.substring(0, openSSLPath.lastIndexOf('/'));
openSSLLibPath = openSSLLibPath + "/../lib";
}
List<String> cmd = new ArrayList<>();
cmd.add(openSSLPath);
for (String arg : args) {
cmd.add(arg);
}
ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[cmd.size()]));
if (openSSLLibPath != null) {
Map<String,String> env = pb.environment();
String libraryPath = env.get("LD_LIBRARY_PATH");
if (libraryPath == null) {
libraryPath = openSSLLibPath;
} else {
libraryPath = libraryPath + ":" + openSSLLibPath;
}
env.put("LD_LIBRARY_PATH", libraryPath);
}
Process p = pb.start();
InputStreamToText stdout = new InputStreamToText(p.getInputStream());
InputStreamToText stderr = new InputStreamToText(p.getErrorStream());
Thread t1 = new Thread(stdout);
t1.setName("OpenSSL stdout reader");
t1.start();
Thread t2 = new Thread(stderr);
t2.setName("OpenSSL stderr reader");
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
throw new IOException(e);
}
String errorText = stderr.getText();
if (errorText.length() > 0) {
System.err.println(errorText);
}
return stdout.getText().trim();
}
private static class InputStreamToText implements Runnable {
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
private final InputStream is;
InputStreamToText(InputStream is) {
this.is = is;
}
@Override
public void run() {
try {
IOTools.flow(is, baos);
} catch (IOException e) {
// Ignore
}
}
public String getText() {
return baos.toString();
}
}
}

Binary file not shown.

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.res;
import org.junit.Assert;
import org.junit.Test;
public class TestStringManager {
private static final StringManager sm =
StringManager.getManager("org.apache.naming");
@Test
public void testNullKey() {
boolean iaeThrown = false;
try {
sm.getString(null);
} catch (IllegalArgumentException iae) {
iaeThrown = true;
}
Assert.assertTrue("IAE not thrown on null key", iaeThrown);
}
@Test
public void testBug46933() {
// Check null args are OK
sm.getString("namingContext.nameNotBound");
sm.getString("namingContext.nameNotBound", (Object[]) null);
sm.getString("namingContext.nameNotBound", new Object[1]);
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.scan;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.util.IOTools;
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
import org.apache.tomcat.Jar;
public class TestAbstractInputStreamJar {
@Before
public void register() {
TomcatURLStreamHandlerFactory.register();
}
@Test
public void testNestedJarGetInputStream() throws Exception {
File f = new File("test/webresources/war-url-connection.war");
StringBuilder sb = new StringBuilder("war:");
sb.append(f.toURI().toURL());
sb.append("*/WEB-INF/lib/test.jar");
Jar jar = JarFactory.newInstance(new URL(sb.toString()));
InputStream is1 = jar.getInputStream("META-INF/resources/index.html");
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
IOTools.flow(is1, baos1);
InputStream is2 = jar.getInputStream("META-INF/resources/index.html");
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
IOTools.flow(is2, baos2);
Assert.assertArrayEquals(baos1.toByteArray(), baos2.toByteArray());
}
}

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.scan;
import java.util.StringTokenizer;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestJarScanner extends TomcatBaseTest {
@Test
public void testJarsToSkipFormat() {
String jarList = System.getProperty(Constants.SKIP_JARS_PROPERTY);
Assert.assertNotNull("Jar skip list is null", jarList);
Assert.assertFalse("Jar skip list is empty", jarList.isEmpty());
StringTokenizer tokenizer = new StringTokenizer(jarList, ",");
String token;
while (tokenizer.hasMoreElements()) {
token = tokenizer.nextToken();
Assert.assertTrue("Token \"" + token + "\" does not end with \".jar\"",
token.endsWith(".jar"));
Assert.assertEquals("Token \"" + token + "\" contains sub string \".jar\"" +
" or separator \",\" is missing",
token.length() - ".jar".length(),
token.indexOf(".jar"));
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.scan;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.apache.tomcat.Jar;
import org.apache.tomcat.JarScanType;
import org.apache.tomcat.JarScannerCallback;
import org.apache.tomcat.unittest.TesterServletContext;
import org.apache.tomcat.util.compat.JreCompat;
public class TestStandardJarScanner {
@Test
public void testWebappClassPath() {
Assume.assumeFalse("No URLClassLoader with Java 9", JreCompat.isJre9Available());
StandardJarScanner scanner = new StandardJarScanner();
scanner.setScanClassPath(true);
// When running the test on Java 9, one or more URLs to jimage files may
// be returned. By setting the scanAllFiles option, a callback will be
// generated for these files which in turn will mean the number of URLs
// and the number of call backs will agree and this test will pass.
// There is a TODO in StandardJarScanner to add 'proper' Java 9 support.
scanner.setScanAllFiles(true);
LoggingCallback callback = new LoggingCallback();
scanner.scan(JarScanType.PLUGGABILITY, new TesterServletContext(), callback);
List<String> callbacks = callback.getCallbacks();
ClassLoader cl = TesterServletContext.class.getClassLoader();
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
int size;
if (urls == null) {
size = 0;
} else {
size = urls.length;
}
// Some JREs (Gump) construct a class path that includes JARs that
// reference additional JARs via the Class-Path attribute of the
// Manifest. These JARs are not returned in ClassLoader.getURLs().
// Therefore, this test looks for at least as many JARs as there are
// URLs but it can't check for an exact match.
Assert.assertTrue("[" + callbacks.size() + "] callbacks but expected at least [" +
size + "]", callbacks.size() >= size);
} else {
Assert.fail("Unexpected class loader type: " + cl.getClass().getName());
}
}
/**
* Tomcat should ignore URLs which do not have a file part and do not use the file scheme.
*/
@Test
public void skipsInvalidClasspathURLNoFilePartNoFileScheme() {
StandardJarScanner scanner = new StandardJarScanner();
LoggingCallback callback = new LoggingCallback();
TesterServletContext context = new TesterServletContext() {
@Override
public ClassLoader getClassLoader() {
URLClassLoader urlClassLoader;
try {
urlClassLoader = new URLClassLoader(
new URL[] { new URL("http://felix.extensions:9/") });
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
return urlClassLoader;
}
};
scanner.scan(JarScanType.PLUGGABILITY, context, callback);
}
private static class LoggingCallback implements JarScannerCallback {
List<String> callbacks = new ArrayList<>();
@Override
public void scan(Jar jar, String webappPath,
boolean isWebapp) throws IOException {
callbacks.add(jar.getJarFileURL().toString() + "::" + webappPath + "::" + isWebapp);
}
@Override
public void scan(File file, String webappPath, boolean isWebapp)
throws IOException {
callbacks.add(file.toString() + "::" + webappPath + "::" + isWebapp);
}
@Override
public void scanWebInfClasses() throws IOException {
callbacks.add("N/A::WEB-INF/classes::N/A");
}
public List<String> getCallbacks() {
return callbacks;
}
}
}

View File

@@ -0,0 +1,236 @@
/*
* 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.threads;
import org.junit.Assert;
import org.junit.Test;
public class TestLimitLatch {
// This should be plenty of time, even on slow systems.
private static final long THREAD_WAIT_TIME = 60000;
@Test
public void testNoThreads() throws Exception {
LimitLatch latch = new LimitLatch(0);
Assert.assertFalse("No threads should be waiting", latch.hasQueuedThreads());
}
@Test
public void testOneThreadNoWait() throws Exception {
LimitLatch latch = new LimitLatch(1);
Object lock = new Object();
checkWaitingThreadCount(latch, 0);
TestThread testThread = new TestThread(latch, lock);
testThread.start();
if (!waitForThreadToStart(testThread)) {
Assert.fail("Test thread did not start");
}
checkWaitingThreadCount(latch, 0);
if (!waitForThreadToStop(testThread, lock)) {
Assert.fail("Test thread did not stop");
}
checkWaitingThreadCount(latch, 0);
}
@Test
public void testOneThreadWaitCountDown() throws Exception {
LimitLatch latch = new LimitLatch(1);
Object lock = new Object();
checkWaitingThreadCount(latch, 0);
TestThread testThread = new TestThread(latch, lock);
latch.countUpOrAwait();
testThread.start();
if (!waitForThreadToStart(testThread)) {
Assert.fail("Test thread did not start");
}
checkWaitingThreadCount(latch, 1);
latch.countDown();
if (!waitForThreadToStop(testThread, lock)) {
Assert.fail("Test thread did not stop");
}
checkWaitingThreadCount(latch, 0);
}
@Test
public void testOneRelease() throws Exception {
LimitLatch latch = new LimitLatch(1);
Object lock = new Object();
checkWaitingThreadCount(latch, 0);
TestThread testThread = new TestThread(latch, lock);
latch.countUpOrAwait();
testThread.start();
if (!waitForThreadToStart(testThread)) {
Assert.fail("Test thread did not start");
}
checkWaitingThreadCount(latch, 1);
latch.releaseAll();
if (!waitForThreadToStop(testThread, lock)) {
Assert.fail("Test thread did not stop");
}
checkWaitingThreadCount(latch, 0);
}
@Test
public void testTenWait() throws Exception {
LimitLatch latch = new LimitLatch(10);
Object lock = new Object();
checkWaitingThreadCount(latch, 0);
TestThread[] testThreads = new TestThread[30];
for (int i = 0; i < 30; i++) {
testThreads[i] = new TestThread(latch, lock);
testThreads[i].start();
}
// Should have 10 threads in stage 2 and 20 in stage 1
for (int i = 0; i < 30; i++) {
if (!waitForThreadToStart(testThreads[i])) {
Assert.fail("Test thread [" + i + "] did not start");
}
}
if (!waitForThreadsToReachStage(testThreads, 20, 10, 0)) {
Assert.fail("Failed at 20-10-00");
}
checkWaitingThreadCount(latch, 20);
synchronized (lock) {
lock.notifyAll();
}
if (!waitForThreadsToReachStage(testThreads, 10, 10, 10)) {
Assert.fail("Failed at 10-10-10");
}
checkWaitingThreadCount(latch, 10);
synchronized (lock) {
lock.notifyAll();
}
if (!waitForThreadsToReachStage(testThreads, 0, 10, 20)) {
Assert.fail("Failed at 00-10-20");
}
checkWaitingThreadCount(latch, 0);
synchronized (lock) {
lock.notifyAll();
}
if (!waitForThreadsToReachStage(testThreads, 0, 0, 30)) {
Assert.fail("Failed at 00-00-30");
}
}
private boolean waitForThreadToStart(TestThread t) throws InterruptedException {
long wait = 0;
while (t.getStage() == 0 && wait < THREAD_WAIT_TIME) {
Thread.sleep(100);
wait += 100;
}
return t.getStage() > 0;
}
private boolean waitForThreadToStop(TestThread t, Object lock) throws InterruptedException {
long wait = 0;
while (t.getStage() < 3 && wait < THREAD_WAIT_TIME) {
Thread.sleep(100);
wait += 100;
synchronized (lock) {
lock.notifyAll();
}
}
return t.getStage() == 3;
}
private void checkWaitingThreadCount(LimitLatch latch, int target) throws InterruptedException {
long wait = 0;
while (latch.getQueuedThreads().size() != target && wait < THREAD_WAIT_TIME) {
Thread.sleep(100);
wait += 100;
}
Assert.assertEquals(target, latch.getQueuedThreads().size());
}
private boolean waitForThreadsToReachStage(TestThread[] testThreads,
int stage1Target, int stage2Target, int stage3Target) throws InterruptedException {
long wait = 0;
int stage1 = 0;
int stage2 = 0;
int stage3 = 0;
while((stage1 != stage1Target || stage2 != stage2Target || stage3 != stage3Target) &&
wait < THREAD_WAIT_TIME) {
stage1 = 0;
stage2 = 0;
stage3 = 0;
for (TestThread testThread : testThreads) {
switch(testThread.getStage()){
case 1:
stage1++;
break;
case 2:
stage2++;
break;
case 3:
stage3++;
break;
}
}
Thread.sleep(100);
wait += 100;
}
return stage1 == stage1Target && stage2 == stage2Target && stage3 == stage3Target;
}
private static class TestThread extends Thread {
private final Object lock;
private final LimitLatch latch;
private volatile int stage = 0;
public TestThread(LimitLatch latch, Object lock) {
this.latch = latch;
this.lock = lock;
}
public int getStage() {
return stage;
}
@Override
public void run() {
try {
stage = 1;
latch.countUpOrAwait();
stage = 2;
if (lock != null) {
synchronized (lock) {
lock.wait();
}
}
latch.countDown();
stage = 3;
} catch (InterruptedException x) {
x.printStackTrace();
}
}
}
}