This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push: new 1aa7f56bf5 fix: MUST ignore Range header field if method is out-of-range-support 1aa7f56bf5 is described below commit 1aa7f56bf55c06e76ab19ced9d90353ed3d9e788 Author: Chenjp <ch...@msn.com> AuthorDate: Sat Nov 30 00:38:39 2024 +0800 fix: MUST ignore Range header field if method is out-of-range-support per RFC 9110 - Section 14, currently GET is the only method. A server that turn off accept-range support should ignore Range header field. --- .../apache/catalina/servlets/DefaultServlet.java | 12 +- .../catalina/servlets/TestDefaultServletRfc14.java | 137 +++++++++++++++++++++ 2 files changed, 146 insertions(+), 3 deletions(-) diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java index 2d8196f6b4..ef7c257daf 100644 --- a/java/org/apache/catalina/servlets/DefaultServlet.java +++ b/java/org/apache/catalina/servlets/DefaultServlet.java @@ -1444,9 +1444,15 @@ public class DefaultServlet extends HttpServlet { protected Ranges parseRange(HttpServletRequest request, HttpServletResponse response, WebResource resource) throws IOException { - // Range headers are only valid on GET requests. That implies they are - // also valid on HEAD requests. This method is only called by doGet() - // and doHead() so no need to check the request method. + if (useAcceptRanges == false) { + // Not supported feature, ignore + return FULL; + } + if (!"GET".equals(request.getMethod())) { + // RFC 9110 - Section 14.2: GET is the only method for which range handling is defined. + // Otherwise MUST ignore a Range header field + return FULL; + } // Checking If-Range String headerValue = request.getHeader("If-Range"); diff --git a/test/org/apache/catalina/servlets/TestDefaultServletRfc14.java b/test/org/apache/catalina/servlets/TestDefaultServletRfc14.java new file mode 100644 index 0000000000..f38e0bf26d --- /dev/null +++ b/test/org/apache/catalina/servlets/TestDefaultServletRfc14.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.servlets; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +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.ByteChunk; + +public class TestDefaultServletRfc14 extends TomcatBaseTest { + @Test + public void test_14_optional() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp"); + Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); + + Wrapper w = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + w.addInitParameter("useAcceptRanges", "false"); + ctxt.addServletMappingDecoded("/", "default"); + + tomcat.start(); + + String path = "http://localhost:" + getPort() + "/index.html"; + ByteChunk responseBody = new ByteChunk(); + Map<String,List<String>> responseHeaders = new HashMap<>(); + Map<String,List<String>> requestHeaders = new HashMap<>(); + + String rangeHeader = "bytes=0-10"; + // Get and Head + + requestHeaders.computeIfAbsent("Range", (k) -> List.of(rangeHeader)); + int rc = getUrl(path, responseBody, requestHeaders, responseHeaders); + Assert.assertEquals( + "RFC 9110 - 14: Range requests is turn of, SC_OK of GET is expected", + HttpServletResponse.SC_OK, rc); + Assert.assertFalse( + "RFC 9110 - 14: Range requests is turn of, absence of header `Accept-Ranges: bytes` is expected", + responseHeaders.containsKey("Accept-Ranges") && responseHeaders.get("Accept-Ranges").contains("bytes")); + + tomcat.stop(); + } + + @Test + public void test_14_2_range_handling_defined_methods() throws Exception { + // GET is the only method for which range handling is defined. + + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp"); + Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); + + Wrapper w = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + w.addInitParameter("useAcceptRanges", "true"); + ctxt.addServletMappingDecoded("/", "default"); + + tomcat.start(); + + String path = "http://localhost:" + getPort() + "/index.html"; + ByteChunk responseBody = new ByteChunk(); + Map<String,List<String>> responseHeaders = new HashMap<>(); + Map<String,List<String>> requestHeaders = new HashMap<>(); + + String rangeHeader = "bytes=0-10"; + // Get and Head + + requestHeaders.computeIfAbsent("Range", (k) -> List.of(rangeHeader)); + int rc = getUrl(path, responseBody, requestHeaders, responseHeaders); + Assert.assertEquals("Range requests is turn on, SC_PARTIAL_CONTENT of GET is expected", + HttpServletResponse.SC_PARTIAL_CONTENT, rc); + Assert.assertTrue("Range requests is turn on, header `Accept-Ranges: bytes` is expected", + responseHeaders.containsKey("Accept-Ranges") && responseHeaders.get("Accept-Ranges").contains("bytes")); + + rc = methodUrl(path, responseBody, DEFAULT_CLIENT_TIMEOUT_MS, requestHeaders, responseHeaders, "HEAD"); + Assert.assertEquals("Range requests is turn on, SC_OK of HEAD is expected", HttpServletResponse.SC_OK, rc); + Assert.assertTrue("Range requests is turn on, header `Accept-Ranges: bytes` is expected", + responseHeaders.containsKey("Accept-Ranges") && responseHeaders.get("Accept-Ranges").contains("bytes")); + + tomcat.stop(); + } + + @Test + public void test_14_2_unsupported_rangeunit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp"); + Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); + + Wrapper w = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + w.addInitParameter("useAcceptRanges", "true"); + ctxt.addServletMappingDecoded("/", "default"); + + tomcat.start(); + + String path = "http://localhost:" + getPort() + "/index.html"; + ByteChunk responseBody = new ByteChunk(); + Map<String,List<String>> responseHeaders = new HashMap<>(); + Map<String,List<String>> requestHeaders = new HashMap<>(); + + String rangeHeader = "Chars=0-10"; + // Get and Head + + requestHeaders.computeIfAbsent("Range", (k) -> List.of(rangeHeader)); + int rc = getUrl(path, responseBody, requestHeaders, responseHeaders); + Assert.assertEquals( + "RFC 9110 - 14.2: An origin server MUST ignore a Range header field that contains a range unit it does not understand. `Chars` is not a understandable RangeUnit, SC_OK is expected", + HttpServletResponse.SC_OK, rc); + + tomcat.stop(); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org