You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/03/13 11:02:34 UTC

svn commit: r1666386 - in /tomcat/trunk: java/org/apache/coyote/http11/ java/org/apache/coyote/http11/filters/ test/org/apache/coyote/http11/filters/

Author: markt
Date: Fri Mar 13 10:02:34 2015
New Revision: 1666386

URL: http://svn.apache.org/r1666386
Log:
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=57570
Make processing of trailer headers for chunked input optional.
Trailer headers to process must be added to the allowedTrailerHeader list or they will be ignored

Modified:
    tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
    tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java
    tomcat/trunk/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
    tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
    tomcat/trunk/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java

Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java?rev=1666386&r1=1666385&r2=1666386&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java Fri Mar 13 10:02:34 2015
@@ -18,6 +18,13 @@ package org.apache.coyote.http11;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.http.HttpUpgradeHandler;
 
@@ -196,6 +203,59 @@ public abstract class AbstractHttp11Prot
     }
 
 
+    /**
+     * The names of headers that are allowed to be sent via a trailer when using
+     * chunked encoding. They are stored in lower case.
+     */
+    private Set<String> allowedTrailerHeaders =
+            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+    public void setAllowedTrailerHeaders(String commaSeparatedHeaders) {
+        // Jump through some hoops so we don't end up with an empty set while
+        // doing updates.
+        Set<String> toRemove = new HashSet<>();
+        toRemove.addAll(allowedTrailerHeaders);
+        if (commaSeparatedHeaders != null) {
+            String[] headers = commaSeparatedHeaders.split(",");
+            for (String header : headers) {
+                String trimmedHeader = header.trim().toLowerCase(Locale.ENGLISH);
+                if (toRemove.contains(trimmedHeader)) {
+                    toRemove.remove(trimmedHeader);
+                } else {
+                    allowedTrailerHeaders.add(trimmedHeader);
+                }
+            }
+            allowedTrailerHeaders.removeAll(toRemove);
+        }
+    }
+    public String getAllowedTrailerHeaders() {
+        // Chances of a size change between these lines are small enough that a
+        // sync is unnecessary.
+        List<String> copy = new ArrayList<>(allowedTrailerHeaders.size());
+        copy.addAll(allowedTrailerHeaders);
+        StringBuilder result = new StringBuilder();
+        boolean first = true;
+        for (String header : copy) {
+            if (first) {
+                first = false;
+            } else {
+                result.append(',');
+            }
+            result.append(header);
+        }
+        return result.toString();
+    }
+    public void addAllowedTrailerHeader(String header) {
+        if (header != null) {
+            allowedTrailerHeaders.add(header.trim().toLowerCase(Locale.ENGLISH));
+        }
+    }
+    public void removeAllowedTrailerHeader(String header) {
+        if (header != null) {
+            allowedTrailerHeaders.remove(header.trim().toLowerCase(Locale.ENGLISH));
+        }
+    }
+
+
     // ------------------------------------------------ HTTP specific properties
     // ------------------------------------------ passed through to the EndPoint
 
@@ -271,8 +331,8 @@ public abstract class AbstractHttp11Prot
         @Override
         public Http11Processor createProcessor() {
             Http11Processor processor = new Http11Processor(
-                    proto.getMaxHttpHeaderSize(), proto.getEndpoint(),
-                    proto.getMaxTrailerSize(), proto.getMaxExtensionSize(),
+                    proto.getMaxHttpHeaderSize(), proto.getEndpoint(), proto.getMaxTrailerSize(),
+                    proto.allowedTrailerHeaders, proto.getMaxExtensionSize(),
                     proto.getMaxSwallowSize());
             proto.configureProcessor(processor);
             register(processor);

Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java?rev=1666386&r1=1666385&r2=1666386&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java Fri Mar 13 10:02:34 2015
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.nio.ByteBuffer;
 import java.util.Locale;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
@@ -238,8 +239,8 @@ public class Http11Processor extends Abs
     protected SSLSupport sslSupport;
 
 
-    public Http11Processor(int maxHttpHeaderSize, AbstractEndpoint<?> endpoint,
-            int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) {
+    public Http11Processor(int maxHttpHeaderSize, AbstractEndpoint<?> endpoint,int maxTrailerSize,
+            Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
 
         super(endpoint);
         userDataHelper = new UserDataHelper(log);
@@ -255,8 +256,8 @@ public class Http11Processor extends Abs
         outputBuffer.addFilter(new IdentityOutputFilter());
 
         // Create and add the chunked filters.
-        inputBuffer.addFilter(
-                new ChunkedInputFilter(maxTrailerSize, maxExtensionSize, maxSwallowSize));
+        inputBuffer.addFilter(new ChunkedInputFilter(maxTrailerSize, allowedTrailerHeaders,
+                maxExtensionSize, maxSwallowSize));
         outputBuffer.addFilter(new ChunkedOutputFilter());
 
         // Create and add the void filters.

Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/BufferedInputFilter.java?rev=1666386&r1=1666385&r2=1666386&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/filters/BufferedInputFilter.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/filters/BufferedInputFilter.java Fri Mar 13 10:02:34 2015
@@ -59,6 +59,8 @@ public class BufferedInputFilter impleme
     /**
      * Set the buffering limit. This should be reset every time the buffer is
      * used.
+     *
+     * @param limit The maximum number of bytes that will be buffered
      */
     public void setLimit(int limit) {
         if (buffered == null) {

Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java?rev=1666386&r1=1666385&r2=1666386&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java Fri Mar 13 10:02:34 2015
@@ -19,6 +19,8 @@ package org.apache.coyote.http11.filters
 import java.io.EOFException;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import java.util.Set;
 
 import org.apache.coyote.InputBuffer;
 import org.apache.coyote.Request;
@@ -146,10 +148,14 @@ public class ChunkedInputFilter implemen
     private boolean error;
 
 
+    private final Set<String> allowedTrailerHeaders;
+
     // ----------------------------------------------------------- Constructors
 
-    public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) {
+    public ChunkedInputFilter(int maxTrailerSize, Set<String> allowedTrailerHeaders,
+            int maxExtensionSize, int maxSwallowSize) {
         this.trailingHeaders.setLimit(maxTrailerSize);
+        this.allowedTrailerHeaders = allowedTrailerHeaders;
         this.maxExtensionSize = maxExtensionSize;
         this.maxTrailerSize = maxTrailerSize;
         this.maxSwallowSize = maxSwallowSize;
@@ -469,7 +475,7 @@ public class ChunkedInputFilter implemen
         }
 
         // Mark the current buffer position
-        int start = trailingHeaders.getEnd();
+        int startPos = trailingHeaders.getEnd();
 
         //
         // Reading the header name
@@ -500,11 +506,7 @@ public class ChunkedInputFilter implemen
             pos++;
 
         }
-        MessageBytes headerValue = headers.addValue(trailingHeaders.getBytes(),
-                start, trailingHeaders.getEnd() - start);
-
-        // Mark the current buffer position
-        start = trailingHeaders.getEnd();
+        int colonPos = trailingHeaders.getEnd();
 
         //
         // Reading the header value (which can be spanned over multiple lines)
@@ -592,9 +594,16 @@ public class ChunkedInputFilter implemen
 
         }
 
-        // Set the header value
-        headerValue.setBytes(trailingHeaders.getBytes(), start,
-                lastSignificantChar - start);
+        String headerName = new String(trailingHeaders.getBytes(), startPos,
+                colonPos - startPos, StandardCharsets.ISO_8859_1);
+
+        if (allowedTrailerHeaders.contains(headerName.trim().toLowerCase(Locale.ENGLISH))) {
+            MessageBytes headerValue = headers.addValue(headerName);
+
+            // Set the header value
+            headerValue.setBytes(trailingHeaders.getBytes(), colonPos,
+                    lastSignificantChar - colonPos);
+        }
 
         return true;
     }

Modified: tomcat/trunk/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java?rev=1666386&r1=1666385&r2=1666386&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java (original)
+++ tomcat/trunk/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java Fri Mar 13 10:02:34 2015
@@ -104,6 +104,9 @@ public class TestChunkedInputFilter exte
         // No file system docBase required
         Context ctx = tomcat.addContext("", null);
 
+        // Configure allowed trailer headers
+        tomcat.getConnector().setProperty("allowedTrailerHeaders", "X-Trailer1,X-Trailer2");
+
         EchoHeaderServlet servlet = new EchoHeaderServlet(expectPass);
         Tomcat.addServlet(ctx, "servlet", servlet);
         ctx.addServletMapping("/", "servlet");



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org