/*
* 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 static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
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() {
assertEquals("foo1=bar1", SIMPLE.toString());
// Note: testing requires that ' ' is encoded as '+',
// because that is what browsers will send us.
assertEquals("foo2=bar1&foo2=bar2&foo2=hello+world&foo2=%3F%25%40",
SIMPLE_MULTIPLE.toString());
assertEquals("foo3", NO_VALUE.toString());
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");
assertNull(value);
Enumeration<String> names = p.getParameterNames();
assertFalse(names.hasMoreElements());
String[] values = p.getParameterValues("foo");
assertNull(values);
}
@Test
public void testAddParameters() {
Parameters p = new Parameters();
// Empty at this point
Enumeration<String> names = p.getParameterNames();
assertFalse(names.hasMoreElements());
String[] values = p.getParameterValues("foo");
assertNull(values);
// Add a parameter with two values
p.addParameter("foo", "value1");
p.addParameter("foo", "value2");
names = p.getParameterNames();
assertTrue(names.hasMoreElements());
assertEquals("foo", names.nextElement());
assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo");
assertEquals(2, values.length);
assertEquals("value1", values[0]);
assertEquals("value2", values[1]);
// Add two more values
p.addParameter("foo", "value3");
p.addParameter("foo", "value4");
names = p.getParameterNames();
assertTrue(names.hasMoreElements());
assertEquals("foo", names.nextElement());
assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo");
assertEquals(4, values.length);
assertEquals("value1", values[0]);
assertEquals("value2", values[1]);
assertEquals("value3", values[2]);
assertEquals("value4", values[3]);
}
@Test
public void testAddParametersLimit() {
Parameters p = new Parameters();
p.setLimit(2);
// Empty at this point
Enumeration<String> names = p.getParameterNames();
assertFalse(names.hasMoreElements());
String[] values = p.getParameterValues("foo1");
assertNull(values);
// Add a parameter
p.addParameter("foo1", "value1");
names = p.getParameterNames();
assertTrue(names.hasMoreElements());
assertEquals("foo1", names.nextElement());
assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo1");
assertEquals(1, values.length);
assertEquals("value1", values[0]);
// Add another parameter
p.addParameter("foo2", "value2");
names = p.getParameterNames();
assertTrue(names.hasMoreElements());
assertEquals("foo1", names.nextElement());
assertEquals("foo2", names.nextElement());
assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo1");
assertEquals(1, values.length);
assertEquals("value1", values[0]);
values = p.getParameterValues("foo2");
assertEquals(1, values.length);
assertEquals("value2", values[0]);
// Add another parameter
IllegalStateException e = null;
try {
p.addParameter("foo3", "value3");
} catch (IllegalStateException ise) {
e = ise;
}
assertNotNull(e);
// Check current parameters remain unaffected
names = p.getParameterNames();
assertTrue(names.hasMoreElements());
assertEquals("foo1", names.nextElement());
assertEquals("foo2", names.nextElement());
assertFalse(names.hasMoreElements());
values = p.getParameterValues("foo1");
assertEquals(1, values.length);
assertEquals("value1", values[0]);
values = p.getParameterValues("foo2");
assertEquals(1, values.length);
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
assertArrayEquals(new String[] {""}, values);
} else {
assertArrayEquals(parameter.getValues(), values);
}
break;
}
}
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();
}
}
}
}