Author: kkolinko
Date: Mon Nov  7 10:46:14 2011
New Revision: 1198696

URL: http://svn.apache.org/viewvc?rev=1198696&view=rev
Log:
Introduce new request attribute to be used to mark request if there was a 
failure during parameter parsing,
and a Filter that triggers parameter parsing and rejects requests marked with 
that attribute.

Added:
    tomcat/trunk/java/org/apache/catalina/filters/FailedRequestFilter.java   
(with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/Globals.java
    tomcat/trunk/java/org/apache/catalina/connector/Request.java
    tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java

Modified: tomcat/trunk/java/org/apache/catalina/Globals.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Globals.java?rev=1198696&r1=1198695&r2=1198696&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Globals.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Globals.java Mon Nov  7 10:46:14 2011
@@ -226,6 +226,17 @@ public final class Globals {
 
 
     /**
+     * The request attribute that is set to {@code Boolean.TRUE} if some 
request
+     * parameters have been ignored during request parameters parsing. It can
+     * happen, for example, if there is a limit on the total count of parseable
+     * parameters, or if parameter cannot be decoded, or any other error
+     * happened during parameter parsing.
+     */
+    public static final String PARAMETER_PARSE_FAILED_ATTR =
+        "org.apache.catalina.parameter_parse_failed";
+
+
+    /**
      * The master flag which controls strict servlet specification
      * compliance.
      */

Modified: tomcat/trunk/java/org/apache/catalina/connector/Request.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Request.java?rev=1198696&r1=1198695&r2=1198696&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Request.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Request.java Mon Nov  7 
10:46:14 2011
@@ -2383,6 +2383,12 @@ public class Request
         }
     }
 
+    private void checkParameterParseFailed() {
+        if (getCoyoteRequest().getParameters().isParseFailed()) {
+            setAttribute(Globals.PARAMETER_PARSE_FAILED_ATTR, Boolean.TRUE);
+        }
+    }
+
     public void cometClose() {
         coyoteRequest.action(ActionCode.COMET_CLOSE,getEvent());
         setComet(false);
@@ -2487,109 +2493,117 @@ public class Request
 
         Parameters parameters = coyoteRequest.getParameters();
 
-        File location;
-        String locationStr = mce.getLocation();
-        if (locationStr == null || locationStr.length() == 0) {
-            location = ((File) context.getServletContext().getAttribute(
-                    ServletContext.TEMPDIR));
-        } else {
-            location = new File(locationStr);
-        }
-
-        if (!location.isAbsolute() || !location.isDirectory()) {
-            partsParseException = new IOException(
-                    sm.getString("coyoteRequest.uploadLocationInvalid",
-                            location));
-            return;
-        }
-
-        // Create a new file upload handler
-        DiskFileItemFactory factory = new DiskFileItemFactory();
+        boolean success = false;
         try {
-            factory.setRepository(location.getCanonicalFile());
-        } catch (IOException ioe) {
-            partsParseException = ioe;
-            return;
-        }
-        factory.setSizeThreshold(mce.getFileSizeThreshold());
+            File location;
+            String locationStr = mce.getLocation();
+            if (locationStr == null || locationStr.length() == 0) {
+                location = ((File) context.getServletContext().getAttribute(
+                        ServletContext.TEMPDIR));
+            } else {
+                location = new File(locationStr);
+            }
 
-        ServletFileUpload upload = new ServletFileUpload();
-        upload.setFileItemFactory(factory);
-        upload.setFileSizeMax(mce.getMaxFileSize());
-        upload.setSizeMax(mce.getMaxRequestSize());
+            if (!location.isAbsolute() || !location.isDirectory()) {
+                partsParseException = new IOException(
+                        sm.getString("coyoteRequest.uploadLocationInvalid",
+                                location));
+                return;
+            }
 
-        parts = new ArrayList<Part>();
-        try {
-            List<FileItem> items = upload.parseRequest(this);
-            int maxPostSize = getConnector().getMaxPostSize();
-            int postSize = 0;
-            String enc = getCharacterEncoding();
-            Charset charset = null;
-            if (enc != null) {
-                try {
-                    charset = B2CConverter.getCharset(enc);
-                } catch (UnsupportedEncodingException e) {
-                    // Ignore
-                }
+
+            // Create a new file upload handler
+            DiskFileItemFactory factory = new DiskFileItemFactory();
+            try {
+                factory.setRepository(location.getCanonicalFile());
+            } catch (IOException ioe) {
+                partsParseException = ioe;
+                return;
             }
-            for (FileItem item : items) {
-                ApplicationPart part = new ApplicationPart(item, mce);
-                parts.add(part);
-                if (part.getFilename() == null) {
-                    String name = part.getName();
-                    String value = null;
+            factory.setSizeThreshold(mce.getFileSizeThreshold());
+
+            ServletFileUpload upload = new ServletFileUpload();
+            upload.setFileItemFactory(factory);
+            upload.setFileSizeMax(mce.getMaxFileSize());
+            upload.setSizeMax(mce.getMaxRequestSize());
+
+            parts = new ArrayList<Part>();
+            try {
+                List<FileItem> items = upload.parseRequest(this);
+                int maxPostSize = getConnector().getMaxPostSize();
+                int postSize = 0;
+                String enc = getCharacterEncoding();
+                Charset charset = null;
+                if (enc != null) {
                     try {
-                        String encoding = parameters.getEncoding();
-                        if (encoding == null) {
-                            encoding = Parameters.DEFAULT_ENCODING;
-                        }
-                        value = part.getString(encoding);
-                    } catch (UnsupportedEncodingException uee) {
-                        try {
-                            value = 
part.getString(Parameters.DEFAULT_ENCODING);
-                        } catch (UnsupportedEncodingException e) {
-                            // Should not be possible
-                        }
+                        charset = B2CConverter.getCharset(enc);
+                    } catch (UnsupportedEncodingException e) {
+                        // Ignore
                     }
-                    if (maxPostSize > 0) {
-                        // Have to calculate equivalent size. Not completely
-                        // accurate but close enough.
-                        if (charset == null) {
-                            // Name length
-                            postSize += name.getBytes().length;
-                        } else {
-                            postSize += name.getBytes(charset).length;
+                }
+                for (FileItem item : items) {
+                    ApplicationPart part = new ApplicationPart(item, mce);
+                    parts.add(part);
+                    if (part.getFilename() == null) {
+                        String name = part.getName();
+                        String value = null;
+                        try {
+                            String encoding = parameters.getEncoding();
+                            if (encoding == null) {
+                                encoding = Parameters.DEFAULT_ENCODING;
+                            }
+                            value = part.getString(encoding);
+                        } catch (UnsupportedEncodingException uee) {
+                            try {
+                                value = 
part.getString(Parameters.DEFAULT_ENCODING);
+                            } catch (UnsupportedEncodingException e) {
+                                // Should not be possible
+                            }
                         }
-                        if (value != null) {
-                            // Equals sign
+                        if (maxPostSize > 0) {
+                            // Have to calculate equivalent size. Not 
completely
+                            // accurate but close enough.
+                            if (charset == null) {
+                                // Name length
+                                postSize += name.getBytes().length;
+                            } else {
+                                postSize += name.getBytes(charset).length;
+                            }
+                            if (value != null) {
+                                // Equals sign
+                                postSize++;
+                                // Value length
+                                postSize += part.getSize();
+                            }
+                            // Value separator
                             postSize++;
-                            // Value length
-                            postSize += part.getSize();
-                        }
-                        // Value separator
-                        postSize++;
-                        if (postSize > maxPostSize) {
-                            throw new IllegalStateException(sm.getString(
-                                    "coyoteRequest.maxPostSizeExceeded"));
+                            if (postSize > maxPostSize) {
+                                throw new IllegalStateException(sm.getString(
+                                        "coyoteRequest.maxPostSizeExceeded"));
+                            }
                         }
+                        parameters.addParameter(name, value);
                     }
-                    parameters.addParameter(name, value);
                 }
-            }
 
-        } catch (InvalidContentTypeException e) {
-            partsParseException = new ServletException(e);
-        } catch (FileUploadBase.SizeException e) {
-            checkSwallowInput();
-            partsParseException = new IllegalStateException(e);
-        } catch (FileUploadException e) {
-            partsParseException = new IOException(e);
-        } catch (IllegalStateException e) {
-            checkSwallowInput();
-            partsParseException = e;
+                success = true;
+            } catch (InvalidContentTypeException e) {
+                partsParseException = new ServletException(e);
+            } catch (FileUploadBase.SizeException e) {
+                checkSwallowInput();
+                partsParseException = new IllegalStateException(e);
+            } catch (FileUploadException e) {
+                partsParseException = new IOException(e);
+            } catch (IllegalStateException e) {
+                checkSwallowInput();
+                partsParseException = e;
+            }
+        } finally {
+            if (partsParseException != null || !success) {
+                parameters.setParseFailed(true);
+            }
+            checkParameterParseFailed();
         }
-
-        return;
     }
 
 
@@ -2774,108 +2788,120 @@ public class Request
         parametersParsed = true;
 
         Parameters parameters = coyoteRequest.getParameters();
-        // Set this every time in case limit has been changed via JMX
-        parameters.setLimit(getConnector().getMaxParameterCount());
+        boolean success = false;
+        try {
+            // Set this every time in case limit has been changed via JMX
+            parameters.setLimit(getConnector().getMaxParameterCount());
 
-        // getCharacterEncoding() may have been overridden to search for
-        // hidden form field containing request encoding
-        String enc = getCharacterEncoding();
-
-        boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
-        if (enc != null) {
-            parameters.setEncoding(enc);
-            if (useBodyEncodingForURI) {
-                parameters.setQueryStringEncoding(enc);
-            }
-        } else {
-            parameters.setEncoding
-                (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
-            if (useBodyEncodingForURI) {
-                parameters.setQueryStringEncoding
+            // getCharacterEncoding() may have been overridden to search for
+            // hidden form field containing request encoding
+            String enc = getCharacterEncoding();
+
+            boolean useBodyEncodingForURI = 
connector.getUseBodyEncodingForURI();
+            if (enc != null) {
+                parameters.setEncoding(enc);
+                if (useBodyEncodingForURI) {
+                    parameters.setQueryStringEncoding(enc);
+                }
+            } else {
+                parameters.setEncoding
                     (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+                if (useBodyEncodingForURI) {
+                    parameters.setQueryStringEncoding
+                        
(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+                }
             }
-        }
 
-        parameters.handleQueryParameters();
+            parameters.handleQueryParameters();
 
-        if (usingInputStream || usingReader) {
-            return;
-        }
+            if (usingInputStream || usingReader) {
+                success = true;
+                return;
+            }
 
-        if( !getConnector().isParseBodyMethod(getMethod()) ) {
-            return;
-        }
+            if( !getConnector().isParseBodyMethod(getMethod()) ) {
+                success = true;
+                return;
+            }
 
-        String contentType = getContentType();
-        if (contentType == null) {
-            contentType = "";
-        }
-        int semicolon = contentType.indexOf(';');
-        if (semicolon >= 0) {
-            contentType = contentType.substring(0, semicolon).trim();
-        } else {
-            contentType = contentType.trim();
-        }
+            String contentType = getContentType();
+            if (contentType == null) {
+                contentType = "";
+            }
+            int semicolon = contentType.indexOf(';');
+            if (semicolon >= 0) {
+                contentType = contentType.substring(0, semicolon).trim();
+            } else {
+                contentType = contentType.trim();
+            }
 
-        if ("multipart/form-data".equals(contentType)) {
-            parseParts();
-            return;
-        }
+            if ("multipart/form-data".equals(contentType)) {
+                parseParts();
+                success = true;
+                return;
+            }
 
-        if (!("application/x-www-form-urlencoded".equals(contentType))) {
-            return;
-        }
+            if (!("application/x-www-form-urlencoded".equals(contentType))) {
+                success = true;
+                return;
+            }
 
-        int len = getContentLength();
+            int len = getContentLength();
 
-        if (len > 0) {
-            int maxPostSize = connector.getMaxPostSize();
-            if ((maxPostSize > 0) && (len > maxPostSize)) {
-                if (context.getLogger().isDebugEnabled()) {
-                    context.getLogger().debug(
-                            sm.getString("coyoteRequest.postTooLarge"));
+            if (len > 0) {
+                int maxPostSize = connector.getMaxPostSize();
+                if ((maxPostSize > 0) && (len > maxPostSize)) {
+                    if (context.getLogger().isDebugEnabled()) {
+                        context.getLogger().debug(
+                                sm.getString("coyoteRequest.postTooLarge"));
+                    }
+                    checkSwallowInput();
+                    return;
                 }
-                checkSwallowInput();
-                return;
-            }
-            byte[] formData = null;
-            if (len < CACHED_POST_LEN) {
-                if (postData == null) {
-                    postData = new byte[CACHED_POST_LEN];
+                byte[] formData = null;
+                if (len < CACHED_POST_LEN) {
+                    if (postData == null) {
+                        postData = new byte[CACHED_POST_LEN];
+                    }
+                    formData = postData;
+                } else {
+                    formData = new byte[len];
                 }
-                formData = postData;
-            } else {
-                formData = new byte[len];
-            }
-            try {
-                if (readPostBody(formData, len) != len) {
+                try {
+                    if (readPostBody(formData, len) != len) {
+                        return;
+                    }
+                } catch (IOException e) {
+                    // Client disconnect
+                    if (context.getLogger().isDebugEnabled()) {
+                        context.getLogger().debug(
+                                sm.getString("coyoteRequest.parseParameters"), 
e);
+                    }
                     return;
                 }
-            } catch (IOException e) {
-                // Client disconnect
-                if (context.getLogger().isDebugEnabled()) {
-                    context.getLogger().debug(
-                            sm.getString("coyoteRequest.parseParameters"), e);
+                parameters.processParameters(formData, 0, len);
+            } else if ("chunked".equalsIgnoreCase(
+                    coyoteRequest.getHeader("transfer-encoding"))) {
+                byte[] formData = null;
+                try {
+                    formData = readChunkedPostBody();
+                } catch (IOException e) {
+                    // Client disconnect
+                    if (context.getLogger().isDebugEnabled()) {
+                        context.getLogger().debug(
+                                sm.getString("coyoteRequest.parseParameters"), 
e);
+                    }
+                    return;
                 }
-                return;
-            }
-            parameters.processParameters(formData, 0, len);
-        } else if ("chunked".equalsIgnoreCase(
-                coyoteRequest.getHeader("transfer-encoding"))) {
-            byte[] formData = null;
-            try {
-                formData = readChunkedPostBody();
-            } catch (IOException e) {
-                // Client disconnect
-                if (context.getLogger().isDebugEnabled()) {
-                    context.getLogger().debug(
-                            sm.getString("coyoteRequest.parseParameters"), e);
+                if (formData != null) {
+                    parameters.processParameters(formData, 0, formData.length);
                 }
-                return;
             }
-            if (formData != null) {
-                parameters.processParameters(formData, 0, formData.length);
+        } finally {
+            if (!success) {
+                parameters.setParseFailed(true);
             }
+            checkParameterParseFailed();
         }
 
     }

Added: tomcat/trunk/java/org/apache/catalina/filters/FailedRequestFilter.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/filters/FailedRequestFilter.java?rev=1198696&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/filters/FailedRequestFilter.java 
(added)
+++ tomcat/trunk/java/org/apache/catalina/filters/FailedRequestFilter.java Mon 
Nov  7 10:46:14 2011
@@ -0,0 +1,93 @@
+/*
+ * 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.filters;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.comet.CometEvent;
+import org.apache.catalina.comet.CometFilter;
+import org.apache.catalina.comet.CometFilterChain;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * Filter that will reject requests if there was a failure during parameter
+ * parsing. This filter can be used to ensure that none parameter values
+ * submitted by client are lost.
+ *
+ * <p>
+ * Note that it has side effect that it triggers parameter parsing and thus
+ * consumes the body for POST requests, so use it only with addresses that do 
not
+ * use <code>request.getInputStream()</code> and
+ * <code>request.getReader()</code>.
+ */
+public class FailedRequestFilter extends FilterBase implements CometFilter {
+
+    private static final Log log = 
LogFactory.getLog(FailedRequestFilter.class);
+
+    @Override
+    protected Log getLogger() {
+        return log;
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+        if (!isGoodRequest(request)) {
+            ((HttpServletResponse) response)
+                    .sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return;
+        }
+        chain.doFilter(request, response);
+    }
+
+    @Override
+    public void doFilterEvent(CometEvent event, CometFilterChain chain)
+            throws IOException, ServletException {
+        if (event.getEventType() == CometEvent.EventType.BEGIN
+                && !isGoodRequest(event.getHttpServletRequest())) {
+            event.getHttpServletResponse().sendError(
+                    HttpServletResponse.SC_BAD_REQUEST);
+            event.close();
+            return;
+        }
+        chain.doFilterEvent(event);
+    }
+
+    private boolean isGoodRequest(ServletRequest request) {
+        // Trigger parsing of parameters
+        request.getParameter("none");
+        // Detect failure
+        if (request.getAttribute(Globals.PARAMETER_PARSE_FAILED_ATTR) != null) 
{
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean isConfigProblemFatal() {
+        return true;
+    }
+
+}

Propchange: 
tomcat/trunk/java/org/apache/catalina/filters/FailedRequestFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java?rev=1198696&r1=1198695&r2=1198696&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java Mon Nov  7 
10:46:14 2011
@@ -59,6 +59,12 @@ public final class Parameters {
     private int limit = -1;
     private int parameterCount = 0;
 
+    /**
+     * Is set to <code>true</code> if there were failures during parameter
+     * parsing.
+     */
+    private boolean parseFailed = false;
+
     public Parameters() {
         // NO-OP
     }
@@ -89,12 +95,21 @@ public final class Parameters {
         }
     }
 
+    public boolean isParseFailed() {
+        return parseFailed;
+    }
+
+    public void setParseFailed(boolean parseFailed) {
+        this.parseFailed = parseFailed;
+    }
+
     public void recycle() {
         parameterCount = 0;
         paramHashValues.clear();
         didQueryParameters=false;
         encoding=null;
         decodedQuery.recycle();
+        parseFailed = false;
     }
 
     // -------------------- Data access --------------------
@@ -168,6 +183,7 @@ public final class Parameters {
         if (limit > -1 && parameterCount > limit) {
             // Processing this parameter will push us over the limit. ISE is
             // what Request.parseParts() uses for requests that are too big
+            parseFailed = true;
             throw new IllegalStateException(sm.getString(
                     "parameters.maxCountFail", Integer.valueOf(limit)));
         }
@@ -296,6 +312,7 @@ public final class Parameters {
                                 null));
                     }
                 }
+                parseFailed = true;
                 continue;
                 // invalid chunk - it's better to ignore
             }
@@ -337,10 +354,12 @@ public final class Parameters {
                 } catch (IllegalStateException ise) {
                     // Hitting limit stops processing further params but does
                     // not cause request to fail.
+                    parseFailed = true;
                     log.warn(ise.getMessage());
                     break;
                 }
             } catch (IOException e) {
+                parseFailed = true;
                 decodeFailCount++;
                 if (decodeFailCount == 1 || log.isDebugEnabled()) {
                     if (log.isDebugEnabled()) {



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

Reply via email to