Skip to content

Commit

Permalink
Thrift to Metadata filter proto design (#33919)
Browse files Browse the repository at this point in the history
For apache thrift compatible HTTP requests and responses, this filter parses the thrift metadata and put them into filter dynamic metadata for other filter usage.

This is the initial proto design, which refers to other filters like json_to_metadata and payload_to_metadata.

Risk Level: low
Testing: build
Docs Changes: yes

#29371

Signed-off-by: kuochunghsu <kuochunghsu@pinterest.com>
  • Loading branch information
JuniorHsu committed May 24, 2024
1 parent 3fab851 commit 7081e56
Show file tree
Hide file tree
Showing 18 changed files with 741 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123
# Thrift
/*/extensions/filters/network/thrift_proxy @zuercher @JuniorHsu
/*/extensions/health_checkers/thrift @zuercher @JuniorHsu
# Thrift to metadata
/*/extensions/filters/http/thrift_to_metadata @JuniorHsu @zuercher
# IP tagging
/*/extensions/filters/http/ip_tagging @alyssawilk @JuniorHsu
# Header to metadata
Expand Down
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ proto_library(
"//envoy/extensions/filters/http/set_metadata/v3:pkg",
"//envoy/extensions/filters/http/stateful_session/v3:pkg",
"//envoy/extensions/filters/http/tap/v3:pkg",
"//envoy/extensions/filters/http/thrift_to_metadata/v3:pkg",
"//envoy/extensions/filters/http/upstream_codec/v3:pkg",
"//envoy/extensions/filters/http/wasm/v3:pkg",
"//envoy/extensions/filters/listener/http_inspector/v3:pkg",
Expand Down
13 changes: 13 additions & 0 deletions api/envoy/extensions/filters/http/thrift_to_metadata/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/extensions/filters/network/thrift_proxy/v3:pkg",
"@com_github_cncf_xds//udpa/annotations:pkg",
"@com_github_cncf_xds//xds/annotations/v3:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
syntax = "proto3";

package envoy.extensions.filters.http.thrift_to_metadata.v3;

import "envoy/extensions/filters/network/thrift_proxy/v3/thrift_proxy.proto";

import "google/protobuf/struct.proto";

import "xds/annotations/v3/status.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.http.thrift_to_metadata.v3";
option java_outer_classname = "ThriftToMetadataProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/thrift_to_metadata/v3;thrift_to_metadatav3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;
option (xds.annotations.v3.file_status).work_in_progress = true;

// [#protodoc-title: Thrift-To-Metadata Filter]
//
// The Thrift to Metadata filter serves for thrift over HTTP traffic, expecting serialized
// Thrift request and response bodies in the HTTP payload. It extracts *thrift metadata* from the
// HTTP body and put them into the *filter metadata*. This is useful for matching load balancer
// subsets, logging, etc.
//
// Thrift to Metadata :ref:`configuration overview <config_http_filters_thrift_to_metadata>`.
// [#extension: envoy.filters.http.thrift_to_metadata]

enum Field {
// The Thrift method name, string value.
METHOD_NAME = 0;

// The Thrift protocol name, string value. Values are "binary", "binary/non-strict", and "compact", with "(auto)" suffix if
// :ref:`protocol <envoy_v3_api_field_extensions.filters.http.thrift_to_metadata.v3.ThriftToMetadata.protocol>`
// is set to :ref:`AUTO_PROTOCOL<envoy_v3_api_enum_value_extensions.filters.network.thrift_proxy.v3.ProtocolType.AUTO_PROTOCOL>`
PROTOCOL = 1;

// The Thrift transport name, string value. Values are "framed", "header", and "unframed", with "(auto)" suffix if
// :ref:`transport <envoy_v3_api_field_extensions.filters.http.thrift_to_metadata.v3.ThriftToMetadata.transport>`
// is set to :ref:`AUTO_TRANSPORT<envoy_v3_api_enum_value_extensions.filters.network.thrift_proxy.v3.TransportType.AUTO_TRANSPORT>`
TRANSPORT = 2;

// The Thrift message type, singed 16-bit integer value.
HEADER_FLAGS = 3;

// The Thrift sequence ID, singed 32-bit integer value.
SEQUENCE_ID = 4;

// The Thrift message type, string value. Values in request are "call" and "oneway", and in response are "reply" and "exception".
MESSAGE_TYPE = 5;

// The Thrift reply type, string value. This is only valid for response rules. Values are "success" and "error".
REPLY_TYPE = 6;
}

message KeyValuePair {
// The namespace — if this is empty, the filter's namespace will be used.
string metadata_namespace = 1;

// The key to use within the namespace.
string key = 2 [(validate.rules).string = {min_len: 1}];

// When used for on_present case, if value is non-empty it'll be used instead
// of the field value.
//
// When used for on_missing case, a non-empty value must be provided.
google.protobuf.Value value = 3;
}

message FieldSelector {
option (xds.annotations.v3.message_status).work_in_progress = true;

// field name to log
string name = 1 [(validate.rules).string = {min_len: 1}];

// field id to match
int32 id = 2 [(validate.rules).int32 = {lte: 32767 gte: -32768}];

// next node of the field selector
FieldSelector child = 3;
}

// [#next-free-field: 6]
message Rule {
// The field to match on. If set, takes precedence over field_selector.
Field field = 1;

// Specifies that a match will be performed on the value of a field in the thrift body.
// If set, the whole http body will be buffered to extract the field value, which
// may have performance implications.
//
// It's a thrift over http version of
// :ref:`field_selector<envoy_v3_api_field_extensions.filters.network.thrift_proxy.filters.payload_to_metadata.v3.PayloadToMetadata.Rule.field_selector>`.
//
// See also `payload-to-metadata <https://www.envoyproxy.io/docs/envoy/latest/configuration/other_protocols/thrift_filters/payload_to_metadata_filter>`_
// for more reference.
//
// Example:
//
// .. code-block:: yaml
//
// method_name: foo
// field_selector:
// name: info
// id: 2
// child:
// name: version
// id: 1
//
// The above yaml will match on value of ``info.version`` in the below thrift schema as input of
// :ref:`on_present<envoy_v3_api_field_extensions.filters.http.thrift_to_metadata.v3.Rule.on_present>` or
// :ref:`on_missing<envoy_v3_api_field_extensions.filters.http.thrift_to_metadata.v3.Rule.on_missing>`
// while we are processing ``foo`` method. This rule won't be applied to ``bar`` method.
//
// .. code-block:: thrift
//
// struct Info {
// 1: required string version;
// }
// service Server {
// bool foo(1: i32 id, 2: Info info);
// bool bar(1: i32 id, 2: Info info);
// }
//
FieldSelector field_selector = 2 [(xds.annotations.v3.field_status).work_in_progress = true];

// If specified, :ref:`field_selector<envoy_v3_api_field_extensions.filters.http.thrift_to_metadata.v3.Rule.field_selector>`
// will be used to extract the field value *only* on the thrift message with method name.
string method_name = 3 [(xds.annotations.v3.field_status).work_in_progress = true];

// The key-value pair to set in the *filter metadata* if the field is present
// in *thrift metadata*.
//
// If the value in the KeyValuePair is non-empty, it'll be used instead
// of field value.
KeyValuePair on_present = 4;

// The key-value pair to set in the *filter metadata* if the field is missing
// in *thrift metadata*.
//
// The value in the KeyValuePair must be set, since it'll be used in lieu
// of the missing field value.
KeyValuePair on_missing = 5;
}

// The configuration for transforming thrift metadata into filter metadata.
//
// [#next-free-field: 7]
message ThriftToMetadata {
// The list of rules to apply to http request body to extract thrift metadata.
repeated Rule request_rules = 1;

// The list of rules to apply to http response body to extract thrift metadata.
repeated Rule response_rules = 2;

// Supplies the type of transport that the Thrift proxy should use. Defaults to
// :ref:`AUTO_TRANSPORT<envoy_v3_api_enum_value_extensions.filters.network.thrift_proxy.v3.TransportType.AUTO_TRANSPORT>`.
network.thrift_proxy.v3.TransportType transport = 3
[(validate.rules).enum = {defined_only: true}];

// Supplies the type of protocol that the Thrift proxy should use. Defaults to
// :ref:`AUTO_PROTOCOL<envoy_v3_api_enum_value_extensions.filters.network.thrift_proxy.v3.ProtocolType.AUTO_PROTOCOL>`.
// Note that :ref:`TWITTER<envoy_v3_api_enum_value_extensions.filters.network.thrift_proxy.v3.ProtocolType.TWITTER>` is
// not supported due to deprecation in envoy.
network.thrift_proxy.v3.ProtocolType protocol = 4 [(validate.rules).enum = {defined_only: true}];

// Allowed content-type for thrift payload to filter metadata transformation.
// Default to ``{"application/x-thrift"}``.
//
// Set ``allow_empty_content_type`` if empty/missing content-type header
// is allowed.
repeated string allow_content_types = 5
[(validate.rules).repeated = {items {string {min_len: 1}}}];

// Allowed empty content-type for thrift payload to filter metadata transformation.
// Default to false.
bool allow_empty_content_type = 6;
}

// Thrift to metadata configuration on a per-route basis, which overrides the global configuration for
// request rules and responses rules.
message ThriftToMetadataPerRoute {
// The list of rules to apply to http request body to extract thrift metadata.
repeated Rule request_rules = 1;

// The list of rules to apply to http response body to extract thrift metadata.
repeated Rule response_rules = 2;
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ proto_library(
"//envoy/extensions/filters/http/set_metadata/v3:pkg",
"//envoy/extensions/filters/http/stateful_session/v3:pkg",
"//envoy/extensions/filters/http/tap/v3:pkg",
"//envoy/extensions/filters/http/thrift_to_metadata/v3:pkg",
"//envoy/extensions/filters/http/upstream_codec/v3:pkg",
"//envoy/extensions/filters/http/wasm/v3:pkg",
"//envoy/extensions/filters/listener/http_inspector/v3:pkg",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: AUTO
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: app
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: versioned-cluster
http_filters:
- name: envoy.filters.http.thrift_to_metadata
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.thrift_to_metadata.v3.ThriftToMetadata
request_rules:
- field: PROTOCOL
on_present:
metadata_namespace: envoy.lb
key: protocol
on_missing:
metadata_namespace: envoy.lb
key: protocol
value: "unknown"
- field: TRANSPORT
on_present:
metadata_namespace: envoy.lb
key: transport
on_missing:
metadata_namespace: envoy.lb
key: transport
value: "unknown"
response_rules:
- field: MESSAGE_TYPE
on_present:
metadata_namespace: envoy.filters.http.thrift_to_metadata
key: response_message_type
on_missing:
metadata_namespace: envoy.filters.http.thrift_to_metadata
key: response_message_type
value: "exception"
- field: REPLY_TYPE
on_present:
metadata_namespace: envoy.filters.http.thrift_to_metadata
key: response_reply_type
on_missing:
metadata_namespace: envoy.filters.http.thrift_to_metadata
key: response_reply_type
value: "error"
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

clusters:
- name: versioned-cluster
type: STRICT_DNS
lb_policy: ROUND_ROBIN
lb_subset_config:
fallback_policy: ANY_ENDPOINT
subset_selectors:
- keys:
- protocol
- transport
load_assignment:
cluster_name: versioned-cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8080
metadata:
filter_metadata:
envoy.lb:
default: "true"
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8081
metadata:
filter_metadata:
envoy.lb:
version: "1.0"
1 change: 1 addition & 0 deletions docs/root/configuration/http/http_filters/http_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ HTTP filters
stateful_session_filter
sxg_filter
tap_filter
thrift_to_metadata_filter
upstream_codec_filter
wasm_filter
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.. _config_http_filters_thrift_to_metadata:

Envoy Thrift-To-Metadata Filter
===============================
* This filter should be configured with the type URL ``type.googleapis.com/envoy.extensions.filters.http.thrift_to_metadata.v3.ThriftToMetadata``.
* :ref:`v3 API reference <envoy_v3_api_msg_extensions.filters.http.thrift_to_metadata.v3.ThriftToMetadata>`

The Thrift to Metadata filter serves for thrift over HTTP traffic, expecting serialized Thrift request and response bodies
in the HTTP payload. This filter is configured with rules that will be matched against Apache thrift compatible requests and
responses in HTTP payload. The filter will parse the thrift body, extract *thrift metadata* or *thrift payload*, and add them to
*dynamic filter metadata* based on the configuration of the rule.

The *filter metadata* can then be used for load balancing decisions, consumed from logs, etc.

A typical use case for this filter is to dynamically match a specified thrift method of requests
with rate limit. For this, thrift method name is attached to the request as dynamic filter metadata which
would then be used to match a rate limit action on filter metadata.

Example
-------

A sample filter configuration to route traffic to endpoints based on the presence or
absence of a version attribute could be:

.. literalinclude:: _include/thrift-to-metadata-filter.yaml
:language: yaml
:lines: 25-55
:lineno-start: 25
:linenos:
:caption: :download:`thrift-to-metadata-filter.yaml <_include/thrift-to-metadata-filter.yaml>`

Statistics
----------

The thrift to metadata filter outputs statistics in the *http.<stat_prefix>.thrift_to_metadata.* namespace. The :ref:`stat prefix
<envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stat_prefix>`
comes from the owning HTTP connection manager.

.. csv-table::
:header: Name, Type, Description
:widths: 1, 1, 2

rq_success, Counter, Total requests that succeed to parse the thrift body.
rq_mismatched_content_type, Counter, Total requests that mismatch the content type
rq_no_body, Counter, Total requests without content body
rq_invalid_thrift_body, Counter, Total requests with invalid thrift body
resp_success, Counter, Total responses that succeed to parse the thrift body.
resp_mismatched_content_type, Counter, Total responses that mismatch the content type
resp_no_body, Counter, Total responses without content body
resp_invalid_thrift_body, Counter, Total responses with invalid thrift body
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ EXTENSIONS = {
"envoy.filters.http.set_filter_state": "//source/extensions/filters/http/set_filter_state:config",
"envoy.filters.http.set_metadata": "//source/extensions/filters/http/set_metadata:config",
"envoy.filters.http.tap": "//source/extensions/filters/http/tap:config",
"envoy.filters.http.thrift_to_metadata": "//source/extensions/filters/http/thrift_to_metadata:config",
"envoy.filters.http.wasm": "//source/extensions/filters/http/wasm:config",
"envoy.filters.http.stateful_session": "//source/extensions/filters/http/stateful_session:config",
"envoy.filters.http.header_mutation": "//source/extensions/filters/http/header_mutation:config",
Expand Down

0 comments on commit 7081e56

Please sign in to comment.