diff options
author | Stefan Eissing <icing@apache.org> | 2022-04-14 12:12:20 +0200 |
---|---|---|
committer | Stefan Eissing <icing@apache.org> | 2022-04-14 12:12:20 +0200 |
commit | ffbfbf1c590bae60b32fd3decc63bfe6ca454877 (patch) | |
tree | 785263de65675e3ed07bab80a6c4712d2dd9f3c2 | |
parent | mod_heartmonitor: Fix setting and comparison of IPs fields. (diff) | |
download | apache2-ffbfbf1c590bae60b32fd3decc63bfe6ca454877.tar.xz apache2-ffbfbf1c590bae60b32fd3decc63bfe6ca454877.zip |
*) mod_http: on seeing EOC buckets in ap_http_header_filter(), further
analysis of the brigade is stopped. This means a RESPONSE will not
be generated (if that had not already happened due to other buckets).
Also, improve the `header_only` handling to cope with brigades that
contain buckets like `RESPONSE DATA EOS` in one pass.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1899842 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | modules/http/http_filters.c | 120 |
1 files changed, 72 insertions, 48 deletions
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c index c3ca4aafeb..1cd4ad51d5 100644 --- a/modules/http/http_filters.c +++ b/modules/http/http_filters.c @@ -1148,7 +1148,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, { request_rec *r = f->r; conn_rec *c = r->connection; - apr_bucket *e, *respb = NULL, *trigger = NULL, *eos = NULL; + apr_bucket *e, *next, *eos = NULL, *bcontent = NULL; header_filter_ctx *ctx = f->ctx; ap_bucket_error *eb = NULL; apr_status_t rv = APR_SUCCESS; @@ -1160,25 +1160,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx)); } - if (ctx->final_status) { - /* Sent the final status, eat body if response must not have one. */ - if (ctx->final_header_only) { - /* Still next filters may be waiting for EOS, so pass it (alone) - * when encountered and be done with this filter. - */ - e = APR_BRIGADE_LAST(b); - if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) { - APR_BUCKET_REMOVE(e); - apr_brigade_cleanup(b); - APR_BRIGADE_INSERT_HEAD(b, e); - ap_remove_output_filter(f); - rv = ap_pass_brigade(f->next, b); - } - apr_brigade_cleanup(b); - return rv; - } - } - else { + if (!ctx->final_status) { /* Determine if it is time to insert the response bucket for * the request. Request handlers just write content or an EOS * and that needs to take the current state of request_rec to @@ -1192,42 +1174,56 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, * unfortunately, as some handlers trigger response generation * by just writing a FLUSH (see mod_lua's websocket for example). */ + apr_bucket *respb = NULL; + ap_bucket_response *resp; + for (e = APR_BRIGADE_FIRST(b); - e != APR_BRIGADE_SENTINEL(b) && !trigger; + e != APR_BRIGADE_SENTINEL(b) && !bcontent; e = APR_BUCKET_NEXT(e)) { - if (AP_BUCKET_IS_RESPONSE(e)) { - /* remember the last one if there are many. */ - respb = e; - } - else if (APR_BUCKET_IS_FLUSH(e)) { - /* flush without response bucket triggers */ - if (!respb) trigger = e; - } - else if (APR_BUCKET_IS_EOS(e)) { - trigger = e; - } - else if (AP_BUCKET_IS_ERROR(e)) { - /* Need to handle this below via ap_die() */ - break; + if (APR_BUCKET_IS_METADATA(e)) { + if (AP_BUCKET_IS_RESPONSE(e)) { + /* RESPONSE buckets may get passed by others, for + * example ap_send_interim_response() or ap_die(). + * We examine the status code of such a RESPONSE below + * to determine if the response answers the request + * or is just interim. + */ + respb = e; + resp = respb->data; + if (!ctx->final_status + && (resp->status >= 200 || resp->status == HTTP_SWITCHING_PROTOCOLS)) { + ctx->final_status = resp->status; + ctx->final_header_only = AP_STATUS_IS_HEADER_ONLY(resp->status); + bcontent = APR_BUCKET_NEXT(e); + break; + } + } + else if (APR_BUCKET_IS_FLUSH(e)) { + /* flush without response bucket triggers */ + if (!respb) bcontent = e; + } + else if (APR_BUCKET_IS_EOS(e)) { + bcontent = e; + } + else if (AP_BUCKET_IS_ERROR(e)) { + /* Need to handle this below via ap_die() */ + break; + } + else if (AP_BUCKET_IS_EOC(e)) { + /* Need to handle this below. EOC prevents generating + * a RESPONSE if not already triggered by buckets preceding + * it. */ + break; + } } else { /* First content bucket, always triggering the response.*/ - trigger = e; - } - } - - if (respb) { - ap_bucket_response *resp = respb->data; - if (resp->status >= 200 || resp->status == HTTP_SWITCHING_PROTOCOLS) { - /* Someone is passing the final response, remember it - * so we no longer generate one. */ - ctx->final_status = resp->status; - ctx->final_header_only = AP_STATUS_IS_HEADER_ONLY(resp->status); + bcontent = e; } } - if (trigger && !ctx->final_status) { + if (!ctx->final_status && bcontent) { ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "ap_http_header_filter prep response status %d", r->status); @@ -1258,14 +1254,42 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_set_content_length(r, 0); recursive_error = 1; } + /* insert the RESPONSE before the first content bucket */ respb = create_response_bucket(r, b->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(trigger, respb); + APR_BUCKET_INSERT_BEFORE(bcontent, respb); ctx->final_status = r->status; ctx->final_header_only = (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status)); r->sent_bodyct = 1; /* Whatever follows is real body stuff... */ } } + if (ctx->final_status && ctx->final_header_only) { + /* The final RESPONSE has already been sent or is in front of `bcontent` + * in the brigade. For a header_only respsone, remove all content buckets + * up to the first EOS. On seeing EOS, we remove ourself and are done. + * NOTE that `header_only` responses never generate trailes. + */ + for (e = bcontent? bcontent : APR_BRIGADE_FIRST(b); + e != APR_BRIGADE_SENTINEL(b); + e = next) + { + next = APR_BUCKET_NEXT(e); + if (APR_BUCKET_IS_EOS(e)) { + eos = e; + break; + } + apr_bucket_delete(e); + } + + if (eos) { + ap_remove_output_filter(f); + } + if (!APR_BRIGADE_EMPTY(b)) { + rv = ap_pass_brigade(f->next, b); + } + return rv; + } + /* Look for ERROR/EOC/EOS that require special handling. */ for (e = APR_BRIGADE_FIRST(b); e != APR_BRIGADE_SENTINEL(b); |