This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/master by this push:
new bef507e Improve entity tag handling
bef507e is described below
commit bef507e1b7ac2eb0ff012d0d40035e218a5839cc
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Aug 11 15:27:45 2020 +0100
Improve entity tag handling
---
conf/web.xml | 8 ++
.../apache/catalina/servlets/DefaultServlet.java | 116 +++++++++--------
.../apache/tomcat/util/http/parser/EntityTag.java | 82 ++++++++++++
.../TestDefaultServletIfMatchRequests.java | 143 +++++++++++++++++----
webapps/docs/changelog.xml | 7 +
webapps/docs/default-servlet.xml | 8 +-
6 files changed, 284 insertions(+), 80 deletions(-)
diff --git a/conf/web.xml b/conf/web.xml
index a685947..b5e0b30 100644
--- a/conf/web.xml
+++ b/conf/web.xml
@@ -114,6 +114,14 @@
<!-- with a Range header as a partial PUT? Note -->
<!-- that RFC 7233 clarified that Range headers are -->
<!-- only valid for GET requests. [true] -->
+ <!-- -->
+ <!-- useWeakComparisonWithIfMatch -->
+ <!-- When comparing entity tags for If-Match -->
+ <!-- headers should a weak comparison be used -->
+ <!-- rather than the strong comparison required by -->
+ <!-- RFC 7232? A weak comparison is used by default -->
+ <!-- since the default resources implementation -->
+ <!-- generates weak entity tags. [true] -->
<servlet>
<servlet-name>default</servlet-name>
diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java
b/java/org/apache/catalina/servlets/DefaultServlet.java
index 466f1df..513353f 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -43,7 +43,7 @@ import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
-import java.util.StringTokenizer;
+import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -81,6 +81,7 @@ import org.apache.catalina.webresources.CachedResource;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.http.ResponseUtil;
import org.apache.tomcat.util.http.parser.ContentRange;
+import org.apache.tomcat.util.http.parser.EntityTag;
import org.apache.tomcat.util.http.parser.Ranges;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.security.Escape;
@@ -279,6 +280,8 @@ public class DefaultServlet extends HttpServlet {
*/
private boolean allowPartialPut = true;
+ protected boolean useWeakComparisonWithIfMatch = true;
+
// --------------------------------------------------------- Public Methods
@@ -297,27 +300,31 @@ public class DefaultServlet extends HttpServlet {
@Override
public void init() throws ServletException {
- if (getServletConfig().getInitParameter("debug") != null)
+ if (getServletConfig().getInitParameter("debug") != null) {
debug =
Integer.parseInt(getServletConfig().getInitParameter("debug"));
+ }
- if (getServletConfig().getInitParameter("input") != null)
+ if (getServletConfig().getInitParameter("input") != null) {
input =
Integer.parseInt(getServletConfig().getInitParameter("input"));
+ }
- if (getServletConfig().getInitParameter("output") != null)
+ if (getServletConfig().getInitParameter("output") != null) {
output =
Integer.parseInt(getServletConfig().getInitParameter("output"));
+ }
listings =
Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));
- if (getServletConfig().getInitParameter("readonly") != null)
+ if (getServletConfig().getInitParameter("readonly") != null) {
readOnly =
Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));
+ }
compressionFormats = parseCompressionFormats(
getServletConfig().getInitParameter("precompressed"),
getServletConfig().getInitParameter("gzip"));
- if (getServletConfig().getInitParameter("sendfileSize") != null)
- sendfileSize =
-
Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;
+ if (getServletConfig().getInitParameter("sendfileSize") != null) {
+ sendfileSize =
Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;
+ }
fileEncoding = getServletConfig().getInitParameter("fileEncoding");
if (fileEncoding == null) {
@@ -331,23 +338,31 @@ public class DefaultServlet extends HttpServlet {
}
}
- if (getServletConfig().getInitParameter("useBomIfPresent") != null)
- useBomIfPresent = Boolean.parseBoolean(
- getServletConfig().getInitParameter("useBomIfPresent"));
+ if (getServletConfig().getInitParameter("useBomIfPresent") != null) {
+ useBomIfPresent =
Boolean.parseBoolean(getServletConfig().getInitParameter("useBomIfPresent"));
+ }
globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
contextXsltFile =
getServletConfig().getInitParameter("contextXsltFile");
localXsltFile = getServletConfig().getInitParameter("localXsltFile");
readmeFile = getServletConfig().getInitParameter("readmeFile");
- if (getServletConfig().getInitParameter("useAcceptRanges") != null)
+ if (getServletConfig().getInitParameter("useAcceptRanges") != null) {
useAcceptRanges =
Boolean.parseBoolean(getServletConfig().getInitParameter("useAcceptRanges"));
+ }
+
+ if
(getServletConfig().getInitParameter("useWeakComparisonWithIfMatch") != null) {
+ useWeakComparisonWithIfMatch = Boolean.parseBoolean(
+
getServletConfig().getInitParameter("useWeakComparisonWithIfMatch"));
+ }
// Sanity check on the specified buffer sizes
- if (input < 256)
+ if (input < 256) {
input = 256;
- if (output < 256)
+ }
+ if (output < 256) {
output = 256;
+ }
if (debug > 0) {
log("DefaultServlet.init: input buffer size=" + input +
@@ -355,8 +370,7 @@ public class DefaultServlet extends HttpServlet {
}
// Load the web resources
- resources = (WebResourceRoot) getServletContext().getAttribute(
- Globals.RESOURCES_ATTR);
+ resources = (WebResourceRoot)
getServletContext().getAttribute(Globals.RESOURCES_ATTR);
if (resources == null) {
throw new
UnavailableException(sm.getString("defaultServlet.noResources"));
@@ -2168,30 +2182,22 @@ public class DefaultServlet extends HttpServlet {
eTag = eTag.substring(2);
}
String headerValue = request.getHeader("If-Match");
- if (headerValue != null) {
- if (headerValue.indexOf('*') == -1) {
+ if (headerValue != null && !headerValue.equals("*")) {
- StringTokenizer commaTokenizer = new
StringTokenizer(headerValue, ",");
- boolean conditionSatisfied = false;
-
- while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
- String currentToken = commaTokenizer.nextToken();
- currentToken = currentToken.trim();
- if (currentToken.startsWith("W/")) {
- currentToken = currentToken.substring(2);
- }
- if (currentToken.equals(eTag))
- conditionSatisfied = true;
- }
-
- // If none of the given ETags match, 412 Precondition failed is
- // sent back
- if (!conditionSatisfied) {
- response.sendError
- (HttpServletResponse.SC_PRECONDITION_FAILED);
- return false;
+ Set<String> eTags = EntityTag.parseEntityTag(new
StringReader(headerValue), useWeakComparisonWithIfMatch);
+ if (eTags == null) {
+ if (debug > 10) {
+ log("DefaultServlet.checkIfMatch: Invalid header value ["
+ headerValue + "]");
}
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return false;
+ }
+ // If none of the given ETags match, 412 Precondition failed is
+ // sent back
+ if (!eTags.contains(eTag)) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return false;
}
}
return true;
@@ -2250,46 +2256,48 @@ public class DefaultServlet extends HttpServlet {
throws IOException {
String eTag = generateETag(resource);
+ // If-None-Match uses weak comparison so strip the weak indicator if
+ // present
+ if (eTag.startsWith("W/")) {
+ eTag = eTag.substring(2);
+ }
String headerValue = request.getHeader("If-None-Match");
if (headerValue != null) {
boolean conditionSatisfied = false;
if (!headerValue.equals("*")) {
-
- StringTokenizer commaTokenizer =
- new StringTokenizer(headerValue, ",");
-
- while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
- String currentToken = commaTokenizer.nextToken();
- if (currentToken.trim().equals(eTag))
- conditionSatisfied = true;
+ Set<String> eTags = EntityTag.parseEntityTag(new
StringReader(headerValue), true);
+ if (eTags == null) {
+ if (debug > 10) {
+ log("DefaultServlet.checkIfNoneMatch: Invalid header
value [" + headerValue + "]");
+ }
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return false;
}
-
+ conditionSatisfied = eTags.contains(eTag);
} else {
conditionSatisfied = true;
}
if (conditionSatisfied) {
-
// For GET and HEAD, we should respond with
// 304 Not Modified.
// For every other method, 412 Precondition Failed is sent
// back.
- if ( ("GET".equals(request.getMethod()))
- || ("HEAD".equals(request.getMethod())) ) {
+ if ("GET".equals(request.getMethod()) ||
"HEAD".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", eTag);
-
- return false;
+ } else {
+
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
}
- response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
return true;
}
+
/**
* Check if the if-unmodified-since condition is satisfied.
*
@@ -2325,7 +2333,9 @@ public class DefaultServlet extends HttpServlet {
/**
* Provides the entity tag (the ETag header) for the given resource.
* Intended to be over-ridden by custom DefaultServlet implementations that
- * wish to use an alternative format for the entity tag.
+ * wish to use an alternative format for the entity tag. Such custom
+ * implementations that generate strong entity tags may also want to change
+ * the default value of {@link #useWeakComparisonWithIfMatch}.
*
* @param resource The resource for which an entity tag is required.
*
diff --git a/java/org/apache/tomcat/util/http/parser/EntityTag.java
b/java/org/apache/tomcat/util/http/parser/EntityTag.java
new file mode 100644
index 0000000..abab6b7
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/parser/EntityTag.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http.parser;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Set;
+
+public class EntityTag {
+
+ /**
+ *
+ * @param input
+ * @param includeWeak
+ *
+ * @return The set of parsed entity tags or {@code null} if the header is
+ * invalid
+ *
+ * @throws IOException
+ */
+ public static Set<String> parseEntityTag(StringReader input, boolean
includeWeak) throws IOException {
+
+ HashSet<String> result = new HashSet<>();
+
+ while (true) {
+ boolean strong = false;
+ HttpParser.skipLws(input);
+
+ switch (HttpParser.skipConstant(input, "W/")) {
+ case EOF:
+ // Empty values are invalid
+ return null;
+ case NOT_FOUND:
+ strong = true;
+ break;
+ case FOUND:
+ strong = false;
+ break;
+ }
+
+ // Note: RFC 2616 allowed quoted string
+ // RFC 7232 does not allow " in the entity-tag
+ String value = HttpParser.readQuotedString(input, true);
+ if (value == null) {
+ // Not a quoted string so the header is invalid
+ return null;
+ }
+
+ if (strong || includeWeak) {
+ result.add(value);
+ }
+
+ HttpParser.skipLws(input);
+
+ switch (HttpParser.skipConstant(input, ",")) {
+ case EOF:
+ return result;
+ case NOT_FOUND:
+ // Not EOF and not "," so must be invalid
+ return null;
+ case FOUND:
+ // Parse next entry
+ break;
+ }
+ }
+ }
+}
diff --git
a/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java
b/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java
index ffd3e92..102fc0f 100644
--- a/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java
+++ b/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java
@@ -19,6 +19,7 @@ package org.apache.catalina.servlets;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,6 +31,7 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.Context;
+import org.apache.catalina.WebResource;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
@@ -37,52 +39,126 @@ import org.apache.tomcat.util.buf.ByteChunk;
@RunWith(Parameterized.class)
public class TestDefaultServletIfMatchRequests extends TomcatBaseTest {
- @Parameterized.Parameters(name = "{index} ifMatchHeader [{0}]")
+ private static final Integer RC_200 = Integer.valueOf(200);
+ private static final Integer RC_304 = Integer.valueOf(304);
+ private static final Integer RC_400 = Integer.valueOf(400);
+ private static final Integer RC_412 = Integer.valueOf(412);
+
+ private static final String[] CONCAT = new String[] { ",", " ,", ", ", " ,
" };
+
+ @Parameterized.Parameters(name = "{index} resource-strong [{0}],
matchHeader [{1}]")
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");
- String strongETag = "\"" + index.length() + "-" +
index.lastModified() + "\"";
- String weakETag = "W/" + strongETag;
+ String resourceETagStrong = "\"" + index.length() + "-" +
index.lastModified() + "\"";
+ String resourceETagWeak = "W/" + resourceETagStrong;
+
+ String otherETagStrong = "\"123456789\"";
+ String otherETagWeak = "\"123456789\"";
List<Object[]> parameterSets = new ArrayList<>();
- parameterSets.add(new Object[] { null, Integer.valueOf(200) });
- parameterSets.add(new Object[] { "*", Integer.valueOf(200) });
- parameterSets.add(new Object[] { weakETag, Integer.valueOf(200) });
- parameterSets.add(new Object[] { strongETag, Integer.valueOf(200) });
- parameterSets.add(new Object[] { weakETag + ",123456789",
Integer.valueOf(200) });
- parameterSets.add(new Object[] { strongETag + ",123456789",
Integer.valueOf(200) });
- parameterSets.add(new Object[] { weakETag + " ,123456789",
Integer.valueOf(200) });
- parameterSets.add(new Object[] { strongETag + " ,123456789",
Integer.valueOf(200) });
- parameterSets.add(new Object[] { "123456789," + weakETag,
Integer.valueOf(200) });
- parameterSets.add(new Object[] { "123456789," + strongETag,
Integer.valueOf(200) });
- parameterSets.add(new Object[] { "123456789, " + weakETag,
Integer.valueOf(200) });
- parameterSets.add(new Object[] { "123456789, " + strongETag,
Integer.valueOf(200) });
- parameterSets.add(new Object[] { "123456789", Integer.valueOf(412) });
- parameterSets.add(new Object[] { "W/123456789", Integer.valueOf(412)
});
- parameterSets.add(new Object[] { "W/", Integer.valueOf(412) });
+ for (Boolean resourceWithStrongETag : booleans) {
+ // No match header
+ parameterSets.add(new Object[] { resourceWithStrongETag, null,
RC_200, RC_200 });
+
+ // match header is invalid
+ parameterSets.add(new Object[] { resourceWithStrongETag, "",
RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag, "W",
RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag, "W/",
RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag, "w/" +
resourceETagStrong, RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagStrong + " x", RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagStrong + "x", RC_400, RC_400 });
+ for (String concat : CONCAT) {
+ parameterSets.add(new Object[] { resourceWithStrongETag,
concat + resourceETagStrong, RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
concat + resourceETagWeak, RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagStrong + concat, RC_400, RC_400 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagWeak + concat, RC_400, RC_400 });
+ }
+
+ // match header always matches resource (leading and trailing
space should be ignored)
+ parameterSets.add(new Object[] { resourceWithStrongETag, "*",
RC_200, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag, " *",
RC_200, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag, "* ",
RC_200, RC_304 });
+
+ // match header never matches resource
+ parameterSets.add(new Object[] { resourceWithStrongETag,
otherETagStrong, RC_412, RC_200 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
otherETagWeak, RC_412, RC_200 });
+
+ // match header includes weak tag
+ // Results depend on whether strong or weak comparison is used
+ Integer rcWeak;
+ if (resourceWithStrongETag.booleanValue()) {
+ rcWeak = RC_412;
+ } else {
+ rcWeak = RC_200;
+ }
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagWeak, rcWeak, RC_304 });
+ for (String concat : CONCAT) {
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagWeak + concat + otherETagWeak,
+ rcWeak, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagWeak + concat + otherETagStrong,
+ rcWeak, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
otherETagWeak + concat + resourceETagWeak,
+ rcWeak, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
otherETagStrong + concat + resourceETagWeak,
+ rcWeak, RC_304 });
+ }
+
+ // match header includes strong tag
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagStrong, RC_200, RC_304 });
+ for (String concat : CONCAT) {
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagStrong + concat + otherETagWeak,
+ RC_200, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
resourceETagStrong + concat + otherETagStrong,
+ RC_200, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
otherETagWeak + concat + resourceETagStrong,
+ RC_200, RC_304 });
+ parameterSets.add(new Object[] { resourceWithStrongETag,
otherETagStrong + concat + resourceETagStrong,
+ RC_200, RC_304 });
+ }
+ }
return parameterSets;
}
@Parameter(0)
- public String ifMatchHeader;
+ public boolean resourceHasStrongETag;
@Parameter(1)
- public int responseCodeExpected;
+ public String matchHeader;
+
+ @Parameter(2)
+ public int ifMatchResponseCode;
+ @Parameter(3)
+ public int ifNoneMatchResponseCode;
@Test
public void testIfMatch() throws Exception {
+ doMatchTest("If-Match", ifMatchResponseCode);
+ }
+
+
+ @Test
+ public void testIfNoneMatch() throws Exception {
+ doMatchTest("If-None-Match", ifNoneMatchResponseCode);
+ }
+
+ private void doMatchTest(String headerName, int responseCodeExpected)
throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp");
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
- Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName());
+ if (resourceHasStrongETag) {
+ Tomcat.addServlet(ctxt, "default",
DefaultWithStrongETag.class.getName());
+ } else {
+ Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName());
+ }
ctxt.addServletMappingDecoded("/", "default");
tomcat.start();
@@ -93,11 +169,9 @@ public class TestDefaultServletIfMatchRequests extends
TomcatBaseTest {
Map<String,List<String>> responseHeaders = new HashMap<>();
Map<String,List<String>> requestHeaders = null;
- if (ifMatchHeader != null) {
- requestHeaders = new HashMap<>();
- List<String> values = new ArrayList<>(1);
- values.add(ifMatchHeader);
- requestHeaders.put("If-Match", values);
+ if (matchHeader != null) {
+ List<String> values = Collections.singletonList(matchHeader);
+ requestHeaders = Collections.singletonMap(headerName, values);
}
int rc = getUrl(path, responseBody, requestHeaders, responseHeaders);
@@ -105,4 +179,21 @@ public class TestDefaultServletIfMatchRequests extends
TomcatBaseTest {
// Check the result
Assert.assertEquals(responseCodeExpected, rc);
}
+
+
+ public static class DefaultWithStrongETag extends DefaultServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ public DefaultWithStrongETag() {
+ useWeakComparisonWithIfMatch = false;
+ }
+
+ @Override
+ protected String generateETag(WebResource resource) {
+ String weakETag = super.generateETag(resource);
+ // Make it a strong ETag
+ return weakETag.substring(2);
+ }
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f3bef3f..53adb16 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -74,6 +74,13 @@
overridden (<code>generateETag()</code>) should a custom entity tag
format be required. (markt)
</add>
+ <fix>
+ Improve the validation of entity tags provided with conditional
+ requests. Requests with headers that contain invalid entity tags will
be
+ rejected with a 400 response code. Improve the matching algorithm used
+ to compare entity tags in conditional requests with the entity tag for
+ the requested resource. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
diff --git a/webapps/docs/default-servlet.xml b/webapps/docs/default-servlet.xml
index b6d1e62..b382895 100644
--- a/webapps/docs/default-servlet.xml
+++ b/webapps/docs/default-servlet.xml
@@ -92,7 +92,7 @@ Tomcat.</p>
<property name="debug">
Debugging level. It is not very useful unless you are a tomcat
developer. As
- of this writing, useful values are 0, 1, 11, 1000. [0]
+ of this writing, useful values are 0, 1, 11. [0]
</property>
<property name="listings">
If no welcome file is present, can a directory listing be
@@ -206,6 +206,12 @@ Tomcat.</p>
partial PUT? Note that RFC 7233 clarified that Range headers are only
valid for GET requests. [true]
</property>
+ <property name="useWeakComparisonWithIfMatch">
+ When comparing entity tags for If-Match headers should a weak
comparison
+ be used rather than the strong comparison required by RFC 7232? A weak
+ comparison is used by default since the default resources
implementation
+ generates weak entity tags. [true]
+ </property>
</properties>
</section>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]