Skip to content

Local reply mapper - implementation#8921

Closed
lukidzi wants to merge 19 commits intoenvoyproxy:masterfrom
lukidzi:local_reply_mapper_impl
Closed

Local reply mapper - implementation#8921
lukidzi wants to merge 19 commits intoenvoyproxy:masterfrom
lukidzi:local_reply_mapper_impl

Conversation

@lukidzi
Copy link
Contributor

@lukidzi lukidzi commented Nov 6, 2019

This is implementation of local reply mapper with ability to change format of response to custom Json. I haven't written any docs yet because I want to ensure you are ok with implementation proposition.

Description: Allows to create custom mapper of response code based on filters. Filter based on response headers might not work because when there is local reply we don't have any response headers. Also added possibility to map response to custom Text or Json format.
Risk Level: Low
Testing: I will add after implementation proposition review.
Docs Changes:
Release Notes:
Fixes #7537

Follow up #8126

Lukasz Dziedziak added 2 commits November 5, 2019 22:22
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
@repokitteh-read-only
Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/.

🐱

Caused by: #8921 was opened by lukidzi.

see: more, trace.

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

Awesome, some comments to kick this off. Tests will be required; I think you can start work on integration tests without further feedback, since these will look the same regardless of implementation. API looks good, as previously agreed.
/wait

}
}

// [#next-major-version: In the v3 API, we should consider renaming
Copy link
Member

Choose a reason for hiding this comment

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

Nit: please stick to 110 character line lengths, so this shouldn't wrap.

// `HTTP spec <https://tools.ietf.org/html/rfc3986>` and is provided for convenience.
bool merge_slashes = 33;

// [#not-implemented-hide:]
Copy link
Member

Choose a reason for hiding this comment

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

Please uncomment if you are going to land this in this PR.

}

void Utility::sendLocalReply(
bool is_grpc, std::function<void(HeaderMapPtr&& headers, bool end_stream)> encode_headers,
Copy link
Member

Choose a reason for hiding this comment

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

This method signature keeps growing and growing :) Can we wrap the parameters up into one or more structs that group things together?

}

ResponseMapper::ResponseMapper(AccessLog::FilterPtr&& filter, ResponseRewriterPtr&& rewriter) {
filter_ = std::move(filter);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: prefer using initialization args.

const StreamInfo::StreamInfo& stream_info) const {
const auto output_map = toMap(request_headers, response_headers, response_trailers, stream_info);
const StreamInfo::StreamInfo& stream_info,
const absl::string_view& body) const {
Copy link
Member

Choose a reason for hiding this comment

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

This only applies to locally generated responses, right? We don't have to worry about the remote having a 100MB response? What if someone specifies RESP_BODY on a regular access log formatter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Body is passed only for local_reply, for access log I'm passing empty string so this shouldn't be a problem.

Copy link
Member

Choose a reason for hiding this comment

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

I think the direct response route action also goes through sendLocalReply. So, keep in mind that potentially largeish files could be present.

//
// string_format: "My custom message"
//
string string_format = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

should this field be DataSource to allow fetching body from the control plane/disk?

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, it looks like string_format assumes formatting.
In this case my ask is to add 3rd option with non-formattable DataSource.

Copy link
Member

Choose a reason for hiding this comment

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

I guess one reasonable question is "which API fields in general should be data sources?". Do we have a governing principle? Is it basically anything you might want to load from a disk? Do you have a concrete needs for this @euroelessar?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have a concrete needs for this @euroelessar?

We want an ability to return custom hard-coded user-facing html page on errors generated by envoy.
In addition to that we want to be able to serve compile-time compressed version of it for clients which support gzip/brotli.

Combination of this two factors implies that we need an ability to return potentially large binary body.
Adding this bodies to the config itself is potentially non-practical, therefore the ask for DataSource.

To clarify, I'm fine with it not being part of this particular pull request, we can contribute the necessary pieces as follow ups, assuming the API can be extended to this needs.

Copy link
Member

Choose a reason for hiding this comment

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

I think it would be nice to get the API clean here, what you're asking for isn't unreasonable. The actual code to consume from data source is a 1 liner, so if folks are in agreement let's do this.

}

// Configuration of new value for matched local response.
message ResponseRewriter {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be an ability to override header values?
For example to set content-type or some application-specific chillout header.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might be added later.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree.

// Allows to define custom format of local reply.
// It is allowed to use :ref:`command operators <config_access_log_command_operators>`
// which will be resolved to actual data.
type.StringOrJson format = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can the format be on per mapper basis, please?
Example: we want to return different body and/or content type depending on the generated status code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

During config proposition we aggred that this might be added later. So I might add it in next PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Have read the comments for prior version of pull request. It resolves the comment so no concerns from my side here.

//
// string_format: "My custom message"
//
string string_format = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, it looks like string_format assumes formatting.
In this case my ask is to add 3rd option with non-formattable DataSource.

Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
lukidzi and others added 4 commits November 11, 2019 21:38
Signed-off-by: Lukasz Dziedziak <lukidzi@gmail.com>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
@mattklein123
Copy link
Member

@lukidzi can you fix format and merge master? Is this ready for full review? Thank you.

/wait

@lukidzi
Copy link
Contributor Author

lukidzi commented Nov 13, 2019

I'm working on docs so I'll write when it's ready : )

Lukasz Dziedziak added 3 commits November 17, 2019 13:48
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
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 discussion point while you're doing clean-up...


struct LocalReplyData {
// Tells if this is a response to a gRPC request.
bool is_grpc_;
Copy link
Contributor

Choose a reason for hiding this comment

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

As long as we're doing an invasive refactor here, I wonder if we want to keep these special cased, or clean this up as well.
Arguably sendLocalReply could use request headers to determine is grpc and is head request. Or we could rewrite the two special cases we have as modification functions rather than preserving them as hard-coded one off special cases.

@mattklein123 any thoughts here?

Lukasz Dziedziak added 7 commits November 24, 2019 23:48
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
Signed-off-by: Lukasz Dziedziak <lukasz.dziedziak@allegro.pl>
@repokitteh-read-only
Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/.

🐱

Caused by: #8921 was synchronize by lukidzi.

see: more, trace.

@htuch
Copy link
Member

htuch commented Nov 26, 2019

@lukidzi can you ping the reviewers on this PR when this is ready for another pass? It's a bit unclear with the current activity whether reviewer feedback is addressed.

@lukidzi
Copy link
Contributor Author

lukidzi commented Nov 27, 2019

I think it is ready for first review. I am trying to fix asan build but maybe you will see something that I missed. @htuch @alyssawilk @mattklein123

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

Generally looks good, I'm curious to see if we can get a fuzzer also in this PR.

Local reply modification
========================

The :ref:`HTTP connection manager <arch_overview_http_conn_man>` supports modification of local reply which is response returned by Envoy itself.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: "which is the response"

--------------------------------

There is support for modification of local replies. You can specify list of :ref:`mappers <envoy_api_field_config.filter.network.http_connection_manager.v2.LocalReplyConfig.mapper>` which contains
pairs of :ref:`filter <envoy_api_msg_config.filter.accesslog.v2.AccessLogFilter>` and :ref:`rewriter <envoy_api_msg_config.filter.network.http_connection_manager.v2.ResponseRewriter>`. Both elements in pair has to be
Copy link
Member

Choose a reason for hiding this comment

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

Nit: "Both elements in the pair have to be specified."

CodeUtility::toString(Http::Code::InternalServerError), absl::nullopt,
parent_.is_head_request_);
parent_.state_.destroyed_,
Utility::EncodeFunctions{[&](HeaderMapPtr&& response_headers, bool end_stream) -> void {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: this isn't your code, but if you can remove the wildcard & capture here, all the better.

response_headers->setReferenceContentType(Headers::get().ContentTypeValues.Text);
HeaderMapPtr response_headers{new HeaderMapImpl{
{Headers::get().Status, std::to_string(enumToInt(local_reply_data.response_code_))}}};
std::string formatted_body{};
Copy link
Member

Choose a reason for hiding this comment

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

Nit: no need for {} here.

@stale
Copy link

stale bot commented Dec 4, 2019

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Dec 4, 2019
@mattklein123 mattklein123 added the no stalebot Disables stalebot from closing an issue label Dec 4, 2019
@stale stale bot removed the stale stalebot believes this issue/PR has not been touched recently label Dec 4, 2019
@keontang
Copy link

keontang commented Dec 23, 2019

any progress?@lukidzi

@mattklein123 mattklein123 removed the no stalebot Disables stalebot from closing an issue label Jan 26, 2020
@stale
Copy link

stale bot commented Feb 2, 2020

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Feb 2, 2020
@stale
Copy link

stale bot commented Feb 9, 2020

This pull request has been automatically closed because it has not had activity in the last 14 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot closed this Feb 9, 2020
@SaveTheRbtz
Copy link
Contributor

@lukidzi, friendly ping =)

@qiwzhang
Copy link
Contributor

@lukidzi we need this feature, are you going to finish this? or you prefer someone else to take over?

@qiwzhang
Copy link
Contributor

@mattklein123 @htuch can you re-open this PR?

@mattklein123
Copy link
Member

@qiwzhang I want this finished also. If you want to finish it can you just open a new PR if @lukidzi doesn't respond?

@qiwzhang
Copy link
Contributor

ok, sure. I will wait for a couple days then.

@mattklein123
Copy link
Member

cc @Augustyniak also ^ as you recently asked for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api stale stalebot believes this issue/PR has not been touched recently waiting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Customize response code on upstream reset

8 participants