On 4/14/22 10:18 AM, Stefan Eissing wrote:
> 
> 
>> Am 13.04.2022 um 17:16 schrieb Ruediger Pluem <rpl...@apache.org>:
>>
>>
>>
>> On 4/7/22 12:41 PM, ic...@apache.org wrote:
>>> Author: icing
>>> Date: Thu Apr  7 10:41:46 2022
>>> New Revision: 1899648
>>>
>>> URL: http://svn.apache.org/viewvc?rev=1899648&view=rev
>>> Log:
>>>  *) core/mod_http: use RESPONSE meta buckets and a new HTTP/1.x specific
>>>     filter to send responses through the output filter chain.
>>>     Specifically: the HTTP_HEADER output filter and 
>>> ap_send_interim_response()
>>>     create a RESPONSE bucket and no longer are concerned with HTTP/1.x
>>>     serialization.
>>>     A new HTTP1_RESPONSE_OUT transcode filter writes the proper HTTP/1.x
>>>     bytes when dealing with a RESPONSE bucket. That filter installs itself
>>>     on the pre_read_request hook when the connection has protocol 
>>> 'http/1.1'.
>>>
>>>
>>> Added:
>>>    httpd/httpd/trunk/changes-entries/core_response_buckets.txt
>>> Modified:
>>>    httpd/httpd/trunk/include/mod_core.h
>>>    httpd/httpd/trunk/modules/http/http_core.c
>>>    httpd/httpd/trunk/modules/http/http_filters.c
>>>    httpd/httpd/trunk/modules/http/http_protocol.c
>>>    httpd/httpd/trunk/server/protocol.c
>>>
>>
>>> Modified: httpd/httpd/trunk/modules/http/http_filters.c
>>> URL: 
>>> http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http/http_filters.c?rev=1899648&r1=1899647&r2=1899648&view=diff
>>> ==============================================================================
>>> --- httpd/httpd/trunk/modules/http/http_filters.c (original)
>>> +++ httpd/httpd/trunk/modules/http/http_filters.c Thu Apr  7 10:41:46 2022
>>
>>> @@ -1364,62 +1178,124 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_
>>>             return rv;
>>>         }
>>>     }
>>> -
>>> -    for (e = APR_BRIGADE_FIRST(b);
>>> -         e != APR_BRIGADE_SENTINEL(b);
>>> -         e = APR_BUCKET_NEXT(e))
>>> -    {
>>> -        if (AP_BUCKET_IS_ERROR(e) && !eb) {
>>> -            eb = e->data;
>>> -            continue;
>>> -        }
>>> -        if (APR_BUCKET_IS_EOS(e)) {
>>> -            if (!eos) eos = e;
>>> -            continue;
>>> -        }
>>> -        /*
>>> -         * If we see an EOC bucket it is a signal that we should get out
>>> -         * of the way doing nothing.
>>> +    else {
>>> +        /* 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
>>> +         * send on status and headers as a response bucket.
>>> +         *
>>> +         * But we also send interim responses (as response buckets)
>>> +         * through this filter and that must not trigger generating
>>> +         * an additional response bucket.
>>> +         *
>>> +         * Waiting on a DATA/ERROR/EOS bucket alone is not enough,
>>> +         * unfortunately, as some handlers trigger response generation
>>> +         * by just writing a FLUSH (see mod_lua's websocket for example).
>>>          */
>>> -        if (AP_BUCKET_IS_EOC(e)) {
>>> -            ap_remove_output_filter(f);
>>> -            return ap_pass_brigade(f->next, b);
>>> +        for (e = APR_BRIGADE_FIRST(b);
>>> +             e != APR_BRIGADE_SENTINEL(b) && !trigger;
>>> +             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;
>>> +            }
>>> +            else {
>>> +                /* First content bucket, always triggering the response.*/
>>> +                trigger = e;
>>> +            }
>>
>> Why don't we remove ourselves any longer if we hit an EOC bucket and pass 
>> stuff on immediately?
>> If we want to handle this HTTP protocol agnostic here we need to ensure that 
>> the EOC bucket
>> triggers the immediate removal of the ap_h1_response_out_filter once the 
>> ap_h1_response_out_filter
>> sees it without putting any headers on the wire.
>> I am not sure what the appropriate behaviour would be in the HTTP > 1.1 case.
> 
> Valid point. The previous filter implementation removed itself immediately on
> encountering an EOC, regardless of where it appeared in the brigade.
> 
> The current implementation inserts, if not done previously, a RESPONSE bucket.
> Afterwards it checks on EOC, remove itself and passes on the brigade.
> 
> The behaviour differs, if an EOC is sent *without* a final RESPONSE having 
> been
> created before that. The question is what the intended behaviour of passing an
> EOC is. Reading the comment in mod_proxy_http.c:1096, it seems that the 
> intention really
> is to NOT generate a response.

Correct. The idea is that when the backend just closed the connection after we 
send the request
we do the same with our client: Just closing the connection without any 
response provided that we
had a keepalive connection to the frontend. This comes across to our client as 
if it hit a race
between sending its request to us and a keepalive closure from our side.
I am aware that this is HTTP/1.1 specific. In the light of the separation you 
do in these patches
I am fine if the ap_h1_response_out_filter acts accordingly and just does not 
process a RESPONSE bucket in
case there is an EOC bucket as well. This would allow other protocol versions 
to adopt to their needs.

Regards

RĂ¼diger

Reply via email to