Index: modules/proxy/mod_proxy_http.c =================================================================== --- modules/proxy/mod_proxy_http.c (revision 1599220) +++ modules/proxy/mod_proxy_http.c (working copy) @@ -299,7 +299,8 @@ static int stream_reqbody_chunked(apr_pool_t *p, proxy_conn_rec *p_conn, conn_rec *origin, apr_bucket_brigade *header_brigade, - apr_bucket_brigade *input_brigade) + apr_bucket_brigade *input_brigade, + int flushall) { int seen_eos = 0, rv = OK; apr_size_t hdr_len; @@ -312,14 +313,20 @@ static int stream_reqbody_chunked(apr_pool_t *p, add_te_chunked(p, bucket_alloc, header_brigade); terminate_headers(bucket_alloc, header_brigade); - while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) + while (APR_BRIGADE_EMPTY(input_brigade) + || !APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) { char chunk_hdr[20]; /* must be here due to transient bucket. */ + int flush = flushall; + if (!APR_BRIGADE_EMPTY(input_brigade)) { /* If this brigade contains EOS, either stop or remove it. */ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { seen_eos = 1; + /* The request is flushed below this loop with the EOS chunk */ + flush = 0; + /* We can't pass this EOS to the output_filters. */ e = APR_BRIGADE_LAST(input_brigade); apr_bucket_delete(e); @@ -341,6 +348,7 @@ static int stream_reqbody_chunked(apr_pool_t *p, */ e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc); APR_BRIGADE_INSERT_TAIL(input_brigade, e); + } if (header_brigade) { /* we never sent the header brigade, so go ahead and @@ -348,6 +356,12 @@ static int stream_reqbody_chunked(apr_pool_t *p, */ bb = header_brigade; + /* Flush now, unless it is done below this loop with the EOS chunk, + * to minimize the delay between connect (or is_socket_connected) + * and the first bytes sent. + */ + flush = !seen_eos; + /* * Save input_brigade in bb brigade. (At least) in the SSL case * input_brigade contains transient buckets whose data would get @@ -369,7 +383,7 @@ static int stream_reqbody_chunked(apr_pool_t *p, } /* The request is flushed below this loop with chunk EOS header */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0); + rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, flush); if (rv != OK) { return rv; } @@ -420,7 +434,7 @@ static int stream_reqbody_cl(apr_pool_t *p, conn_rec *origin, apr_bucket_brigade *header_brigade, apr_bucket_brigade *input_brigade, - const char *old_cl_val) + const char *old_cl_val, int flushall) { int seen_eos = 0, rv = 0; apr_status_t status = APR_SUCCESS; @@ -446,8 +460,12 @@ static int stream_reqbody_cl(apr_pool_t *p, } terminate_headers(bucket_alloc, header_brigade); - while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) + while (APR_BRIGADE_EMPTY(input_brigade) + || !APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) { + int flush = flushall; + + if (!APR_BRIGADE_EMPTY(input_brigade)) { apr_brigade_length(input_brigade, 1, &bytes); bytes_streamed += bytes; @@ -455,6 +473,9 @@ static int stream_reqbody_cl(apr_pool_t *p, if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { seen_eos = 1; + /* Once we hit EOS, we are ready to flush. */ + flush = 1; + /* We can't pass this EOS to the output_filters. */ e = APR_BRIGADE_LAST(input_brigade); apr_bucket_delete(e); @@ -476,6 +497,7 @@ static int stream_reqbody_cl(apr_pool_t *p, bytes_streamed, cl_val); return HTTP_INTERNAL_SERVER_ERROR; } + } if (header_brigade) { /* we never sent the header brigade, so go ahead and @@ -483,6 +505,11 @@ static int stream_reqbody_cl(apr_pool_t *p, */ bb = header_brigade; + /* Flush now to minimize the delay between connect (or + * is_socket_connected) and the first bytes sent. + */ + flush = 1; + /* * Save input_brigade in bb brigade. (At least) in the SSL case * input_brigade contains transient buckets whose data would get @@ -503,8 +530,7 @@ static int stream_reqbody_cl(apr_pool_t *p, bb = input_brigade; } - /* Once we hit EOS, we are ready to flush. */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos); + rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, flush); if (rv != OK) { return rv ; } @@ -689,7 +715,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_r proxy_conn_rec *p_conn, conn_rec *origin, proxy_server_conf *conf, apr_uri_t *uri, - char *url, char *server_portstr) + char *url, char *server_portstr, + int flushall) { conn_rec *c = r->connection; apr_bucket_alloc_t *bucket_alloc = c->bucket_alloc; @@ -710,6 +737,7 @@ int ap_proxy_http_request(apr_pool_t *p, request_r apr_off_t bytes; int force10, rv; apr_table_t *headers_in_copy; + apr_read_type_e block; header_brigade = apr_brigade_create(p, bucket_alloc); @@ -990,10 +1018,17 @@ int ap_proxy_http_request(apr_pool_t *p, request_r * reasonable size. */ temp_brigade = apr_brigade_create(p, bucket_alloc); + block = (flushall) ? APR_NONBLOCK_READ : APR_BLOCK_READ; do { status = ap_get_brigade(r->input_filters, temp_brigade, - AP_MODE_READBYTES, APR_BLOCK_READ, + AP_MODE_READBYTES, block, MAX_MEM_SPOOL - bytes_read); + if (block == APR_NONBLOCK_READ + && (APR_STATUS_IS_EAGAIN(status) + || (status == APR_SUCCESS + && APR_BRIGADE_EMPTY(temp_brigade)))) { + break; + } if (status != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: prefetch request body failed to %pI (%s)" @@ -1031,7 +1066,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_r * (an arbitrary value.) */ } while ((bytes_read < MAX_MEM_SPOOL - 80) - && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))); + && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)) + && !flushall); /* Use chunked request body encoding or send a content-length body? * @@ -1068,7 +1104,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_r * is absent, and the filters are unchanged (the body won't * be resized by another content filter). */ - if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + 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 filtered bytes read from the client for the request * body Content-Length. @@ -1137,11 +1174,11 @@ skip_body: switch(rb_method) { case RB_STREAM_CHUNKED: rv = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade, - input_brigade); + input_brigade, flushall); break; case RB_STREAM_CL: rv = stream_reqbody_cl(p, r, p_conn, origin, header_brigade, - input_brigade, old_cl_val); + input_brigade, old_cl_val, flushall); break; case RB_SPOOL_CL: rv = spool_reqbody_cl(p, r, p_conn, origin, header_brigade, @@ -1937,7 +1974,7 @@ static int proxy_http_handler(request_rec *r, prox const char *proxy_function; const char *u; proxy_conn_rec *backend = NULL; - int is_ssl = 0; + int is_ssl = 0, flushall = 0; conn_rec *c = r->connection; /* * Use a shorter-lived pool to reduce memory usage @@ -1992,6 +2029,10 @@ static int proxy_http_handler(request_rec *r, prox ap_proxy_ssl_connection_cleanup(backend, r); } + if (apr_table_get(r->subprocess_env, "proxy-flushall")) { + flushall = 1; + } + /* * In the case that we are handling a reverse proxy connection and this * is not a request that is coming over an already kept alive connection @@ -2050,7 +2091,8 @@ static int proxy_http_handler(request_rec *r, prox /* Step Four: Send the Request */ if ((status = ap_proxy_http_request(p, r, backend, backend->connection, - conf, uri, url, server_portstr)) != OK) + conf, uri, url, server_portstr, + flushall)) != OK) goto cleanup; /* Step Five: Receive the Response */