Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: allow any FilterHeaderStatus::Stop* after sending a local reply. #15496

Merged
merged 6 commits into from
Mar 24, 2021

Conversation

PiotrSikora
Copy link
Contributor

Fixes #14957.

Signed-off-by: Piotr Sikora piotrsikora@google.com

Fixes envoyproxy#14957.

Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Copy link
Contributor

@asraa asraa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! No idea why this went out of my radar.

Went through some history, this would now match up with continue_iteration which returns false in all three of these cases so I think this is more consistent, and at least won't cause the bug I was trying to avoid (where it returns true, but local is complete)

if (stoppedAll() || status == FilterHeadersStatus::StopIteration) {

Could we simplify it by making this change:

 const auto continue_iteration =
        (*entry)->commonHandleAfterHeadersCallback(status, end_stream);
ENVOY_BUG(continue_iteration && state_.local_complete_, "Filter did not return StopAll or StopIteration after sending a local reply");

and then adjust so that we still return early at local complete?

if ((!continue_iteration || state_.local_complete_) && std::next(entry) != decoder_filters_.end()) {

@asraa
Copy link
Contributor

asraa commented Mar 22, 2021

@rgs1

@rgs1
Copy link
Member

rgs1 commented Mar 22, 2021

Ah interesting, so this didn't break for us because we always do StopIteration after a local reply. OOI, what's the use case for Stop and Buffer after a local reply?

…iteration

Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
@PiotrSikora PiotrSikora requested a review from asraa March 22, 2021 23:33
@PiotrSikora
Copy link
Contributor Author

Could we simplify it by making this change:

const auto continue_iteration =
        (*entry)->commonHandleAfterHeadersCallback(status, end_stream);
ENVOY_BUG(continue_iteration && state_.local_complete_, "Filter did not return StopAll or StopIteration after sending a local reply");

and then adjust so that we still return early at local complete?

if ((!continue_iteration || state_.local_complete_) && std::next(entry) != decoder_filters_.end()) {

Done, with correct assert (!continue_iteration || !state_.local_complete_).

@PiotrSikora
Copy link
Contributor Author

Ah interesting, so this didn't break for us because we always do StopIteration after a local reply. OOI, what's the use case for Stop and Buffer after a local reply?

The use case is the same for all 3 values - to stop request processing.

Some extensions (e.g. Wasm, ext_authz) already return FilterHeadersStatus::StopAllIterationAndWatermark since it provides better behavior than FilterHeadersStatus::StopIteration, and it incorrectly triggerred this ENVOY_BUG.

@rgs1
Copy link
Member

rgs1 commented Mar 22, 2021

Ah interesting, so this didn't break for us because we always do StopIteration after a local reply. OOI, what's the use case for Stop and Buffer after a local reply?

The use case is the same for all 3 values - to stop request processing.

Some extensions (e.g. Wasm, ext_authz) already return FilterHeadersStatus::StopAllIterationAndWatermark since it provides better behavior than FilterHeadersStatus::StopIteration, and it incorrectly triggerred this ENVOY_BUG.

What does better behavior mean (besides not triggering ENVOY_BUG)? Wondering if there's any docs we could improve (maybe the filters contract doc), given that this stuff has bitten us a few times.

@PiotrSikora
Copy link
Contributor Author

What does better behavior mean (besides not triggering ENVOY_BUG)? Wondering if there's any docs we could improve (maybe the filters contract doc), given that this stuff has bitten us a few times.

FilterHeadersStatus::StopIteration stops processing of HTTP request/response headers, but not of the HTTP request/response body, which means that if your extension returns FilterHeadersStatus::StopIteration from HTTP request headers (e.g. to consult external service), then it might be called to process HTTP request body (if such exists) even before receiving response from the external service and resuming processing of HTTP request headers. This doesn't follow HTTP flow and it's a pretty unusual behavior.

FilterHeadersStatus::StopAllIterationAnd{Buffer,Watermark} stop processing of both: HTTP request/response headers and body.

@rgs1
Copy link
Member

rgs1 commented Mar 23, 2021

What does better behavior mean (besides not triggering ENVOY_BUG)? Wondering if there's any docs we could improve (maybe the filters contract doc), given that this stuff has bitten us a few times.

FilterHeadersStatus::StopIteration stops processing of HTTP request/response headers, but not of the HTTP request/response body, which means that if your extension returns FilterHeadersStatus::StopIteration from HTTP request headers (e.g. to consult external service), then it might be called to process HTTP request body (if such exists) even before receiving response from the external service and resuming processing of HTTP request headers. This doesn't follow HTTP flow and it's a pretty unusual behavior.

FilterHeadersStatus::StopAllIterationAnd{Buffer,Watermark} stop processing of both: HTTP request/response headers and body.

... but none of that should matter after a local reply, in that case they should all be the same? Which takes me back to my original question, after a local reply the most idiomatic thing would be a plain StopIteration given that the behavior is the same across Stop* once a local reply was sent.

@PiotrSikora
Copy link
Contributor Author

... but none of that should matter after a local reply, in that case they should all be the same? Which takes me back to my original question, after a local reply the most idiomatic thing would be a plain StopIteration given that the behavior is the same across Stop* once a local reply was sent.

Right, it doesn't really matter which value do you return in this particular case... Although, I'd argue that the plain StopIteration shouldn't be the most idiomatic thing, considering that the intent is to stop all processing.

However, my point was that because of this weird behavior, we completely removed StopIteration from the Wasm APIs, so users have no way (or reason, really) to return it after sending a local reply.

@rgs1
Copy link
Member

rgs1 commented Mar 23, 2021

... but none of that should matter after a local reply, in that case they should all be the same? Which takes me back to my original question, after a local reply the most idiomatic thing would be a plain StopIteration given that the behavior is the same across Stop* once a local reply was sent.

Right, it doesn't really matter which value do you return in this particular case... Although, I'd argue that the plain StopIteration shouldn't be the most idiomatic thing, considering that the intent is to stop all processing.

However, my point was that because of this weird behavior, we completely removed StopIteration from the Wasm APIs, so users have no way (or reason, really) to return it after sending a local reply.

Ah, I see -- that makes sense. Linking this to #13737 for potential follow-ups on docs & comments.

Signed-off-by: Piotr Sikora <piotrsikora@google.com>
asraa
asraa previously approved these changes Mar 23, 2021
Copy link
Contributor

@asraa asraa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM (and thanks for the explanations)

@asraa
Copy link
Contributor

asraa commented Mar 23, 2021

@envoyproxy/senior-maintainers For a quick merge (and FYI)

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - thanks for fixing this one

source/common/http/filter_manager.cc Show resolved Hide resolved
@PiotrSikora
Copy link
Contributor Author

/retest

@repokitteh-read-only
Copy link

Retrying Azure Pipelines:
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #15496 (comment) was created by @PiotrSikora.

see: more, trace.

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one last question, otherwise good to go

source/common/http/filter_manager.cc Show resolved Hide resolved
(*entry)->commonHandleAfterHeadersCallback(status, end_stream) && !state_.local_complete_;
const auto continue_iteration = (*entry)->commonHandleAfterHeadersCallback(status, end_stream);
ENVOY_BUG(!continue_iteration || !state_.local_complete_,
"Filter did not return StopAll or StopIteration after sending a local reply.");

// If this filter ended the stream, decodeComplete() should be called for it.
if ((*entry)->end_stream_) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want to do any of this work (handle new metadata, add decoded data, etc).
Would it make more sense to early return or does that break something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don't know the codebase well enough to answer that. Commenting out maybeContinueDecoding() below breaks a bunch of tests, but I don't know if those are real failures or tests that are not representative of the production code.

In any case, I don't think such risky change should be a part of this bugfix PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given it's unlikely folks will be doing sendLocalReply and add decoded data / metadata I buy that as an argument.

Would you instead either throw a TODO my way or, if you want to avoid another round of CI, open a tracking issue to look into it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's skip the 3h wait on the CI, I filled #15654.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks LGTM other than Alyssa's remaining comment.

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Please do fill out release notes (risk, tests etc) next time!

@PiotrSikora
Copy link
Contributor Author

/backport

@repokitteh-read-only repokitteh-read-only bot added the backport/review Request to backport to stable releases label May 26, 2021
@phlax phlax removed the backport/review Request to backport to stable releases label Feb 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make ENVOY_BUG only log critical in official debug builds
6 participants