This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit b19862c87b3a3c163cda60ff839f59297d438983 Author: Mark Thomas <ma...@apache.org> AuthorDate: Fri Aug 18 15:58:44 2023 +0100 Add tests for changes in parameter handling --- .../servlet/TestServletRequestParameters.java | 63 ++++++++++ .../servlet/TestServletRequestParametersBase.java | 90 ++++++++++++++ ...TestServletRequestParametersFormUrlEncoded.java | 104 ++++++++++++++++ ...stServletRequestParametersMultipartEncoded.java | 131 +++++++++++++++++++++ .../TestServletRequestParametersQueryString.java | 112 ++++++++++++++++++ 5 files changed, 500 insertions(+) diff --git a/test/jakarta/servlet/TestServletRequestParameters.java b/test/jakarta/servlet/TestServletRequestParameters.java new file mode 100644 index 0000000000..1f50f4e5dd --- /dev/null +++ b/test/jakarta/servlet/TestServletRequestParameters.java @@ -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 jakarta.servlet; + +import java.nio.charset.StandardCharsets; + +import org.junit.Assert; +import org.junit.Test; + +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; + +public class TestServletRequestParameters extends TestServletRequestParametersBase { + + @Test + public void testClientDisconnect() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setMaxPostSize(20); + Assert.assertTrue(tomcat.getConnector().setProperty("connectionTimeout", "1000")); + + // No file system docBase required + StandardContext ctx = (StandardContext) tomcat.addContext("", null); + + // Map the test Servlet + ParameterParsingServlet parameterParsingServlet = new ParameterParsingServlet(); + Tomcat.addServlet(ctx, "parameterParsingServlet", parameterParsingServlet); + ctx.addServletMappingDecoded("/", "parameterParsingServlet"); + + tomcat.start(); + + TestParameterClient client = new TestParameterClient(); + client.setPort(getPort()); + client.setRequest(new String[] { "POST / HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + "Transfer-Encoding: chunked" + CRLF + + "Content-Type: application/x-www-form-urlencoded" + CRLF + CRLF + "0a" + CRLF + "var1=val1&" + CRLF }); + + client.setResponseBodyEncoding(StandardCharsets.UTF_8); + client.connect(); + // Incomplete request will look timeout reading body and behave like a client disconnect + client.processRequest(); + + // Connection should be closed by the server. + //readLine() will receive an EOF reading the status line resuting in a null + Assert.assertNull(client.getResponseLine()); + } +} diff --git a/test/jakarta/servlet/TestServletRequestParametersBase.java b/test/jakarta/servlet/TestServletRequestParametersBase.java new file mode 100644 index 0000000000..fce787175e --- /dev/null +++ b/test/jakarta/servlet/TestServletRequestParametersBase.java @@ -0,0 +1,90 @@ +/* + * 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 jakarta.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestServletRequestParametersBase extends TomcatBaseTest { + + protected Map<String,List<String>> parseReportedParameters(SimpleHttpClient client) { + Map<String,List<String>> parameters = new LinkedHashMap<>(); + if (client.isResponse200()) { + String[] lines = client.getResponseBody().split(System.lineSeparator()); + for (String line : lines) { + // Every line should be name=value + int equalsPos = line.indexOf('='); + String name = line.substring(0, equalsPos); + String value = line.substring(equalsPos + 1); + + List<String> values = parameters.computeIfAbsent(name, k -> new ArrayList<>()); + values.add(value); + } + } + return parameters; + } + + + protected static class ParameterParsingServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + resp.setContentType("text/plain"); + resp.setCharacterEncoding(StandardCharsets.UTF_8); + PrintWriter pw = resp.getWriter(); + + Enumeration<String> names = req.getParameterNames(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + for (String value : req.getParameterValues(name)) { + pw.print(name + "=" + value + '\n'); + } + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + // Required parameter processing is the same as for GET + doGet(req, resp); + } + } + + + protected static class TestParameterClient extends SimpleHttpClient { + + @Override + public boolean isResponseBodyOK() { + return true; + } + } +} diff --git a/test/jakarta/servlet/TestServletRequestParametersFormUrlEncoded.java b/test/jakarta/servlet/TestServletRequestParametersFormUrlEncoded.java new file mode 100644 index 0000000000..4266eef7c6 --- /dev/null +++ b/test/jakarta/servlet/TestServletRequestParametersFormUrlEncoded.java @@ -0,0 +1,104 @@ +/* + * 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 jakarta.servlet; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import jakarta.servlet.http.HttpServletResponse; + +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 static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; + +@RunWith(Parameterized.class) +public class TestServletRequestParametersFormUrlEncoded extends TestServletRequestParametersBase { + + @Parameterized.Parameters(name = "{index}: chunked[{0}]") + public static Collection<Object[]> parameters() { + List<Object[]> parameterSets = new ArrayList<>(); + + for (Boolean chunked : booleans) { + parameterSets.add(new Object[] { chunked }); + } + + return parameterSets; + } + + @Parameter(0) + public boolean chunked; + + + @Test + public void testBodyTooLarge() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setMaxPostSize(20); + + // No file system docBase required + StandardContext ctx = (StandardContext) tomcat.addContext("", null); + + // Map the test Servlet + ParameterParsingServlet parameterParsingServlet = new ParameterParsingServlet(); + Tomcat.addServlet(ctx, "parameterParsingServlet", parameterParsingServlet); + ctx.addServletMappingDecoded("/", "parameterParsingServlet"); + + tomcat.start(); + + TestParameterClient client = new TestParameterClient(); + client.setPort(getPort()); + if (chunked) { + client.setRequest(new String[] { + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + "Transfer-Encoding: chunked" + CRLF + + "Content-Type: application/x-www-form-urlencoded" + CRLF + + CRLF + + "0a" + CRLF + + "var1=val1&" + CRLF + + "0a" + CRLF + + "var2=val2&" + CRLF + + "0a" + CRLF + + "var3=val3&" + CRLF + + "0" + CRLF }); + } else { + client.setRequest(new String[] { + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + "Content-Length: 50" + CRLF + + "Content-Type: application/x-www-form-urlencoded" + CRLF + + CRLF + + "01234567890123456789012345678901234567890123456789" }); + } + client.setResponseBodyEncoding(StandardCharsets.UTF_8); + client.connect(); + client.processRequest(); + + Assert.assertEquals(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, client.getStatusCode()); + } +} diff --git a/test/jakarta/servlet/TestServletRequestParametersMultipartEncoded.java b/test/jakarta/servlet/TestServletRequestParametersMultipartEncoded.java new file mode 100644 index 0000000000..ac0941f853 --- /dev/null +++ b/test/jakarta/servlet/TestServletRequestParametersMultipartEncoded.java @@ -0,0 +1,131 @@ +/* + * 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 jakarta.servlet; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import jakarta.servlet.http.HttpServletResponse; + +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 static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; + +@RunWith(Parameterized.class) +public class TestServletRequestParametersMultipartEncoded extends TestServletRequestParametersBase { + + @Parameterized.Parameters(name = "{index}: chunked[{0}]") + public static Collection<Object[]> parameters() { + List<Object[]> parameterSets = new ArrayList<>(); + + for (Boolean chunked : booleans) { + parameterSets.add(new Object[] { chunked }); + } + + return parameterSets; + } + + @Parameter(0) + public boolean chunked; + + + @Test + public void testBodyTooLarge() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setMaxPostSize(50); + + // No file system docBase required + StandardContext ctx = (StandardContext) tomcat.addContext("", null); + ctx.setAllowCasualMultipartParsing(true); + + // Map the test Servlet + ParameterParsingServlet parameterParsingServlet = new ParameterParsingServlet(); + Tomcat.addServlet(ctx, "parameterParsingServlet", parameterParsingServlet); + ctx.addServletMappingDecoded("/", "parameterParsingServlet"); + + tomcat.start(); + + TestParameterClient client = new TestParameterClient(); + client.setPort(getPort()); + if (chunked) { + client.setRequest(new String[] { + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + "Transfer-Encoding: chunked" + CRLF + + "Content-Type: multipart/form-data; boundary=AaBbCc" + CRLF + + CRLF + + "3f" + CRLF + + "--AaBbCc" + CRLF + + "Content-Disposition: form-data; name=\"var1\"" + CRLF + + CRLF + + "val1" + CRLF + + CRLF + + "3f" + CRLF + + "--AaBbCc" + CRLF + + "Content-Disposition: form-data; name=\"var2\"" + CRLF + + CRLF + + "val2" + CRLF + + CRLF + + "3f" + CRLF + + "--AaBbCc" + CRLF + + "Content-Disposition: form-data; name=\"var3\"" + CRLF + + CRLF + + "val3" + CRLF + + CRLF + + "0a" + CRLF + + "--AaBbCc--" + CRLF + + "0" + CRLF}); + } else { + client.setRequest(new String[] { + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + "Content-Length: 199" + CRLF + + "Content-Type: multipart/form-data; boundary=AaBbCc" + CRLF + + CRLF + + "--AaBbCc" + CRLF + + "Content-Disposition: form-data; name=\"var1\"" + CRLF + + CRLF + + "val1" + CRLF + + "--AaBbCc" + CRLF + + "Content-Disposition: form-data; name=\"var2\"" + CRLF + + CRLF + + "val2" + CRLF + + "--AaBbCc" + CRLF + + "Content-Disposition: form-data; name=\"var3\"" + CRLF + + CRLF + + "val3" + CRLF + + "--AaBbCc--"}); + } + client.setResponseBodyEncoding(StandardCharsets.UTF_8); + client.connect(); + client.processRequest(); + + Assert.assertEquals(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, client.getStatusCode()); + } +} diff --git a/test/jakarta/servlet/TestServletRequestParametersQueryString.java b/test/jakarta/servlet/TestServletRequestParametersQueryString.java new file mode 100644 index 0000000000..037bdd55cc --- /dev/null +++ b/test/jakarta/servlet/TestServletRequestParametersQueryString.java @@ -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 jakarta.servlet; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.http.HttpServletResponse; + +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 static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; + +@RunWith(Parameterized.class) +public class TestServletRequestParametersQueryString extends TestServletRequestParametersBase { + + private static final Integer SC_OK = Integer.valueOf(HttpServletResponse.SC_OK); + private static final Integer SC_BAD_REQUEST = Integer.valueOf(HttpServletResponse.SC_BAD_REQUEST); + private static final Integer ZERO = Integer.valueOf(0); + private static final Integer TWO = Integer.valueOf(2); + + @Parameterized.Parameters(name = "{index}: queryString[{0}], expectedStatusCode[{1}]") + public static Collection<Object[]> parameters() { + List<Object[]> parameterSets = new ArrayList<>(); + + // Empty parameter + parameterSets.add(new Object[] { "before=aaa&&after=zzz", SC_OK, TWO} ); + + // Invalid parameter + parameterSets.add(new Object[] { "before=aaa&=value&after=zzz", SC_BAD_REQUEST, ZERO} ); + + // Invalid %nn encoding + parameterSets.add(new Object[] { "before=aaa&test=val%GGue&after=zzz", SC_BAD_REQUEST, ZERO} ); + + // Invalid UTF-8 byte + parameterSets.add(new Object[] { "before=aaa&test=val%FFue&after=zzz", SC_BAD_REQUEST, ZERO} ); + + // There are no unmappable UTF-8 code points + + // Too many parameters + parameterSets.add(new Object[] { "before=aaa&test=value&after=zzz&extra=yyy", SC_BAD_REQUEST, ZERO} ); + + return parameterSets; + } + + @Parameter(0) + public String queryString; + + @Parameter(1) + public int expectedStatusCode; + + @Parameter(2) + public int expectedValidParameterCount; + + + @Test + public void testParameterParsing() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setMaxParameterCount(3); + + // No file system docBase required + StandardContext ctx = (StandardContext) tomcat.addContext("", null); + + // Map the test Servlet + ParameterParsingServlet parameterParsingServlet = new ParameterParsingServlet(); + Tomcat.addServlet(ctx, "parameterParsingServlet", parameterParsingServlet); + ctx.addServletMappingDecoded("/", "parameterParsingServlet"); + + tomcat.start(); + + TestParameterClient client = new TestParameterClient(); + client.setPort(getPort()); + client.setRequest(new String[] { + "GET /?" + queryString +" HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + CRLF }); + client.setResponseBodyEncoding(StandardCharsets.UTF_8); + client.connect(); + client.processRequest(); + + Assert.assertEquals(expectedStatusCode, client.getStatusCode()); + + Map<String,List<String>> parameters = parseReportedParameters(client); + + Assert.assertEquals(expectedValidParameterCount, parameters.size()); + } +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org