You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by yl...@apache.org on 2020/12/03 16:00:14 UTC
svn commit: r1884069 - /httpd/httpd/trunk/modules/proxy/mod_proxy_fcgi.c
Author: ylavic
Date: Thu Dec 3 16:00:13 2020
New Revision: 1884069
URL: http://svn.apache.org/viewvc?rev=1884069&view=rev
Log:
mod_proxy_fcgi: follow up to r1884068.
Use the same heuristic as mod_proxy_http to determinine whether we need to
spool the request body.
Modified:
httpd/httpd/trunk/modules/proxy/mod_proxy_fcgi.c
Modified: httpd/httpd/trunk/modules/proxy/mod_proxy_fcgi.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/proxy/mod_proxy_fcgi.c?rev=1884069&r1=1884068&r2=1884069&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/proxy/mod_proxy_fcgi.c (original)
+++ httpd/httpd/trunk/modules/proxy/mod_proxy_fcgi.c Thu Dec 3 16:00:13 2020
@@ -1085,45 +1085,94 @@ static int proxy_fcgi_handler(request_re
goto cleanup;
}
- /* Prefetch (nonlocking) the request body so to increase the chance to get
- * the whole (or enough) body and determine Content-Length vs chunked or
- * spooled. By doing this before connecting or reusing the backend, we want
- * to minimize the delay between this connection is considered alive and
- * the first bytes sent (should the client's link be slow or some input
- * filter retain the data). This is a best effort to prevent the backend
- * from closing (from under us) what it thinks is an idle connection, hence
- * to reduce to the minimum the unavoidable local is_socket_connected() vs
- * remote keepalive race condition.
+ /* We possibly reuse input data prefetched in previous call(s), e.g. for a
+ * balancer fallback scenario.
*/
- input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
- status = ap_proxy_prefetch_input(r, backend, input_brigade,
- APR_NONBLOCK_READ, &input_bytes,
- MAX_MEM_SPOOL);
- if (status != OK) {
- goto cleanup;
- }
- if (apr_table_get(r->subprocess_env, "proxy-sendcl")
- && (APR_BRIGADE_EMPTY(input_brigade)
- || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
- apr_bucket_brigade *tmp_bb;
- apr_off_t remaining_bytes = 0;
-
- AP_DEBUG_ASSERT(MAX_MEM_SPOOL >= input_bytes);
- tmp_bb = apr_brigade_create(p, r->connection->bucket_alloc);
- status = ap_proxy_spool_input(r, backend, tmp_bb, &remaining_bytes,
- MAX_MEM_SPOOL - input_bytes);
+ apr_pool_userdata_get((void **)&input_brigade, "proxy-fcgi-input", p);
+ if (input_brigade == NULL) {
+ const char *old_te = apr_table_get(r->headers_in, "Transfer-Encoding");
+ const char *old_cl = NULL;
+ if (!old_te) {
+ old_cl = apr_table_get(r->headers_in, "Content-Length");
+ }
+
+ input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
+ apr_pool_userdata_setn(input_brigade, "proxy-fcgi-input", NULL, p);
+
+ /* Prefetch (nonlocking) the request body so to increase the chance
+ * to get the whole (or enough) body and determine Content-Length vs
+ * chunked or spooled. By doing this before connecting or reusing the
+ * backend, we want to minimize the delay between this connection is
+ * considered alive and the first bytes sent (should the client's link
+ * be slow or some input filter retain the data). This is a best effort
+ * to prevent the backend from closing (from under us) what it thinks is
+ * an idle connection, hence to reduce to the minimum the unavoidable
+ * local is_socket_connected() vs remote keepalive race condition.
+ */
+ status = ap_proxy_prefetch_input(r, backend, input_brigade,
+ APR_NONBLOCK_READ, &input_bytes,
+ MAX_MEM_SPOOL);
if (status != OK) {
goto cleanup;
}
- APR_BRIGADE_CONCAT(input_brigade, tmp_bb);
- input_bytes += remaining_bytes;
- }
- if (!APR_BRIGADE_EMPTY(input_brigade)
- && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
- apr_table_unset(r->headers_in, "Transfer-Encoding");
- apr_table_set(r->headers_in, "Content-Length",
- apr_off_t_toa(p, input_bytes));
+ /*
+ * The request body is streamed by default, using either C-L or
+ * chunked T-E, like this:
+ *
+ * The whole body (including no body) was received on prefetch, i.e.
+ * the input brigade ends with EOS => C-L = input_bytes.
+ *
+ * C-L is known and reliable, i.e. only protocol filters in the input
+ * chain thus none should change the body => use C-L from client.
+ *
+ * The administrator has not "proxy-sendcl" which prevents T-E => use
+ * T-E and chunks.
+ *
+ * Otherwise we need to determine and set a content-length, so spool
+ * the entire request body to memory/temporary file (MAX_MEM_SPOOL),
+ * such that we finally know its length => C-L = input_bytes.
+ */
+ if (!APR_BRIGADE_EMPTY(input_brigade)
+ && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
+ /* The whole thing fit, so our decision is trivial, use the input
+ * bytes for the Content-Length. If we expected no body, and read
+ * no body, do not set the Content-Length.
+ */
+ if (old_cl || old_te || input_bytes) {
+ apr_table_set(r->headers_in, "Content-Length",
+ apr_off_t_toa(p, input_bytes));
+ if (old_te) {
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ }
+ }
+ }
+ else if (old_cl && r->input_filters == r->proto_input_filters) {
+ /* Streaming is possible by preserving the existing C-L */
+ }
+ else if (!apr_table_get(r->subprocess_env, "proxy-sendcl")) {
+ /* Streaming is possible using T-E: chunked */
+ }
+ else {
+ /* No streaming, C-L is the only option so spool to memory/file */
+ apr_bucket_brigade *tmp_bb;
+ apr_off_t remaining_bytes = 0;
+
+ AP_DEBUG_ASSERT(MAX_MEM_SPOOL >= input_bytes);
+ tmp_bb = apr_brigade_create(p, r->connection->bucket_alloc);
+ status = ap_proxy_spool_input(r, backend, tmp_bb, &remaining_bytes,
+ MAX_MEM_SPOOL - input_bytes);
+ if (status != OK) {
+ goto cleanup;
+ }
+
+ APR_BRIGADE_CONCAT(input_brigade, tmp_bb);
+ input_bytes += remaining_bytes;
+
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ apr_table_set(r->headers_in, "Content-Length",
+ apr_off_t_toa(p, input_bytes));
+ }
}
/* This scheme handler does not reuse connections by default, to