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