Author: schultz
Date: Fri Dec 3 16:07:50 2010
New Revision: 1041892
URL: http://svn.apache.org/viewvc?rev=1041892&view=rev
Log:
Fixed bug 48692: Provide option to parse application/x-www-form-urlencoded PUT
requests
Modified:
tomcat/trunk/java/org/apache/catalina/connector/Connector.java
tomcat/trunk/java/org/apache/catalina/connector/Request.java
tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java
tomcat/trunk/webapps/docs/changelog.xml
tomcat/trunk/webapps/docs/config/ajp.xml
tomcat/trunk/webapps/docs/config/http.xml
Modified: tomcat/trunk/java/org/apache/catalina/connector/Connector.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Connector.java?rev=1041892&r1=1041891&r2=1041892&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Connector.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Connector.java Fri Dec 3
16:07:50 2010
@@ -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.String
/**
- * 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 Lifecycle
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 Lifecycle
}
+ 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 Lifecycle
protocolHandler.setAdapter(adapter);
protocolHandler.setDomain(getDomain());
+ // Make sure parseBodyMethodsSet has a default
+ if(null == parseBodyMethodsSet)
+ setParseBodyMethods(getParseBodyMethods());
+
try {
protocolHandler.init();
} catch (Exception e) {
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=1041892&r1=1041891&r2=1041892&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Request.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Request.java Fri Dec 3
16:07:50 2010
@@ -2798,7 +2798,7 @@ public class Request
if (usingInputStream || usingReader)
return;
- if (!getMethod().equalsIgnoreCase("POST"))
+ if(!getConnector().isParseBodyMethod(getMethod()))
return;
String contentType = getContentType();
Modified: tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java?rev=1041892&r1=1041891&r2=1041892&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java (original)
+++ tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java Fri Dec 3
16:07:50 2010
@@ -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 TomcatB
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<String,String[]> parameters = new
TreeMap<String,String[]>(req.getParameterMap());
+
+ boolean first = true;
+
+ for(String name: parameters.keySet()) {
+ String[] values = req.getParameterValues(name);
+
+ java.util.Arrays.sort(values);
+
+ for(int i=0; i<values.length; ++i)
+ {
+ if(first)
+ first = false;
+ else
+ out.print(",");
+
+ out.print(name + "=" + values[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Bug 48692 test client: test for allowing PUT request bodies.
+ */
+ private class Bug48692Client extends SimpleHttpClient {
+
+ private boolean init;
+
+ private synchronized void init() throws Exception {
+ if (init) return;
+
+ Tomcat tomcat = getTomcatInstance();
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "EchoParameters", new
EchoParametersServlet());
+ root.addServletMapping("/echo", "EchoParameters");
+ tomcat.start();
+
+ init = true;
+ }
+
+ private Exception doRequest(String method,
+ String queryString,
+ String contentType,
+ String requestBody,
+ boolean allowBody) {
+ Tomcat tomcat = getTomcatInstance();
+
+ try {
+ init();
+ if(allowBody)
+ tomcat.getConnector().setParseBodyMethods(method);
+ else
+ tomcat.getConnector().setParseBodyMethods(""); // never
parse
+
+ // Open connection
+ connect();
+
+ // Re-encode the request body so that bytes = characters
+ if(null != requestBody)
+ requestBody = new String(requestBody.getBytes("UTF-8"),
"ASCII");
+
+ // Send specified request body using method
+ String[] request = {
+ (
+ method + " http://localhost:" + getPort() + "/echo"
+ + (null == queryString ? "" : ("?" + queryString))
+ + " HTTP/1.1" + CRLF
+ + "Host: localhost" + CRLF
+ + (null == contentType ? ""
+ : ("Content-Type: " + contentType + CRLF))
+ + "Connection: close" + CRLF
+ + (null == requestBody ? "" : "Content-Length: " +
requestBody.length() + CRLF)
+ + CRLF
+ + (null == requestBody ? "" : requestBody)
+ )
+ };
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ return false; // Don't care
+ }
+ }
+
private HttpURLConnection getConnection() throws IOException {
final String query = "http://localhost:" + getPort() + "/";
URL postURL;
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1041892&r1=1041891&r2=1041892&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Fri Dec 3 16:07:50 2010
@@ -32,6 +32,7 @@
<author email="[email protected]">Keiichi Fujino</author>
<author email="[email protected]">Tim Whittington</author>
<author email="[email protected]">Mladen Turk</author>
+ <author email="[email protected]">Christopher Schultz</author>
<title>Changelog</title>
</properties>
@@ -40,6 +41,10 @@
<section name="Tomcat 7.0.6 (markt)">
<subsection name="Catalina">
<changelog>
+ <update>
+ <bug>48692</bug>: Provide option to parse
+ <code>application/x-www-form-urlencoded</code> PUT requests. (schultz)
+ </update>
<fix>
<bug>8705</bug>: <code>org.apache.catalina.SessionListener</code> now
extends <code>java.util.EventListener</code>. (markt)
Modified: tomcat/trunk/webapps/docs/config/ajp.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/ajp.xml?rev=1041892&r1=1041891&r2=1041892&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/ajp.xml (original)
+++ tomcat/trunk/webapps/docs/config/ajp.xml Fri Dec 3 16:07:50 2010
@@ -115,6 +115,18 @@
to 4096 (4 kilobytes).</p>
</attribute>
+ <attribute name="parseBodyMethods" required="false">
+ <p>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 <code>POST</code> 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 <code>POST</code></p>
+ </attribute>
+
<attribute name="port" required="true">
<p>The TCP port number on which this <strong>Connector</strong>
will create a server socket and await incoming connections. Your
Modified: tomcat/trunk/webapps/docs/config/http.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/http.xml?rev=1041892&r1=1041891&r2=1041892&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/http.xml (original)
+++ tomcat/trunk/webapps/docs/config/http.xml Fri Dec 3 16:07:50 2010
@@ -115,6 +115,18 @@
to 4096 (4 kilobytes).</p>
</attribute>
+ <attribute name="parseBodyMethods" required="false">
+ <p>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 <code>POST</code> 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 <code>POST</code></p>
+ </attribute>
+
<attribute name="port" required="true">
<p>The TCP port number on which this <strong>Connector</strong>
will create a server socket and await incoming connections. Your
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]