diff options
author | Sree Kuchibhotla <sreek@google.com> | 2018-05-08 10:24:30 -0700 |
---|---|---|
committer | Sree Kuchibhotla <sreek@google.com> | 2018-05-08 10:24:30 -0700 |
commit | 6d5c2c250d4f96af7cc413ce245dc131a9cf4cf9 (patch) | |
tree | 29c94b81a45c1182d41f6dce8dc8e2391614b933 /test/core | |
parent | dc01cb64c25abc14f6d0ab24db1afd019a37d7ee (diff) | |
parent | 978b9b179e631348cf8b62bf64af7fc65e322fc4 (diff) |
Merge branch 'master' into fix-time
Diffstat (limited to 'test/core')
18 files changed, 1011 insertions, 80 deletions
diff --git a/test/core/client_channel/resolvers/dns_resolver_cooldown_test.cc b/test/core/client_channel/resolvers/dns_resolver_cooldown_test.cc index 4fd7aedc6c..521fc3107e 100644 --- a/test/core/client_channel/resolvers/dns_resolver_cooldown_test.cc +++ b/test/core/client_channel/resolvers/dns_resolver_cooldown_test.cc @@ -145,7 +145,6 @@ struct OnResolutionCallbackArg { grpc_core::OrphanablePtr<grpc_core::Resolver> resolver; grpc_channel_args* result = nullptr; grpc_millis delay_before_second_resolution = 0; - bool using_cares = false; }; // Counter for the number of times a resolution notification callback has been @@ -155,81 +154,100 @@ static int g_on_resolution_invocations_count; // Set to true by the last callback in the resolution chain. bool g_all_callbacks_invoked; -void on_third_resolution(void* arg, grpc_error* error) { +void on_fourth_resolution(void* arg, grpc_error* error) { OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg); + grpc_channel_args_destroy(cb_arg->result); GPR_ASSERT(error == GRPC_ERROR_NONE); ++g_on_resolution_invocations_count; - grpc_channel_args_destroy(cb_arg->result); gpr_log(GPR_INFO, - "3rd: g_on_resolution_invocations_count: %d, g_resolution_count: %d", + "4th: g_on_resolution_invocations_count: %d, g_resolution_count: %d", g_on_resolution_invocations_count, g_resolution_count); // In this case we expect to have incurred in another system-level resolution - // because on_second_resolution slept for longer than the min resolution + // because on_third_resolution slept for longer than the min resolution // period. - GPR_ASSERT(g_on_resolution_invocations_count == 3); - GPR_ASSERT(g_resolution_count == 2); + GPR_ASSERT(g_on_resolution_invocations_count == 4); + GPR_ASSERT(g_resolution_count == 3); cb_arg->resolver.reset(); - if (cb_arg->using_cares) { - gpr_atm_rel_store(&g_iomgr_args.done_atm, 1); - gpr_mu_lock(g_iomgr_args.mu); - GRPC_LOG_IF_ERROR("pollset_kick", - grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); - gpr_mu_unlock(g_iomgr_args.mu); - } + gpr_atm_rel_store(&g_iomgr_args.done_atm, 1); + gpr_mu_lock(g_iomgr_args.mu); + GRPC_LOG_IF_ERROR("pollset_kick", + grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); + gpr_mu_unlock(g_iomgr_args.mu); grpc_core::Delete(cb_arg); g_all_callbacks_invoked = true; } -void on_second_resolution(void* arg, grpc_error* error) { +void on_third_resolution(void* arg, grpc_error* error) { OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg); - ++g_on_resolution_invocations_count; grpc_channel_args_destroy(cb_arg->result); + GPR_ASSERT(error == GRPC_ERROR_NONE); + ++g_on_resolution_invocations_count; + gpr_log(GPR_INFO, + "3rd: g_on_resolution_invocations_count: %d, g_resolution_count: %d", + g_on_resolution_invocations_count, g_resolution_count); + // The timer set because of the previous re-resolution request fires, so a new + // system-level resolution happened. + GPR_ASSERT(g_on_resolution_invocations_count == 3); + GPR_ASSERT(g_resolution_count == 2); + grpc_core::ExecCtx::Get()->TestOnlySetNow( + cb_arg->delay_before_second_resolution * 2); + cb_arg->resolver->NextLocked( + &cb_arg->result, + GRPC_CLOSURE_CREATE(on_fourth_resolution, arg, + grpc_combiner_scheduler(g_combiner))); + cb_arg->resolver->RequestReresolutionLocked(); + gpr_mu_lock(g_iomgr_args.mu); + GRPC_LOG_IF_ERROR("pollset_kick", + grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); + gpr_mu_unlock(g_iomgr_args.mu); +} +void on_second_resolution(void* arg, grpc_error* error) { + OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg); + grpc_channel_args_destroy(cb_arg->result); + GPR_ASSERT(error == GRPC_ERROR_NONE); + ++g_on_resolution_invocations_count; gpr_log(GPR_INFO, "2nd: g_on_resolution_invocations_count: %d, g_resolution_count: %d", g_on_resolution_invocations_count, g_resolution_count); // The resolution request for which this function is the callback happened // before the min resolution period. Therefore, no new system-level - // resolutions happened, as indicated by g_resolution_count. + // resolutions happened, as indicated by g_resolution_count. But a resolution + // timer was set to fire when the cooldown finishes. GPR_ASSERT(g_on_resolution_invocations_count == 2); GPR_ASSERT(g_resolution_count == 1); - grpc_core::ExecCtx::Get()->TestOnlySetNow( - cb_arg->delay_before_second_resolution * 2); + // Register a new callback to capture the timer firing. cb_arg->resolver->NextLocked( &cb_arg->result, GRPC_CLOSURE_CREATE(on_third_resolution, arg, grpc_combiner_scheduler(g_combiner))); - cb_arg->resolver->RequestReresolutionLocked(); - if (cb_arg->using_cares) { - gpr_mu_lock(g_iomgr_args.mu); - GRPC_LOG_IF_ERROR("pollset_kick", - grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); - gpr_mu_unlock(g_iomgr_args.mu); - } + gpr_mu_lock(g_iomgr_args.mu); + GRPC_LOG_IF_ERROR("pollset_kick", + grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); + gpr_mu_unlock(g_iomgr_args.mu); } void on_first_resolution(void* arg, grpc_error* error) { OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg); - ++g_on_resolution_invocations_count; grpc_channel_args_destroy(cb_arg->result); - cb_arg->resolver->NextLocked( - &cb_arg->result, - GRPC_CLOSURE_CREATE(on_second_resolution, arg, - grpc_combiner_scheduler(g_combiner))); - cb_arg->resolver->RequestReresolutionLocked(); + GPR_ASSERT(error == GRPC_ERROR_NONE); + ++g_on_resolution_invocations_count; gpr_log(GPR_INFO, "1st: g_on_resolution_invocations_count: %d, g_resolution_count: %d", g_on_resolution_invocations_count, g_resolution_count); - // Theres one initial system-level resolution and one invocation of a + // There's one initial system-level resolution and one invocation of a // notification callback (the current function). GPR_ASSERT(g_on_resolution_invocations_count == 1); GPR_ASSERT(g_resolution_count == 1); - if (cb_arg->using_cares) { - gpr_mu_lock(g_iomgr_args.mu); - GRPC_LOG_IF_ERROR("pollset_kick", - grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); - gpr_mu_unlock(g_iomgr_args.mu); - } + cb_arg->resolver->NextLocked( + &cb_arg->result, + GRPC_CLOSURE_CREATE(on_second_resolution, arg, + grpc_combiner_scheduler(g_combiner))); + cb_arg->resolver->RequestReresolutionLocked(); + gpr_mu_lock(g_iomgr_args.mu); + GRPC_LOG_IF_ERROR("pollset_kick", + grpc_pollset_kick(g_iomgr_args.pollset, nullptr)); + gpr_mu_unlock(g_iomgr_args.mu); } static void start_test_under_combiner(void* arg, grpc_error* error) { @@ -269,22 +287,19 @@ static void start_test_under_combiner(void* arg, grpc_error* error) { grpc_uri_destroy(uri); } -static void test_cooldown(bool using_cares) { +static void test_cooldown() { grpc_core::ExecCtx exec_ctx; - if (using_cares) iomgr_args_init(&g_iomgr_args); + iomgr_args_init(&g_iomgr_args); OnResolutionCallbackArg* res_cb_arg = grpc_core::New<OnResolutionCallbackArg>(); res_cb_arg->uri_str = "dns:127.0.0.1"; - res_cb_arg->using_cares = using_cares; GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(start_test_under_combiner, res_cb_arg, grpc_combiner_scheduler(g_combiner)), GRPC_ERROR_NONE); - if (using_cares) { - grpc_core::ExecCtx::Get()->Flush(); - poll_pollset_until_request_done(&g_iomgr_args); - iomgr_args_finish(&g_iomgr_args); - } + grpc_core::ExecCtx::Get()->Flush(); + poll_pollset_until_request_done(&g_iomgr_args); + iomgr_args_finish(&g_iomgr_args); } int main(int argc, char** argv) { @@ -293,16 +308,12 @@ int main(int argc, char** argv) { g_combiner = grpc_combiner_create(); - bool using_cares = false; -#if GRPC_ARES == 1 - using_cares = true; -#endif g_default_dns_lookup_ares = grpc_dns_lookup_ares; grpc_dns_lookup_ares = test_dns_lookup_ares; default_resolve_address = grpc_resolve_address_impl; grpc_set_resolver_impl(&test_resolver); - test_cooldown(using_cares); + test_cooldown(); { grpc_core::ExecCtx exec_ctx; diff --git a/test/core/client_channel/resolvers/dns_resolver_test.cc b/test/core/client_channel/resolvers/dns_resolver_test.cc index e3fba2838c..103b2916c4 100644 --- a/test/core/client_channel/resolvers/dns_resolver_test.cc +++ b/test/core/client_channel/resolvers/dns_resolver_test.cc @@ -70,11 +70,12 @@ int main(int argc, char** argv) { test_succeeds(dns, "dns:10.2.1.1"); test_succeeds(dns, "dns:10.2.1.1:1234"); - test_succeeds(dns, "ipv4:www.google.com"); + test_succeeds(dns, "dns:www.google.com"); + test_succeeds(dns, "dns:///www.google.com"); if (grpc_resolve_address == grpc_resolve_address_ares) { - test_succeeds(dns, "ipv4://8.8.8.8/8.8.8.8:8888"); + test_succeeds(dns, "dns://8.8.8.8/8.8.8.8:8888"); } else { - test_fails(dns, "ipv4://8.8.8.8/8.8.8.8:8888"); + test_fails(dns, "dns://8.8.8.8/8.8.8.8:8888"); } { diff --git a/test/core/end2end/end2end_nosec_tests.cc b/test/core/end2end/end2end_nosec_tests.cc index 297408cd92..59eb643a93 100644 --- a/test/core/end2end/end2end_nosec_tests.cc +++ b/test/core/end2end/end2end_nosec_tests.cc @@ -132,6 +132,8 @@ extern void retry_exceeds_buffer_size_in_subsequent_batch(grpc_end2end_test_conf extern void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void); extern void retry_non_retriable_status(grpc_end2end_test_config config); extern void retry_non_retriable_status_pre_init(void); +extern void retry_non_retriable_status_before_recv_trailing_metadata_started(grpc_end2end_test_config config); +extern void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(void); extern void retry_recv_initial_metadata(grpc_end2end_test_config config); extern void retry_recv_initial_metadata_pre_init(void); extern void retry_recv_message(grpc_end2end_test_config config); @@ -236,6 +238,7 @@ void grpc_end2end_tests_pre_init(void) { retry_exceeds_buffer_size_in_initial_batch_pre_init(); retry_exceeds_buffer_size_in_subsequent_batch_pre_init(); retry_non_retriable_status_pre_init(); + retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(); retry_recv_initial_metadata_pre_init(); retry_recv_message_pre_init(); retry_server_pushback_delay_pre_init(); @@ -320,6 +323,7 @@ void grpc_end2end_tests(int argc, char **argv, retry_exceeds_buffer_size_in_initial_batch(config); retry_exceeds_buffer_size_in_subsequent_batch(config); retry_non_retriable_status(config); + retry_non_retriable_status_before_recv_trailing_metadata_started(config); retry_recv_initial_metadata(config); retry_recv_message(config); retry_server_pushback_delay(config); @@ -552,6 +556,10 @@ void grpc_end2end_tests(int argc, char **argv, retry_non_retriable_status(config); continue; } + if (0 == strcmp("retry_non_retriable_status_before_recv_trailing_metadata_started", argv[i])) { + retry_non_retriable_status_before_recv_trailing_metadata_started(config); + continue; + } if (0 == strcmp("retry_recv_initial_metadata", argv[i])) { retry_recv_initial_metadata(config); continue; diff --git a/test/core/end2end/end2end_tests.cc b/test/core/end2end/end2end_tests.cc index 9b3f655254..9f164b4ead 100644 --- a/test/core/end2end/end2end_tests.cc +++ b/test/core/end2end/end2end_tests.cc @@ -134,6 +134,8 @@ extern void retry_exceeds_buffer_size_in_subsequent_batch(grpc_end2end_test_conf extern void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void); extern void retry_non_retriable_status(grpc_end2end_test_config config); extern void retry_non_retriable_status_pre_init(void); +extern void retry_non_retriable_status_before_recv_trailing_metadata_started(grpc_end2end_test_config config); +extern void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(void); extern void retry_recv_initial_metadata(grpc_end2end_test_config config); extern void retry_recv_initial_metadata_pre_init(void); extern void retry_recv_message(grpc_end2end_test_config config); @@ -239,6 +241,7 @@ void grpc_end2end_tests_pre_init(void) { retry_exceeds_buffer_size_in_initial_batch_pre_init(); retry_exceeds_buffer_size_in_subsequent_batch_pre_init(); retry_non_retriable_status_pre_init(); + retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init(); retry_recv_initial_metadata_pre_init(); retry_recv_message_pre_init(); retry_server_pushback_delay_pre_init(); @@ -324,6 +327,7 @@ void grpc_end2end_tests(int argc, char **argv, retry_exceeds_buffer_size_in_initial_batch(config); retry_exceeds_buffer_size_in_subsequent_batch(config); retry_non_retriable_status(config); + retry_non_retriable_status_before_recv_trailing_metadata_started(config); retry_recv_initial_metadata(config); retry_recv_message(config); retry_server_pushback_delay(config); @@ -560,6 +564,10 @@ void grpc_end2end_tests(int argc, char **argv, retry_non_retriable_status(config); continue; } + if (0 == strcmp("retry_non_retriable_status_before_recv_trailing_metadata_started", argv[i])) { + retry_non_retriable_status_before_recv_trailing_metadata_started(config); + continue; + } if (0 == strcmp("retry_recv_initial_metadata", argv[i])) { retry_recv_initial_metadata(config); continue; diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py index c7b0362574..c355fc24b5 100755 --- a/test/core/end2end/gen_build_yaml.py +++ b/test/core/end2end/gen_build_yaml.py @@ -170,6 +170,9 @@ END2END_TESTS = { proxyable=False), 'retry_non_retriable_status': default_test_options._replace( cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False), + 'retry_non_retriable_status_before_recv_trailing_metadata_started': + default_test_options._replace( + cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False), 'retry_recv_initial_metadata': default_test_options._replace( cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False), 'retry_recv_message': default_test_options._replace( diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl index 37fd1837f4..11fc576165 100755 --- a/test/core/end2end/generate_tests.bzl +++ b/test/core/end2end/generate_tests.bzl @@ -158,6 +158,8 @@ END2END_TESTS = { needs_client_channel=True, proxyable=False), 'retry_non_retriable_status': test_options(needs_client_channel=True, proxyable=False), + 'retry_non_retriable_status_before_recv_trailing_metadata_started': + test_options(needs_client_channel=True, proxyable=False), 'retry_recv_initial_metadata': test_options(needs_client_channel=True, proxyable=False), 'retry_recv_message': test_options(needs_client_channel=True, diff --git a/test/core/end2end/tests/bad_ping.cc b/test/core/end2end/tests/bad_ping.cc index 22481d1be7..98d893f64d 100644 --- a/test/core/end2end/tests/bad_ping.cc +++ b/test/core/end2end/tests/bad_ping.cc @@ -355,6 +355,11 @@ static void test_pings_without_data(grpc_end2end_test_config config) { grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead)); CQ_EXPECT_COMPLETION(cqv, tag(0xdead), 1); + + // Also expect the previously blocked pings to complete with an error + CQ_EXPECT_COMPLETION(cqv, tag(200 + MAX_PING_STRIKES + 1), 0); + CQ_EXPECT_COMPLETION(cqv, tag(200 + MAX_PING_STRIKES + 2), 0); + cq_verify(cqv); grpc_call_unref(s); diff --git a/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc b/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc new file mode 100644 index 0000000000..eb016a3de9 --- /dev/null +++ b/test/core/end2end/tests/retry_non_retriable_status_before_recv_trailing_metadata_started.cc @@ -0,0 +1,266 @@ +/* + * + * Copyright 2018 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 "test/core/end2end/end2end_tests.h" + +#include <stdio.h> +#include <string.h> + +#include <grpc/byte_buffer.h> +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/time.h> + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/transport/static_metadata.h" + +#include "test/core/end2end/cq_verifier.h" +#include "test/core/end2end/tests/cancel_test_helpers.h" + +static void* tag(intptr_t t) { return (void*)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->shutdown_cq, tag(1000)); + GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000), + grpc_timeout_seconds_to_deadline(5), + nullptr) + .type == GRPC_OP_COMPLETE); + 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); + grpc_completion_queue_destroy(f->shutdown_cq); +} + +// Tests that we don't retry for non-retryable status codes, even if +// status is received before the recv_trailing_metadata op is started. +// - 1 retry allowed for ABORTED status +// - first attempt gets INVALID_ARGUMENT, so no retry is done +static void +test_retry_non_retriable_status_before_recv_trailing_metadata_started( + grpc_end2end_test_config config) { + grpc_call* c; + grpc_call* s; + 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_slice request_payload_slice = grpc_slice_from_static_string("foo"); + grpc_slice response_payload_slice = grpc_slice_from_static_string("bar"); + grpc_byte_buffer* request_payload = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + grpc_byte_buffer* response_payload = + grpc_raw_byte_buffer_create(&response_payload_slice, 1); + grpc_byte_buffer* request_payload_recv = nullptr; + grpc_byte_buffer* response_payload_recv = nullptr; + grpc_status_code status; + grpc_call_error error; + grpc_slice details; + int was_cancelled = 2; + char* peer; + + grpc_arg arg; + arg.type = GRPC_ARG_STRING; + arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG); + arg.value.string = const_cast<char*>( + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"); + grpc_channel_args client_args = {1, &arg}; + grpc_end2end_test_fixture f = + begin_test(config, "retry_non_retriable_status", &client_args, nullptr); + + cq_verifier* cqv = cq_verifier_create(f.cq); + + 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("/service/method"), + nullptr, deadline, nullptr); + GPR_ASSERT(c); + + peer = grpc_call_get_peer(c); + GPR_ASSERT(peer != nullptr); + gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); + gpr_free(peer); + + 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); + grpc_slice status_details = grpc_slice_from_static_string("xyz"); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op++; + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = request_payload; + op++; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &response_payload_recv; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; + op++; + error = grpc_call_start_batch(c, ops, (size_t)(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), true); + cq_verify(cqv); + + peer = grpc_call_get_peer(s); + GPR_ASSERT(peer != nullptr); + gpr_log(GPR_DEBUG, "server_peer=%s", peer); + gpr_free(peer); + peer = grpc_call_get_peer(c); + GPR_ASSERT(peer != nullptr); + gpr_log(GPR_DEBUG, "client_peer=%s", peer); + gpr_free(peer); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + 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_INVALID_ARGUMENT; + op->data.send_status_from_server.status_details = &status_details; + op++; + op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; + op->data.recv_close_on_server.cancelled = &was_cancelled; + op++; + error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + CQ_EXPECT_COMPLETION(cqv, tag(102), true); + CQ_EXPECT_COMPLETION(cqv, tag(1), true); + cq_verify(cqv); + + memset(ops, 0, sizeof(ops)); + op = ops; + 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++; + error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + CQ_EXPECT_COMPLETION(cqv, tag(2), true); + cq_verify(cqv); + + GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT); + GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); + GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method")); + GPR_ASSERT(0 == call_details.flags); + GPR_ASSERT(was_cancelled == 1); + + 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_byte_buffer_destroy(request_payload); + grpc_byte_buffer_destroy(response_payload); + grpc_byte_buffer_destroy(request_payload_recv); + grpc_byte_buffer_destroy(response_payload_recv); + + grpc_call_unref(c); + grpc_call_unref(s); + + cq_verifier_destroy(cqv); + + end_test(&f); + config.tear_down_data(&f); +} + +void retry_non_retriable_status_before_recv_trailing_metadata_started( + grpc_end2end_test_config config) { + GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL); + test_retry_non_retriable_status_before_recv_trailing_metadata_started(config); +} + +void retry_non_retriable_status_before_recv_trailing_metadata_started_pre_init() { +} diff --git a/test/core/gprpp/ref_counted_ptr_test.cc b/test/core/gprpp/ref_counted_ptr_test.cc index 2e398a7722..c810345166 100644 --- a/test/core/gprpp/ref_counted_ptr_test.cc +++ b/test/core/gprpp/ref_counted_ptr_test.cc @@ -88,7 +88,7 @@ TEST(RefCountedPtr, CopyAssignmentWhenEmpty) { TEST(RefCountedPtr, CopyAssignmentToSelf) { RefCountedPtr<Foo> foo(New<Foo>()); - foo = foo; + foo = *&foo; // The "*&" avoids warnings from LLVM -Wself-assign. } TEST(RefCountedPtr, EnclosedScope) { diff --git a/test/core/handshake/readahead_handshaker_server_ssl.cc b/test/core/handshake/readahead_handshaker_server_ssl.cc index 9788320e0d..97e9c20ee4 100644 --- a/test/core/handshake/readahead_handshaker_server_ssl.cc +++ b/test/core/handshake/readahead_handshaker_server_ssl.cc @@ -64,7 +64,7 @@ static void readahead_handshaker_do_handshake( const grpc_handshaker_vtable readahead_handshaker_vtable = { readahead_handshaker_destroy, readahead_handshaker_shutdown, - readahead_handshaker_do_handshake}; + readahead_handshaker_do_handshake, "read_ahead"}; static grpc_handshaker* readahead_handshaker_create() { grpc_handshaker* h = diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index ce92e21d73..2b90939ab8 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -867,6 +867,7 @@ static void set_google_default_creds_env_var_with_file_contents( static void test_google_default_creds_auth_key(void) { grpc_core::ExecCtx exec_ctx; grpc_service_account_jwt_access_credentials* jwt; + grpc_google_default_channel_credentials* default_creds; grpc_composite_channel_credentials* creds; char* json_key = test_json_key_str(); grpc_flush_cached_google_default_credentials(); @@ -875,7 +876,9 @@ static void test_google_default_creds_auth_key(void) { gpr_free(json_key); creds = reinterpret_cast<grpc_composite_channel_credentials*>( grpc_google_default_credentials_create()); - GPR_ASSERT(creds != nullptr); + default_creds = reinterpret_cast<grpc_google_default_channel_credentials*>( + creds->inner_creds); + GPR_ASSERT(default_creds->ssl_creds != nullptr); jwt = reinterpret_cast<grpc_service_account_jwt_access_credentials*>( creds->call_creds); GPR_ASSERT( @@ -889,13 +892,16 @@ static void test_google_default_creds_auth_key(void) { static void test_google_default_creds_refresh_token(void) { grpc_core::ExecCtx exec_ctx; grpc_google_refresh_token_credentials* refresh; + grpc_google_default_channel_credentials* default_creds; grpc_composite_channel_credentials* creds; grpc_flush_cached_google_default_credentials(); set_google_default_creds_env_var_with_file_contents( "refresh_token_google_default_creds", test_refresh_token_str); creds = reinterpret_cast<grpc_composite_channel_credentials*>( grpc_google_default_credentials_create()); - GPR_ASSERT(creds != nullptr); + default_creds = reinterpret_cast<grpc_google_default_channel_credentials*>( + creds->inner_creds); + GPR_ASSERT(default_creds->ssl_creds != nullptr); refresh = reinterpret_cast<grpc_google_refresh_token_credentials*>( creds->call_creds); GPR_ASSERT(strcmp(refresh->refresh_token.client_id, diff --git a/test/core/security/security_connector_test.cc b/test/core/security/security_connector_test.cc index f03f4ccdbd..e4c3ace6b4 100644 --- a/test/core/security/security_connector_test.cc +++ b/test/core/security/security_connector_test.cc @@ -87,15 +87,15 @@ static void test_unauthenticated_ssl_peer(void) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, &peer.properties[0]) == TSI_OK); - ctx = tsi_ssl_peer_to_auth_context(&peer); + ctx = grpc_ssl_peer_to_auth_context(&peer); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(!grpc_auth_context_peer_is_authenticated(ctx)); GPR_ASSERT(check_transport_security_type(ctx)); - rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx); + rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx); GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); - tsi_shallow_peer_destruct(&rpeer); + grpc_shallow_peer_destruct(&rpeer); tsi_peer_destruct(&peer); GRPC_AUTH_CONTEXT_UNREF(ctx, "test"); } @@ -187,7 +187,7 @@ static void test_cn_only_ssl_peer_to_auth_context(void) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert, &peer.properties[2]) == TSI_OK); - ctx = tsi_ssl_peer_to_auth_context(&peer); + ctx = grpc_ssl_peer_to_auth_context(&peer); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx)); GPR_ASSERT(check_identity(ctx, GRPC_X509_CN_PROPERTY_NAME, &expected_cn, 1)); @@ -195,10 +195,10 @@ static void test_cn_only_ssl_peer_to_auth_context(void) { GPR_ASSERT(check_x509_cn(ctx, expected_cn)); GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert)); - rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx); + rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx); GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); - tsi_shallow_peer_destruct(&rpeer); + grpc_shallow_peer_destruct(&rpeer); tsi_peer_destruct(&peer); GRPC_AUTH_CONTEXT_UNREF(ctx, "test"); } @@ -223,7 +223,7 @@ static void test_cn_and_one_san_ssl_peer_to_auth_context(void) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert, &peer.properties[3]) == TSI_OK); - ctx = tsi_ssl_peer_to_auth_context(&peer); + ctx = grpc_ssl_peer_to_auth_context(&peer); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx)); GPR_ASSERT( @@ -232,10 +232,10 @@ static void test_cn_and_one_san_ssl_peer_to_auth_context(void) { GPR_ASSERT(check_x509_cn(ctx, expected_cn)); GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert)); - rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx); + rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx); GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); - tsi_shallow_peer_destruct(&rpeer); + grpc_shallow_peer_destruct(&rpeer); tsi_peer_destruct(&peer); GRPC_AUTH_CONTEXT_UNREF(ctx, "test"); } @@ -264,7 +264,7 @@ static void test_cn_and_multiple_sans_ssl_peer_to_auth_context(void) { TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_sans[i], &peer.properties[3 + i]) == TSI_OK); } - ctx = tsi_ssl_peer_to_auth_context(&peer); + ctx = grpc_ssl_peer_to_auth_context(&peer); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx)); GPR_ASSERT(check_identity(ctx, GRPC_X509_SAN_PROPERTY_NAME, expected_sans, @@ -273,10 +273,10 @@ static void test_cn_and_multiple_sans_ssl_peer_to_auth_context(void) { GPR_ASSERT(check_x509_cn(ctx, expected_cn)); GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert)); - rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx); + rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx); GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); - tsi_shallow_peer_destruct(&rpeer); + grpc_shallow_peer_destruct(&rpeer); tsi_peer_destruct(&peer); GRPC_AUTH_CONTEXT_UNREF(ctx, "test"); } @@ -310,7 +310,7 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_sans[i], &peer.properties[5 + i]) == TSI_OK); } - ctx = tsi_ssl_peer_to_auth_context(&peer); + ctx = grpc_ssl_peer_to_auth_context(&peer); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx)); GPR_ASSERT(check_identity(ctx, GRPC_X509_SAN_PROPERTY_NAME, expected_sans, @@ -319,10 +319,10 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context( GPR_ASSERT(check_x509_cn(ctx, expected_cn)); GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert)); - rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx); + rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx); GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); - tsi_shallow_peer_destruct(&rpeer); + grpc_shallow_peer_destruct(&rpeer); tsi_peer_destruct(&peer); GRPC_AUTH_CONTEXT_UNREF(ctx, "test"); } @@ -340,6 +340,26 @@ static grpc_ssl_roots_override_result override_roots_permanent_failure( return GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY; } +static void test_ipv6_address_san(void) { + const char* addresses[] = { + "2001:db8::1", "fe80::abcd:ef65:4321%em0", "fd11:feed:beef:0:cafe::4", + "128.10.0.1:8888", "[2001:db8::1]:8080", "[2001:db8::1%em1]:8080", + }; + const char* san_ips[] = { + "2001:db8::1", "fe80::abcd:ef65:4321", "fd11:feed:beef:0:cafe::4", + "128.10.0.1", "2001:db8::1", "2001:db8::1", + }; + tsi_peer peer; + GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); + for (size_t i = 0; i < GPR_ARRAY_SIZE(addresses); i++) { + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, san_ips[i], + &peer.properties[0]) == TSI_OK); + GPR_ASSERT(grpc_ssl_host_matches_name(&peer, addresses[i])); + tsi_peer_property_destruct(&peer.properties[0]); + } + tsi_peer_destruct(&peer); +} namespace grpc_core { namespace { @@ -416,6 +436,7 @@ int main(int argc, char** argv) { test_cn_and_one_san_ssl_peer_to_auth_context(); test_cn_and_multiple_sans_ssl_peer_to_auth_context(); test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context(); + test_ipv6_address_san(); test_default_ssl_roots(); grpc_shutdown(); diff --git a/test/core/tsi/alts/fake_handshaker/BUILD b/test/core/tsi/alts/fake_handshaker/BUILD new file mode 100644 index 0000000000..a09a046d27 --- /dev/null +++ b/test/core/tsi/alts/fake_handshaker/BUILD @@ -0,0 +1,57 @@ +# Copyright 2018 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. + +licenses(["notice"]) # Apache v2 + +load("//bazel:grpc_build_system.bzl", "grpc_proto_library", "grpc_cc_library", "grpc_cc_binary", "grpc_package") + +grpc_package(name = "test/core/tsi/alts/fake_handshaker", visibility = "public") + +grpc_proto_library( + name = "transport_security_common_proto", + srcs = ["transport_security_common.proto"], + has_services = False, +) + +grpc_proto_library( + name = "handshaker_proto", + srcs = ["handshaker.proto"], + has_services = True, + deps = [ + "transport_security_common_proto", + ], +) + +grpc_cc_library( + name = "fake_handshaker_lib", + testonly = True, + srcs = ["fake_handshaker_server.cc"], + language = "C++", + deps = [ + "handshaker_proto", + "transport_security_common_proto", + "//:grpc++", + "//test/cpp/util:test_config", + ], +) + +grpc_cc_binary( + name = "fake_handshaker_server", + testonly = True, + srcs = ["fake_handshaker_server.cc"], + language = "C++", + deps = [ + "fake_handshaker_lib", + ], +) diff --git a/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc new file mode 100644 index 0000000000..f6a4791b49 --- /dev/null +++ b/test/core/tsi/alts/fake_handshaker/fake_handshaker_server.cc @@ -0,0 +1,268 @@ +/* + * + * Copyright 2018 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 <memory> +#include <sstream> +#include <string> + +#include <gflags/gflags.h> +#include <grpc/grpc.h> +#include <grpc/support/log.h> +#include <grpcpp/impl/codegen/async_stream.h> +#include <grpcpp/security/server_credentials.h> +#include <grpcpp/server.h> +#include <grpcpp/server_builder.h> +#include <grpcpp/server_context.h> + +#include "test/core/tsi/alts/fake_handshaker/handshaker.grpc.pb.h" +#include "test/core/tsi/alts/fake_handshaker/handshaker.pb.h" +#include "test/core/tsi/alts/fake_handshaker/transport_security_common.pb.h" +#include "test/cpp/util/test_config.h" + +DEFINE_int32(handshaker_port, 55056, + "TCP port on which the fake handshaker server listens to."); + +// Fake handshake messages. +constexpr char kClientInitFrame[] = "ClientInit"; +constexpr char kServerFrame[] = "ServerInitAndFinished"; +constexpr char kClientFinishFrame[] = "ClientFinished"; +// Error messages. +constexpr char kInvalidFrameError[] = "Invalid input frame."; +constexpr char kWrongStateError[] = "Wrong handshake state."; + +namespace grpc { +namespace gcp { + +// FakeHandshakeService implements a fake handshaker service using a fake key +// exchange protocol. The fake key exchange protocol is a 3-message protocol: +// - Client first sends ClientInit message to Server. +// - Server then sends ServerInitAndFinished message back to Client. +// - Client finally sends ClientFinished message to Server. +// This fake handshaker service is intended for ALTS integration testing without +// relying on real ALTS handshaker service inside GCE. +// It is thread-safe. +class FakeHandshakerService : public HandshakerService::Service { + public: + Status DoHandshake( + ServerContext* server_context, + ServerReaderWriter<HandshakerResp, HandshakerReq>* stream) override { + Status status; + HandshakerContext context; + HandshakerReq request; + HandshakerResp response; + gpr_log(GPR_DEBUG, "Start a new handshake."); + while (stream->Read(&request)) { + status = ProcessRequest(&context, request, &response); + if (!status.ok()) return WriteErrorResponse(stream, status); + stream->Write(response); + if (context.state == COMPLETED) return Status::OK; + request.Clear(); + } + return Status::OK; + } + + private: + // HandshakeState is used by fake handshaker server to keep track of client's + // handshake status. In the beginning of a handshake, the state is INITIAL. + // If start_client or start_server request is called, the state becomes at + // least STARTED. When the handshaker server produces the first fame, the + // state becomes SENT. After the handshaker server processes the final frame + // from the peer, the state becomes COMPLETED. + enum HandshakeState { INITIAL, STARTED, SENT, COMPLETED }; + + struct HandshakerContext { + bool is_client = true; + HandshakeState state = INITIAL; + }; + + Status ProcessRequest(HandshakerContext* context, + const HandshakerReq& request, + HandshakerResp* response) { + GPR_ASSERT(context != nullptr && response != nullptr); + response->Clear(); + if (request.has_client_start()) { + gpr_log(GPR_DEBUG, "Process client start request."); + return ProcessClientStart(context, request.client_start(), response); + } else if (request.has_server_start()) { + gpr_log(GPR_DEBUG, "Process server start request."); + return ProcessServerStart(context, request.server_start(), response); + } else if (request.has_next()) { + gpr_log(GPR_DEBUG, "Process next request."); + return ProcessNext(context, request.next(), response); + } + return Status(StatusCode::INVALID_ARGUMENT, "Request is empty."); + } + + Status ProcessClientStart(HandshakerContext* context, + const StartClientHandshakeReq& request, + HandshakerResp* response) { + GPR_ASSERT(context != nullptr && response != nullptr); + // Checks request. + if (context->state != INITIAL) { + return Status(StatusCode::FAILED_PRECONDITION, kWrongStateError); + } + if (request.application_protocols_size() == 0) { + return Status(StatusCode::INVALID_ARGUMENT, + "At least one application protocol needed."); + } + if (request.record_protocols_size() == 0) { + return Status(StatusCode::INVALID_ARGUMENT, + "At least one record protocol needed."); + } + // Sets response. + response->set_out_frames(kClientInitFrame); + response->set_bytes_consumed(0); + response->mutable_status()->set_code(StatusCode::OK); + // Updates handshaker context. + context->is_client = true; + context->state = SENT; + return Status::OK; + } + + Status ProcessServerStart(HandshakerContext* context, + const StartServerHandshakeReq& request, + HandshakerResp* response) { + GPR_ASSERT(context != nullptr && response != nullptr); + // Checks request. + if (context->state != INITIAL) { + return Status(StatusCode::FAILED_PRECONDITION, kWrongStateError); + } + if (request.application_protocols_size() == 0) { + return Status(StatusCode::INVALID_ARGUMENT, + "At least one application protocol needed."); + } + if (request.handshake_parameters().empty()) { + return Status(StatusCode::INVALID_ARGUMENT, + "At least one set of handshake parameters needed."); + } + // Sets response. + if (request.in_bytes().empty()) { + // start_server request does not have in_bytes. + response->set_bytes_consumed(0); + context->state = STARTED; + } else { + // start_server request has in_bytes. + if (request.in_bytes() == kClientInitFrame) { + response->set_out_frames(kServerFrame); + response->set_bytes_consumed(strlen(kClientInitFrame)); + context->state = SENT; + } else { + return Status(StatusCode::UNKNOWN, kInvalidFrameError); + } + } + response->mutable_status()->set_code(StatusCode::OK); + context->is_client = false; + return Status::OK; + } + + Status ProcessNext(HandshakerContext* context, + const NextHandshakeMessageReq& request, + HandshakerResp* response) { + GPR_ASSERT(context != nullptr && response != nullptr); + if (context->is_client) { + // Processes next request on client side. + if (context->state != SENT) { + return Status(StatusCode::FAILED_PRECONDITION, kWrongStateError); + } + if (request.in_bytes() != kServerFrame) { + return Status(StatusCode::UNKNOWN, kInvalidFrameError); + } + response->set_out_frames(kClientFinishFrame); + response->set_bytes_consumed(strlen(kServerFrame)); + context->state = COMPLETED; + } else { + // Processes next request on server side. + HandshakeState current_state = context->state; + if (current_state == STARTED) { + if (request.in_bytes() != kClientInitFrame) { + return Status(StatusCode::UNKNOWN, kInvalidFrameError); + } + response->set_out_frames(kServerFrame); + response->set_bytes_consumed(strlen(kClientInitFrame)); + context->state = SENT; + } else if (current_state == SENT) { + // Client finish frame may be sent along with the first payload from the + // client, handshaker only consumes the client finish frame. + if (request.in_bytes().substr(0, strlen(kClientFinishFrame)) != + kClientFinishFrame) { + return Status(StatusCode::UNKNOWN, kInvalidFrameError); + } + response->set_bytes_consumed(strlen(kClientFinishFrame)); + context->state = COMPLETED; + } else { + return Status(StatusCode::FAILED_PRECONDITION, kWrongStateError); + } + } + // At this point, processing next request succeeded. + response->mutable_status()->set_code(StatusCode::OK); + if (context->state == COMPLETED) { + *response->mutable_result() = GetHandshakerResult(); + } + return Status::OK; + } + + Status WriteErrorResponse( + ServerReaderWriter<HandshakerResp, HandshakerReq>* stream, + const Status& status) { + GPR_ASSERT(!status.ok()); + HandshakerResp response; + response.mutable_status()->set_code(status.error_code()); + response.mutable_status()->set_details(status.error_message()); + stream->Write(response); + return status; + } + + HandshakerResult GetHandshakerResult() { + HandshakerResult result; + result.set_application_protocol("grpc"); + result.set_record_protocol("ALTSRP_GCM_AES128_REKEY"); + result.mutable_peer_identity()->set_service_account("peer_identity"); + result.mutable_local_identity()->set_service_account("local_identity"); + string key(1024, '\0'); + result.set_key_data(key); + result.mutable_peer_rpc_versions()->mutable_max_rpc_version()->set_major(2); + result.mutable_peer_rpc_versions()->mutable_max_rpc_version()->set_minor(1); + result.mutable_peer_rpc_versions()->mutable_min_rpc_version()->set_major(2); + result.mutable_peer_rpc_versions()->mutable_min_rpc_version()->set_minor(1); + return result; + } +}; + +} // namespace gcp +} // namespace grpc + +void RunServer() { + GPR_ASSERT(FLAGS_handshaker_port != 0); + std::ostringstream server_address; + server_address << "[::1]:" << FLAGS_handshaker_port; + grpc::gcp::FakeHandshakerService service; + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address.str(), + grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); + gpr_log(GPR_INFO, "Fake handshaker server listening on %s", + server_address.str().c_str()); + server->Wait(); +} + +int main(int argc, char** argv) { + grpc::testing::InitTest(&argc, &argv, true); + RunServer(); + return 0; +} diff --git a/test/core/tsi/alts/fake_handshaker/handshaker.proto b/test/core/tsi/alts/fake_handshaker/handshaker.proto new file mode 100644 index 0000000000..8af9abfbf5 --- /dev/null +++ b/test/core/tsi/alts/fake_handshaker/handshaker.proto @@ -0,0 +1,224 @@ +// Copyright 2018 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. + +syntax = "proto3"; + +import "test/core/tsi/alts/fake_handshaker/transport_security_common.proto"; + +package grpc.gcp; + +option java_package = "io.grpc.alts.internal"; + +enum HandshakeProtocol { + // Default value. + HANDSHAKE_PROTOCOL_UNSPECIFIED = 0; + + // TLS handshake protocol. + TLS = 1; + + // Application Layer Transport Security handshake protocol. + ALTS = 2; +} + +enum NetworkProtocol { + NETWORK_PROTOCOL_UNSPECIFIED = 0; + TCP = 1; + UDP = 2; +} + +message Endpoint { + // IP address. It should contain an IPv4 or IPv6 string literal, e.g. + // "192.168.0.1" or "2001:db8::1". + string ip_address = 1; + + // Port number. + int32 port = 2; + + // Network protocol (e.g., TCP, UDP) associated with this endpoint. + NetworkProtocol protocol = 3; +} + +message Identity { + oneof identity_oneof { + // Service account of a connection endpoint. + string service_account = 1; + + // Hostname of a connection endpoint. + string hostname = 2; + } +} + +message StartClientHandshakeReq { + // Handshake security protocol requested by the client. + HandshakeProtocol handshake_security_protocol = 1; + + // The application protocols supported by the client, e.g., "h2" (for http2), + // "grpc". + repeated string application_protocols = 2; + + // The record protocols supported by the client, e.g., + // "ALTSRP_GCM_AES128". + repeated string record_protocols = 3; + + // (Optional) Describes which server identities are acceptable by the client. + // If target identities are provided and none of them matches the peer + // identity of the server, handshake will fail. + repeated Identity target_identities = 4; + + // (Optional) Application may specify a local identity. Otherwise, the + // handshaker chooses a default local identity. + Identity local_identity = 5; + + // (Optional) Local endpoint information of the connection to the server, + // such as local IP address, port number, and network protocol. + Endpoint local_endpoint = 6; + + // (Optional) Endpoint information of the remote server, such as IP address, + // port number, and network protocol. + Endpoint remote_endpoint = 7; + + // (Optional) If target name is provided, a secure naming check is performed + // to verify that the peer authenticated identity is indeed authorized to run + // the target name. + string target_name = 8; + + // (Optional) RPC protocol versions supported by the client. + RpcProtocolVersions rpc_versions = 9; +} + +message ServerHandshakeParameters { + // The record protocols supported by the server, e.g., + // "ALTSRP_GCM_AES128". + repeated string record_protocols = 1; + + // (Optional) A list of local identities supported by the server, if + // specified. Otherwise, the handshaker chooses a default local identity. + repeated Identity local_identities = 2; +} + +message StartServerHandshakeReq { + // The application protocols supported by the server, e.g., "h2" (for http2), + // "grpc". + repeated string application_protocols = 1; + + // Handshake parameters (record protocols and local identities supported by + // the server) mapped by the handshake protocol. Each handshake security + // protocol (e.g., TLS or ALTS) has its own set of record protocols and local + // identities. Since protobuf does not support enum as key to the map, the key + // to handshake_parameters is the integer value of HandshakeProtocol enum. + map<int32, ServerHandshakeParameters> handshake_parameters = 2; + + // Bytes in out_frames returned from the peer's HandshakerResp. It is possible + // that the peer's out_frames are split into multiple HandshakReq messages. + bytes in_bytes = 3; + + // (Optional) Local endpoint information of the connection to the client, + // such as local IP address, port number, and network protocol. + Endpoint local_endpoint = 4; + + // (Optional) Endpoint information of the remote client, such as IP address, + // port number, and network protocol. + Endpoint remote_endpoint = 5; + + // (Optional) RPC protocol versions supported by the server. + RpcProtocolVersions rpc_versions = 6; +} + +message NextHandshakeMessageReq { + // Bytes in out_frames returned from the peer's HandshakerResp. It is possible + // that the peer's out_frames are split into multiple NextHandshakerMessageReq + // messages. + bytes in_bytes = 1; +} + +message HandshakerReq { + oneof req_oneof { + // The start client handshake request message. + StartClientHandshakeReq client_start = 1; + + // The start server handshake request message. + StartServerHandshakeReq server_start = 2; + + // The next handshake request message. + NextHandshakeMessageReq next = 3; + } +} + +message HandshakerResult { + // The application protocol negotiated for this connection. + string application_protocol = 1; + + // The record protocol negotiated for this connection. + string record_protocol = 2; + + // Cryptographic key data. The key data may be more than the key length + // required for the record protocol, thus the client of the handshaker + // service needs to truncate the key data into the right key length. + bytes key_data = 3; + + // The authenticated identity of the peer. + Identity peer_identity = 4; + + // The local identity used in the handshake. + Identity local_identity = 5; + + // Indicate whether the handshaker service client should keep the channel + // between the handshaker service open, e.g., in order to handle + // post-handshake messages in the future. + bool keep_channel_open = 6; + + // The RPC protocol versions supported by the peer. + RpcProtocolVersions peer_rpc_versions = 7; +} + +message HandshakerStatus { + // The status code. This could be the gRPC status code. + uint32 code = 1; + + // The status details. + string details = 2; +} + +message HandshakerResp { + // Frames to be given to the peer for the NextHandshakeMessageReq. May be + // empty if no out_frames have to be sent to the peer or if in_bytes in the + // HandshakerReq are incomplete. All the non-empty out frames must be sent to + // the peer even if the handshaker status is not OK as these frames may + // contain the alert frames. + bytes out_frames = 1; + + // Number of bytes in the in_bytes consumed by the handshaker. It is possible + // that part of in_bytes in HandshakerReq was unrelated to the handshake + // process. + uint32 bytes_consumed = 2; + + // This is set iff the handshake was successful. out_frames may still be set + // to frames that needs to be forwarded to the peer. + HandshakerResult result = 3; + + // Status of the handshaker. + HandshakerStatus status = 4; +} + +service HandshakerService { + // Handshaker service accepts a stream of handshaker request, returning a + // stream of handshaker response. Client is expected to send exactly one + // message with either client_start or server_start followed by one or more + // messages with next. Each time client sends a request, the handshaker + // service expects to respond. Client does not have to wait for service's + // response before sending next request. + rpc DoHandshake(stream HandshakerReq) + returns (stream HandshakerResp) { + } +} diff --git a/test/core/tsi/alts/fake_handshaker/transport_security_common.proto b/test/core/tsi/alts/fake_handshaker/transport_security_common.proto new file mode 100644 index 0000000000..d0f861e644 --- /dev/null +++ b/test/core/tsi/alts/fake_handshaker/transport_security_common.proto @@ -0,0 +1,40 @@ +// Copyright 2018 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. + +syntax = "proto3"; + +package grpc.gcp; + +option java_package = "io.grpc.alts.internal"; + +// The security level of the created channel. The list is sorted in increasing +// level of security. This order must always be maintained. +enum SecurityLevel { + SECURITY_NONE = 0; + INTEGRITY_ONLY = 1; + INTEGRITY_AND_PRIVACY = 2; +} + +// Max and min supported RPC protocol versions. +message RpcProtocolVersions { + // RPC version contains a major version and a minor version. + message Version { + uint32 major = 1; + uint32 minor = 2; + } + // Maximum supported RPC version. + Version max_rpc_version = 1; + // Minimum supported RPC version. + Version min_rpc_version = 2; +} diff --git a/test/core/tsi/ssl_session_cache_test.cc b/test/core/tsi/ssl_session_cache_test.cc index 72df0e545c..c86cefb3ff 100644 --- a/test/core/tsi/ssl_session_cache_test.cc +++ b/test/core/tsi/ssl_session_cache_test.cc @@ -88,7 +88,6 @@ TEST(SslSessionCacheTest, InitialState) { // Verify session initial state. { tsi::SslSessionPtr tmp_sess = tracker.NewSession(1); - EXPECT_EQ(tmp_sess->references, 1); EXPECT_TRUE(tracker.IsAlive(1)); EXPECT_EQ(tracker.AliveCount(), 1); } diff --git a/test/core/tsi/ssl_transport_security_test.cc b/test/core/tsi/ssl_transport_security_test.cc index 88f1abc18c..cf1ac82413 100644 --- a/test/core/tsi/ssl_transport_security_test.cc +++ b/test/core/tsi/ssl_transport_security_test.cc @@ -34,6 +34,10 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> +extern "C" { +#include <openssl/crypto.h> +} + #define SSL_TSI_TEST_ALPN1 "foo" #define SSL_TSI_TEST_ALPN2 "toto" #define SSL_TSI_TEST_ALPN3 "baz" @@ -42,6 +46,14 @@ #define SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM 1 #define SSL_TSI_TEST_CREDENTIALS_DIR "src/core/tsi/test_creds/" +// OpenSSL 1.1 uses AES256 for encryption session ticket by default so specify +// different STEK size. +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_IS_BORINGSSL) +const size_t kSessionTicketEncryptionKeySize = 80; +#else +const size_t kSessionTicketEncryptionKeySize = 48; +#endif + typedef enum AlpnMode { NO_ALPN, ALPN_CLIENT_NO_SERVER, @@ -624,7 +636,7 @@ void ssl_tsi_test_do_round_trip_odd_buffer_size() { void ssl_tsi_test_do_handshake_session_cache() { tsi_ssl_session_cache* session_cache = tsi_ssl_session_cache_create_lru(16); - char session_ticket_key[48]; + char session_ticket_key[kSessionTicketEncryptionKeySize]; auto do_handshake = [&session_ticket_key, &session_cache](bool session_reused) { tsi_test_fixture* fixture = ssl_tsi_test_fixture_create(); @@ -633,22 +645,22 @@ void ssl_tsi_test_do_handshake_session_cache() { ssl_fixture->server_name_indication = const_cast<char*>("waterzooi.test.google.be"); ssl_fixture->session_ticket_key = session_ticket_key; - ssl_fixture->session_ticket_key_size = 48; + ssl_fixture->session_ticket_key_size = sizeof(session_ticket_key); tsi_ssl_session_cache_ref(session_cache); ssl_fixture->session_cache = session_cache; ssl_fixture->session_reused = session_reused; tsi_test_do_round_trip(&ssl_fixture->base); tsi_test_fixture_destroy(fixture); }; - memset(session_ticket_key, 'a', 48); + memset(session_ticket_key, 'a', sizeof(session_ticket_key)); do_handshake(false); do_handshake(true); do_handshake(true); // Changing session_ticket_key on server invalidates ticket. - memset(session_ticket_key, 'b', 48); + memset(session_ticket_key, 'b', sizeof(session_ticket_key)); do_handshake(false); do_handshake(true); - memset(session_ticket_key, 'c', 48); + memset(session_ticket_key, 'c', sizeof(session_ticket_key)); do_handshake(false); do_handshake(true); tsi_ssl_session_cache_unref(session_cache); |