From 9e022727ac858cd434d8e2a56970e63a5fa9131e Mon Sep 17 00:00:00 2001 From: schultz Date: Fri, 3 Dec 2010 16:07:50 +0000 Subject: [PATCH] Fixed bug 48692: Provide option to parse application/x-www-form-urlencoded PUT requests git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1041892 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/connector/Connector.java | 37 +++- java/org/apache/catalina/connector/Request.java | 2 +- .../org/apache/catalina/connector/TestRequest.java | 193 +++++++++++++++++++++ webapps/docs/changelog.xml | 5 + webapps/docs/config/ajp.xml | 12 ++ webapps/docs/config/http.xml | 12 ++ 6 files changed, 259 insertions(+), 2 deletions(-) diff --git a/java/org/apache/catalina/connector/Connector.java b/java/org/apache/catalina/connector/Connector.java index 64e58463d..6197ca378 100644 --- a/java/org/apache/catalina/connector/Connector.java +++ b/java/org/apache/catalina/connector/Connector.java @@ -18,7 +18,9 @@ package org.apache.catalina.connector; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import javax.management.ObjectName; @@ -38,7 +40,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implementation of a Coyote connector for Tomcat 5.x. + * Implementation of a Coyote connector. * * @author Craig R. McClanahan * @author Remy Maucherat @@ -184,6 +186,11 @@ public class Connector extends LifecycleMBeanBase { protected int maxSavePostSize = 4 * 1024; + protected String parseBodyMethods = "POST"; + + protected HashSet parseBodyMethodsSet; + + /** * Flag to use IP-based virtual hosting. */ @@ -449,6 +456,30 @@ public class Connector extends LifecycleMBeanBase { } + public String getParseBodyMethods() + { + return (this.parseBodyMethods); + } + + public void setParseBodyMethods(String methods) + { + HashSet methodSet = new HashSet(); + + if(null != methods) + methodSet.addAll(Arrays.asList(methods.split("\\s*,\\s*"))); + + if(methodSet.contains("TRACE")) + throw new IllegalArgumentException("TRACE method MUST NOT include an entity (see RFC 2616 Section 9.6)"); + + this.parseBodyMethods = methods; + this.parseBodyMethodsSet = methodSet; + } + + public boolean isParseBodyMethod(String method) + { + return parseBodyMethodsSet.contains(method); + } + /** * Return the port number on which we listen for requests. */ @@ -866,6 +897,10 @@ public class Connector extends LifecycleMBeanBase { protocolHandler.setAdapter(adapter); protocolHandler.setDomain(getDomain()); + // Make sure parseBodyMethodsSet has a default + if(null == parseBodyMethodsSet) + setParseBodyMethods(getParseBodyMethods()); + try { protocolHandler.init(); } catch (Exception e) { diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java index f2b4a97ff..60936bee6 100644 --- a/java/org/apache/catalina/connector/Request.java +++ b/java/org/apache/catalina/connector/Request.java @@ -2798,7 +2798,7 @@ public class Request if (usingInputStream || usingReader) return; - if (!getMethod().equalsIgnoreCase("POST")) + if(!getConnector().isParseBodyMethod(getMethod())) return; String contentType = getContentType(); diff --git a/test/org/apache/catalina/connector/TestRequest.java b/test/org/apache/catalina/connector/TestRequest.java index 53a2f4cb0..4c5beed17 100644 --- a/test/org/apache/catalina/connector/TestRequest.java +++ b/test/org/apache/catalina/connector/TestRequest.java @@ -23,6 +23,7 @@ import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import java.util.Enumeration; +import java.util.TreeMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -331,6 +332,198 @@ public class TestRequest extends TomcatBaseTest { assertNotNull(is); } + /** + * Test case for https://issues.apache.org/bugzilla/show_bug.cgi?id=48692 + * PUT requests should be able to fetch request parameters coming from + * the request body (when properly configured using the new parseBodyMethod + * setting). + */ + public void testBug48692() { + Bug48692Client client = new Bug48692Client(); + client.setPort(getPort()); + + // Make sure GET works properly + client.doRequest("GET", "foo=bar", null, null, false); + + assertTrue("Non-200 response for GET request", + client.isResponse200()); + assertEquals("Incorrect response for GET request", + "foo=bar", + client.getResponseBody()); + + client.reset(); + + // + // Make sure POST works properly + // + // POST with separate GET and POST parameters + client.doRequest("POST", "foo=bar", "application/x-www-form-urlencoded", "bar=baz", true); + + assertTrue("Non-200 response for POST request", + client.isResponse200()); + assertEquals("Incorrect response for POST request", + "bar=baz,foo=bar", + client.getResponseBody()); + + client.reset(); + + // POST with overlapping GET and POST parameters + client.doRequest("POST", "foo=bar&bar=foo", "application/x-www-form-urlencoded", "bar=baz&foo=baz", true); + + assertTrue("Non-200 response for POST request", + client.isResponse200()); + assertEquals("Incorrect response for POST request", + "bar=baz,bar=foo,foo=bar,foo=baz", + client.getResponseBody()); + + client.reset(); + + // PUT without POST-style parsing + client.doRequest("PUT", "foo=bar&bar=foo", "application/x-www-form-urlencoded", "bar=baz&foo=baz", false); + + assertTrue("Non-200 response for PUT/noparse request", + client.isResponse200()); + assertEquals("Incorrect response for PUT request", + "bar=foo,foo=bar", + client.getResponseBody()); + + client.reset(); + + // PUT with POST-style parsing + client.doRequest("PUT", "foo=bar&bar=foo", "application/x-www-form-urlencoded", "bar=baz&foo=baz", true); + + assertTrue("Non-200 response for PUT request", + client.isResponse200()); + assertEquals("Incorrect response for PUT/parse request", + "bar=baz,bar=foo,foo=bar,foo=baz", + client.getResponseBody()); + + client.reset(); + + /* + private Exception doRequest(String method, + String queryString, + String contentType, + String requestBody, + boolean allowBody) { + */ + } + + /** + * + */ + private static class EchoParametersServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + /** + * Only interested in the parameters and values for requests. + * Note: echos parameters in alphabetical order. + */ + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + // Just echo the parameters and values back as plain text + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + + PrintWriter out = resp.getWriter(); + + TreeMap parameters = new TreeMap(req.getParameterMap()); + + boolean first = true; + + for(String name: parameters.keySet()) { + String[] values = req.getParameterValues(name); + + java.util.Arrays.sort(values); + + for(int i=0; iKeiichi Fujino Tim Whittington Mladen Turk + Christopher Schultz Changelog @@ -40,6 +41,10 @@
+ + 48692: Provide option to parse + application/x-www-form-urlencoded PUT requests. (schultz) + 8705: org.apache.catalina.SessionListener now extends java.util.EventListener. (markt) diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml index 638ec1722..b82136f13 100644 --- a/webapps/docs/config/ajp.xml +++ b/webapps/docs/config/ajp.xml @@ -115,6 +115,18 @@ to 4096 (4 kilobytes).

+ +

A comma-separated list of HTTP methods for which request + bodies will be parsed for request parameters identically + to POST. This is useful in RESTful applications that want to + support POST-style semantics for PUT requests. + Note that any setting other than POST causes Tomcat + to behave in a way that violates the servlet specification. + The HTTP method TRACE is specifically forbidden here in accordance + with the HTTP specification. + The default is POST

+
+

The TCP port number on which this Connector will create a server socket and await incoming connections. Your diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml index fe8339cd0..ca3108866 100644 --- a/webapps/docs/config/http.xml +++ b/webapps/docs/config/http.xml @@ -115,6 +115,18 @@ to 4096 (4 kilobytes).

+ +

A comma-separated list of HTTP methods for which request + bodies will be parsed for request parameters identically + to POST. This is useful in RESTful applications that want to + support POST-style semantics for PUT requests. + Note that any setting other than POST causes Tomcat + to behave in a way that violates the servlet specification. + The HTTP method TRACE is specifically forbidden here in accordance + with the HTTP specification. + The default is POST

+
+

The TCP port number on which this Connector will create a server socket and await incoming connections. Your -- 2.11.0