diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a5b502a733..efdaf8936d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1144,6 +1144,7 @@ add_library(end2end_tests test/core/end2end/tests/filter_init_fails.cc test/core/end2end/tests/filter_latency.cc test/core/end2end/tests/filter_status_code.cc + test/core/end2end/tests/filtered_metadata.cc test/core/end2end/tests/graceful_server_shutdown.cc test/core/end2end/tests/grpc_authz.cc test/core/end2end/tests/high_initial_seqno.cc diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 685cfcc9319..883cfb682b0 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -80,6 +80,7 @@ libs: - test/core/end2end/tests/filter_init_fails.cc - test/core/end2end/tests/filter_latency.cc - test/core/end2end/tests/filter_status_code.cc + - test/core/end2end/tests/filtered_metadata.cc - test/core/end2end/tests/graceful_server_shutdown.cc - test/core/end2end/tests/grpc_authz.cc - test/core/end2end/tests/high_initial_seqno.cc diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 22c8720227c..0d9e6019cb0 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -2479,6 +2479,7 @@ Pod::Spec.new do |s| 'test/core/end2end/tests/filter_init_fails.cc', 'test/core/end2end/tests/filter_latency.cc', 'test/core/end2end/tests/filter_status_code.cc', + 'test/core/end2end/tests/filtered_metadata.cc', 'test/core/end2end/tests/graceful_server_shutdown.cc', 'test/core/end2end/tests/grpc_authz.cc', 'test/core/end2end/tests/high_initial_seqno.cc', diff --git a/grpc.gyp b/grpc.gyp index 614a02c959e..d2aa69acb8e 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -218,6 +218,7 @@ 'test/core/end2end/tests/filter_init_fails.cc', 'test/core/end2end/tests/filter_latency.cc', 'test/core/end2end/tests/filter_status_code.cc', + 'test/core/end2end/tests/filtered_metadata.cc', 'test/core/end2end/tests/graceful_server_shutdown.cc', 'test/core/end2end/tests/grpc_authz.cc', 'test/core/end2end/tests/high_initial_seqno.cc', diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index a46eb575183..dab2f50c403 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -836,6 +836,9 @@ bool FilterStackCall::PrepareApplicationMetadata(size_t count, } else if (GRPC_SLICE_LENGTH(md->value) >= UINT32_MAX) { // HTTP2 hpack encoding has a maximum limit. return false; + } else if (grpc_slice_str_cmp(md->key, "content-length") == 0) { + // Filter "content-length metadata" + continue; } batch->Append(StringViewFromSlice(md->key), Slice(grpc_slice_ref_internal(md->value)), diff --git a/test/core/end2end/end2end_tests.cc b/test/core/end2end/end2end_tests.cc index 9b77f266594..79fb8b65c4a 100644 --- a/test/core/end2end/end2end_tests.cc +++ b/test/core/end2end/end2end_tests.cc @@ -79,6 +79,8 @@ extern void filter_latency(grpc_end2end_test_config config); extern void filter_latency_pre_init(void); extern void filter_status_code(grpc_end2end_test_config config); extern void filter_status_code_pre_init(void); +extern void filtered_metadata(grpc_end2end_test_config config); +extern void filtered_metadata_pre_init(void); extern void graceful_server_shutdown(grpc_end2end_test_config config); extern void graceful_server_shutdown_pre_init(void); extern void grpc_authz(grpc_end2end_test_config config); @@ -244,6 +246,7 @@ void grpc_end2end_tests_pre_init(void) { filter_init_fails_pre_init(); filter_latency_pre_init(); filter_status_code_pre_init(); + filtered_metadata_pre_init(); graceful_server_shutdown_pre_init(); grpc_authz_pre_init(); high_initial_seqno_pre_init(); @@ -347,6 +350,7 @@ void grpc_end2end_tests(int argc, char **argv, filter_init_fails(config); filter_latency(config); filter_status_code(config); + filtered_metadata(config); graceful_server_shutdown(config); grpc_authz(config); high_initial_seqno(config); @@ -519,6 +523,10 @@ void grpc_end2end_tests(int argc, char **argv, filter_status_code(config); continue; } + if (0 == strcmp("filtered_metadata", argv[i])) { + filtered_metadata(config); + continue; + } if (0 == strcmp("graceful_server_shutdown", argv[i])) { graceful_server_shutdown(config); continue; diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl index 32d75d63f39..e585de0aff7 100755 --- a/test/core/end2end/generate_tests.bzl +++ b/test/core/end2end/generate_tests.bzl @@ -216,6 +216,7 @@ END2END_TESTS = { "filter_causes_close": _test_options(), "filter_init_fails": _test_options(), "filter_context": _test_options(), + "filtered_metadata": _test_options(), "graceful_server_shutdown": _test_options(exclude_inproc = True), "grpc_authz": _test_options(secure = True), "hpack_size": _test_options( diff --git a/test/core/end2end/tests/filtered_metadata.cc b/test/core/end2end/tests/filtered_metadata.cc new file mode 100644 index 00000000000..a8877c4ac79 --- /dev/null +++ b/test/core/end2end/tests/filtered_metadata.cc @@ -0,0 +1,234 @@ +// +// +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include +#include + +#include +#include +#include +#include + +#include "test/core/end2end/cq_verifier.h" +#include "test/core/end2end/end2end_tests.h" + +static void* tag(intptr_t t) { return reinterpret_cast(t); } + +static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config, + const char* test_name, + grpc_channel_args* client_args, + grpc_channel_args* server_args) { + grpc_end2end_test_fixture f; + gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); + f = config.create_fixture(client_args, server_args); + config.init_server(&f, server_args); + config.init_client(&f, client_args); + return f; +} + +static gpr_timespec n_seconds_from_now(int n) { + return grpc_timeout_seconds_to_deadline(n); +} + +static gpr_timespec five_seconds_from_now(void) { + return n_seconds_from_now(5); +} + +static void drain_cq(grpc_completion_queue* cq) { + grpc_event ev; + do { + ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr); + } while (ev.type != GRPC_QUEUE_SHUTDOWN); +} + +static void shutdown_server(grpc_end2end_test_fixture* f) { + if (!f->server) return; + grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000)); + grpc_event ev; + do { + ev = grpc_completion_queue_next(f->cq, grpc_timeout_seconds_to_deadline(5), + nullptr); + } while (ev.type != GRPC_OP_COMPLETE || ev.tag != tag(1000)); + grpc_server_destroy(f->server); + f->server = nullptr; +} + +static void shutdown_client(grpc_end2end_test_fixture* f) { + if (!f->client) return; + grpc_channel_destroy(f->client); + f->client = nullptr; +} + +static void end_test(grpc_end2end_test_fixture* f) { + shutdown_server(f); + shutdown_client(f); + + grpc_completion_queue_shutdown(f->cq); + drain_cq(f->cq); + grpc_completion_queue_destroy(f->cq); +} + +// Request/response with metadata which should be filtered +static void test_request_response_with_metadata_to_be_filtered( + grpc_end2end_test_config config, const char* filtered_md_key, + const char* filter_md_value) { + grpc_call* c; + grpc_call* s; + grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), + grpc_slice_from_static_string("val1"), + {{nullptr, nullptr, nullptr, nullptr}}}, + {grpc_slice_from_static_string(filtered_md_key), + grpc_slice_from_static_string(filter_md_value), + {{nullptr, nullptr, nullptr, nullptr}}}}; + grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key2"), + grpc_slice_from_static_string("val2"), + {{nullptr, nullptr, nullptr, nullptr}}}, + {grpc_slice_from_static_string(filtered_md_key), + grpc_slice_from_static_string(filter_md_value), + {{nullptr, nullptr, nullptr, nullptr}}}}; + grpc_end2end_test_fixture f = + begin_test(config, "test_request_response_with_metadata_to_be_filtered", + nullptr, nullptr); + cq_verifier* cqv = cq_verifier_create(f.cq); + grpc_op ops[6]; + grpc_op* op; + grpc_metadata_array initial_metadata_recv; + grpc_metadata_array trailing_metadata_recv; + grpc_metadata_array request_metadata_recv; + grpc_call_details call_details; + grpc_status_code status; + grpc_call_error error; + grpc_slice details; + int was_cancelled = 2; + + gpr_timespec deadline = five_seconds_from_now(); + c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq, + grpc_slice_from_static_string("/foo"), nullptr, + deadline, nullptr); + GPR_ASSERT(c); + + grpc_metadata_array_init(&initial_metadata_recv); + grpc_metadata_array_init(&trailing_metadata_recv); + grpc_metadata_array_init(&request_metadata_recv); + grpc_call_details_init(&call_details); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 2; + op->data.send_initial_metadata.metadata = meta_c; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; + op->data.recv_status_on_client.status = &status; + op->data.recv_status_on_client.status_details = &details; + op->flags = 0; + op->reserved = nullptr; + op++; + error = grpc_call_start_batch(c, ops, static_cast(op - ops), tag(1), + nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + error = + grpc_server_request_call(f.server, &s, &call_details, + &request_metadata_recv, f.cq, f.cq, tag(101)); + GPR_ASSERT(GRPC_CALL_OK == error); + CQ_EXPECT_COMPLETION(cqv, tag(101), 1); + cq_verify(cqv); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 2; + op->data.send_initial_metadata.metadata = meta_s; + op->flags = 0; + op->reserved = nullptr; + op++; + error = grpc_call_start_batch(s, ops, static_cast(op - ops), tag(102), + nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + CQ_EXPECT_COMPLETION(cqv, tag(102), 1); + cq_verify(cqv); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; + op->data.recv_close_on_server.cancelled = &was_cancelled; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; + op->data.send_status_from_server.trailing_metadata_count = 0; + op->data.send_status_from_server.status = GRPC_STATUS_OK; + grpc_slice status_details = grpc_slice_from_static_string("xyz"); + op->data.send_status_from_server.status_details = &status_details; + op->flags = 0; + op->reserved = nullptr; + op++; + error = grpc_call_start_batch(s, ops, static_cast(op - ops), tag(103), + nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + CQ_EXPECT_COMPLETION(cqv, tag(103), 1); + CQ_EXPECT_COMPLETION(cqv, tag(1), 1); + cq_verify(cqv); + + GPR_ASSERT(status == GRPC_STATUS_OK); + GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); + GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); + GPR_ASSERT(was_cancelled == 0); + GPR_ASSERT(contains_metadata(&request_metadata_recv, "key1", "val1")); + GPR_ASSERT(!contains_metadata(&request_metadata_recv, filtered_md_key, + filter_md_value)); + GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key2", "val2")); + GPR_ASSERT(!contains_metadata(&initial_metadata_recv, filtered_md_key, + filter_md_value)); + + grpc_slice_unref(details); + grpc_metadata_array_destroy(&initial_metadata_recv); + grpc_metadata_array_destroy(&trailing_metadata_recv); + grpc_metadata_array_destroy(&request_metadata_recv); + grpc_call_details_destroy(&call_details); + + grpc_call_unref(c); + grpc_call_unref(s); + + cq_verifier_destroy(cqv); + + end_test(&f); + config.tear_down_data(&f); +} + +void filtered_metadata(grpc_end2end_test_config config) { + test_request_response_with_metadata_to_be_filtered(config, "content-length", + "45"); +} + +void filtered_metadata_pre_init(void) {}