From e6d185e8831758c1f60200e88fc9717b527d9ab3 Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Wed, 13 Jan 2021 13:44:33 -0800 Subject: [PATCH 1/7] access-logger: support %REQUEST_HEADER_BYTES%, %RESPONSE_HEADER_BYTES%, %RESPONSE_TRAILER_BYTES% Signed-off-by: Xuyang Tao --- .../observability/access_log/usage.rst | 21 +++++++++ docs/root/version_history/current.rst | 1 + .../formatter/substitution_formatter.cc | 47 +++++++++++++++++++ .../common/formatter/substitution_formatter.h | 26 ++++++++++ .../formatter/substitution_formatter_test.cc | 36 ++++++++++++++ .../common/router/router_upstream_log_test.cc | 38 +++++++++++++++ 6 files changed, 169 insertions(+) diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 9a00ac1a29d93..902c83ff3991d 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -163,6 +163,13 @@ The following command operators are supported: In typed JSON logs, START_TIME is always rendered as a string. +%REQUEST_HEADERS_BYTES% + HTTP + Request headers bytes. + + TCP + Not implemented ("-"). + %BYTES_RECEIVED% HTTP Body bytes received. @@ -213,6 +220,20 @@ The following command operators are supported: Connection termination details may provide additional information about why the connection was terminated by Envoy for L4 reasons. +%RESPONSE_HEADERS_BYTES% + HTTP + Response headers bytes. + + TCP + Not implemented ("-"). + +%RESPONSE_TRAILERS_BYTES% + HTTP + Response trailers bytes. + + TCP + Not implemented ("-"). + %BYTES_SENT% HTTP Body bytes sent. For WebSocket connection it will also include response header bytes. diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 4367cea808065..1f7785add86bf 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -31,6 +31,7 @@ Removed Config or Runtime New Features ------------ * access log: added the :ref:`formatters ` extension point for custom formatters (command operators). +* access log: support command operator: %REQUEST_HEADERS_BYTES%, %RESPONSE_HEADERS_BYTES% and %RESPONSE_TRAILERS_BYTES%. * tcp_proxy: add support for converting raw TCP streams into HTTP/1.1 CONNECT requests. See :ref:`upgrade documentation ` for details. Deprecated diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index d7fca3cf7a493..313ebff114e48 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -357,6 +357,15 @@ FormatterProviderPtr SubstitutionFormatParser::parseBuiltinCommand(const std::st return std::make_unique(token); } else if (absl::StartsWith(token, "GRPC_STATUS")) { return std::make_unique("grpc-status", "", absl::optional()); + } else if (absl::StartsWith(token, "REQUEST_HEADERS_BYTES")) { + return std::make_unique( + HeadersByteSizeFormatter::HeaderType::RequestHeaders); + } else if (absl::StartsWith(token, "RESPONSE_HEADERS_BYTES")) { + return std::make_unique( + HeadersByteSizeFormatter::HeaderType::ResponseHeaders); + } else if (absl::StartsWith(token, "RESPONSE_TRAILERS_BYTES")) { + return std::make_unique( + HeadersByteSizeFormatter::HeaderType::ResponseTrailers); } return nullptr; @@ -955,6 +964,44 @@ ResponseTrailerFormatter::formatValue(const Http::RequestHeaderMap&, const Http: return HeaderFormatter::formatValue(response_trailers); } +HeadersByteSizeFormatter::HeadersByteSizeFormatter(const HeaderType header_type) + : header_type_(header_type) {} + +std::string HeadersByteSizeFormatter::extractHeadersByteSize( + const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers) const { + std::string val; + switch (header_type_) { + case HeaderType::RequestHeaders: + val = absl::StrCat(request_headers.byteSize()); + break; + case HeaderType::ResponseHeaders: + val = absl::StrCat(response_headers.byteSize()); + break; + case HeaderType::ResponseTrailers: + val = absl::StrCat(response_trailers.byteSize()); + break; + } + return val; +} + +absl::optional +HeadersByteSizeFormatter::format(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo&, absl::string_view) const { + return extractHeadersByteSize(request_headers, response_headers, response_trailers); +} + +ProtobufWkt::Value +HeadersByteSizeFormatter::formatValue(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo&, absl::string_view) const { + std::string val = extractHeadersByteSize(request_headers, response_headers, response_trailers); + return ValueUtil::stringValue(val); +} + GrpcStatusFormatter::GrpcStatusFormatter(const std::string& main_header, const std::string& alternative_header, absl::optional max_length) diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index 2d1f121c4872e..e05cde2e9545d 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -228,6 +228,32 @@ class HeaderFormatter { absl::optional max_length_; }; +/** + * FormatterProvider for headers byte size. + */ +class HeadersByteSizeFormatter : public FormatterProvider { +public: + enum class HeaderType { RequestHeaders, ResponseHeaders, ResponseTrailers }; + + HeadersByteSizeFormatter(const HeaderType header_type); + + absl::optional format(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo&, + absl::string_view) const override; + ProtobufWkt::Value formatValue(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo&, absl::string_view) const override; + +private: + std::string extractHeadersByteSize(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers) const; + HeaderType header_type_; +}; + /** * FormatterProvider for request headers. */ diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index 87d81342169be..c5d13b67e0855 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -1050,6 +1050,42 @@ TEST(SubstitutionFormatterTest, requestHeaderFormatter) { } } +TEST(SubstitutionFormatterTest, headersByteSizeFormatter) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{":method", "GET"}, {":path", "/"}}; + Http::TestResponseHeaderMapImpl response_header{{":method", "PUT"}}; + Http::TestResponseTrailerMapImpl response_trailer{{":method", "POST"}, {"test-2", "test-2"}}; + std::string body; + + { + HeadersByteSizeFormatter formatter(HeadersByteSizeFormatter::HeaderType::RequestHeaders); + EXPECT_EQ( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + "16"); + EXPECT_THAT( + formatter.formatValue(request_header, response_header, response_trailer, stream_info, body), + ProtoEq(ValueUtil::stringValue("16"))); + } + { + HeadersByteSizeFormatter formatter(HeadersByteSizeFormatter::HeaderType::ResponseHeaders); + EXPECT_EQ( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + "10"); + EXPECT_THAT( + formatter.formatValue(request_header, response_header, response_trailer, stream_info, body), + ProtoEq(ValueUtil::stringValue("10"))); + } + { + HeadersByteSizeFormatter formatter(HeadersByteSizeFormatter::HeaderType::ResponseTrailers); + EXPECT_EQ( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + "23"); + EXPECT_THAT( + formatter.formatValue(request_header, response_header, response_trailer, stream_info, body), + ProtoEq(ValueUtil::stringValue("23"))); + } +} + TEST(SubstitutionFormatterTest, responseHeaderFormatter) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{{":method", "GET"}, {":path", "/"}}; diff --git a/test/common/router/router_upstream_log_test.cc b/test/common/router/router_upstream_log_test.cc index 7821291c80a55..3a7508903c819 100644 --- a/test/common/router/router_upstream_log_test.cc +++ b/test/common/router/router_upstream_log_test.cc @@ -326,5 +326,43 @@ name: accesslog EXPECT_LE(std::abs(std::difftime(log_time, now)), 300); } +// Test request headers/response headers/response trailers byte size. +TEST_F(RouterUpstreamLogTest, HeaderByteSize) { + const std::string yaml = R"EOF( +name: accesslog +typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + log_format: + text_format_source: + inline_string: "%REQUEST_HEADERS_BYTES% %RESPONSE_HEADERS_BYTES% %RESPONSE_TRAILERS_BYTES%" + path: "/dev/null" + )EOF"; + + envoy::config::accesslog::v3::AccessLog upstream_log; + TestUtility::loadFromYaml(yaml, upstream_log); + + init(absl::optional(upstream_log)); + run(200, {{"request-header-name", "request-header-val"}}, + {{"response-header-name", "response-header-val"}}, + {{"response-trailer-name", "response-trailer-val"}}); + + EXPECT_EQ(output_.size(), 1U); + // Request headers: + // scheme: http + // :method: GET + // :authority: host + // :path: / + // x-envoy-expected-rq-timeout-ms: 10 + // request-header-name: request-header-val + + // Response headers: + // :status: 200 + // response-header-name: response-header-val + + // Response trailers: + // response-trailer-name: response-trailer-val + EXPECT_EQ(output_.front(), "110 49 41"); +} + } // namespace Router } // namespace Envoy From 1f28f9b2647e515757b3307b1e0d86b2e857d542 Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Wed, 13 Jan 2021 16:14:11 -0800 Subject: [PATCH 2/7] change to numberValue Signed-off-by: Xuyang Tao NIT --- .../observability/access_log/usage.rst | 6 +++--- .../formatter/substitution_formatter.cc | 21 ++++++++----------- .../common/formatter/substitution_formatter.h | 6 +++--- .../formatter/substitution_formatter_test.cc | 6 +++--- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 902c83ff3991d..30bbb85f7ac49 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -225,14 +225,14 @@ The following command operators are supported: Response headers bytes. TCP - Not implemented ("-"). + Not implemented (0). %RESPONSE_TRAILERS_BYTES% HTTP Response trailers bytes. TCP - Not implemented ("-"). + Not implemented (0). %BYTES_SENT% HTTP @@ -258,7 +258,7 @@ The following command operators are supported: the request received from the downstream. TCP - Not implemented ("-"). + Not implemented (0). Renders a numeric value in typed JSON logs. diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 313ebff114e48..de491a4885584 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -967,22 +967,19 @@ ResponseTrailerFormatter::formatValue(const Http::RequestHeaderMap&, const Http: HeadersByteSizeFormatter::HeadersByteSizeFormatter(const HeaderType header_type) : header_type_(header_type) {} -std::string HeadersByteSizeFormatter::extractHeadersByteSize( +uint64_t HeadersByteSizeFormatter::extractHeadersByteSize( const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers, const Http::ResponseTrailerMap& response_trailers) const { - std::string val; switch (header_type_) { case HeaderType::RequestHeaders: - val = absl::StrCat(request_headers.byteSize()); - break; + return request_headers.byteSize(); case HeaderType::ResponseHeaders: - val = absl::StrCat(response_headers.byteSize()); - break; + return response_headers.byteSize(); case HeaderType::ResponseTrailers: - val = absl::StrCat(response_trailers.byteSize()); - break; + return response_trailers.byteSize(); + default: + NOT_REACHED_GCOVR_EXCL_LINE; } - return val; } absl::optional @@ -990,7 +987,7 @@ HeadersByteSizeFormatter::format(const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers, const Http::ResponseTrailerMap& response_trailers, const StreamInfo::StreamInfo&, absl::string_view) const { - return extractHeadersByteSize(request_headers, response_headers, response_trailers); + return absl::StrCat(extractHeadersByteSize(request_headers, response_headers, response_trailers)); } ProtobufWkt::Value @@ -998,8 +995,8 @@ HeadersByteSizeFormatter::formatValue(const Http::RequestHeaderMap& request_head const Http::ResponseHeaderMap& response_headers, const Http::ResponseTrailerMap& response_trailers, const StreamInfo::StreamInfo&, absl::string_view) const { - std::string val = extractHeadersByteSize(request_headers, response_headers, response_trailers); - return ValueUtil::stringValue(val); + return ValueUtil::numberValue( + extractHeadersByteSize(request_headers, response_headers, response_trailers)); } GrpcStatusFormatter::GrpcStatusFormatter(const std::string& main_header, diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index e05cde2e9545d..8443507a45716 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -248,9 +248,9 @@ class HeadersByteSizeFormatter : public FormatterProvider { const StreamInfo::StreamInfo&, absl::string_view) const override; private: - std::string extractHeadersByteSize(const Http::RequestHeaderMap& request_headers, - const Http::ResponseHeaderMap& response_headers, - const Http::ResponseTrailerMap& response_trailers) const; + uint64_t extractHeadersByteSize(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers) const; HeaderType header_type_; }; diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index c5d13b67e0855..75e9a2c8abdb4 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -1064,7 +1064,7 @@ TEST(SubstitutionFormatterTest, headersByteSizeFormatter) { "16"); EXPECT_THAT( formatter.formatValue(request_header, response_header, response_trailer, stream_info, body), - ProtoEq(ValueUtil::stringValue("16"))); + ProtoEq(ValueUtil::numberValue(16))); } { HeadersByteSizeFormatter formatter(HeadersByteSizeFormatter::HeaderType::ResponseHeaders); @@ -1073,7 +1073,7 @@ TEST(SubstitutionFormatterTest, headersByteSizeFormatter) { "10"); EXPECT_THAT( formatter.formatValue(request_header, response_header, response_trailer, stream_info, body), - ProtoEq(ValueUtil::stringValue("10"))); + ProtoEq(ValueUtil::numberValue(10))); } { HeadersByteSizeFormatter formatter(HeadersByteSizeFormatter::HeaderType::ResponseTrailers); @@ -1082,7 +1082,7 @@ TEST(SubstitutionFormatterTest, headersByteSizeFormatter) { "23"); EXPECT_THAT( formatter.formatValue(request_header, response_header, response_trailer, stream_info, body), - ProtoEq(ValueUtil::stringValue("23"))); + ProtoEq(ValueUtil::numberValue(23))); } } From d2a8d89feef47c8286e5d513967d0bc320e22d10 Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Wed, 13 Jan 2021 16:50:01 -0800 Subject: [PATCH 3/7] NIT Signed-off-by: Xuyang Tao --- docs/root/configuration/observability/access_log/usage.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 30bbb85f7ac49..d8dd386c20302 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -168,7 +168,7 @@ The following command operators are supported: Request headers bytes. TCP - Not implemented ("-"). + Not implemented (0). %BYTES_RECEIVED% HTTP @@ -258,7 +258,7 @@ The following command operators are supported: the request received from the downstream. TCP - Not implemented (0). + Not implemented (-). Renders a numeric value in typed JSON logs. From 70882b9975525e2b1b59f50fbab3dd0e26de31d3 Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Wed, 13 Jan 2021 16:51:52 -0800 Subject: [PATCH 4/7] NIT Signed-off-by: Xuyang Tao --- docs/root/configuration/observability/access_log/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index d8dd386c20302..9c6ae7c12d6c3 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -258,7 +258,7 @@ The following command operators are supported: the request received from the downstream. TCP - Not implemented (-). + Not implemented ("-"). Renders a numeric value in typed JSON logs. From da2c1d67130f542d06187a54bdc60e310113e1dd Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Thu, 14 Jan 2021 20:29:30 -0800 Subject: [PATCH 5/7] add one unit test Signed-off-by: Xuyang Tao --- .../common/access_log/access_log_impl_test.cc | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index 1e6eb05c0cea8..4d3fc963bb25f 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -141,6 +141,40 @@ name: accesslog output_); } +TEST_F(AccessLogImplTest, HeadersBytes) { + const std::string yaml = R"EOF( +name: accesslog +typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + log_format: + text_format_source: + inline_string: "%REQUEST_HEADERS_BYTES% %RESPONSE_HEADERS_BYTES% %RESPONSE_TRAILERS_BYTES%" + )EOF"; + + InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); + + EXPECT_CALL(*file_, write(_)); + request_headers_.addCopy("request_header_key", "request_header_val"); + response_headers_.addCopy("response_header_key", "response_header_val"); + response_trailers_.addCopy("response_trailer_key", "response_trailer_val"); + + // request headers: + // :method: GET + // :path: / + // request_header_key: request_header_val + + // response headers: + // response_header_key: response_header_val + // + // response trailers: + // response_trailer_key: response_trailer_val + + log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); + + EXPECT_EQ(output_, "52 38 40"); +} + TEST_F(AccessLogImplTest, EnvoyUpstreamServiceTime) { const std::string yaml = R"EOF( name: accesslog From ff2161910cee0ee4cb6eabcdc8d570f9134bbfeb Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Fri, 15 Jan 2021 08:37:30 -0800 Subject: [PATCH 6/7] add TODO Signed-off-by: Xuyang Tao --- source/common/formatter/substitution_formatter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index 8443507a45716..d59027ce9d8c5 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -233,6 +233,7 @@ class HeaderFormatter { */ class HeadersByteSizeFormatter : public FormatterProvider { public: + // TODO(htuch): Add RequestTrailers here. enum class HeaderType { RequestHeaders, ResponseHeaders, ResponseTrailers }; HeadersByteSizeFormatter(const HeaderType header_type); From d50f5673726a070cc8f95c4dce226874149d7759 Mon Sep 17 00:00:00 2001 From: Xuyang Tao Date: Sun, 17 Jan 2021 18:17:09 -0800 Subject: [PATCH 7/7] fix comments Signed-off-by: Xuyang Tao --- .../configuration/observability/access_log/usage.rst | 6 +++--- source/common/formatter/substitution_formatter.h | 2 +- test/common/access_log/access_log_impl_test.cc | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 9c6ae7c12d6c3..6497767fde45e 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -165,7 +165,7 @@ The following command operators are supported: %REQUEST_HEADERS_BYTES% HTTP - Request headers bytes. + Uncompressed bytes of request headers. TCP Not implemented (0). @@ -222,14 +222,14 @@ The following command operators are supported: %RESPONSE_HEADERS_BYTES% HTTP - Response headers bytes. + Uncompressed bytes of response headers. TCP Not implemented (0). %RESPONSE_TRAILERS_BYTES% HTTP - Response trailers bytes. + Uncompressed bytes of response trailers. TCP Not implemented (0). diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index d59027ce9d8c5..4d53c03374f6f 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -233,7 +233,7 @@ class HeaderFormatter { */ class HeadersByteSizeFormatter : public FormatterProvider { public: - // TODO(htuch): Add RequestTrailers here. + // TODO(taoxuy): Add RequestTrailers here. enum class HeaderType { RequestHeaders, ResponseHeaders, ResponseTrailers }; HeadersByteSizeFormatter(const HeaderType header_type); diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index 4d3fc963bb25f..ce4d3909af73d 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -159,12 +159,12 @@ name: accesslog response_headers_.addCopy("response_header_key", "response_header_val"); response_trailers_.addCopy("response_trailer_key", "response_trailer_val"); - // request headers: - // :method: GET - // :path: / - // request_header_key: request_header_val - - // response headers: + // request headers: + // :method: GET + // :path: / + // request_header_key: request_header_val + // + // response headers: // response_header_key: response_header_val // // response trailers: