init
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Wrapper;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.TomcatBaseTest;
|
||||
import org.apache.tomcat.util.buf.B2CConverter;
|
||||
import org.apache.tomcat.util.buf.ByteChunk;
|
||||
import org.apache.tomcat.util.http.parser.MediaType;
|
||||
|
||||
/*
|
||||
* Note: This test is split using two base classes. This is because, as a single
|
||||
* test, it takes so long to run it dominates the time taken to run the
|
||||
* tests when running tests using multiple threads. For example, on a
|
||||
* system with 12 cores, the tests take ~5 minutes per connector with this
|
||||
* test as a single test and ~3.5 minutes per connector with this test
|
||||
* split in two.
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public abstract class DefaultServletEncodingBaseTest extends TomcatBaseTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: contextEnc[{0}], fileEnc[{1}], target[{2}]," +
|
||||
" useInclude[{3}], outputEnc[{4}], callSetCharacterEnc[{5}], useWriter[{6}]")
|
||||
public static Collection<Object[]> parameters() {
|
||||
|
||||
String[] encodings = new String[] {
|
||||
"utf-8", "ibm850", "cp1252", "iso-8859-1" };
|
||||
|
||||
String[] targetFiles = new String[] {
|
||||
"cp1252", "ibm850", "iso-8859-1", "utf-8-bom", "utf-8" };
|
||||
|
||||
List<Object[]> parameterSets = new ArrayList<>();
|
||||
|
||||
for (String contextResponseEncoding : encodings) {
|
||||
for (String fileEncoding : encodings) {
|
||||
for (String targetFile : targetFiles) {
|
||||
for (Boolean useInclude : booleans) {
|
||||
if (useInclude.booleanValue()) {
|
||||
for (String outputEncoding : encodings) {
|
||||
for (Boolean callSetCharacterEncoding : booleans) {
|
||||
for (Boolean useWriter : booleans) {
|
||||
parameterSets.add(new Object[] { contextResponseEncoding,
|
||||
fileEncoding, targetFile,
|
||||
useInclude, outputEncoding,
|
||||
callSetCharacterEncoding, useWriter });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Not using include so ignore outputEncoding,
|
||||
* callSetCharacterEncoding and useWriter
|
||||
*
|
||||
* Tests that do not use include are always expected to
|
||||
* pass.
|
||||
*/
|
||||
String encoding = targetFile;
|
||||
if (encoding.endsWith("-bom")) {
|
||||
encoding = encoding.substring(0, encoding.length() - 4);
|
||||
}
|
||||
parameterSets.add(new Object[] { contextResponseEncoding, fileEncoding,
|
||||
targetFile, useInclude, encoding, Boolean.FALSE,
|
||||
Boolean.FALSE });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameterSets;
|
||||
}
|
||||
|
||||
|
||||
private static boolean getExpected(String fileEncoding, boolean useBom, String targetFile,
|
||||
String outputEncoding, boolean callSetCharacterEncoding, boolean useWriter) {
|
||||
if (useWriter || callSetCharacterEncoding) {
|
||||
/*
|
||||
* Using a writer or setting the output character encoding means the
|
||||
* response will specify a character set. These cases therefore
|
||||
* reduce to can the file be read with the correct encoding.
|
||||
* (Assuming any BOM is always skipped in the included output.)
|
||||
*/
|
||||
if (targetFile.endsWith("-bom") && useBom ||
|
||||
targetFile.startsWith(fileEncoding) ||
|
||||
targetFile.equals("cp1252") && fileEncoding.equals("iso-8859-1") ||
|
||||
targetFile.equals("iso-8859-1") && fileEncoding.equals("cp1252")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (!(targetFile.startsWith(outputEncoding) ||
|
||||
targetFile.equals("cp1252") && outputEncoding.equals("iso-8859-1") ||
|
||||
targetFile.equals("iso-8859-1") && outputEncoding.equals("cp1252"))) {
|
||||
/*
|
||||
* The non-writer use cases read the target file as bytes. These
|
||||
* cases therefore reduce to can the bytes from the target file be
|
||||
* included in the output without corruption? The character used in
|
||||
* the tests has been chosen so that, apart from iso-8859-1 and
|
||||
* cp1252, the bytes vary by character set.
|
||||
* (Assuming any BOM is always skipped in the included output.)
|
||||
*/
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Parameter(0)
|
||||
public String contextResponseEncoding;
|
||||
@Parameter(1)
|
||||
public String fileEncoding;
|
||||
@Parameter(2)
|
||||
public String targetFile;
|
||||
@Parameter(3)
|
||||
public boolean useInclude;
|
||||
@Parameter(4)
|
||||
public String outputEncoding;
|
||||
@Parameter(5)
|
||||
public boolean callSetCharacterEncoding;
|
||||
@Parameter(6)
|
||||
public boolean useWriter;
|
||||
|
||||
|
||||
protected abstract boolean getUseBom();
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncoding() throws Exception {
|
||||
|
||||
boolean expectedPass = getExpected(fileEncoding, getUseBom(), targetFile, outputEncoding,
|
||||
callSetCharacterEncoding, useWriter);
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
ctxt.setResponseCharacterEncoding(contextResponseEncoding);
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName());
|
||||
defaultServlet.addInitParameter("fileEncoding", fileEncoding);
|
||||
defaultServlet.addInitParameter("useBomIfPresent", Boolean.toString(getUseBom()));
|
||||
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
|
||||
if (useInclude) {
|
||||
Tomcat.addServlet(ctxt, "include", new EncodingServlet(
|
||||
outputEncoding, callSetCharacterEncoding, targetFile, useWriter));
|
||||
ctxt.addServletMappingDecoded("/include", "include");
|
||||
}
|
||||
|
||||
tomcat.start();
|
||||
|
||||
final ByteChunk res = new ByteChunk();
|
||||
Map<String,List<String>> headers = new HashMap<>();
|
||||
|
||||
String target;
|
||||
if (useInclude) {
|
||||
target = "http://localhost:" + getPort() + "/include";
|
||||
} else {
|
||||
target = "http://localhost:" + getPort() + "/bug49nnn/bug49464-" + targetFile + ".txt";
|
||||
}
|
||||
int rc = getUrl(target, res, headers);
|
||||
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
String contentType = getSingleHeader("Content-Type", headers);
|
||||
if (contentType != null) {
|
||||
MediaType mediaType = MediaType.parseMediaType(new StringReader(contentType));
|
||||
String charset = mediaType.getCharset();
|
||||
if (charset == null) {
|
||||
res.setCharset(B2CConverter.getCharset(outputEncoding));
|
||||
} else {
|
||||
res.setCharset(B2CConverter.getCharset(charset));
|
||||
}
|
||||
} else {
|
||||
res.setCharset(B2CConverter.getCharset(outputEncoding));
|
||||
}
|
||||
String body = res.toString();
|
||||
/*
|
||||
* Remove BOM before checking content
|
||||
* BOM (should be) removed by Tomcat when file is included
|
||||
*/
|
||||
if (!useInclude && targetFile.endsWith("-bom")) {
|
||||
body = body.substring(1);
|
||||
}
|
||||
|
||||
if (expectedPass) {
|
||||
if (useInclude) {
|
||||
Assert.assertEquals("\u00bd-\u00bd-\u00bd", body);
|
||||
} else {
|
||||
Assert.assertEquals("\u00bd", body);
|
||||
}
|
||||
} else {
|
||||
if (useInclude) {
|
||||
Assert.assertNotEquals("\u00bd-\u00bd-\u00bd", body);
|
||||
} else {
|
||||
Assert.assertNotEquals("\u00bd", body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class EncodingServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String outputEncoding;
|
||||
private final boolean callSetCharacterEncoding;
|
||||
private final String includeTarget;
|
||||
private final boolean useWriter;
|
||||
|
||||
public EncodingServlet(String outputEncoding, boolean callSetCharacterEncoding,
|
||||
String includeTarget, boolean useWriter) {
|
||||
this.outputEncoding = outputEncoding;
|
||||
this.callSetCharacterEncoding = callSetCharacterEncoding;
|
||||
this.includeTarget = includeTarget;
|
||||
this.useWriter = useWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
resp.setContentType("text/plain");
|
||||
if (callSetCharacterEncoding) {
|
||||
resp.setCharacterEncoding(outputEncoding);
|
||||
}
|
||||
if (useWriter) {
|
||||
PrintWriter pw = resp.getWriter();
|
||||
pw.print("\u00bd-");
|
||||
} else {
|
||||
resp.getOutputStream().write("\u00bd-".getBytes(outputEncoding));
|
||||
}
|
||||
resp.flushBuffer();
|
||||
RequestDispatcher rd =
|
||||
req.getRequestDispatcher("/bug49nnn/bug49464-" + includeTarget + ".txt");
|
||||
rd.include(req, resp);
|
||||
if (useWriter) {
|
||||
PrintWriter pw = resp.getWriter();
|
||||
pw.print("-\u00bd");
|
||||
} else {
|
||||
resp.getOutputStream().write("-\u00bd".getBytes(outputEncoding));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
161
test/org/apache/catalina/servlets/ServletOptionsBaseTest.java
Normal file
161
test/org/apache/catalina/servlets/ServletOptionsBaseTest.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
|
||||
import static org.apache.catalina.startup.SimpleHttpClient.CRLF;
|
||||
import org.apache.catalina.Wrapper;
|
||||
import org.apache.catalina.startup.SimpleHttpClient;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.TomcatBaseTest;
|
||||
|
||||
public abstract class ServletOptionsBaseTest extends TomcatBaseTest {
|
||||
|
||||
protected static final String COLLECTION_NAME = "collection";
|
||||
protected static final String FILE_NAME = "file";
|
||||
protected static final String UNKNOWN_NAME = "unknown";
|
||||
|
||||
@Parameter(0)
|
||||
public boolean listings;
|
||||
|
||||
@Parameter(1)
|
||||
public boolean readonly;
|
||||
|
||||
@Parameter(2)
|
||||
public boolean trace;
|
||||
|
||||
@Parameter(3)
|
||||
public String url;
|
||||
|
||||
@Parameter(4)
|
||||
public String method;
|
||||
|
||||
|
||||
/*
|
||||
* Check that methods returned by OPTIONS are consistent with the return
|
||||
* http status code.
|
||||
* Method not present in options response -> 405 expected
|
||||
* Method present in options response -> anything other than 405 expected
|
||||
*/
|
||||
@Test
|
||||
public void testOptions() throws Exception {
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
tomcat.getConnector().setAllowTrace(trace);
|
||||
|
||||
File docBase = new File(getTemporaryDirectory(), "webdav");
|
||||
File collection = new File(docBase, COLLECTION_NAME);
|
||||
Assert.assertTrue(collection.mkdirs());
|
||||
File file = new File(docBase, FILE_NAME);
|
||||
Assert.assertTrue(file.createNewFile());
|
||||
|
||||
addDeleteOnTearDown(docBase);
|
||||
|
||||
// app dir is relative to server home
|
||||
org.apache.catalina.Context ctx =
|
||||
tomcat.addWebapp(null, "/servlet", docBase.getAbsolutePath());
|
||||
|
||||
Wrapper w = Tomcat.addServlet(ctx, "servlet", createServlet());
|
||||
w.addInitParameter("listings", Boolean.toString(listings));
|
||||
w.addInitParameter("readonly", Boolean.toString(readonly));
|
||||
|
||||
ctx.addServletMappingDecoded("/*", "servlet");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
OptionsHttpClient client = new OptionsHttpClient();
|
||||
client.setPort(getPort());
|
||||
client.setRequest(new String[] {
|
||||
"OPTIONS /servlet/" + url + " HTTP/1.1" + CRLF +
|
||||
"Host: localhost:" + getPort() + CRLF +
|
||||
"Connection: close" + CRLF +
|
||||
CRLF });
|
||||
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
Set<String> allowed = client.getAllowedMethods();
|
||||
|
||||
client.disconnect();
|
||||
client.reset();
|
||||
|
||||
client.setRequest(new String[] {
|
||||
method + " /servlet/" + url + " HTTP/1.1" + CRLF +
|
||||
"Host: localhost:" + getPort() + CRLF +
|
||||
"Connection: close" + CRLF +
|
||||
CRLF });
|
||||
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
|
||||
String msg = "Listings[" + listings + "], readonly [" + readonly +
|
||||
"], trace[ " + trace + "], url[" + url + "], method[" + method + "]";
|
||||
|
||||
Assert.assertNotNull(client.getResponseLine());
|
||||
|
||||
if (allowed.contains(method)) {
|
||||
Assert.assertFalse(msg, client.isResponse405());
|
||||
} else {
|
||||
Assert.assertTrue(msg, client.isResponse405());
|
||||
allowed = client.getAllowedMethods();
|
||||
Assert.assertFalse(msg, allowed.contains(method));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract Servlet createServlet();
|
||||
|
||||
|
||||
private static class OptionsHttpClient extends SimpleHttpClient {
|
||||
|
||||
@Override
|
||||
public boolean isResponseBodyOK() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getAllowedMethods() {
|
||||
String valueList = null;
|
||||
for (String header : getResponseHeaders()) {
|
||||
if (header.startsWith("Allow:")) {
|
||||
valueList = header.substring(6).trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertNotNull(valueList);
|
||||
String[] values = valueList.split(",");
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = values[i].trim();
|
||||
}
|
||||
Set<String> allowed = new HashSet<>();
|
||||
allowed.addAll(Arrays.asList(values));
|
||||
|
||||
return allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.catalina.servlets;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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.tomcat.util.compat.JrePlatform;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TestCGIServletCmdLineArguments {
|
||||
|
||||
private static final Pattern defaultDecodedPatternWindows;
|
||||
|
||||
static {
|
||||
/*
|
||||
* There are various ways to handle the platform specific behaviour
|
||||
* here. This was chosen as it is simple and the tests are run on
|
||||
* Windows as part of every release cycle.
|
||||
*/
|
||||
defaultDecodedPatternWindows = Pattern.compile("[a-zA-Z0-9\\Q-_.\\/:\\E]+");
|
||||
|
||||
if (JrePlatform.IS_WINDOWS) {
|
||||
Pattern p = null;
|
||||
try {
|
||||
CGIServlet cgiServlet = new CGIServlet();
|
||||
Field f = CGIServlet.class.getDeclaredField("cmdLineArgumentsDecodedPattern");
|
||||
f.setAccessible(true);
|
||||
p = (Pattern) f.get(cgiServlet);
|
||||
} catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
|
||||
}
|
||||
|
||||
Assert.assertNotNull(p);
|
||||
Assert.assertEquals(defaultDecodedPatternWindows.toString(), p.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: argument[{0}], allowed[{1}]")
|
||||
public static Collection<Object[]> parameters() {
|
||||
List<Object[]> params = new ArrayList<>();
|
||||
params.add(new Object[] { "", Boolean.FALSE } );
|
||||
params.add(new Object[] { "<", Boolean.FALSE } );
|
||||
params.add(new Object[] { "\"", Boolean.FALSE } );
|
||||
params.add(new Object[] { "'", Boolean.FALSE } );
|
||||
params.add(new Object[] { "|", Boolean.FALSE } );
|
||||
params.add(new Object[] { ">", Boolean.FALSE } );
|
||||
|
||||
params.add(new Object[] { ".", Boolean.TRUE } );
|
||||
params.add(new Object[] { "-p", Boolean.TRUE } );
|
||||
params.add(new Object[] { "--p", Boolean.TRUE } );
|
||||
params.add(new Object[] { "/p", Boolean.TRUE } );
|
||||
params.add(new Object[] { "abc", Boolean.TRUE } );
|
||||
params.add(new Object[] { "a_b_c", Boolean.TRUE } );
|
||||
params.add(new Object[] { "123", Boolean.TRUE } );
|
||||
params.add(new Object[] { "file.txt", Boolean.TRUE } );
|
||||
params.add(new Object[] { "C:\\some\\path\\file.txt", Boolean.TRUE } );
|
||||
|
||||
params.add(new Object[] { "file.txt file2.txt", Boolean.FALSE } );
|
||||
params.add(new Object[] { "C:\\some\\path with space\\file.txt", Boolean.FALSE } );
|
||||
params.add(new Object[] { "\"C:\\some\\quoted path with space\\file.txt\"", Boolean.FALSE } );
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
@Parameter(0)
|
||||
public String argument;
|
||||
|
||||
@Parameter(1)
|
||||
public Boolean allowed;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
if (allowed.booleanValue()) {
|
||||
Assert.assertTrue(defaultDecodedPatternWindows.matcher(argument).matches());
|
||||
} else {
|
||||
Assert.assertFalse(defaultDecodedPatternWindows.matcher(argument).matches());
|
||||
}
|
||||
}
|
||||
}
|
||||
611
test/org/apache/catalina/servlets/TestDefaultServlet.java
Normal file
611
test/org/apache/catalina/servlets/TestDefaultServlet.java
Normal file
@@ -0,0 +1,611 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.apache.catalina.startup.SimpleHttpClient.CRLF;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Wrapper;
|
||||
import org.apache.catalina.startup.SimpleHttpClient;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.TomcatBaseTest;
|
||||
import org.apache.tomcat.util.buf.ByteChunk;
|
||||
import org.apache.tomcat.util.descriptor.web.ErrorPage;
|
||||
import org.apache.tomcat.websocket.server.WsContextListener;
|
||||
|
||||
public class TestDefaultServlet extends TomcatBaseTest {
|
||||
|
||||
/*
|
||||
* Test attempting to access special paths (WEB-INF/META-INF) using
|
||||
* DefaultServlet.
|
||||
*/
|
||||
@Test
|
||||
public void testGetSpecials() throws Exception {
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
String contextPath = "/examples";
|
||||
|
||||
File appDir = new File(getBuildDirectory(), "webapps" + contextPath);
|
||||
// app dir is relative to server home
|
||||
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
|
||||
|
||||
tomcat.start();
|
||||
|
||||
final ByteChunk res = new ByteChunk();
|
||||
|
||||
int rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/WEB-INF/web.xml", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/WEB-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/WEB-INF/", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/META-INF/MANIFEST.MF", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/META-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify serving of gzipped resources from context root.
|
||||
*/
|
||||
@Test
|
||||
public void testGzippedFile() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
|
||||
File gzipIndex = new File(appDir, "index.html.gz");
|
||||
long gzipSize = gzipIndex.length();
|
||||
|
||||
File index = new File(appDir, "index.html");
|
||||
long indexSize = index.length();
|
||||
|
||||
// app dir is relative to server home
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
|
||||
"org.apache.catalina.servlets.DefaultServlet");
|
||||
defaultServlet.addInitParameter("gzip", "true");
|
||||
defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1");
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
|
||||
ctxt.addMimeMapping("html", "text/html");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
TestCompressedClient gzipClient = new TestCompressedClient(getPort());
|
||||
|
||||
gzipClient.reset();
|
||||
gzipClient.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: gzip, br" + CRLF + CRLF });
|
||||
gzipClient.connect();
|
||||
gzipClient.processRequest();
|
||||
Assert.assertTrue(gzipClient.isResponse200());
|
||||
List<String> responseHeaders = gzipClient.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: gzip"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + gzipSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
|
||||
gzipClient.reset();
|
||||
gzipClient.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF+ CRLF });
|
||||
gzipClient.connect();
|
||||
gzipClient.processRequest();
|
||||
Assert.assertTrue(gzipClient.isResponse200());
|
||||
responseHeaders = gzipClient.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Type: text/html"));
|
||||
Assert.assertFalse(responseHeaders.contains("Content-Encoding: gzip"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + indexSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify serving of brotli compressed resources from context root.
|
||||
*/
|
||||
@Test
|
||||
public void testBrotliCompressedFile() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
|
||||
long brSize = new File(appDir, "index.html.br").length();
|
||||
long indexSize = new File(appDir, "index.html").length();
|
||||
|
||||
// app dir is relative to server home
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
|
||||
"org.apache.catalina.servlets.DefaultServlet");
|
||||
defaultServlet.addInitParameter("precompressed", "true");
|
||||
defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1");
|
||||
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
ctxt.addMimeMapping("html", "text/html");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
TestCompressedClient client = new TestCompressedClient(getPort());
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: br, gzip" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
List<String> responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: br"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + brSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF+ CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Type: text/html"));
|
||||
Assert.assertFalse(responseHeaders.contains("Content-Encoding"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + indexSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify serving of custom compressed resources from context root.
|
||||
*/
|
||||
@Test
|
||||
public void testCustomCompressedFile() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
|
||||
long brSize = new File(appDir, "index.html.br").length();
|
||||
long gzSize = new File(appDir, "index.html.gz").length();
|
||||
|
||||
// app dir is relative to server home
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
|
||||
DefaultServlet.class.getName());
|
||||
defaultServlet.addInitParameter("precompressed", "gzip=.gz,custom=.br");
|
||||
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
ctxt.addMimeMapping("html", "text/html");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
TestCompressedClient client = new TestCompressedClient(getPort());
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: br, gzip ; q = 0.5 , custom" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
List<String> responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: custom"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + brSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: br;q=1,gzip,custom" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: gzip"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + gzSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that "*" and "identity" values are handled correctly in accept-encoding header.
|
||||
*/
|
||||
@Test
|
||||
public void testIdentityAndStarAcceptEncodings() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
|
||||
long brSize = new File(appDir, "index.html.br").length();
|
||||
long indexSize = new File(appDir, "index.html").length();
|
||||
|
||||
// app dir is relative to server home
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
|
||||
DefaultServlet.class.getName());
|
||||
defaultServlet.addInitParameter("precompressed", "br=.br,gzip=.gz");
|
||||
defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1");
|
||||
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
ctxt.addMimeMapping("html", "text/html");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
TestCompressedClient client = new TestCompressedClient(getPort());
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: gzip;q=0.9,*" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
List<String> responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: br"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + brSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: gzip;q=0.9,br;q=0,identity," + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertFalse(responseHeaders.contains("Content-Encoding"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + indexSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify preferring of brotli in default configuration for actual Firefox and Chrome requests.
|
||||
*/
|
||||
@Test
|
||||
public void testBrotliPreference() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
|
||||
long brSize = new File(appDir, "index.html.br").length();
|
||||
|
||||
// app dir is relative to server home
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
|
||||
DefaultServlet.class.getName());
|
||||
defaultServlet.addInitParameter("precompressed", "true");
|
||||
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
ctxt.addMimeMapping("html", "text/html");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
TestCompressedClient client = new TestCompressedClient(getPort());
|
||||
|
||||
// Firefox 45 Accept-Encoding
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: gzip, deflate, br" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
List<String> responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: br"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + brSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
|
||||
// Chrome 50 Accept-Encoding
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /index.html HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: Close" + CRLF +
|
||||
"Accept-Encoding: gzip, deflate, sdch, br" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
responseHeaders = client.getResponseHeaders();
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Encoding: br"));
|
||||
Assert.assertTrue(responseHeaders.contains("Content-Length: " + brSize));
|
||||
Assert.assertTrue(responseHeaders.contains("vary: accept-encoding"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test https://bz.apache.org/bugzilla/show_bug.cgi?id=50026
|
||||
* Verify serving of resources from context root with subpath mapping.
|
||||
*/
|
||||
@Test
|
||||
public void testGetWithSubpathmount() throws Exception {
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
String contextPath = "/examples";
|
||||
|
||||
File appDir = new File(getBuildDirectory(), "webapps" + contextPath);
|
||||
// app dir is relative to server home
|
||||
org.apache.catalina.Context ctx =
|
||||
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
|
||||
ctx.addApplicationListener(WsContextListener.class.getName());
|
||||
|
||||
// Override the default servlet with our own mappings
|
||||
Tomcat.addServlet(ctx, "default2", new DefaultServlet());
|
||||
ctx.addServletMappingDecoded("/", "default2");
|
||||
ctx.addServletMappingDecoded("/servlets/*", "default2");
|
||||
ctx.addServletMappingDecoded("/static/*", "default2");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
final ByteChunk res = new ByteChunk();
|
||||
|
||||
// Make sure DefaultServlet isn't exposing special directories
|
||||
// by remounting the webapp under a sub-path
|
||||
|
||||
int rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/static/WEB-INF/web.xml", res, null);
|
||||
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/static/WEB-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/static/WEB-INF/", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/static/META-INF/MANIFEST.MF", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/static/META-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
// Make sure DefaultServlet is serving resources relative to the
|
||||
// context root regardless of where the it is mapped
|
||||
|
||||
final ByteChunk rootResource = new ByteChunk();
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/index.html", rootResource, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
|
||||
final ByteChunk subpathResource = new ByteChunk();
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/servlets/index.html", subpathResource, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
|
||||
Assert.assertFalse(rootResource.toString().equals(subpathResource.toString()));
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/static/index.html", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Test https://bz.apache.org/bugzilla/show_bug.cgi?id=50413 Serving a
|
||||
* custom error page
|
||||
*/
|
||||
@Test
|
||||
public void testCustomErrorPage() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
|
||||
// app dir is relative to server home
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
|
||||
DefaultServlet.class.getName());
|
||||
defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1");
|
||||
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
ctxt.addMimeMapping("html", "text/html");
|
||||
ErrorPage ep = new ErrorPage();
|
||||
ep.setErrorCode(404);
|
||||
ep.setLocation("/404.html");
|
||||
ctxt.addErrorPage(ep);
|
||||
|
||||
tomcat.start();
|
||||
|
||||
TestCustomErrorClient client =
|
||||
new TestCustomErrorClient(tomcat.getConnector().getLocalPort());
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /MyApp/missing HTTP/1.0" +CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse404());
|
||||
Assert.assertEquals("It is 404.html", client.getResponseBody());
|
||||
|
||||
SimpleDateFormat format = new SimpleDateFormat(
|
||||
"EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
|
||||
format.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
String tomorrow = format.format(new Date(System.currentTimeMillis()
|
||||
+ 24 * 60 * 60 * 1000));
|
||||
|
||||
// https://bz.apache.org/bugzilla/show_bug.cgi?id=50413
|
||||
//
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /MyApp/missing HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: close" + CRLF +
|
||||
"If-Modified-Since: " + tomorrow + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse404());
|
||||
Assert.assertEquals("It is 404.html", client.getResponseBody());
|
||||
|
||||
// https://bz.apache.org/bugzilla/show_bug.cgi?id=50413#c6
|
||||
//
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /MyApp/missing HTTP/1.1" + CRLF +
|
||||
"Host: localhost" + CRLF +
|
||||
"Connection: close" + CRLF +
|
||||
"Range: bytes=0-100" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse404());
|
||||
Assert.assertEquals("It is 404.html", client.getResponseBody());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test what happens if a custom 404 page is configured,
|
||||
* but its file is actually missing.
|
||||
*/
|
||||
@Test
|
||||
public void testCustomErrorPageMissing() throws Exception {
|
||||
File appDir = new File(getTemporaryDirectory(), "MyApp");
|
||||
File webInf = new File(appDir, "WEB-INF");
|
||||
addDeleteOnTearDown(appDir);
|
||||
if (!webInf.mkdirs() && !webInf.isDirectory()) {
|
||||
Assert.fail("Unable to create directory [" + webInf + "]");
|
||||
}
|
||||
|
||||
File webxml = new File(appDir, "WEB-INF/web.xml");
|
||||
try (FileOutputStream fos = new FileOutputStream(webxml);
|
||||
Writer w = new OutputStreamWriter(fos, "UTF-8")) {
|
||||
w.write("<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<web-app xmlns='http://java.sun.com/xml/ns/j2ee' "
|
||||
+ " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
|
||||
+ " xsi:schemaLocation='http://java.sun.com/xml/ns/j2ee "
|
||||
+ " http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd'"
|
||||
+ " version='2.4'>\n"
|
||||
+ "<error-page>\n<error-code>404</error-code>\n"
|
||||
+ "<location>/404-absent.html</location>\n</error-page>\n"
|
||||
+ "</web-app>\n");
|
||||
}
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
String contextPath = "/MyApp";
|
||||
tomcat.addWebapp(null, contextPath, appDir.getAbsolutePath());
|
||||
tomcat.start();
|
||||
|
||||
TestCustomErrorClient client =
|
||||
new TestCustomErrorClient(tomcat.getConnector().getLocalPort());
|
||||
|
||||
client.reset();
|
||||
client.setRequest(new String[] {
|
||||
"GET /MyApp/missing HTTP/1.0" + CRLF + CRLF });
|
||||
client.connect();
|
||||
client.processRequest();
|
||||
Assert.assertTrue(client.isResponse404());
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that the same Content-Length is returned for both GET and HEAD
|
||||
* operations when a static resource served by the DefaultServlet is
|
||||
* included.
|
||||
*/
|
||||
@Test
|
||||
public void testBug57601() throws Exception {
|
||||
Tomcat tomcat = getTomcatInstanceTestWebapp(false, true);
|
||||
|
||||
Map<String,List<String>> resHeaders= new HashMap<>();
|
||||
String path = "http://localhost:" + getPort() + "/test/bug5nnnn/bug57601.jsp";
|
||||
ByteChunk out = new ByteChunk();
|
||||
|
||||
int rc = getUrl(path, out, resHeaders);
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
String length = resHeaders.get("Content-Length").get(0);
|
||||
Assert.assertEquals(Long.parseLong(length), out.getLength());
|
||||
out.recycle();
|
||||
|
||||
rc = headUrl(path, out, resHeaders);
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
Assert.assertEquals(0, out.getLength());
|
||||
Assert.assertEquals(length, resHeaders.get("Content-Length").get(0));
|
||||
|
||||
tomcat.stop();
|
||||
}
|
||||
|
||||
public static int getUrl(String path, ByteChunk out,
|
||||
Map<String, List<String>> resHead) throws IOException {
|
||||
out.recycle();
|
||||
return TomcatBaseTest.getUrl(path, out, resHead);
|
||||
}
|
||||
|
||||
private static class TestCustomErrorClient extends SimpleHttpClient {
|
||||
|
||||
public TestCustomErrorClient(int port) {
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResponseBodyOK() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestCompressedClient extends SimpleHttpClient {
|
||||
|
||||
public TestCompressedClient(int port) {
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResponseBodyOK() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
|
||||
public class TestDefaultServletEncodingWithBom extends DefaultServletEncodingBaseTest {
|
||||
|
||||
@Override
|
||||
protected boolean getUseBom() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
|
||||
public class TestDefaultServletEncodingWithoutBom extends DefaultServletEncodingBaseTest {
|
||||
|
||||
@Override
|
||||
protected boolean getUseBom() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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.catalina.servlets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TestDefaultServletOptions extends ServletOptionsBaseTest {
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> inputs() {
|
||||
String[] urls = new String[] { COLLECTION_NAME, FILE_NAME, UNKNOWN_NAME };
|
||||
String[] methods = new String[] { "GET", "POST", "HEAD", "TRACE", "PUT", "DELETE" };
|
||||
|
||||
List<Object[]> result = new ArrayList<>();
|
||||
|
||||
for (Boolean listingsValue : booleans) {
|
||||
for (Boolean readOnlyValue : booleans) {
|
||||
for (Boolean traceValue : booleans) {
|
||||
for (String url : urls) {
|
||||
for (String method : methods) {
|
||||
result.add(new Object[] {
|
||||
listingsValue, readOnlyValue, traceValue, url, method } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Servlet createServlet() {
|
||||
return new DefaultServlet();
|
||||
}
|
||||
}
|
||||
185
test/org/apache/catalina/servlets/TestDefaultServletPut.java
Normal file
185
test/org/apache/catalina/servlets/TestDefaultServletPut.java
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
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 static org.apache.catalina.startup.SimpleHttpClient.CRLF;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Wrapper;
|
||||
import org.apache.catalina.startup.ExpandWar;
|
||||
import org.apache.catalina.startup.SimpleHttpClient;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.TomcatBaseTest;
|
||||
import org.apache.tomcat.util.buf.ByteChunk;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TestDefaultServletPut extends TomcatBaseTest {
|
||||
|
||||
private static final String START_TEXT= "Starting text";
|
||||
private static final String START_LEN = Integer.toString(START_TEXT.length());
|
||||
private static final String PATCH_TEXT= "Ending *";
|
||||
private static final String PATCH_LEN = Integer.toString(PATCH_TEXT.length());
|
||||
private static final String END_TEXT= "Ending * text";
|
||||
|
||||
@Parameterized.Parameters(name = "{index} rangeHeader [{0}]")
|
||||
public static Collection<Object[]> parameters() {
|
||||
List<Object[]> parameterSets = new ArrayList<>();
|
||||
|
||||
// Valid partial PUT
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.TRUE, END_TEXT });
|
||||
// Full PUT
|
||||
parameterSets.add(new Object[] {
|
||||
"", null, PATCH_TEXT });
|
||||
// Invalid range
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: apples=0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes00-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=9-7/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=-7/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=9-/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=9-X/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=0-5/" + CRLF, Boolean.FALSE, START_TEXT });
|
||||
parameterSets.add(new Object[] {
|
||||
"Content-Range: bytes=0-5/0x5" + CRLF, Boolean.FALSE, START_TEXT });
|
||||
|
||||
return parameterSets;
|
||||
}
|
||||
|
||||
|
||||
private File tempDocBase;
|
||||
|
||||
@Parameter(0)
|
||||
public String contentRangeHeader;
|
||||
|
||||
@Parameter(1)
|
||||
public Boolean contentRangeHeaderValid;
|
||||
|
||||
@Parameter(2)
|
||||
public String expectedEndText;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
tempDocBase = Files.createTempDirectory(getTemporaryDirectory().toPath(), "put").toFile();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Replaces the text at the start of START_TEXT with PATCH_TEXT.
|
||||
*/
|
||||
@Test
|
||||
public void testPut() throws Exception {
|
||||
// Configure a web app with a read/write default servlet
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
Context ctxt = tomcat.addContext("", tempDocBase.getAbsolutePath());
|
||||
|
||||
Wrapper w = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName());
|
||||
w.addInitParameter("readonly", "false");
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
// Disable caching
|
||||
ctxt.getResources().setCachingAllowed(false);
|
||||
|
||||
// Full PUT
|
||||
PutClient putClient = new PutClient(getPort());
|
||||
|
||||
putClient.setRequest(new String[] {
|
||||
"PUT /test.txt HTTP/1.1" + CRLF +
|
||||
"Host: localhost:" + getPort() + CRLF +
|
||||
"Content-Length: " + START_LEN + CRLF +
|
||||
CRLF +
|
||||
START_TEXT
|
||||
});
|
||||
putClient.connect();
|
||||
putClient.processRequest(false);
|
||||
Assert.assertTrue(putClient.isResponse201());
|
||||
putClient.disconnect();
|
||||
|
||||
putClient.reset();
|
||||
|
||||
// Partial PUT
|
||||
putClient.connect();
|
||||
putClient.setRequest(new String[] {
|
||||
"PUT /test.txt HTTP/1.1" + CRLF +
|
||||
"Host: localhost:" + getPort() + CRLF +
|
||||
contentRangeHeader +
|
||||
"Content-Length: " + PATCH_LEN + CRLF +
|
||||
CRLF +
|
||||
PATCH_TEXT
|
||||
});
|
||||
putClient.processRequest(false);
|
||||
if (contentRangeHeaderValid == null) {
|
||||
// Not present (so will do a full PUT, replacing the existing)
|
||||
Assert.assertTrue(putClient.isResponse204());
|
||||
} else if (contentRangeHeaderValid.booleanValue()) {
|
||||
// Valid
|
||||
Assert.assertTrue(putClient.isResponse204());
|
||||
} else {
|
||||
// Not valid
|
||||
Assert.assertTrue(putClient.isResponse400());
|
||||
}
|
||||
|
||||
// Check for the final resource
|
||||
String path = "http://localhost:" + getPort() + "/test.txt";
|
||||
ByteChunk responseBody = new ByteChunk();
|
||||
|
||||
int rc = getUrl(path, responseBody, null);
|
||||
|
||||
Assert.assertEquals(200, rc);
|
||||
Assert.assertEquals(expectedEndText, responseBody.toString());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
ExpandWar.deleteDir(tempDocBase, false);
|
||||
}
|
||||
|
||||
|
||||
private static class PutClient extends SimpleHttpClient {
|
||||
|
||||
public PutClient(int port) {
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isResponseBodyOK() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.Context;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.TomcatBaseTest;
|
||||
import org.apache.tomcat.util.buf.ByteChunk;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TestDefaultServletRangeRequests extends TomcatBaseTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index} rangeHeader [{0}]")
|
||||
public static Collection<Object[]> parameters() {
|
||||
|
||||
// Get the length of the file used for this test
|
||||
// It varies by platform due to line-endings
|
||||
File index = new File("test/webapp/index.html");
|
||||
long len = index.length();
|
||||
String strLen = Long.toString(len);
|
||||
|
||||
List<Object[]> parameterSets = new ArrayList<>();
|
||||
|
||||
parameterSets.add(new Object[] { "", Integer.valueOf(200), strLen, "" });
|
||||
// Invalid
|
||||
parameterSets.add(new Object[] { "bytes", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "bytes=", Integer.valueOf(416), "", "*/" + len });
|
||||
// Invalid with unknown type
|
||||
parameterSets.add(new Object[] { "unknown", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "unknown=", Integer.valueOf(416), "", "*/" + len });
|
||||
// Invalid ranges
|
||||
parameterSets.add(new Object[] { "bytes=-", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "bytes=10-b", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "bytes=b-10", Integer.valueOf(416), "", "*/" + len });
|
||||
// Invalid no equals
|
||||
parameterSets.add(new Object[] { "bytes 1-10", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "bytes1-10", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "bytes10-", Integer.valueOf(416), "", "*/" + len });
|
||||
parameterSets.add(new Object[] { "bytes-10", Integer.valueOf(416), "", "*/" + len });
|
||||
// Unknown types
|
||||
parameterSets.add(new Object[] { "unknown=1-2", Integer.valueOf(200), strLen, "" });
|
||||
parameterSets.add(new Object[] { "bytesX=1-2", Integer.valueOf(200), strLen, "" });
|
||||
parameterSets.add(new Object[] { "Xbytes=1-2", Integer.valueOf(200), strLen, "" });
|
||||
// Valid range
|
||||
parameterSets.add(new Object[] { "bytes=0-9", Integer.valueOf(206), "10", "0-9/" + len });
|
||||
parameterSets.add(new Object[] { "bytes=-100", Integer.valueOf(206), "100", (len - 100) + "-" + (len - 1) + "/" + len });
|
||||
parameterSets.add(new Object[] { "bytes=100-", Integer.valueOf(206), "" + (len - 100), "100-" + (len - 1) + "/" + len });
|
||||
// Valid range (too much)
|
||||
parameterSets.add(new Object[] { "bytes=0-1000", Integer.valueOf(206), strLen, "0-" + (len - 1) + "/" + len });
|
||||
parameterSets.add(new Object[] { "bytes=-1000", Integer.valueOf(206), strLen, "0-" + (len - 1) + "/" + len });
|
||||
return parameterSets;
|
||||
}
|
||||
|
||||
@Parameter(0)
|
||||
public String rangeHeader;
|
||||
@Parameter(1)
|
||||
public int responseCodeExpected;
|
||||
@Parameter(2)
|
||||
public String contentLengthExpected;
|
||||
@Parameter(3)
|
||||
public String responseRangeExpected;
|
||||
|
||||
@Test
|
||||
public void testRange() throws Exception {
|
||||
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
File appDir = new File("test/webapp");
|
||||
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
|
||||
Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName());
|
||||
ctxt.addServletMappingDecoded("/", "default");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
// Set up parameters
|
||||
String path = "http://localhost:" + getPort() + "/index.html";
|
||||
ByteChunk responseBody = new ByteChunk();
|
||||
Map<String,List<String>> responseHeaders = new HashMap<>();
|
||||
|
||||
int rc = getUrl(path, responseBody, buildRangeHeader(rangeHeader), responseHeaders);
|
||||
|
||||
// Check the result
|
||||
Assert.assertEquals(responseCodeExpected, rc);
|
||||
|
||||
if (contentLengthExpected.length() > 0) {
|
||||
String contentLength = responseHeaders.get("Content-Length").get(0);
|
||||
Assert.assertEquals(contentLengthExpected, contentLength);
|
||||
}
|
||||
|
||||
if (responseRangeExpected.length() > 0) {
|
||||
String responseRange = null;
|
||||
List<String> headerValues = responseHeaders.get("Content-Range");
|
||||
if (headerValues != null && headerValues.size() == 1) {
|
||||
responseRange = headerValues.get(0);
|
||||
}
|
||||
Assert.assertEquals("bytes " + responseRangeExpected, responseRange);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Map<String,List<String>> buildRangeHeader(String... headerValues) {
|
||||
Map<String,List<String>> requestHeaders = new HashMap<>();
|
||||
List<String> values = new ArrayList<>();
|
||||
for (String headerValue : headerValues) {
|
||||
if (headerValue.length() > 0) {
|
||||
values.add(headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (values.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
requestHeaders.put("range", values);
|
||||
|
||||
return requestHeaders;
|
||||
}
|
||||
}
|
||||
152
test/org/apache/catalina/servlets/TestWebdavServlet.java
Normal file
152
test/org/apache/catalina/servlets/TestWebdavServlet.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.catalina.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
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.buf.ByteChunk;
|
||||
import org.apache.tomcat.websocket.server.WsContextListener;
|
||||
|
||||
public class TestWebdavServlet extends TomcatBaseTest {
|
||||
|
||||
/*
|
||||
* Test attempting to access special paths (WEB-INF/META-INF) using WebdavServlet
|
||||
*/
|
||||
@Test
|
||||
public void testGetSpecials() throws Exception {
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
String contextPath = "/examples";
|
||||
|
||||
File appDir = new File(getBuildDirectory(), "webapps" + contextPath);
|
||||
// app dir is relative to server home
|
||||
org.apache.catalina.Context ctx =
|
||||
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
|
||||
|
||||
Tomcat.addServlet(ctx, "webdav", new WebdavServlet());
|
||||
ctx.addServletMappingDecoded("/*", "webdav");
|
||||
|
||||
tomcat.start();
|
||||
|
||||
final ByteChunk res = new ByteChunk();
|
||||
|
||||
int rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/WEB-INF/web.xml", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/WEB-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/WEB-INF/", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/META-INF/MANIFEST.MF", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/META-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Test https://bz.apache.org/bugzilla/show_bug.cgi?id=50026
|
||||
* Verify protection of special paths with re-mount of web app resource root.
|
||||
*/
|
||||
@Test
|
||||
public void testGetWithSubpathmount() throws Exception {
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
String contextPath = "/examples";
|
||||
|
||||
File appDir = new File(getBuildDirectory(), "webapps" + contextPath);
|
||||
// app dir is relative to server home
|
||||
org.apache.catalina.Context ctx =
|
||||
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
|
||||
|
||||
Tomcat.addServlet(ctx, "webdav", new WebdavServlet());
|
||||
ctx.addServletMappingDecoded("/webdav/*", "webdav");
|
||||
ctx.addApplicationListener(WsContextListener.class.getName());
|
||||
|
||||
tomcat.start();
|
||||
|
||||
final ByteChunk res = new ByteChunk();
|
||||
|
||||
// Make sure WebdavServlet isn't exposing special directories
|
||||
// by remounting the webapp under a sub-path
|
||||
|
||||
int rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/WEB-INF/web.xml", res, null);
|
||||
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/WEB-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/WEB-INF/", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/META-INF/MANIFEST.MF", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/META-INF/doesntexistanywhere", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
// Make sure WebdavServlet is serving resources
|
||||
// relative to the map/mount point
|
||||
final ByteChunk rootResource = new ByteChunk();
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/index.html", rootResource, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
|
||||
final ByteChunk subpathResource = new ByteChunk();
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/index.html", subpathResource, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
|
||||
Assert.assertEquals(rootResource.toString(), subpathResource.toString());
|
||||
|
||||
rc =getUrl("http://localhost:" + getPort() + contextPath +
|
||||
"/webdav/static/index.html", res, null);
|
||||
Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
|
||||
|
||||
}
|
||||
|
||||
public static int getUrl(String path, ByteChunk out,
|
||||
Map<String, List<String>> resHead) throws IOException {
|
||||
out.recycle();
|
||||
return TomcatBaseTest.getUrl(path, out, resHead);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.catalina.servlets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TestWebdavServletOptions extends ServletOptionsBaseTest {
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> inputs() {
|
||||
String[] urls = new String[] { COLLECTION_NAME, FILE_NAME, UNKNOWN_NAME };
|
||||
String[] methods = new String[] { "GET", "POST", "HEAD", "TRACE", "PUT", "DELETE",
|
||||
"MKCOL", "LOCK", "UNLOCK", "COPY", "MOVE", "PROPFIND", "PROPPATCH" };
|
||||
|
||||
List<Object[]> result = new ArrayList<>();
|
||||
|
||||
for (Boolean listingsValue : booleans) {
|
||||
for (Boolean readOnlyValue : booleans) {
|
||||
for (Boolean traceValue : booleans) {
|
||||
for (String url : urls) {
|
||||
for (String method : methods) {
|
||||
result.add(new Object[] {
|
||||
listingsValue, readOnlyValue, traceValue, url, method } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Servlet createServlet() {
|
||||
return new WebdavServlet();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user