<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -359,6 +359,8 @@ private:
 	 * is contained in this method.
 	 */
 	int handleRequest(request_rec *r) {
+		/********** Step 1: preparation work **********/
+		
 		/* Check whether an error occured in prepareRequest() that should be reported
 		 * to the browser.
 		 */
@@ -392,7 +394,10 @@ private:
 			return reportDocumentRootDeterminationError(r);
 		}
 		
-		int httpStatus = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
+		
+		/********** Step 2: handle HTTP upload data, if any **********/
+		
+		int httpStatus = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
     		if (httpStatus != OK) {
 			return httpStatus;
 		}
@@ -405,13 +410,36 @@ private:
 			Application::SessionPtr session;
 			bool expectingUploadData;
 			shared_ptr&lt;BufferedUpload&gt; uploadData;
+			const char *contentLength;
 			
 			expectingUploadData = ap_should_client_block(r);
-			if (expectingUploadData &amp;&amp; atol(lookupHeader(r, &quot;Content-Length&quot;))
-			                                 &gt; UPLOAD_ACCELERATION_THRESHOLD) {
-				uploadData = receiveRequestBody(r);
+			contentLength = lookupHeader(r, &quot;Content-Length&quot;);
+			
+			// If the HTTP upload data is larger than a threshold, or if the HTTP
+			// client sent HTTP upload data using the &quot;chunked&quot; transfer encoding
+			// (which implies Content-Length == NULL), then buffer the upload
+			// data into a tempfile.
+			if (expectingUploadData &amp;&amp; (
+			          contentLength == NULL ||
+			          atol(contentLength) &gt; UPLOAD_ACCELERATION_THRESHOLD
+			     )
+			) {
+				uploadData = receiveRequestBody(r, contentLength);
 			}
 			
+			if (expectingUploadData &amp;&amp; contentLength == NULL) {
+				// In case of &quot;chunked&quot; transfer encoding, we'll set the
+				// Content-Length header to the length of the received upload
+				// data. Rails requires this header for its HTTP upload data
+				// multipart parsing process.
+				apr_table_set(r-&gt;headers_in, &quot;Content-Length&quot;,
+					toString(ftell(uploadData-&gt;handle)).c_str());
+			}
+			
+			
+			/********** Step 3: forwarding the request to a backend
+			                    process from the application pool **********/
+			
 			UPDATE_TRACE_POINT();
 			try {
 				ServerConfig *sconfig = getServerConfig(r-&gt;server);
@@ -469,6 +497,10 @@ private:
 			}
 			session-&gt;shutdownWriter();
 			
+			
+			/********** Step 4: forwarding the response from the backend
+			                    process back to the HTTP client **********/
+			
 			UPDATE_TRACE_POINT();
 			apr_file_t *readerPipe = NULL;
 			int reader = session-&gt;getStream();
@@ -682,7 +714,16 @@ private:
 		return APR_SUCCESS;
 	}
 	
-	shared_ptr&lt;BufferedUpload&gt; receiveRequestBody(request_rec *r) {
+	/**
+	 * Receive the HTTP upload data and buffer it into a BufferedUpload temp file.
+	 *
+	 * @param r The request.
+	 * @param contentLength The value of the HTTP Content-Length header. This is used
+	 *                      to check whether the HTTP client has sent complete upload
+	 *                      data. NULL indicates that there is no Content-Length header,
+	 *                      i.e. that the HTTP client used chunked transfer encoding.
+	 */
+	shared_ptr&lt;BufferedUpload&gt; receiveRequestBody(request_rec *r, const char *contentLength) {
 		TRACE_POINT();
 		shared_ptr&lt;BufferedUpload&gt; tempFile(new BufferedUpload());
 		char buf[1024 * 32];
@@ -719,7 +760,8 @@ private:
 		if (len == -1) {
 			throw IOException(&quot;An error occurred while receiving HTTP upload data.&quot;);
 		}
-		if (ftell(tempFile-&gt;handle) != atol(lookupHeader(r, &quot;Content-Length&quot;))) {
+		
+		if (contentLength != NULL &amp;&amp; ftell(tempFile-&gt;handle) != atol(contentLength)) {
 			throw IOException(&quot;The HTTP client sent incomplete upload data.&quot;);
 		}
 		return tempFile;</diff>
      <filename>ext/apache2/Hooks.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -74,6 +74,28 @@ shared_examples_for &quot;MyCook(tm) beta&quot; do
 		end
 	end
 	
+	it &quot;supports HTTP POST with 'chunked' transfer encoding&quot; do
+		uri = URI.parse(@server)
+		base_uri = uri.path.sub(%r(/$), '')
+		socket = TCPSocket.new(uri.host, uri.port)
+		begin
+			socket.write(&quot;POST #{base_uri}/uploads/single HTTP/1.1\r\n&quot;)
+			socket.write(&quot;Host: #{uri.host}\r\n&quot;)
+			socket.write(&quot;Transfer-Encoding: chunked\r\n&quot;)
+			socket.write(&quot;\r\n&quot;)
+			
+			chunk = &quot;foo=bar!&quot;
+			socket.write(&quot;%X\r\n%s\r\n&quot; % [chunk.size, chunk])
+			socket.write(&quot;0\r\n&quot;)
+			socket.close_write
+			
+			lines = socket.read.split(/\r?\n/)
+			lines.last.should == &quot;bar!&quot;
+		ensure
+			socket.close
+		end
+	end
+	
 	it &quot;can properly handle custom headers&quot; do
 		response = get_response('/welcome/headers_test')
 		response[&quot;X-Foo&quot;].should == &quot;Bar&quot;</diff>
      <filename>test/integration_tests.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,4 +8,8 @@ class UploadsController &lt; ApplicationController
 			&quot;name 2 = &quot; + params[:upload][:name2] + &quot;\n&quot; +
 			&quot;data = &quot; + params[:upload][:data].read
 	end
+	
+	def single
+		render :text =&gt; params[:foo]
+	end
 end</diff>
      <filename>test/stub/rails_apps/mycook/app/controllers/uploads_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,7 +25,7 @@ ActionController::Routing::Routes.draw do |map|
   #   end
   
   map.resources :recipes
-  map.resources :uploads
+  map.resources :uploads, :collection =&gt; { :single =&gt; :post }
 
   # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
   map.root :controller =&gt; &quot;welcome&quot;</diff>
      <filename>test/stub/rails_apps/mycook/config/routes.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>e38d48ef3c84fccafebae00da272479784e8c89c</id>
    </parent>
  </parents>
  <author>
    <name>Hongli Lai (Phusion)</name>
    <login></login>
    <email>hongli@phusion.nl</email>
  </author>
  <url>http://github.com/FooBarWidget/passenger/commit/5038157db90d2cd12c65d29154774c98a9e6a0b7</url>
  <id>5038157db90d2cd12c65d29154774c98a9e6a0b7</id>
  <committed-date>2009-04-01T06:18:50-07:00</committed-date>
  <authored-date>2009-04-01T04:55:15-07:00</authored-date>
  <message>Add support for HTTP uploads that use the 'chunked' transfer encoding. Fixes issue #114.</message>
  <tree>827da48e136739e8f1c517d5891c386fc000ec4b</tree>
  <committer>
    <name>Hongli Lai (Phusion)</name>
    <login></login>
    <email>hongli@phusion.nl</email>
  </committer>
</commit>
