Mailing List Archive

Re: svn commit: r1885605 - in /httpd/httpd/branches/2.4.x: ./ include/ modules/proxy/
On 1/17/21 5:21 PM, minfrin@apache.org wrote:
> Author: minfrin
> Date: Sun Jan 17 16:21:35 2021
> New Revision: 1885605
>
> URL: http://svn.apache.org/viewvc?rev=1885605&view=rev
> Log:
> Backport to v2.4:
>
> *) mod_proxy_http: handle upgrade/tunneling protocols. BZ 61616 is about
> mod_proxy_connect but there has been wstunnel reports
> on dev@ about that too lately.
> trunk patch: https://svn.apache.org/r1678771
> https://svn.apache.org/r1832348
> https://svn.apache.org/r1869338
> https://svn.apache.org/r1869420
> https://svn.apache.org/r1878367
> https://svn.apache.org/r1877557
> https://svn.apache.org/r1877558
> https://svn.apache.org/r1877646
> https://svn.apache.org/r1877695
> https://svn.apache.org/r1879401
> https://svn.apache.org/r1879402
> https://svn.apache.org/r1880200
> https://svn.apache.org/r1885239
> https://svn.apache.org/r1885240
> https://svn.apache.org/r1885244
> 2.4.x patch: http://people.apache.org/~ylavic/patches/2.4.x-mod_proxy_http-upgrade-4on5-v2.patch
> https://github.com/apache/httpd/pull/158
> +1: ylavic, covener, minfrin
> ylavic: All the corresponding trunk changes to mod_proxy_wstunnel (but
> r1885239) have been dropped for this backport proposal, the goal
> being to handle upgrade in mod_proxy_http from now, while r1885239
> allows to benefit from the Upgrade improvements done in proxy_http
> with existing wstunnel configurations (provided mod_proxy_http
> module is loaded).
>
>
> Modified:
> httpd/httpd/branches/2.4.x/CHANGES
> httpd/httpd/branches/2.4.x/STATUS
> httpd/httpd/branches/2.4.x/include/ap_mmn.h
> httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.c
> httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy.h
> httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_connect.c
> httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_http.c
> httpd/httpd/branches/2.4.x/modules/proxy/mod_proxy_wstunnel.c
> httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c
>

> Modified: httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c?rev=1885605&r1=1885604&r2=1885605&view=diff
> ==============================================================================
> --- httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c (original)
> +++ httpd/httpd/branches/2.4.x/modules/proxy/proxy_util.c Sun Jan 17 16:21:35 2021
i,
> @@ -4119,81 +4168,498 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tra
> const char *name,
> int *sent,
> apr_off_t bsize,
> - int after)
> + int flags)
> {
> apr_status_t rv;
> + int flush_each = 0;
> + unsigned int num_reads = 0;
> #ifdef DEBUGGING
> apr_off_t len;
> #endif
>
> - do {
> + /*
> + * Compat: since FLUSH_EACH is default (and zero) for legacy reasons, we
> + * pretend it's no FLUSH_AFTER nor YIELD_PENDING flags, the latter because
> + * flushing would defeat the purpose of checking for pending data (hence
> + * determine whether or not the output chain/stack is full for stopping).
> + */
> + if (!(flags & (AP_PROXY_TRANSFER_FLUSH_AFTER |
> + AP_PROXY_TRANSFER_YIELD_PENDING))) {
> + flush_each = 1;
> + }
> +
> + for (;;) {
> apr_brigade_cleanup(bb_i);
> rv = ap_get_brigade(c_i->input_filters, bb_i, AP_MODE_READBYTES,
> APR_NONBLOCK_READ, bsize);
> - if (rv == APR_SUCCESS) {
> - if (c_o->aborted) {
> - return APR_EPIPE;
> - }
> - if (APR_BRIGADE_EMPTY(bb_i)) {
> - break;
> + if (rv != APR_SUCCESS) {
> + if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
> + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(03308)
> + "ap_proxy_transfer_between_connections: "
> + "error on %s - ap_get_brigade",
> + name);
> + if (rv == APR_INCOMPLETE) {
> + /* Don't return APR_INCOMPLETE, it'd mean "should yield"
> + * for the caller, while it means "incomplete body" here
> + * from ap_http_filter(), which is an error.
> + */
> + rv = APR_EGENERAL;
> + }
> }
> + break;
> + }
> +
> + if (c_o->aborted) {
> + apr_brigade_cleanup(bb_i);
> + flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER;
> + rv = APR_EPIPE;
> + break;
> + }
> + if (APR_BRIGADE_EMPTY(bb_i)) {
> + break;
> + }
> #ifdef DEBUGGING
> - len = -1;
> - apr_brigade_length(bb_i, 0, &len);
> - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03306)
> - "ap_proxy_transfer_between_connections: "
> - "read %" APR_OFF_T_FMT
> - " bytes from %s", len, name);
> + len = -1;
> + apr_brigade_length(bb_i, 0, &len);
> + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03306)
> + "ap_proxy_transfer_between_connections: "
> + "read %" APR_OFF_T_FMT
> + " bytes from %s", len, name);
> #endif
> - if (sent) {
> - *sent = 1;
> - }
> - ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o);
> - if (!after) {
> - apr_bucket *b;
> + if (sent) {
> + *sent = 1;
> + }
> + ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o);
> + if (flush_each) {
> + apr_bucket *b;
> + /*
> + * Do not use ap_fflush here since this would cause the flush
> + * bucket to be sent in a separate brigade afterwards which
> + * causes some filters to set aside the buckets from the first
> + * brigade and process them when FLUSH arrives in the second
> + * brigade. As set asides of our transformed buckets involve
> + * memory copying we try to avoid this. If we have the flush
> + * bucket in the first brigade they directly process the
> + * buckets without setting them aside.
> + */
> + b = apr_bucket_flush_create(bb_o->bucket_alloc);
> + APR_BRIGADE_INSERT_TAIL(bb_o, b);
> + }
> + rv = ap_pass_brigade(c_o->output_filters, bb_o);
> + apr_brigade_cleanup(bb_o);
> + if (rv != APR_SUCCESS) {
> + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(03307)
> + "ap_proxy_transfer_between_connections: "
> + "error on %s - ap_pass_brigade",
> + name);
> + flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER;
> + break;
> + }
>
> - /*
> - * Do not use ap_fflush here since this would cause the flush
> - * bucket to be sent in a separate brigade afterwards which
> - * causes some filters to set aside the buckets from the first
> - * brigade and process them when the flush arrives in the second
> - * brigade. As set asides of our transformed buckets involve
> - * memory copying we try to avoid this. If we have the flush
> - * bucket in the first brigade they directly process the
> - * buckets without setting them aside.
> - */
> - b = apr_bucket_flush_create(bb_o->bucket_alloc);
> - APR_BRIGADE_INSERT_TAIL(bb_o, b);
> + /* Yield if the output filters stack is full? This is to avoid
> + * blocking and give the caller a chance to POLLOUT async.
> + */
> + if (flags & AP_PROXY_TRANSFER_YIELD_PENDING) {
> + int rc = OK;
> +
> + if (!ap_filter_should_yield(c_o->output_filters)) {
> + rc = ap_filter_output_pending(c_o);

I am confused here: !ap_filter_should_yield(c_o->output_filters) means there is no pending output.
Why should we try to send it then? Shouldn't it be the other way round?


> }
> - rv = ap_pass_brigade(c_o->output_filters, bb_o);
> - if (rv != APR_SUCCESS) {
> - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(03307)
> + if (rc == OK) {
> + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
> "ap_proxy_transfer_between_connections: "
> - "error on %s - ap_pass_brigade",
> - name);
> + "yield (output pending)");
> + rv = APR_INCOMPLETE;
> + break;
> + }
> + if (rc != DECLINED) {
> + rv = AP_FILTER_ERROR;
> + break;

Regards

Rüdiger
Re: svn commit: r1885605 - in /httpd/httpd/branches/2.4.x: ./ include/ modules/proxy/ [ In reply to ]
On Tue, Feb 2, 2021 at 10:32 AM Ruediger Pluem <rpluem@apache.org> wrote:
>
> > New Revision: 1885605
> >
> > URL: http://svn.apache.org/viewvc?rev=1885605&view=rev
[]
> > + /* Yield if the output filters stack is full? This is to avoid
> > + * blocking and give the caller a chance to POLLOUT async.
> > + */
> > + if (flags & AP_PROXY_TRANSFER_YIELD_PENDING) {
> > + int rc = OK;
> > +
> > + if (!ap_filter_should_yield(c_o->output_filters)) {
> > + rc = ap_filter_output_pending(c_o);
>
> I am confused here: !ap_filter_should_yield(c_o->output_filters) means there is no pending output.
> Why should we try to send it then? Shouldn't it be the other way round?

Yes, !ap_filter_should_yield() means that there is no pending output,
but only for filters that play the
ap_filter_{setaside,reinstate}_brigade() game.

The goal here is to try to flush pending data of filters that don't
ap_filter_setaside_brigade(), like e.g. ssl_io_filter_coalesce() which
is not using setaside/reinstate (but is kind of aware of them still,
see r1879416, I tried to move it to using setaside/resinstate but the
result is more complicated than the original code, so I passed on that
patch for now and pushed the one-liner..).

But (and this is why the code is like this), we don't want to try to
flush those potential non-setaside pending data if there are setaside
pending data already (like in the core output filter), otherwise we
might block in some filter (like the core filter still) if given more
data while already at the limits.

This poll()ing loop really depends on staying in the POLLOUT state
until there are no more pending data (setaside or not), otherwise we
"risk" the blocking call soon or later, at least with the current "no
EAGAIN" on output mechanism. Hope this clarifies why it's done like
this..

Regards;
Yann.
Re: svn commit: r1885605 - in /httpd/httpd/branches/2.4.x: ./ include/ modules/proxy/ [ In reply to ]
On 2/2/21 4:18 PM, Yann Ylavic wrote:
> On Tue, Feb 2, 2021 at 10:32 AM Ruediger Pluem <rpluem@apache.org> wrote:
>>
>>> New Revision: 1885605
>>>
>>> URL: http://svn.apache.org/viewvc?rev=1885605&view=rev
> []
>>> + /* Yield if the output filters stack is full? This is to avoid
>>> + * blocking and give the caller a chance to POLLOUT async.
>>> + */
>>> + if (flags & AP_PROXY_TRANSFER_YIELD_PENDING) {
>>> + int rc = OK;
>>> +
>>> + if (!ap_filter_should_yield(c_o->output_filters)) {
>>> + rc = ap_filter_output_pending(c_o);
>>
>> I am confused here: !ap_filter_should_yield(c_o->output_filters) means there is no pending output.
>> Why should we try to send it then? Shouldn't it be the other way round?
>
> Yes, !ap_filter_should_yield() means that there is no pending output,
> but only for filters that play the
> ap_filter_{setaside,reinstate}_brigade() game.
>
> The goal here is to try to flush pending data of filters that don't
> ap_filter_setaside_brigade(), like e.g. ssl_io_filter_coalesce() which

But isn't ap_filter_output_pending a noop that always returns DECLINED in case
of !ap_filter_should_yield(c_o->output_filters)?

Regards

Rüdiger
Re: svn commit: r1885605 - in /httpd/httpd/branches/2.4.x: ./ include/ modules/proxy/ [ In reply to ]
Le 03/02/2021 à 09:24, Ruediger Pluem a écrit :
>
>
> On 2/2/21 4:18 PM, Yann Ylavic wrote:
>> On Tue, Feb 2, 2021 at 10:32 AM Ruediger Pluem <rpluem@apache.org> wrote:
>>>
>>>> New Revision: 1885605
>>>>
>>>> URL: http://svn.apache.org/viewvc?rev=1885605&view=rev
>> []
>>>> + /* Yield if the output filters stack is full? This is to avoid
>>>> + * blocking and give the caller a chance to POLLOUT async.
>>>> + */
>>>> + if (flags & AP_PROXY_TRANSFER_YIELD_PENDING) {
>>>> + int rc = OK;
>>>> +
>>>> + if (!ap_filter_should_yield(c_o->output_filters)) {
>>>> + rc = ap_filter_output_pending(c_o);
>>>
>>> I am confused here: !ap_filter_should_yield(c_o->output_filters) means there is no pending output.
>>> Why should we try to send it then? Shouldn't it be the other way round?
>>
>> Yes, !ap_filter_should_yield() means that there is no pending output,
>> but only for filters that play the
>> ap_filter_{setaside,reinstate}_brigade() game.
>>
>> The goal here is to try to flush pending data of filters that don't
>> ap_filter_setaside_brigade(), like e.g. ssl_io_filter_coalesce() which
>
> But isn't ap_filter_output_pending a noop that always returns DECLINED in case
> of !ap_filter_should_yield(c_o->output_filters)?
>
> Regards
>
> Rüdiger
>

Same backport for which a just sent a question about a potential missing
part of r1877558.


Unrelated, but is there still something to discuss here.
The last comment from Rüdiger seems to have been left un-answered.

CJ
Re: svn commit: r1885605 - in /httpd/httpd/branches/2.4.x: ./ include/ modules/proxy/ [ In reply to ]
On Sun, Apr 18, 2021 at 7:37 PM Christophe JAILLET
<christophe.jaillet@wanadoo.fr> wrote:
>
> Le 03/02/2021 à 09:24, Ruediger Pluem a écrit :
> >
> > On 2/2/21 4:18 PM, Yann Ylavic wrote:
> >> On Tue, Feb 2, 2021 at 10:32 AM Ruediger Pluem <rpluem@apache.org> wrote:
> >>>
> >>>> New Revision: 1885605
> >>>>
> >>>> URL: http://svn.apache.org/viewvc?rev=1885605&view=rev
> >> []
> >>>> + /* Yield if the output filters stack is full? This is to avoid
> >>>> + * blocking and give the caller a chance to POLLOUT async.
> >>>> + */
> >>>> + if (flags & AP_PROXY_TRANSFER_YIELD_PENDING) {
> >>>> + int rc = OK;
> >>>> +
> >>>> + if (!ap_filter_should_yield(c_o->output_filters)) {
> >>>> + rc = ap_filter_output_pending(c_o);
> >>>
> >>> I am confused here: !ap_filter_should_yield(c_o->output_filters) means there is no pending output.
> >>> Why should we try to send it then? Shouldn't it be the other way round?
> >>
> >> Yes, !ap_filter_should_yield() means that there is no pending output,
> >> but only for filters that play the
> >> ap_filter_{setaside,reinstate}_brigade() game.
> >>
> >> The goal here is to try to flush pending data of filters that don't
> >> ap_filter_setaside_brigade(), like e.g. ssl_io_filter_coalesce() which
> >
> > But isn't ap_filter_output_pending a noop that always returns DECLINED in case
> > of !ap_filter_should_yield(c_o->output_filters)?
>
> Same backport for which a just sent a question about a potential missing
> part of r1877558.

Like Eric said, this is not relevant for 2.4.

>
> Unrelated, but is there still something to discuss here.

Probably :)

> The last comment from Rüdiger seems to have been left un-answered.

Ah yes, sorry Rüdiger, somehow I missed that one.
First for 2.4.x it's true, if !ap_filter_should_yield() then
ap_filter_output_pending() will always be DECLINED.
But ap_filter_should_yield() and ap_filter_output_pending() in 2.4.x
are just some helpers in proxy_util to keep trunk and 2.4.x code
aligned, not the real util_filter functions.

For trunk though, there is the ssl_io_filter_coalesce() case where
!ap_filter_should_yield() does not mean that
ap_filter_output_pending() has nothing to do. That's because
ssl_io_filter_coalesce() does not play the
ap_filter_{setaside,reinstate}_brigade() game for now, even though it
potentially retains data.
So in r1879416 I made a band aid such that ssl_io_filter_coalesce()
releases its data when it's called from ap_filter_output_pending(),
but the real fix would be that mod_ssl coalesces the data using
apr_brigade_write() and save/restore them with
ap_filter_{setaside,reinstate}_brigade(). But I didn't finish that
patch yet..

Note that in 2.4.x ssl_io_filter_coalesce() in not an issue for the
mod_proxy tunneling loop because ap_proxy_tunnel_create() initially
does:
/* No coalescing filters */
ap_remove_output_filter_byhandle(c_i->output_filters,
"SSL/TLS Coalescing Filter");
ap_remove_output_filter_byhandle(c_o->output_filters,
"SSL/TLS Coalescing Filter");
But in trunk we don't do that anymore, because ultimately
ap_proxy_tunnel_run() could be used for full async mod_proxy(_http),
not only tunneling, meaning that all the filters (and not only
connection ones) need to remain in place. I'm working on that (slowly)
too..


Hopefully I'm a bit more clear about this time..

Regards;
Yann.