Skip to content

Adds grpc-web support for envoy.#943

Merged
htuch merged 40 commits intoenvoyproxy:masterfrom
fengli79:master
May 24, 2017
Merged

Adds grpc-web support for envoy.#943
htuch merged 40 commits intoenvoyproxy:masterfrom
fengli79:master

Conversation

@fengli79
Copy link
Copy Markdown
Contributor

@fengli79 fengli79 commented May 10, 2017

Add the grpc-web support for envoy.

Comment thread source/common/grpc/grpc_web_filter.cc Outdated

namespace Grpc {

const std::string GrpcWebFilter::GRPC_WEB_CONTENT_TYPE{"application/grpc-web"};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Global statics are disallowed (and you know this ;) )

Copy link
Copy Markdown
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.

As discussed offline, we should add curl to the build recipes as shown in https://github.com/lyft/envoy/blob/master/bazel/EXTERNAL_DEPS.md when it comes time to do the integration tests.

Comment thread source/common/http/conn_manager_impl.cc Outdated
}, this);
#endif

if (buffered_response_data_ && buffered_response_data_->length() > 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Per offline convo with @fengli79 I am working on a proper solution to this part which unfortunately is pretty tricky. Hope to land this by the end of the week. It shouldn't change the rest of the code that much so we can continue to review/test in the interim.

@mattklein123
Copy link
Copy Markdown
Member

Please rebase on #959 once merged.

Copy link
Copy Markdown
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.

Looks generally quite good. But, I think this needs some more detailed comments in various places, see feedback below.

namespace Envoy {
namespace Grpc {

const uint8_t GrpcWebFilter::GRPC_WEB_TRAILER = 0b10000000;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: personally I find (1 << 7) much clearer than binary literals where I need to count zeroes. But, feel free to keep it this way if you'd like.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, let's keep this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a comment that this is specifying a "trailer with no compression".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Http::FilterHeadersStatus GrpcWebFilter::decodeHeaders(Http::HeaderMap& headers, bool) {
const Http::HeaderEntry* content_type = headers.ContentType();
if (content_type != nullptr &&
Http::Headers::get().ContentTypeValues.GrpcWebText == content_type->value().c_str()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Based on https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2, I would expect that grpc-web-text to be a prefix, not exact match. The default is "+proto", but what about the other cases?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I plan add follow up pull requests to support other subtypes soon.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a comment here and the other site to this effect, TODO(fengli79): ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Http::Headers::get().ContentTypeValues.GrpcWebText == content_type->value().c_str()) {
is_text_request_ = true;
}
headers.insertContentType().value(Http::Headers::get().ContentTypeValues.Grpc);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From the docs, insertContentType() only inserts a header if it doesn't exist. I'm wondering how this works then, since you have test coverage for this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If there's no content-type header from the request, the headers.insertContentType() will create a HeaderMapEntry and return it, thus I can change its value.
If there's already a content-type header, the inserContentType() will return the existing HeaderMapEntry.
Note that content-type is an inline header, it doesn't support appending.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's correct. When content-type exist, it will return reference to existing entry, and following .value sets the value, so it works.


const Http::HeaderEntry* accept = headers.get(Http::Headers::get().Accept);
if (accept != nullptr &&
Http::Headers::get().ContentTypeValues.GrpcWebText == accept->value().c_str()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same as above comment on grpc-web-text prefix.

Comment thread source/common/grpc/grpc_web_filter.cc Outdated
// Parse application/grpc-web-text format.
if (data.length() + decoding_buffer_.length() < 4) {
decoding_buffer_.move(data);
return Http::FilterDataStatus::Continue;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do you want to use StopIterationAndBuffer here (and have the filter mechanism perform buffering for you until you have 4 bytes), or is there some reason that the filters down the chain should operate on an empty buffer? I think that would simplify the below code as well (you might not even need an explicit decoding_buffer_).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I cannot use StopIterationAndBuffer, as I have to consume the buffer given by the filter completely and put back b64 decoded data in the same buffer. If I let the filter to buffer the data, I will lose the information about the boundary for the b64 decoded data and raw data in the filter buffer.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, got it. Do you want to do StopIterationNoBufferin the < 4 branch here, since there's no data for later filters at this point?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That makes sense. Done.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/common/grpc/grpc_web_filter.cc Outdated
return Http::FilterDataStatus::Continue;
}

// Encodes the response as base64.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you elaborate in some more depth on the transformation that is happening here? My current guess is that we're taking standard gRPC wire format, decoding and deframing, then re-encoding it using the format of gRPC Web? What is the meaning of flags in this context? It's probably not H2 flags, so decoder_ must have figured out what the delimited message flags are? I think lots of verbose comments would help.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

More comments added.

temp->add("\r\n");
}, &temp);
Buffer::OwnedImpl buffer;
buffer.add(&GRPC_WEB_TRAILER, 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please describe that this is a delimited message that is being assembled as per the gRPC spec.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not in gRPC spec. Instead, it's defined in gRPC-Web. Trailers are encoded in a frame denoted with 0b1000000 + 4 bytes length in network order. The frame data is CRLF separated headers.

// Tests request data.
Buffer::OwnedImpl request_buffer(MESSAGE, MESSAGE_SIZE);
EXPECT_EQ(Http::FilterDataStatus::Continue, filter_.decodeData(request_buffer, true));
EXPECT_EQ(0, memcmp(MESSAGE, TestUtility::bufferToString(request_buffer).c_str(), MESSAGE_SIZE));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why not just do string comparison?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because the buffer may contains \0.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can do C++ string comparisons on buffers with embedded nulls.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. However, I have to wrap the MESSAGE with std::string(MESSAGE, MESSAGE_SIZE).

Buffer::OwnedImpl request_buffer(MESSAGE, MESSAGE_SIZE);
EXPECT_EQ(Http::FilterDataStatus::Continue, filter_.decodeData(request_buffer, true));
EXPECT_EQ(0, memcmp(MESSAGE, TestUtility::bufferToString(request_buffer).c_str(), MESSAGE_SIZE));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What about testing request trailers?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

gRPC doesn't use request trailers. And gRPC-Web clients are in browser, also won't send request trailers.
If a request trailer presents, it will be forwarded to the backend without change, and let the backend decide whether to ignore it or reject it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you should still verify this pass-thru behavior, as it's something we intend to have happen and don't want to see a regression for.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Copy Markdown
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.

I think I now get grpc-web :) I'll reiterate my earlier comment that it would be helpful if the grpc-web spec was more than just a terse set of bullet points that provide a diff against the normal gRPC H2 framing, but instead provide a full spec. Thanks.

Http::FilterHeadersStatus GrpcWebFilter::decodeHeaders(Http::HeaderMap& headers, bool) {
const Http::HeaderEntry* content_type = headers.ContentType();
if (content_type != nullptr &&
Http::Headers::get().ContentTypeValues.GrpcWebText == content_type->value().c_str()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a comment here and the other site to this effect, TODO(fengli79): ...

Comment thread source/common/grpc/grpc_web_filter.cc Outdated
// Parse application/grpc-web-text format.
if (data.length() + decoding_buffer_.length() < 4) {
decoding_buffer_.move(data);
return Http::FilterDataStatus::Continue;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, got it. Do you want to do StopIterationNoBufferin the < 4 branch here, since there's no data for later filters at this point?

// Encodes the decoded frames with base64.
for (auto& frame : frames) {
Buffer::OwnedImpl temp;
temp.add(&frame.flags_, 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you please add an explanation of what these flags are? Please point to the relevant specification. As a reader, I find it very confusing to have to think about both H2 flags (which clearly these aren't, but the above decode doesn't make clear), normal gRPC message delimiting flags (compression vs. no compression) and the gRPC-web special flags (with the MSB bit). If I find it confusing, I think others will too. Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Those flags are gRPC frame flags. They are decoded as grpc frames from a grpc backend.
They are documented in the decoder.cc:
https://github.com/lyft/envoy/blob/master/source/common/grpc/codec.h#L49

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's gRPC frame, comes from backend. It's documented in the decoder.h.
https://github.com/lyft/envoy/blob/master/source/common/grpc/codec.h#L49

namespace Envoy {
namespace Grpc {

const uint8_t GrpcWebFilter::GRPC_WEB_TRAILER = 0b10000000;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a comment that this is specifying a "trailer with no compression".

const char B64_MESSAGE[] = "AAAAABJncnBjLXdlYi10ZXh0LWRhdGE=";
const size_t B64_MESSAGE_SIZE = 32;
const char TRAILERS[] = "\x80\x00\x00\x00\x0fgrpc-status:0\r\n";
const size_t TRAILERS_SIZE = 20;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we use sizeof here to make this less fragile, since these are const arrays with known size?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

// Tests request data.
Buffer::OwnedImpl request_buffer(MESSAGE, MESSAGE_SIZE);
EXPECT_EQ(Http::FilterDataStatus::Continue, filter_.decodeData(request_buffer, true));
EXPECT_EQ(0, memcmp(MESSAGE, TestUtility::bufferToString(request_buffer).c_str(), MESSAGE_SIZE));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can do C++ string comparisons on buffers with embedded nulls.

Buffer::OwnedImpl request_buffer(MESSAGE, MESSAGE_SIZE);
EXPECT_EQ(Http::FilterDataStatus::Continue, filter_.decodeData(request_buffer, true));
EXPECT_EQ(0, memcmp(MESSAGE, TestUtility::bufferToString(request_buffer).c_str(), MESSAGE_SIZE));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you should still verify this pass-thru behavior, as it's something we intend to have happen and don't want to see a regression for.

Http::Headers::get().ContentTypeValues.GrpcWeb);
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.decodeHeaders(request_headers, false));
EXPECT_EQ(Http::Headers::get().ContentTypeValues.Grpc,
request_headers.ContentType()->value().c_str());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Where do you check grpc-accept-encoding:identity,deflate,gzip., te:trailers, etc?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

const size_t TEXT_MESSAGE_SIZE = 23;
const char B64_MESSAGE[] = "AAAAABJncnBjLXdlYi10ZXh0LWRhdGE=";
const size_t B64_MESSAGE_SIZE = 32;
const char TRAILERS[] = "\x80\x00\x00\x00\x0fgrpc-status:0\r\n";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should have more than one trailer to verify multi trailer header handling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/common/grpc/grpc_web_filter.cc Outdated
const Http::HeaderEntry* content_type = headers.ContentType();
if (content_type != nullptr &&
Http::Headers::get().ContentTypeValues.GrpcWebText == content_type->value().c_str()) {
// Checks whether gRPC-Web client is sending b64 ncoded request.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

typo ncoded -> encoded.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please expand b64.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

bool is_text_response_;
Buffer::OwnedImpl decoding_buffer_;
Decoder decoder_;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: new line after this and before the closing bracket for namespace Grpc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

buffer.move(temp);
encoder_callbacks_->addEncodedData(buffer);
return Http::FilterTrailersStatus::Continue;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: new line after this line and before bracket closing the namespace Grpc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/server/config/http/grpc_web.cc Outdated
namespace Server {
namespace Configuration {

HttpFilterFactoryCb GrpcWebFilterConfig::tryCreateFilterFactory(HttpFilterType type,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/common/grpc/grpc_web_filter.h Outdated
// Implements StreamDecoderFilter.
Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap&, bool) override;
Http::FilterDataStatus decodeData(Buffer::Instance&, bool) override;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: remove new line

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

namespace Envoy {
namespace Grpc {

// Bit mask denotes a trailers frame of gRPC-Web.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: uncompressed trailer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm going to add another bit mask 0b00000001 for compression. So it's not necessary to mention the compression here once it gets in.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ok, works for me as long as it's WIP in here.

Comment thread source/common/grpc/grpc_web_filter.h Outdated

private:
static const uint8_t GRPC_WEB_TRAILER;
Http::StreamDecoderFilterCallbacks* decoder_callbacks_{};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: unused var

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed.

Comment thread source/common/grpc/grpc_web_filter.h Outdated
static const uint8_t GRPC_WEB_TRAILER;
Http::StreamDecoderFilterCallbacks* decoder_callbacks_{};
Http::StreamEncoderFilterCallbacks* encoder_callbacks_{};
bool is_text_request_;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

for consistency you can init it right here, is_text_request_{} and same with is_text_respose_

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/server/config/http/grpc_web.cc Outdated
const Json::Object&,
const std::string&,
Server::Instance&) {
if (type != HttpFilterType::Both || name != "grpc_web") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just a heads up: #994
HTTP filters registration will be changed in ^

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ack.

htuch
htuch previously approved these changes May 24, 2017
@htuch
Copy link
Copy Markdown
Member

htuch commented May 24, 2017

@fengli79 One final ask before merging - can you switch to the new filter API in #994? Thanks!

Comment thread source/server/config/http/grpc_web.cc Outdated
namespace Server {
namespace Configuration {

HttpFilterFactoryCb GrpcWebFilterConfig::tryCreateFilterFactory(HttpFilterType type,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is still the old filter creation API, can you bump to the new one? Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Copy Markdown
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.

@ccaraman Is this ready to merge?

@htuch htuch merged commit 20bf9e8 into envoyproxy:master May 24, 2017
}

// Adds te:trailers to upstream HTTP2 request. It's required for gRPC.
headers.addStatic(Http::Headers::get().TE, Http::Headers::get().TEValues.Trailers);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Both of these addStatic() calls can use the O(1) insert varieties since you added the headers to the O(1) map. This would yield better performance. @fengli79 not urgent but I would do a follow up or add to a future grpc-web PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will address in a follow up PR. Also, there are other stuffs need to be enhanced, like add some wrapper methods in Buffer::OwnedImpl to avoid the additional buffer allocation. Today I have to use a temp buffer when processing trailers, as the Buffer::OwnedImpl doesn't allow me to insert data to a particular position.

rshriram pushed a commit to rshriram/envoy that referenced this pull request Oct 30, 2018
Automatic merge from submit-queue.

[DO NOT MERGE] Auto PR to update dependencies of proxy

This PR will be merged automatically once checks are successful.
```release-note
none
```
jpsim pushed a commit that referenced this pull request Nov 28, 2022
Description: Adds initial support for Swift presentation of HTTP filter on-headers invocations.
Risk Level: Moderate
Testing: Local end-to-end

Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: JP Simard <jp@jpsim.com>
jpsim pushed a commit that referenced this pull request Nov 29, 2022
Description: Adds initial support for Swift presentation of HTTP filter on-headers invocations.
Risk Level: Moderate
Testing: Local end-to-end

Signed-off-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: JP Simard <jp@jpsim.com>
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.

6 participants