|
|
8b0621 |
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
|
|
|
8b0621 |
index ec1e042..2c0500f 100644
|
|
|
8b0621 |
--- a/modules/proxy/mod_proxy_http.c
|
|
|
8b0621 |
+++ b/modules/proxy/mod_proxy_http.c
|
|
|
8b0621 |
@@ -310,16 +310,18 @@ static int stream_reqbody_read(proxy_http_req_t *req, apr_bucket_brigade *bb,
|
|
|
8b0621 |
return OK;
|
|
|
8b0621 |
}
|
|
|
8b0621 |
|
|
|
8b0621 |
-static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
|
|
|
8b0621 |
+static int stream_reqbody(proxy_http_req_t *req)
|
|
|
8b0621 |
{
|
|
|
8b0621 |
request_rec *r = req->r;
|
|
|
8b0621 |
int seen_eos = 0, rv = OK;
|
|
|
8b0621 |
apr_size_t hdr_len;
|
|
|
8b0621 |
char chunk_hdr[20]; /* must be here due to transient bucket. */
|
|
|
8b0621 |
+ conn_rec *origin = req->origin;
|
|
|
8b0621 |
proxy_conn_rec *p_conn = req->backend;
|
|
|
8b0621 |
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
|
|
|
8b0621 |
apr_bucket_brigade *header_brigade = req->header_brigade;
|
|
|
8b0621 |
apr_bucket_brigade *input_brigade = req->input_brigade;
|
|
|
8b0621 |
+ rb_methods rb_method = req->rb_method;
|
|
|
8b0621 |
apr_off_t bytes, bytes_streamed = 0;
|
|
|
8b0621 |
apr_bucket *e;
|
|
|
8b0621 |
|
|
|
8b0621 |
@@ -333,7 +335,7 @@ static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
|
|
|
8b0621 |
}
|
|
|
8b0621 |
|
|
|
8b0621 |
if (!APR_BRIGADE_EMPTY(input_brigade)) {
|
|
|
8b0621 |
- /* If this brigade contains EOS, either stop or remove it. */
|
|
|
8b0621 |
+ /* If this brigade contains EOS, remove it and be done. */
|
|
|
8b0621 |
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
|
|
|
8b0621 |
seen_eos = 1;
|
|
|
8b0621 |
|
|
|
8b0621 |
@@ -375,7 +377,8 @@ static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
|
|
|
8b0621 |
APR_BRIGADE_INSERT_TAIL(input_brigade, e);
|
|
|
8b0621 |
}
|
|
|
8b0621 |
}
|
|
|
8b0621 |
- else if (bytes_streamed > req->cl_val) {
|
|
|
8b0621 |
+ else if (rb_method == RB_STREAM_CL
|
|
|
8b0621 |
+ && bytes_streamed > req->cl_val) {
|
|
|
8b0621 |
/* C-L < bytes streamed?!?
|
|
|
8b0621 |
* We will error out after the body is completely
|
|
|
8b0621 |
* consumed, but we can't stream more bytes at the
|
|
|
8b0621 |
@@ -407,7 +410,7 @@ static int stream_reqbody(proxy_http_req_t *req, rb_methods rb_method)
|
|
|
8b0621 |
APR_BRIGADE_PREPEND(input_brigade, header_brigade);
|
|
|
8b0621 |
|
|
|
8b0621 |
/* Flush here on EOS because we won't stream_reqbody_read() again */
|
|
|
8b0621 |
- rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, req->origin,
|
|
|
8b0621 |
+ rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin,
|
|
|
8b0621 |
input_brigade, seen_eos);
|
|
|
8b0621 |
if (rv != OK) {
|
|
|
8b0621 |
return rv;
|
|
|
8b0621 |
@@ -454,10 +457,6 @@ static int spool_reqbody_cl(proxy_http_req_t *req, apr_off_t *bytes_spooled)
|
|
|
8b0621 |
/* If this brigade contains EOS, either stop or remove it. */
|
|
|
8b0621 |
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
|
|
|
8b0621 |
seen_eos = 1;
|
|
|
8b0621 |
-
|
|
|
8b0621 |
- /* We can't pass this EOS to the output_filters. */
|
|
|
8b0621 |
- e = APR_BRIGADE_LAST(input_brigade);
|
|
|
8b0621 |
- apr_bucket_delete(e);
|
|
|
8b0621 |
}
|
|
|
8b0621 |
|
|
|
8b0621 |
apr_brigade_length(input_brigade, 1, &bytes);
|
|
|
8b0621 |
@@ -644,7 +643,18 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
|
|
|
8b0621 |
*/
|
|
|
8b0621 |
temp_brigade = apr_brigade_create(p, bucket_alloc);
|
|
|
8b0621 |
block = req->prefetch_nonblocking ? APR_NONBLOCK_READ : APR_BLOCK_READ;
|
|
|
8b0621 |
- do {
|
|
|
8b0621 |
+
|
|
|
8b0621 |
+ /* Account for saved input, if any. */
|
|
|
8b0621 |
+ apr_brigade_length(input_brigade, 0, &bytes_read);
|
|
|
8b0621 |
+
|
|
|
8b0621 |
+ /* Ensure we don't hit a wall where we have a buffer too small
|
|
|
8b0621 |
+ * for ap_get_brigade's filters to fetch us another bucket,
|
|
|
8b0621 |
+ * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
|
|
|
8b0621 |
+ * (an arbitrary value).
|
|
|
8b0621 |
+ */
|
|
|
8b0621 |
+ while (bytes_read < MAX_MEM_SPOOL - 80
|
|
|
8b0621 |
+ && (APR_BRIGADE_EMPTY(input_brigade)
|
|
|
8b0621 |
+ || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
|
|
|
8b0621 |
status = ap_get_brigade(r->input_filters, temp_brigade,
|
|
|
8b0621 |
AP_MODE_READBYTES, block,
|
|
|
8b0621 |
MAX_MEM_SPOOL - bytes_read);
|
|
|
8b0621 |
@@ -686,15 +696,7 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
|
|
|
8b0621 |
c->client_ip, c->remote_host ? c->remote_host: "");
|
|
|
8b0621 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
|
8b0621 |
}
|
|
|
8b0621 |
-
|
|
|
8b0621 |
- /* Ensure we don't hit a wall where we have a buffer too small
|
|
|
8b0621 |
- * for ap_get_brigade's filters to fetch us another bucket,
|
|
|
8b0621 |
- * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
|
|
|
8b0621 |
- * (an arbitrary value.)
|
|
|
8b0621 |
- */
|
|
|
8b0621 |
- } while ((bytes_read < MAX_MEM_SPOOL - 80)
|
|
|
8b0621 |
- && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))
|
|
|
8b0621 |
- && !req->prefetch_nonblocking);
|
|
|
8b0621 |
+ }
|
|
|
8b0621 |
|
|
|
8b0621 |
/* Use chunked request body encoding or send a content-length body?
|
|
|
8b0621 |
*
|
|
|
8b0621 |
@@ -838,35 +840,21 @@ static int ap_proxy_http_request(proxy_http_req_t *req)
|
|
|
8b0621 |
{
|
|
|
8b0621 |
int rv;
|
|
|
8b0621 |
request_rec *r = req->r;
|
|
|
8b0621 |
- apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc;
|
|
|
8b0621 |
- apr_bucket_brigade *header_brigade = req->header_brigade;
|
|
|
8b0621 |
- apr_bucket_brigade *input_brigade = req->input_brigade;
|
|
|
8b0621 |
|
|
|
8b0621 |
/* send the request header/body, if any. */
|
|
|
8b0621 |
switch (req->rb_method) {
|
|
|
8b0621 |
+ case RB_SPOOL_CL:
|
|
|
8b0621 |
case RB_STREAM_CL:
|
|
|
8b0621 |
case RB_STREAM_CHUNKED:
|
|
|
8b0621 |
if (req->do_100_continue) {
|
|
|
8b0621 |
- rv = ap_proxy_pass_brigade(bucket_alloc, r, req->backend,
|
|
|
8b0621 |
- req->origin, header_brigade, 1);
|
|
|
8b0621 |
+ rv = ap_proxy_pass_brigade(req->bucket_alloc, r, req->backend,
|
|
|
8b0621 |
+ req->origin, req->header_brigade, 1);
|
|
|
8b0621 |
}
|
|
|
8b0621 |
else {
|
|
|
8b0621 |
- rv = stream_reqbody(req, req->rb_method);
|
|
|
8b0621 |
+ rv = stream_reqbody(req);
|
|
|
8b0621 |
}
|
|
|
8b0621 |
break;
|
|
|
8b0621 |
|
|
|
8b0621 |
- case RB_SPOOL_CL:
|
|
|
8b0621 |
- /* Prefetch has built the header and spooled the whole body;
|
|
|
8b0621 |
- * if we don't expect 100-continue we can flush both all at once,
|
|
|
8b0621 |
- * otherwise flush the header only.
|
|
|
8b0621 |
- */
|
|
|
8b0621 |
- if (!req->do_100_continue) {
|
|
|
8b0621 |
- APR_BRIGADE_CONCAT(header_brigade, input_brigade);
|
|
|
8b0621 |
- }
|
|
|
8b0621 |
- rv = ap_proxy_pass_brigade(bucket_alloc, r, req->backend,
|
|
|
8b0621 |
- req->origin, header_brigade, 1);
|
|
|
8b0621 |
- break;
|
|
|
8b0621 |
-
|
|
|
8b0621 |
default:
|
|
|
8b0621 |
/* shouldn't be possible */
|
|
|
8b0621 |
rv = HTTP_INTERNAL_SERVER_ERROR;
|
|
|
8b0621 |
@@ -1577,15 +1565,10 @@ int ap_proxy_http_process_response(proxy_http_req_t *req)
|
|
|
8b0621 |
|
|
|
8b0621 |
/* Send the request body (fully). */
|
|
|
8b0621 |
switch(req->rb_method) {
|
|
|
8b0621 |
+ case RB_SPOOL_CL:
|
|
|
8b0621 |
case RB_STREAM_CL:
|
|
|
8b0621 |
case RB_STREAM_CHUNKED:
|
|
|
8b0621 |
- status = stream_reqbody(req, req->rb_method);
|
|
|
8b0621 |
- break;
|
|
|
8b0621 |
- case RB_SPOOL_CL:
|
|
|
8b0621 |
- /* Prefetch has spooled the whole body, flush it. */
|
|
|
8b0621 |
- status = ap_proxy_pass_brigade(req->bucket_alloc, r,
|
|
|
8b0621 |
- backend, origin,
|
|
|
8b0621 |
- req->input_brigade, 1);
|
|
|
8b0621 |
+ status = stream_reqbody(req);
|
|
|
8b0621 |
break;
|
|
|
8b0621 |
default:
|
|
|
8b0621 |
/* Shouldn't happen */
|
|
|
8b0621 |
@@ -1940,6 +1923,7 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
8b0621 |
const char *u;
|
|
|
8b0621 |
proxy_http_req_t *req = NULL;
|
|
|
8b0621 |
proxy_conn_rec *backend = NULL;
|
|
|
8b0621 |
+ apr_bucket_brigade *input_brigade = NULL;
|
|
|
8b0621 |
int is_ssl = 0;
|
|
|
8b0621 |
conn_rec *c = r->connection;
|
|
|
8b0621 |
proxy_dir_conf *dconf;
|
|
|
8b0621 |
@@ -2005,8 +1989,20 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
8b0621 |
|
|
|
8b0621 |
dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
|
|
|
8b0621 |
|
|
|
8b0621 |
+ /* We possibly reuse input data prefetched in previous call(s), e.g. for a
|
|
|
8b0621 |
+ * balancer fallback scenario, and in this case the 100 continue settings
|
|
|
8b0621 |
+ * should be consistent between balancer members. If not, we need to ignore
|
|
|
8b0621 |
+ * Proxy100Continue on=>off once we tried to prefetch already, otherwise
|
|
|
8b0621 |
+ * the HTTP_IN filter won't send 100 Continue for us anymore, and we might
|
|
|
8b0621 |
+ * deadlock with the client waiting for each other. Note that off=>on is
|
|
|
8b0621 |
+ * not an issue because in this case r->expecting_100 is false (the 100
|
|
|
8b0621 |
+ * Continue is out already), but we make sure that prefetch will be
|
|
|
8b0621 |
+ * nonblocking to avoid passing more time there.
|
|
|
8b0621 |
+ */
|
|
|
8b0621 |
+ apr_pool_userdata_get((void **)&input_brigade, "proxy-req-input", p);
|
|
|
8b0621 |
+
|
|
|
8b0621 |
/* Should we handle end-to-end or ping 100-continue? */
|
|
|
8b0621 |
- if ((r->expecting_100 && dconf->forward_100_continue)
|
|
|
8b0621 |
+ if ((r->expecting_100 && (dconf->forward_100_continue || input_brigade))
|
|
|
8b0621 |
|| PROXY_DO_100_CONTINUE(worker, r)) {
|
|
|
8b0621 |
/* We need to reset r->expecting_100 or prefetching will cause
|
|
|
8b0621 |
* ap_http_filter() to send "100 Continue" response by itself. So
|
|
|
8b0621 |
@@ -2023,7 +2019,8 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
8b0621 |
/* Should we block while prefetching the body or try nonblocking and flush
|
|
|
8b0621 |
* data to the backend ASAP?
|
|
|
8b0621 |
*/
|
|
|
8b0621 |
- else if (apr_table_get(r->subprocess_env, "proxy-prefetch-nonblocking")) {
|
|
|
8b0621 |
+ else if (input_brigade || apr_table_get(r->subprocess_env,
|
|
|
8b0621 |
+ "proxy-prefetch-nonblocking")) {
|
|
|
8b0621 |
req->prefetch_nonblocking = 1;
|
|
|
8b0621 |
}
|
|
|
8b0621 |
|
|
|
8b0621 |
@@ -2048,6 +2045,17 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
8b0621 |
sizeof(req->server_portstr))))
|
|
|
8b0621 |
goto cleanup;
|
|
|
8b0621 |
|
|
|
8b0621 |
+ /* The header is always (re-)built since it depends on worker settings,
|
|
|
8b0621 |
+ * but the body can be fetched only once (even partially), so it's saved
|
|
|
8b0621 |
+ * in between proxy_http_handler() calls should we come back here.
|
|
|
8b0621 |
+ */
|
|
|
8b0621 |
+ req->header_brigade = apr_brigade_create(p, req->bucket_alloc);
|
|
|
8b0621 |
+ if (input_brigade == NULL) {
|
|
|
8b0621 |
+ input_brigade = apr_brigade_create(p, req->bucket_alloc);
|
|
|
8b0621 |
+ apr_pool_userdata_setn(input_brigade, "proxy-req-input", NULL, p);
|
|
|
8b0621 |
+ }
|
|
|
8b0621 |
+ req->input_brigade = input_brigade;
|
|
|
8b0621 |
+
|
|
|
8b0621 |
/* Prefetch (nonlocking) the request body so to increase the chance to get
|
|
|
8b0621 |
* the whole (or enough) body and determine Content-Length vs chunked or
|
|
|
8b0621 |
* spooled. By doing this before connecting or reusing the backend, we want
|
|
|
8b0621 |
@@ -2058,8 +2066,6 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
8b0621 |
* to reduce to the minimum the unavoidable local is_socket_connected() vs
|
|
|
8b0621 |
* remote keepalive race condition.
|
|
|
8b0621 |
*/
|
|
|
8b0621 |
- req->input_brigade = apr_brigade_create(p, req->bucket_alloc);
|
|
|
8b0621 |
- req->header_brigade = apr_brigade_create(p, req->bucket_alloc);
|
|
|
8b0621 |
if ((status = ap_proxy_http_prefetch(req, uri, locurl)) != OK)
|
|
|
8b0621 |
goto cleanup;
|
|
|
8b0621 |
|