int handleRequest(request_rec *r) {
		/********** Step 1: preparation work **********/
		
		/* Check whether an error occured in prepareRequest() that should be reported
		 * to the browser.
		 */
		
		RequestNote *note = getRequestNote(r);
		if (note == NULL) {
			return DECLINED;
		} else if (note->errorReport != NULL) {
			/* Did an error occur in any of the previous hook methods during
			 * this request? If so, show the error and stop here.
			 */
			return note->errorReport->report(r);
		} else if (r->handler != NULL && strcmp(r->handler, "redirect-handler") == 0) {
			// mod_rewrite is at work.
			return DECLINED;
		}
		
		TRACE_POINT();
		DirConfig *config = note->config;
		DirectoryMapper &mapper = note->mapper;
		
		try {
			mapper.getPublicDirectory();
		} catch (const DocumentRootDeterminationError &e) {
			return ReportDocumentRootDeterminationError(e).report(r);
		} catch (const FileSystemException &e) {
			/* The application root cannot be determined. This could
			 * happen if, for example, the user specified 'RailsBaseURI /foo'
			 * while there is no filesystem entry called "foo" in the virtual
			 * host's document root.
			 */
			return ReportFileSystemError(e).report(r);
		}
		
		
		UPDATE_TRACE_POINT();
		try {
			/********** Step 2: handle HTTP upload data, if any **********/
			
			int httpStatus = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
	    		if (httpStatus != OK) {
				return httpStatus;
			}
			
			this_thread::disable_interruption di;
			this_thread::disable_syscall_interruption dsi;
			bool expectingUploadData;
			string uploadDataMemory;
			shared_ptr<BufferedUpload> uploadDataFile;
			const char *contentLength;
			
			expectingUploadData = ap_should_client_block(r);
			contentLength = lookupHeader(r, "Content-Length");
			
			/* If the HTTP upload data is larger than a threshold, or if the HTTP
			 * client sent HTTP upload data using the "chunked" transfer encoding
			 * (which implies Content-Length == NULL), then buffer the upload
			 * data into a tempfile. Otherwise, buffer it into memory.
			 *
			 * We never forward the data directly to the backend process because
			 * the HTTP client might block indefinitely until it's done uploading.
			 * This would quickly exhaust the application pool.
			 */
			if (expectingUploadData) {
				if (contentLength == NULL || atol(contentLength) > LARGE_UPLOAD_THRESHOLD) {
					uploadDataFile = receiveRequestBody(r);
				} else {
					receiveRequestBody(r, contentLength, uploadDataMemory);
				}
			}
			
			if (expectingUploadData) {
				/* 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.
				 * There are two reasons why we don't rely on the Content-Length
				 * header as sent by the client:
				 * - The client doesn't always send Content-Length, e.g. in case
				 *   of "chunked" transfer encoding.
				 * - mod_deflate input compression can make it so that the
				 *   amount of upload we receive doesn't match Content-Length:
				 *   http://httpd.apache.org/docs/2.0/mod/mod_deflate.html#enable
				 */
				if (uploadDataFile != NULL) {
					apr_table_set(r->headers_in, "Content-Length",
						toString(ftell(uploadDataFile->handle)).c_str());
				} else {
					apr_table_set(r->headers_in, "Content-Length",
						toString(uploadDataMemory.size()).c_str());
				}
			}
			
			
			/********** Step 3: forwarding the request and request body
			                    to the HelperAgent **********/
			
			vector<StaticString> requestData;
			string headerData;
			unsigned int size;
			char sizeString[16];
			int ret;
			
			requestData.reserve(3);
			headerData.reserve(1024 * 2);
			requestData.push_back(StaticString());
			size = constructHeaders(r, config, requestData, mapper, headerData);
			requestData.push_back(",");
			
			ret = snprintf(sizeString, sizeof(sizeString) - 1, "%u:", size);
			sizeString[ret] = '\0';
			requestData[0] = StaticString(sizeString, ret);
			
			if (expectingUploadData && uploadDataFile == NULL) {
				requestData.push_back(uploadDataMemory);
			}
			
			FileDescriptor conn = connectToHelperAgent();
			gatheredWrite(conn, &requestData[0], requestData.size());
			
			if (expectingUploadData && uploadDataFile != NULL) {
				sendRequestBody(conn, uploadDataFile);
				uploadDataFile.reset();
			}
			
			do {
				ret = shutdown(conn, SHUT_WR);
			} while (ret == -1 && errno == EINTR);
			if (ret == -1 && errno != ENOTCONN) {
				// FreeBSD has a kernel bug which causes shutdown()
				// to harmlessly return ENOTCONN sometimes. See comment
				// in safelyClose().
				int e = errno;
				throw SystemException("Cannot shutdown(SHUT_WR) HelperAgent connection", e);
			}
			

			/********** Step 4: forwarding the response from the backend
			                    process back to the HTTP client **********/
			
			UPDATE_TRACE_POINT();
			apr_bucket_brigade *bb;
			apr_bucket *b;
			PassengerBucketStatePtr bucketState;
			
			/* Setup the bucket brigade. */
			bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
			
			bucketState = make_shared<PassengerBucketState>(conn);
			b = passenger_bucket_create(bucketState, r->connection->bucket_alloc, config->getBufferResponse());
			APR_BRIGADE_INSERT_TAIL(bb, b);
			
			b = apr_bucket_eos_create(r->connection->bucket_alloc);
			APR_BRIGADE_INSERT_TAIL(bb, b);

			/* Now read the HTTP response header, parse it and fill relevant
			 * information in our request_rec structure.
			 */
			
			/* I know the required size for backendData because I read
			 * util_script.c's source. :-(
			 */
			char backendData[MAX_STRING_LEN];
			Timer timer;
			int result = ap_scan_script_header_err_brigade(r, bb, backendData);
			
			if (result == OK) {
				// The API documentation for ap_scan_script_err_brigade() says it
				// returns HTTP_OK on success, but it actually returns OK.
				
				/* We were able to parse the HTTP response header sent by the
				 * backend process! Proceed with passing the bucket brigade,
				 * for forwarding the response body to the HTTP client.
				 */
				
				/* Manually set the Status header because
				 * ap_scan_script_header_err_brigade() filters it
				 * out. Some broken HTTP clients depend on the
				 * Status header for retrieving the HTTP status.
				 */
				if (!r->status_line || *r->status_line == '\0') {
					r->status_line = apr_psprintf(r->pool,
						"%d Unknown Status",
						r->status);
				}
				apr_table_setn(r->headers_out, "Status", r->status_line);
				
				//bool xsendfile = hasModXsendfile() &&
				//	apr_table_get(r->err_headers_out, "X-Sendfile");
				
				UPDATE_TRACE_POINT();
				ap_pass_brigade(r->output_filters, bb);
				
				/*
				if (r->connection->aborted) {
					P_WARN("Either the visitor clicked on the 'Stop' button in the "
						"web browser, or the visitor's connection has stalled "
						"and couldn't receive the data that Apache is sending "
						"to it. As a result, you will probably see a 'Broken Pipe' "
						"error in this log file. Please ignore it, "
						"this is normal. You might also want to increase Apache's "
						"TimeOut configuration option if you experience this "
						"problem often.");
				} else if (!bucketState->completed && !xsendfile) {
					// mod_xsendfile drops the entire output bucket so
					// suppress this message when mod_xsendfile is active.
					P_WARN("Apache stopped forwarding the backend's response, "
						"even though the HTTP client did not close the "
						"connection. Is this an Apache bug?");
				}
				*/
				
				return OK;
			} else if (backendData[0] == '\0') {
				/* if ((long long) timer.elapsed() >= r->server->timeout / 1000) {
					// Looks like an I/O timeout.
					P_ERROR("No data received from " <<
						"the backend application (process " <<
						backendPid << ") within " <<
						(r->server->timeout / 1000) << " msec. Either " <<
						"the backend application is frozen, or " <<
						"your TimeOut value of " <<
						(r->server->timeout / 1000000) <<
						" seconds is too low. Please check " <<
						"whether your application is frozen, or " <<
						"increase the value of the TimeOut " <<
						"configuration directive.");
				} else {
					P_ERROR("The backend application (process " <<
						backendPid << ") did not send a valid " <<
						"HTTP response; instead, it sent nothing " <<
						"at all. It is possible that it has crashed; " <<
						"please check whether there are crashing " <<
						"bugs in this application.");
				} */
				apr_table_setn(r->err_headers_out, "Status", "500 Internal Server Error");
				return HTTP_INTERNAL_SERVER_ERROR;
			} else {
				/* if ((long long) timer.elapsed() >= r->server->timeout / 1000) {
					// Looks like an I/O timeout.
					P_ERROR("The backend application (process " <<
						backendPid << ") hasn't sent a valid " <<
						"HTTP response within " <<
						(r->server->timeout / 1000) << " msec. Either " <<
						"the backend application froze while " <<
						"sending a response, or " <<
						"your TimeOut value of " <<
						(r->server->timeout / 1000000) <<
						" seconds is too low. Please check " <<
						"whether the application is frozen, or " <<
						"increase the value of the TimeOut " <<
						"configuration directive. The application " <<
						"has sent this data so far: [" <<
						backendData << "]");
				} else {
					P_ERROR("The backend application (process " <<
						backendPid << ") didn't send a valid " <<
						"HTTP response. It might have crashed " <<
						"during the middle of sending an HTTP " <<
						"response, so please check whether there " <<
						"are crashing problems in your application. " <<
						"This is the data that it sent: [" <<
						backendData << "]");
				} */
				apr_table_setn(r->err_headers_out, "Status", "500 Internal Server Error");
				return HTTP_INTERNAL_SERVER_ERROR;
			}
			
		} catch (const thread_interrupted &e) {
			P_TRACE(3, "A system call was interrupted during an HTTP request. Apache "
				"is probably restarting or shutting down. Backtrace:\n" <<
				e.backtrace());
			return HTTP_INTERNAL_SERVER_ERROR;
			
		} catch (const tracable_exception &e) {
			P_ERROR("Unexpected error in mod_passenger: " <<
				e.what() << "\n" << "  Backtrace:\n" << e.backtrace());
			return HTTP_INTERNAL_SERVER_ERROR;
		
		} catch (const std::exception &e) {
			P_ERROR("Unexpected error in mod_passenger: " <<
				e.what() << "\n" << "  Backtrace: not available");
			return HTTP_INTERNAL_SERVER_ERROR;
		}
	}