Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ api_proto_library_internal(
"//envoy/api/v2/core:config_source",
"//envoy/api/v2/core:protocol",
"//envoy/config/filter/accesslog/v2:accesslog",
"//envoy/data/core/v2alpha:local_reply_configuration",
"//envoy/type:percent",
],
)
Expand All @@ -26,6 +27,7 @@ api_go_proto_library(
"//envoy/api/v2/core:config_source_go_proto",
"//envoy/api/v2/core:protocol_go_proto",
"//envoy/config/filter/accesslog/v2:accesslog_go_proto",
"//envoy/data/core/v2alpha:local_reply_configuration_go_proto",
"//envoy/type:percent_go_proto",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "envoy/api/v2/core/protocol.proto";
import "envoy/api/v2/rds.proto";
import "envoy/api/v2/srds.proto";
import "envoy/config/filter/accesslog/v2/accesslog.proto";
import "envoy/data/core/v2alpha/local_reply_configuration.proto";
import "envoy/type/percent.proto";

import "google/protobuf/any.proto";
Expand Down Expand Up @@ -424,6 +425,9 @@ message HttpConnectionManager {
// Note that Envoy does not perform
// `case normalization <https://tools.ietf.org/html/rfc3986#section-6.2.2.1>`
google.protobuf.BoolValue normalize_path = 30;

// Used to configure the Local reply the type of the body
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 not super clear. Can you write a more detailed description of what this field is doing?

envoy.data.core.v2alpha.LocalReplyConfiguration local_reply_config = 32;
}

message Rds {
Expand Down
15 changes: 14 additions & 1 deletion api/envoy/data/core/v2alpha/BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library")
load("@envoy_api//bazel:api_build_system.bzl", "api_go_proto_library", "api_proto_library")

licenses(["notice"]) # Apache 2

Expand All @@ -13,3 +13,16 @@ api_proto_library(
"//envoy/api/v2/core:base",
],
)

api_proto_library(
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'm not sure if I would consider this a core type. Is this a generic JSON description or very specific to the idea of returning a JSON reply from HCM?

name = "local_reply_configuration",
srcs = ["local_reply_configuration.proto"],
visibility = [
"//visibility:public",
],
)

api_go_proto_library(
name = "local_reply_configuration",
proto = ":local_reply_configuration",
)
24 changes: 24 additions & 0 deletions api/envoy/data/core/v2alpha/local_reply_configuration.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
syntax = "proto3";

package envoy.data.core.v2alpha;

option java_outer_classname = "LocalReplyConfigurationProto";
option java_multiple_files = true;
option java_package = "io.envoyproxy.envoy.data.core.v2alpha";

// [#protodoc-title: Local reply configuration]

// Configure the local reply of the body 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.

What is the body type?

message LocalReplyConfiguration {
message JsonReply {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It doesn't look like this JsonReply message is really part of the API. If I understand correctly, you're just defining it as a proto so you can use MessageUtil::getJsonStringFromMessage to generate JSON. I'd recommend generating JSON without going through proto first. Maybe you can add a helper in /source/common/json.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

#4705

This issue mentions the need to remove the dependency of RapidJSON. The json conversion uses the method of protobuf to convert, so I defined a protobuf for converting the local reply body.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Oh, interesting, I didn't know about that. In that case, my recommendation would be to just instantiate a ProtobufWkt::Struct rather than defining a message here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I hope to have a clear description scheme

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.

Yeah, Struct would be the canonical representation of JSON in proto.

string body = 1;
}

enum MediaType {
TEXT_PLAIN = 0;
APPLICATION_JSON = 1;
NEGOTIATE_VIA_ACCEPT_HEADER = 2;
}

MediaType media_type = 1;
}
1 change: 1 addition & 0 deletions docs/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ PROTO_RST="
/envoy/config/transport_socket/tap/v2alpha/tap/envoy/config/transport_socket/tap/v2alpha/tap.proto.rst
/envoy/data/accesslog/v2/accesslog/envoy/data/accesslog/v2/accesslog.proto.rst
/envoy/data/core/v2alpha/health_check_event/envoy/data/core/v2alpha/health_check_event.proto.rst
/envoy/data/core/v2alpha/local_reply_configuration/envoy/data/core/v2alpha/local_reply_configuration.proto.rst
/envoy/data/tap/v2alpha/common/envoy/data/tap/v2alpha/common.proto.rst
/envoy/data/tap/v2alpha/transport/envoy/data/tap/v2alpha/transport.proto.rst
/envoy/data/tap/v2alpha/http/envoy/data/tap/v2alpha/http.proto.rst
Expand Down
1 change: 1 addition & 0 deletions docs/root/api-v2/data/core/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Core data
:maxdepth: 2

v2alpha/health_check_event.proto
v2alpha/local_reply_configuration.proto
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ Version history
<config_http_filters_fault_injection_http_header>` to the HTTP fault filter.
* governance: extending Envoy deprecation policy from 1 release (0-3 months) to 2 releases (3-6 months).
* health check: expected response codes in http health checks are now :ref:`configurable <envoy_api_msg_core.HealthCheck.HttpHealthCheck>`.
* http: local reply support return json type.
* http: added new grpc_http1_reverse_bridge filter for converting gRPC requests into HTTP/1.1 requests.
* http: fixed a bug where Content-Length:0 was added to HTTP/1 204 responses.
* http: added :ref:`max request headers size <envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.max_request_headers_kb>`. The default behaviour is unchanged.
Expand Down
2 changes: 1 addition & 1 deletion source/common/access_log/access_log_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include "envoy/runtime/runtime.h"
#include "envoy/server/access_log_config.h"

#include "common/grpc/status.h"
#include "common/grpc/utility.h"
#include "common/http/header_utility.h"
#include "common/protobuf/protobuf.h"

Expand Down
10 changes: 6 additions & 4 deletions source/common/grpc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ envoy_cc_library(
)

envoy_cc_library(
name = "status_lib",
srcs = ["status.cc"],
hdrs = ["status.h"],
name = "utility_lib",
srcs = ["utility.cc"],
hdrs = ["utility.h"],
external_deps = ["abseil_optional"],
deps = [
"//include/envoy/grpc:status",
"//include/envoy/http:header_map_interface",
"//source/common/http:headers_lib",
],
)

Expand All @@ -91,7 +93,7 @@ envoy_cc_library(
"//source/common/common:hash_lib",
"//source/common/common:macros",
"//source/common/common:utility_lib",
"//source/common/grpc:status_lib",
"//source/common/grpc:utility_lib",
"//source/common/http:headers_lib",
"//source/common/http:message_lib",
"//source/common/http:utility_lib",
Expand Down
17 changes: 2 additions & 15 deletions source/common/grpc/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,15 @@
#include "common/common/macros.h"
#include "common/common/stack_array.h"
#include "common/common/utility.h"
#include "common/grpc/utility.h"
#include "common/http/headers.h"
#include "common/http/message_impl.h"
#include "common/http/utility.h"
#include "common/protobuf/protobuf.h"

#include "absl/strings/match.h"

namespace Envoy {
namespace Grpc {

bool Common::hasGrpcContentType(const Http::HeaderMap& headers) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm wondering whether, instead of moving this, it might be more useful to build a simple classifier function. It'd have a signature something like:

ContentType contentType(const Http::HeaderMap& headers);

... where ContentType is probably an enum class, sort of similar to your LocalReplyType. That might give us the beginnings of a general-purpose content type negotiation implementation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good idea

const Http::HeaderEntry* content_type = headers.ContentType();
// Content type is gRPC if it is exactly "application/grpc" or starts with
// "application/grpc+". Specifically, something like application/grpc-web is not gRPC.
return content_type != nullptr &&
absl::StartsWith(content_type->value().getStringView(),
Http::Headers::get().ContentTypeValues.Grpc) &&
(content_type->value().size() == Http::Headers::get().ContentTypeValues.Grpc.size() ||
content_type->value()
.getStringView()[Http::Headers::get().ContentTypeValues.Grpc.size()] == '+');
}

bool Common::isGrpcResponseHeader(const Http::HeaderMap& headers, bool end_stream) {
if (end_stream) {
// Trailers-only response, only grpc-status is required.
Expand All @@ -46,7 +33,7 @@ bool Common::isGrpcResponseHeader(const Http::HeaderMap& headers, bool end_strea
if (Http::Utility::getResponseStatus(headers) != enumToInt(Http::Code::OK)) {
return false;
}
return hasGrpcContentType(headers);
return Utility::hasGrpcContentType(headers);
}

absl::optional<Status::GrpcStatus> Common::getGrpcStatus(const Http::HeaderMap& trailers) {
Expand Down
10 changes: 3 additions & 7 deletions source/common/grpc/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include "envoy/http/header_map.h"
#include "envoy/http/message.h"

#include "common/common/hash.h"
#include "common/grpc/status.h"
#include "common/grpc/utility.h"
#include "common/common/hash.h"

#include "common/protobuf/protobuf.h"

#include "absl/types/optional.h"
Expand All @@ -28,12 +30,6 @@ class Exception : public EnvoyException {

class Common {
public:
/**
* @param headers the headers to parse.
* @return bool indicating whether content-type is gRPC.
*/
static bool hasGrpcContentType(const Http::HeaderMap& headers);

/**
* @param headers the headers to parse.
* @param bool indicating whether the header is at end_stream.
Expand Down
18 changes: 17 additions & 1 deletion source/common/grpc/status.cc → source/common/grpc/utility.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#include "common/grpc/status.h"
#include "common/grpc/utility.h"

#include "common/http/headers.h"

#include "absl/strings/match.h"

namespace Envoy {
namespace Grpc {
Expand Down Expand Up @@ -85,5 +89,17 @@ uint64_t Utility::grpcToHttpStatus(Status::GrpcStatus grpc_status) {
}
}

bool Utility::hasGrpcContentType(const Http::HeaderMap& headers) {
const Http::HeaderEntry* content_type = headers.ContentType();
// Content type is gRPC if it is exactly "application/grpc" or starts with
// "application/grpc+". Specifically, something like application/grpc-web is not gRPC.
return content_type != nullptr &&
absl::StartsWith(content_type->value().getStringView(),
Http::Headers::get().ContentTypeValues.Grpc) &&
(content_type->value().size() == Http::Headers::get().ContentTypeValues.Grpc.size() ||
content_type->value()
.getStringView()[Http::Headers::get().ContentTypeValues.Grpc.size()] == '+');
}

} // namespace Grpc
} // namespace Envoy
9 changes: 8 additions & 1 deletion source/common/grpc/status.h → source/common/grpc/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
#include <cstdint>

#include "envoy/grpc/status.h"
#include "envoy/http/header_map.h"

namespace Envoy {
namespace Grpc {

/**
* Grpc::Status utilities.
* Grpc utilities.
*/
class Utility {
public:
Expand All @@ -26,6 +27,12 @@ class Utility {
* @return uint64_t the canonical HTTP status code corresponding to a gRPC status code.
*/
static uint64_t grpcToHttpStatus(Status::GrpcStatus grpc_status);

/**
* @param headers the headers to parse.
* @return bool indicating whether content-type is gRPC.
*/
static bool hasGrpcContentType(const Http::HeaderMap& headers);
};

} // namespace Grpc
Expand Down
4 changes: 3 additions & 1 deletion source/common/http/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ envoy_cc_library(
"http_parser",
],
deps = [
":conn_manager_config_interface",
":exception_lib",
":header_map_lib",
":headers_lib",
Expand All @@ -301,12 +302,13 @@ envoy_cc_library(
"//source/common/common:empty_string",
"//source/common/common:enum_to_int",
"//source/common/common:utility_lib",
"//source/common/grpc:status_lib",
"//source/common/grpc:utility_lib",
"//source/common/json:json_loader_lib",
"//source/common/network:utility_lib",
"//source/common/protobuf:utility_lib",
"@envoy_api//envoy/api/v2/core:http_uri_cc",
"@envoy_api//envoy/api/v2/core:protocol_cc",
"@envoy_api//envoy/data/core/v2alpha:local_reply_configuration_cc",
],
)

Expand Down
7 changes: 2 additions & 5 deletions source/common/http/async_client_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,8 @@ void AsyncStreamImpl::encodeTrailers(HeaderMapPtr&& trailers) {
}

void AsyncStreamImpl::sendHeaders(HeaderMap& headers, bool end_stream) {
if (Http::Headers::get().MethodValues.Head == headers.Method()->value().getStringView()) {
is_head_request_ = true;
}

is_grpc_request_ = Grpc::Common::hasGrpcContentType(headers);
// TODO(zyfjeff): Make the local reply body type configurable
local_reply_info_ = Utility::generateLocalReplyInfo(headers, Http::MediaType::TextPlain);
headers.insertEnvoyInternalRequest().value().setReference(
Headers::get().EnvoyInternalRequestValues.True);
if (send_xff_) {
Expand Down
7 changes: 3 additions & 4 deletions source/common/http/async_client_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,15 +318,15 @@ class AsyncStreamImpl : public AsyncClient::Stream,
absl::string_view details) override {
stream_info_.setResponseCodeDetails(details);
Utility::sendLocalReply(
is_grpc_request_,
local_reply_info_,
[this, modify_headers](HeaderMapPtr&& headers, bool end_stream) -> void {
if (modify_headers != nullptr) {
modify_headers(*headers);
}
encodeHeaders(std::move(headers), end_stream);
},
[this](Buffer::Instance& data, bool end_stream) -> void { encodeData(data, end_stream); },
remote_closed_, code, body, grpc_status, is_head_request_);
remote_closed_, code, body, grpc_status);
}
// The async client won't pause if sending an Expect: 100-Continue so simply
// swallows any incoming encode100Continue.
Expand Down Expand Up @@ -363,8 +363,7 @@ class AsyncStreamImpl : public AsyncClient::Stream,
bool local_closed_{};
bool remote_closed_{};
Buffer::InstancePtr buffered_body_;
bool is_grpc_request_{};
bool is_head_request_{false};
Utility::LocalReplyInfo local_reply_info_;
bool send_xff_{true};

friend class AsyncClientImpl;
Expand Down
7 changes: 7 additions & 0 deletions source/common/http/conn_manager_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ class DefaultInternalAddressConfig : public Http::InternalAddressConfig {
}
};

enum class MediaType { TextPlain, ApplicationJson, NegotiateViaAcceptHeader };

/**
* Abstract configuration for the connection manager.
*/
Expand Down Expand Up @@ -357,6 +359,11 @@ class ConnectionManagerConfig {
* @return if the HttpConnectionManager should normalize url following RFC3986
*/
virtual bool shouldNormalizePath() const PURE;

/*
* @return MediaType the Local reply body media type
*/
virtual Http::MediaType mediaType() const PURE;
};
} // namespace Http
} // namespace Envoy
Loading