From 3375c1dbba07f2c3a312290708e58b06dd1932ad Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 6 Apr 2021 19:13:26 -0700 Subject: [PATCH 01/20] Move classes having protobuf dependency in io.grpc.services into io.grpc.protobuf.services. Create a forwarding class for HealthStatusManager to avoid breakages. Others are mostly fine, as few people are using them. --- .../services/BinaryLogProvider.java | 2 +- .../services/BinaryLogProviderImpl.java | 2 +- .../services/BinaryLogSink.java | 2 +- .../{ => protobuf}/services/BinaryLogs.java | 2 +- .../{ => protobuf}/services/BinlogHelper.java | 4 +- .../services/ChannelzProtoUtil.java | 2 +- .../services/ChannelzService.java | 2 +- .../HealthCheckingLoadBalancerFactory.java | 2 +- .../HealthCheckingLoadBalancerUtil.java | 2 +- .../services/HealthServiceImpl.java | 2 +- .../services/HealthStatusManager.java | 89 +++++++++++++++++++ .../services/InetAddressUtil.java | 2 +- .../{ => protobuf}/services/TempFileSink.java | 2 +- ...heckingRoundRobinLoadBalancerProvider.java | 4 +- .../java/io/grpc/services/AdminInterface.java | 1 + .../io/grpc/services/HealthStatusManager.java | 26 +++--- .../services/io.grpc.LoadBalancerProvider | 2 +- .../services/BinaryLogProviderImplTest.java | 2 +- .../services/BinaryLogProviderTest.java | 2 +- .../services/BinlogHelperTest.java | 18 ++-- .../services/ChannelzProtoUtilTest.java | 10 +-- .../services/ChannelzServiceTest.java | 8 +- .../services/ChannelzTestHelper.java | 2 +- ...HealthCheckingLoadBalancerFactoryTest.java | 4 +- .../services/HealthStatusManagerTest.java | 2 +- .../services/TempFileSinkTest.java | 5 +- ...ingRoundRobinLoadBalancerProviderTest.java | 4 +- .../grpc/services/CallMetricRecorderTest.java | 2 +- 28 files changed, 148 insertions(+), 59 deletions(-) rename services/src/main/java/io/grpc/{ => protobuf}/services/BinaryLogProvider.java (99%) rename services/src/main/java/io/grpc/{ => protobuf}/services/BinaryLogProviderImpl.java (98%) rename services/src/main/java/io/grpc/{ => protobuf}/services/BinaryLogSink.java (96%) rename services/src/main/java/io/grpc/{ => protobuf}/services/BinaryLogs.java (98%) rename services/src/main/java/io/grpc/{ => protobuf}/services/BinlogHelper.java (99%) rename services/src/main/java/io/grpc/{ => protobuf}/services/ChannelzProtoUtil.java (99%) rename services/src/main/java/io/grpc/{ => protobuf}/services/ChannelzService.java (99%) rename services/src/main/java/io/grpc/{ => protobuf}/services/HealthCheckingLoadBalancerFactory.java (99%) rename services/src/main/java/io/grpc/{ => protobuf}/services/HealthCheckingLoadBalancerUtil.java (98%) rename services/src/main/java/io/grpc/{ => protobuf}/services/HealthServiceImpl.java (99%) create mode 100644 services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java rename services/src/main/java/io/grpc/{ => protobuf}/services/InetAddressUtil.java (98%) rename services/src/main/java/io/grpc/{ => protobuf}/services/TempFileSink.java (98%) rename services/src/main/java/io/grpc/{ => protobuf}/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java (95%) rename services/src/test/java/io/grpc/{ => protobuf}/services/BinaryLogProviderImplTest.java (98%) rename services/src/test/java/io/grpc/{ => protobuf}/services/BinaryLogProviderTest.java (99%) rename services/src/test/java/io/grpc/{ => protobuf}/services/BinlogHelperTest.java (99%) rename services/src/test/java/io/grpc/{ => protobuf}/services/ChannelzProtoUtilTest.java (99%) rename services/src/test/java/io/grpc/{ => protobuf}/services/ChannelzServiceTest.java (97%) rename services/src/test/java/io/grpc/{ => protobuf}/services/ChannelzTestHelper.java (99%) rename services/src/test/java/io/grpc/{ => protobuf}/services/HealthCheckingLoadBalancerFactoryTest.java (99%) rename services/src/test/java/io/grpc/{ => protobuf}/services/HealthStatusManagerTest.java (99%) rename services/src/test/java/io/grpc/{ => protobuf}/services/TempFileSinkTest.java (94%) rename services/src/test/java/io/grpc/{ => protobuf}/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java (94%) diff --git a/services/src/main/java/io/grpc/services/BinaryLogProvider.java b/services/src/main/java/io/grpc/protobuf/services/BinaryLogProvider.java similarity index 99% rename from services/src/main/java/io/grpc/services/BinaryLogProvider.java rename to services/src/main/java/io/grpc/protobuf/services/BinaryLogProvider.java index 3ca5de65558..bbbb88ce6e8 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogProvider.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinaryLogProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; diff --git a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java b/services/src/main/java/io/grpc/protobuf/services/BinaryLogProviderImpl.java similarity index 98% rename from services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java rename to services/src/main/java/io/grpc/protobuf/services/BinaryLogProviderImpl.java index dee13a924c6..f68d67b27b7 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinaryLogProviderImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.common.base.Preconditions; import io.grpc.CallOptions; diff --git a/services/src/main/java/io/grpc/services/BinaryLogSink.java b/services/src/main/java/io/grpc/protobuf/services/BinaryLogSink.java similarity index 96% rename from services/src/main/java/io/grpc/services/BinaryLogSink.java rename to services/src/main/java/io/grpc/protobuf/services/BinaryLogSink.java index 1c1c193e4fe..44e4e2cc856 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogSink.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinaryLogSink.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.protobuf.MessageLite; import io.grpc.ExperimentalApi; diff --git a/services/src/main/java/io/grpc/services/BinaryLogs.java b/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java similarity index 98% rename from services/src/main/java/io/grpc/services/BinaryLogs.java rename to services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java index fc2d8d84ad1..4a7757c8617 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogs.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import io.grpc.BinaryLog; import io.grpc.ExperimentalApi; diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/protobuf/services/BinlogHelper.java similarity index 99% rename from services/src/main/java/io/grpc/services/BinlogHelper.java rename to services/src/main/java/io/grpc/protobuf/services/BinlogHelper.java index d8923160e5c..167eefa44c6 100644 --- a/services/src/main/java/io/grpc/services/BinlogHelper.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinlogHelper.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static io.grpc.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; +import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; diff --git a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java b/services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java similarity index 99% rename from services/src/main/java/io/grpc/services/ChannelzProtoUtil.java rename to services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java index 349995d9f80..cf003b2f881 100644 --- a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java +++ b/services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ListenableFuture; diff --git a/services/src/main/java/io/grpc/services/ChannelzService.java b/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java similarity index 99% rename from services/src/main/java/io/grpc/services/ChannelzService.java rename to services/src/main/java/io/grpc/protobuf/services/ChannelzService.java index 8032d0ad222..66daf559d32 100644 --- a/services/src/main/java/io/grpc/services/ChannelzService.java +++ b/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.common.annotations.VisibleForTesting; import io.grpc.ExperimentalApi; diff --git a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerFactory.java b/services/src/main/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerFactory.java similarity index 99% rename from services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerFactory.java rename to services/src/main/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerFactory.java index e233d94a98a..d027d13f08b 100644 --- a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerFactory.java +++ b/services/src/main/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; diff --git a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java b/services/src/main/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerUtil.java similarity index 98% rename from services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java rename to services/src/main/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerUtil.java index c22716f46cf..7988ed8ff9e 100644 --- a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java +++ b/services/src/main/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import io.grpc.ExperimentalApi; import io.grpc.LoadBalancer; diff --git a/services/src/main/java/io/grpc/services/HealthServiceImpl.java b/services/src/main/java/io/grpc/protobuf/services/HealthServiceImpl.java similarity index 99% rename from services/src/main/java/io/grpc/services/HealthServiceImpl.java rename to services/src/main/java/io/grpc/protobuf/services/HealthServiceImpl.java index 906f5250d36..a1933c632fd 100644 --- a/services/src/main/java/io/grpc/services/HealthServiceImpl.java +++ b/services/src/main/java/io/grpc/protobuf/services/HealthServiceImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.MoreExecutors; diff --git a/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java b/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java new file mode 100644 index 00000000000..56cff68f3aa --- /dev/null +++ b/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java @@ -0,0 +1,89 @@ +/* + * Copyright 2016 The 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. + */ + +package io.grpc.protobuf.services; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.grpc.BindableService; +import io.grpc.health.v1.HealthCheckResponse.ServingStatus; + +/** + * A {@code HealthStatusManager} object manages a health check service. A health check service is + * created in the constructor of {@code HealthStatusManager}, and it can be retrieved by the + * {@link #getHealthService()} method. + * The health status manager can update the health statuses of the server. + * + *

The default, empty-string, service name, {@link #SERVICE_NAME_ALL_SERVICES}, is initialized to + * {@link ServingStatus#SERVING}. + */ +@io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/4696") +public final class HealthStatusManager { + /** + * The special "service name" that represent all services on a GRPC server. It is an empty + * string. + */ + public static final String SERVICE_NAME_ALL_SERVICES = ""; + + private final HealthServiceImpl healthService; + + /** + * Creates a new health service instance. + */ + public HealthStatusManager() { + healthService = new HealthServiceImpl(); + } + + /** + * Gets the health check service created in the constructor. + */ + public BindableService getHealthService() { + return healthService; + } + + /** + * Updates the status of the server. + * @param service the name of some aspect of the server that is associated with a health status. + * This name can have no relation with the gRPC services that the server is running with. + * It can also be an empty String {@code ""} per the gRPC specification. + * @param status is one of the values {@link ServingStatus#SERVING}, + * {@link ServingStatus#NOT_SERVING} and {@link ServingStatus#UNKNOWN}. + */ + public void setStatus(String service, ServingStatus status) { + checkNotNull(status, "status"); + healthService.setStatus(service, status); + } + + /** + * Clears the health status record of a service. The health service will respond with NOT_FOUND + * error on checking the status of a cleared service. + * @param service the name of some aspect of the server that is associated with a health status. + * This name can have no relation with the gRPC services that the server is running with. + * It can also be an empty String {@code ""} per the gRPC specification. + */ + public void clearStatus(String service) { + healthService.clearStatus(service); + } + + /** + * enterTerminalState causes the health status manager to mark all services as not serving, and + * prevents future updates to services. This method is meant to be called prior to server + * shutdown as a way to indicate that clients should redirect their traffic elsewhere. + */ + public void enterTerminalState() { + healthService.enterTerminalState(); + } +} diff --git a/services/src/main/java/io/grpc/services/InetAddressUtil.java b/services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java similarity index 98% rename from services/src/main/java/io/grpc/services/InetAddressUtil.java rename to services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java index 057a8ccb5e6..2ba09c41b26 100644 --- a/services/src/main/java/io/grpc/services/InetAddressUtil.java +++ b/services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/services/src/main/java/io/grpc/services/TempFileSink.java b/services/src/main/java/io/grpc/protobuf/services/TempFileSink.java similarity index 98% rename from services/src/main/java/io/grpc/services/TempFileSink.java rename to services/src/main/java/io/grpc/protobuf/services/TempFileSink.java index c28339d1bd6..0ab91f54329 100644 --- a/services/src/main/java/io/grpc/services/TempFileSink.java +++ b/services/src/main/java/io/grpc/protobuf/services/TempFileSink.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.protobuf.MessageLite; import java.io.BufferedOutputStream; diff --git a/services/src/main/java/io/grpc/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java b/services/src/main/java/io/grpc/protobuf/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java similarity index 95% rename from services/src/main/java/io/grpc/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java rename to services/src/main/java/io/grpc/protobuf/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java index c2c6357b68e..68d67dbeb28 100644 --- a/services/src/main/java/io/grpc/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java +++ b/services/src/main/java/io/grpc/protobuf/services/internal/HealthCheckingRoundRobinLoadBalancerProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services.internal; +package io.grpc.protobuf.services.internal; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; @@ -23,7 +23,7 @@ import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancerProvider; import io.grpc.NameResolver.ConfigOrError; -import io.grpc.services.HealthCheckingLoadBalancerUtil; +import io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil; import java.util.Map; /** diff --git a/services/src/main/java/io/grpc/services/AdminInterface.java b/services/src/main/java/io/grpc/services/AdminInterface.java index 32c467ba63c..2288d844fd1 100644 --- a/services/src/main/java/io/grpc/services/AdminInterface.java +++ b/services/src/main/java/io/grpc/services/AdminInterface.java @@ -19,6 +19,7 @@ import io.grpc.BindableService; import io.grpc.ExperimentalApi; import io.grpc.ServerServiceDefinition; +import io.grpc.protobuf.services.ChannelzService; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; diff --git a/services/src/main/java/io/grpc/services/HealthStatusManager.java b/services/src/main/java/io/grpc/services/HealthStatusManager.java index 1edcb53c77f..fe1b7a8e08a 100644 --- a/services/src/main/java/io/grpc/services/HealthStatusManager.java +++ b/services/src/main/java/io/grpc/services/HealthStatusManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 The gRPC Authors + * Copyright 2018 The gRPC Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package io.grpc.services; -import static com.google.common.base.Preconditions.checkNotNull; - import io.grpc.BindableService; import io.grpc.health.v1.HealthCheckResponse.ServingStatus; @@ -29,29 +27,30 @@ * *

The default, empty-string, service name, {@link #SERVICE_NAME_ALL_SERVICES}, is initialized to * {@link ServingStatus#SERVING}. + * + * @deprecated Use {@link io.grpc.protobuf.services.HealthStatusManager} instead. */ +@Deprecated @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/4696") public final class HealthStatusManager { /** * The special "service name" that represent all services on a GRPC server. It is an empty * string. */ - public static final String SERVICE_NAME_ALL_SERVICES = ""; + public static final String SERVICE_NAME_ALL_SERVICES = + io.grpc.protobuf.services.HealthStatusManager.SERVICE_NAME_ALL_SERVICES; - private final HealthServiceImpl healthService; + private final io.grpc.protobuf.services.HealthStatusManager delegate; - /** - * Creates a new health service instance. - */ public HealthStatusManager() { - healthService = new HealthServiceImpl(); + this.delegate = new io.grpc.protobuf.services.HealthStatusManager(); } /** * Gets the health check service created in the constructor. */ public BindableService getHealthService() { - return healthService; + return delegate.getHealthService(); } /** @@ -63,8 +62,7 @@ public BindableService getHealthService() { * {@link ServingStatus#NOT_SERVING} and {@link ServingStatus#UNKNOWN}. */ public void setStatus(String service, ServingStatus status) { - checkNotNull(status, "status"); - healthService.setStatus(service, status); + delegate.setStatus(service, status); } /** @@ -75,7 +73,7 @@ public void setStatus(String service, ServingStatus status) { * It can also be an empty String {@code ""} per the gRPC specification. */ public void clearStatus(String service) { - healthService.clearStatus(service); + delegate.clearStatus(service); } /** @@ -84,6 +82,6 @@ public void clearStatus(String service) { * shutdown as a way to indicate that clients should redirect their traffic elsewhere. */ public void enterTerminalState() { - healthService.enterTerminalState(); + delegate.enterTerminalState(); } } diff --git a/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider b/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider index bbc2a4902df..d78ce9b58a2 100644 --- a/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider +++ b/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider @@ -1 +1 @@ -io.grpc.services.internal.HealthCheckingRoundRobinLoadBalancerProvider +io.grpc.protobuf.services.internal.HealthCheckingRoundRobinLoadBalancerProvider \ No newline at end of file diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java b/services/src/test/java/io/grpc/protobuf/services/BinaryLogProviderImplTest.java similarity index 98% rename from services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java rename to services/src/test/java/io/grpc/protobuf/services/BinaryLogProviderImplTest.java index 238f5fbc0ef..cab7dcf05e5 100644 --- a/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/BinaryLogProviderImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java b/services/src/test/java/io/grpc/protobuf/services/BinaryLogProviderTest.java similarity index 99% rename from services/src/test/java/io/grpc/services/BinaryLogProviderTest.java rename to services/src/test/java/io/grpc/protobuf/services/BinaryLogProviderTest.java index 6905cbb603d..53f361c83ec 100644 --- a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/BinaryLogProviderTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.base.Charsets.UTF_8; import static com.google.common.truth.Truth.assertThat; diff --git a/services/src/test/java/io/grpc/services/BinlogHelperTest.java b/services/src/test/java/io/grpc/protobuf/services/BinlogHelperTest.java similarity index 99% rename from services/src/test/java/io/grpc/services/BinlogHelperTest.java rename to services/src/test/java/io/grpc/protobuf/services/BinlogHelperTest.java index f7d0d40726c..15c1444fa42 100644 --- a/services/src/test/java/io/grpc/services/BinlogHelperTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/BinlogHelperTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.truth.Truth.assertThat; -import static io.grpc.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; -import static io.grpc.services.BinlogHelper.createMetadataProto; -import static io.grpc.services.BinlogHelper.getPeerSocket; +import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; +import static io.grpc.protobuf.services.BinlogHelper.createMetadataProto; +import static io.grpc.protobuf.services.BinlogHelper.getPeerSocket; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -70,11 +70,11 @@ import io.grpc.internal.NoopClientCall; import io.grpc.internal.NoopServerCall; import io.grpc.protobuf.StatusProto; -import io.grpc.services.BinlogHelper.FactoryImpl; -import io.grpc.services.BinlogHelper.MaybeTruncated; -import io.grpc.services.BinlogHelper.SinkWriter; -import io.grpc.services.BinlogHelper.SinkWriterImpl; -import io.grpc.services.BinlogHelper.TimeProvider; +import io.grpc.protobuf.services.BinlogHelper.FactoryImpl; +import io.grpc.protobuf.services.BinlogHelper.MaybeTruncated; +import io.grpc.protobuf.services.BinlogHelper.SinkWriter; +import io.grpc.protobuf.services.BinlogHelper.SinkWriterImpl; +import io.grpc.protobuf.services.BinlogHelper.TimeProvider; import io.netty.channel.unix.DomainSocketAddress; import java.net.InetAddress; import java.net.InetSocketAddress; diff --git a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java b/services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java similarity index 99% rename from services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java rename to services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java index 45dd8056ddf..4098885fd0d 100644 --- a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.truth.Truth.assertThat; import static io.grpc.InternalChannelz.id; @@ -74,10 +74,10 @@ import io.grpc.channelz.v1.SocketRef; import io.grpc.channelz.v1.Subchannel; import io.grpc.channelz.v1.SubchannelRef; -import io.grpc.services.ChannelzTestHelper.TestChannel; -import io.grpc.services.ChannelzTestHelper.TestListenSocket; -import io.grpc.services.ChannelzTestHelper.TestServer; -import io.grpc.services.ChannelzTestHelper.TestSocket; +import io.grpc.protobuf.services.ChannelzTestHelper.TestChannel; +import io.grpc.protobuf.services.ChannelzTestHelper.TestListenSocket; +import io.grpc.protobuf.services.ChannelzTestHelper.TestServer; +import io.grpc.protobuf.services.ChannelzTestHelper.TestSocket; import io.netty.channel.unix.DomainSocketAddress; import java.net.Inet4Address; import java.net.InetSocketAddress; diff --git a/services/src/test/java/io/grpc/services/ChannelzServiceTest.java b/services/src/test/java/io/grpc/protobuf/services/ChannelzServiceTest.java similarity index 97% rename from services/src/test/java/io/grpc/services/ChannelzServiceTest.java rename to services/src/test/java/io/grpc/protobuf/services/ChannelzServiceTest.java index 54d84358bdd..bb520a16dec 100644 --- a/services/src/test/java/io/grpc/services/ChannelzServiceTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/ChannelzServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; @@ -35,9 +35,9 @@ import io.grpc.channelz.v1.GetSubchannelResponse; import io.grpc.channelz.v1.GetTopChannelsRequest; import io.grpc.channelz.v1.GetTopChannelsResponse; -import io.grpc.services.ChannelzTestHelper.TestChannel; -import io.grpc.services.ChannelzTestHelper.TestServer; -import io.grpc.services.ChannelzTestHelper.TestSocket; +import io.grpc.protobuf.services.ChannelzTestHelper.TestChannel; +import io.grpc.protobuf.services.ChannelzTestHelper.TestServer; +import io.grpc.protobuf.services.ChannelzTestHelper.TestSocket; import io.grpc.stub.StreamObserver; import java.util.concurrent.ExecutionException; import org.junit.Test; diff --git a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java b/services/src/test/java/io/grpc/protobuf/services/ChannelzTestHelper.java similarity index 99% rename from services/src/test/java/io/grpc/services/ChannelzTestHelper.java rename to services/src/test/java/io/grpc/protobuf/services/ChannelzTestHelper.java index 6bd8e8bceb5..0b93a8d9271 100644 --- a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java +++ b/services/src/test/java/io/grpc/protobuf/services/ChannelzTestHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.ListenableFuture; diff --git a/services/src/test/java/io/grpc/services/HealthCheckingLoadBalancerFactoryTest.java b/services/src/test/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerFactoryTest.java similarity index 99% rename from services/src/test/java/io/grpc/services/HealthCheckingLoadBalancerFactoryTest.java rename to services/src/test/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerFactoryTest.java index fc9b3f62ecf..c1e11941a4b 100644 --- a/services/src/test/java/io/grpc/services/HealthCheckingLoadBalancerFactoryTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/HealthCheckingLoadBalancerFactoryTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; @@ -68,7 +68,7 @@ import io.grpc.internal.BackoffPolicy; import io.grpc.internal.FakeClock; import io.grpc.internal.ServiceConfigUtil; -import io.grpc.services.HealthCheckingLoadBalancerFactory.SubchannelImpl; +import io.grpc.protobuf.services.HealthCheckingLoadBalancerFactory.SubchannelImpl; import io.grpc.stub.StreamObserver; import java.net.SocketAddress; import java.text.MessageFormat; diff --git a/services/src/test/java/io/grpc/services/HealthStatusManagerTest.java b/services/src/test/java/io/grpc/protobuf/services/HealthStatusManagerTest.java similarity index 99% rename from services/src/test/java/io/grpc/services/HealthStatusManagerTest.java rename to services/src/test/java/io/grpc/protobuf/services/HealthStatusManagerTest.java index fe04419656f..5c5b30f336d 100644 --- a/services/src/test/java/io/grpc/services/HealthStatusManagerTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/HealthStatusManagerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; diff --git a/services/src/test/java/io/grpc/services/TempFileSinkTest.java b/services/src/test/java/io/grpc/protobuf/services/TempFileSinkTest.java similarity index 94% rename from services/src/test/java/io/grpc/services/TempFileSinkTest.java rename to services/src/test/java/io/grpc/protobuf/services/TempFileSinkTest.java index f47f10569d6..e3b662a8588 100644 --- a/services/src/test/java/io/grpc/services/TempFileSinkTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/TempFileSinkTest.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package io.grpc.services; +package io.grpc.protobuf.services; import static org.junit.Assert.assertEquals; import io.grpc.binarylog.v1.GrpcLogEntry; +import io.grpc.protobuf.services.TempFileSink; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; @@ -27,7 +28,7 @@ import org.junit.runners.JUnit4; /** - * Tests for {@link io.grpc.services.TempFileSink}. + * Tests for {@link TempFileSink}. */ @RunWith(JUnit4.class) public class TempFileSinkTest { diff --git a/services/src/test/java/io/grpc/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java b/services/src/test/java/io/grpc/protobuf/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java similarity index 94% rename from services/src/test/java/io/grpc/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java rename to services/src/test/java/io/grpc/protobuf/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java index 1c122967d9a..470670f2060 100644 --- a/services/src/test/java/io/grpc/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/internal/HealthCheckingRoundRobinLoadBalancerProviderTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.services.internal; +package io.grpc.protobuf.services.internal; import static com.google.common.truth.Truth.assertThat; @@ -25,7 +25,7 @@ import org.junit.runners.JUnit4; /** - * Tests for {@link InternalHealthCheckingRoundRobinLoadBalancerProvider}. + * Tests for {@link HealthCheckingRoundRobinLoadBalancerProvider}. */ @RunWith(JUnit4.class) public class HealthCheckingRoundRobinLoadBalancerProviderTest { diff --git a/services/src/test/java/io/grpc/services/CallMetricRecorderTest.java b/services/src/test/java/io/grpc/services/CallMetricRecorderTest.java index db6b294bf3a..fe7a9c54df8 100644 --- a/services/src/test/java/io/grpc/services/CallMetricRecorderTest.java +++ b/services/src/test/java/io/grpc/services/CallMetricRecorderTest.java @@ -24,7 +24,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Tests for {@link BinlogHelper}. */ +/** Tests for {@link CallMetricRecorder}. */ @RunWith(JUnit4.class) public class CallMetricRecorderTest { From 2f5eb301f4e7d44f192465d2e01c2d945f806b2c Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 6 Apr 2021 19:14:12 -0700 Subject: [PATCH 02/20] Migrate xds interop server's usage for HealthStatusManager. --- .../main/java/io/grpc/testing/integration/XdsTestServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java index c8f94bff074..8b46e849c92 100644 --- a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java +++ b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java @@ -30,9 +30,9 @@ import io.grpc.StatusRuntimeException; import io.grpc.health.v1.HealthCheckResponse.ServingStatus; import io.grpc.netty.NettyServerBuilder; +import io.grpc.protobuf.services.HealthStatusManager; import io.grpc.protobuf.services.ProtoReflectionService; import io.grpc.services.AdminInterface; -import io.grpc.services.HealthStatusManager; import io.grpc.stub.StreamObserver; import io.grpc.testing.integration.Messages.SimpleRequest; import io.grpc.testing.integration.Messages.SimpleResponse; From 04361179dbedb719b62de5b50cbf65e7a6fe216a Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 6 Apr 2021 19:22:43 -0700 Subject: [PATCH 03/20] Creata a forwarding class for HealthCheckingLoadBalancerUtil to allow existing users migrate. --- .../HealthCheckingLoadBalancerUtil.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java diff --git a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java b/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java new file mode 100644 index 00000000000..75373f0a810 --- /dev/null +++ b/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import io.grpc.ExperimentalApi; +import io.grpc.LoadBalancer; +import io.grpc.LoadBalancer.Helper; + +/** + * Utility for enabling + * + * client-side health checking for {@link LoadBalancer}s. + * + * @deprecated Use {@link io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil} instead. + */ +@Deprecated +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5025") +public class HealthCheckingLoadBalancerUtil { + private HealthCheckingLoadBalancerUtil() { + } + + /** + * Creates a health-checking-capable LoadBalancer. This method is used to implement + * health-checking-capable {@link io.grpc.LoadBalancer.Factory}s, which will typically written + * this way: + * + *

+   * public class HealthCheckingFooLbFactory extends LoadBalancer.Factory {
+   *   // This is the original balancer implementation that doesn't have health checking
+   *   private final LoadBalancer.Factory fooLbFactory;
+   *
+   *   ...
+   *
+   *   // Returns the health-checking-capable version of FooLb
+   *   public LoadBalancer newLoadBalancer(Helper helper) {
+   *     return HealthCheckingLoadBalancerUtil.newHealthCheckingLoadBalancer(fooLbFactory, helper);
+   *   }
+   * }
+   * 
+ * + *

As a requirement for the original LoadBalancer, it must call + * {@code Helper.createSubchannel()} from the {@link Helper#getSynchronizationContext() + * Synchronization Context}, or {@code createSubchannel()} will throw. + * + * @param factory the original factory that implements load-balancing logic without health + * checking + * @param helper the helper passed to the resulting health-checking LoadBalancer. + */ + public static LoadBalancer newHealthCheckingLoadBalancer( + LoadBalancer.Factory factory, Helper helper) { + return io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil.newHealthCheckingLoadBalancer( + factory, helper); + } +} From d8412abbd131ee5446ceda3b643f20551b766ac5 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 13 Apr 2021 11:56:57 -0700 Subject: [PATCH 04/20] Keep channelz and binlog implementations in-place, while mark them as deprecated and suggest users to migrate new ones in io.grpc.protobuf.services --- .../grpc/services/BinaryLogProviderImpl.java | 93 ++ .../java/io/grpc/services/BinaryLogSink.java | 35 + .../java/io/grpc/services/BinaryLogs.java | 57 ++ .../java/io/grpc/services/BinlogHelper.java | 880 ++++++++++++++++++ .../io/grpc/services/ChannelzProtoUtil.java | 471 ++++++++++ .../io/grpc/services/ChannelzService.java | 237 +++++ .../io/grpc/services/InetAddressUtil.java | 94 ++ .../java/io/grpc/services/TempFileSink.java | 84 ++ 8 files changed, 1951 insertions(+) create mode 100644 services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java create mode 100644 services/src/main/java/io/grpc/services/BinaryLogSink.java create mode 100644 services/src/main/java/io/grpc/services/BinaryLogs.java create mode 100644 services/src/main/java/io/grpc/services/BinlogHelper.java create mode 100644 services/src/main/java/io/grpc/services/ChannelzProtoUtil.java create mode 100644 services/src/main/java/io/grpc/services/ChannelzService.java create mode 100644 services/src/main/java/io/grpc/services/InetAddressUtil.java create mode 100644 services/src/main/java/io/grpc/services/TempFileSink.java diff --git a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java new file mode 100644 index 00000000000..5c09f8a3ddd --- /dev/null +++ b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import com.google.common.base.Preconditions; +import io.grpc.CallOptions; +import io.grpc.ClientInterceptor; +import io.grpc.ServerInterceptor; +import io.grpc.protobuf.services.BinaryLogProvider; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Nullable; + +/** + * The default implementation of a {@link BinaryLogProvider}. + */ +class BinaryLogProviderImpl extends BinaryLogProvider { + // avoid using 0 because proto3 long fields default to 0 when unset + private static final AtomicLong counter = new AtomicLong(1); + + private final BinlogHelper.Factory factory; + private final BinaryLogSink sink; + + public BinaryLogProviderImpl() throws IOException { + this(new TempFileSink(), System.getenv("GRPC_BINARY_LOG_CONFIG")); + } + + /** + * Deprecated and will be removed in a future version of gRPC. + */ + @Deprecated + public BinaryLogProviderImpl(BinaryLogSink sink) throws IOException { + this(sink, System.getenv("GRPC_BINARY_LOG_CONFIG")); + } + + /** + * Creates an instance. + * @param sink ownership is transferred to this class. + * @param configStr config string to parse to determine logged methods and msg size limits. + * @throws IOException if initialization failed. + */ + public BinaryLogProviderImpl(BinaryLogSink sink, String configStr) throws IOException { + this.sink = Preconditions.checkNotNull(sink); + try { + factory = new BinlogHelper.FactoryImpl(sink, configStr); + } catch (RuntimeException e) { + sink.close(); + // parsing the conf string may throw if it is blank or contains errors + throw new IOException( + "Can not initialize. The env variable GRPC_BINARY_LOG_CONFIG must be valid.", e); + } + } + + @Nullable + @Override + public ServerInterceptor getServerInterceptor(String fullMethodName) { + BinlogHelper helperForMethod = factory.getLog(fullMethodName); + if (helperForMethod == null) { + return null; + } + return helperForMethod.getServerInterceptor(counter.getAndIncrement()); + } + + @Nullable + @Override + public ClientInterceptor getClientInterceptor( + String fullMethodName, CallOptions callOptions) { + BinlogHelper helperForMethod = factory.getLog(fullMethodName); + if (helperForMethod == null) { + return null; + } + return helperForMethod.getClientInterceptor(counter.getAndIncrement()); + } + + @Override + public void close() throws IOException { + sink.close(); + } +} diff --git a/services/src/main/java/io/grpc/services/BinaryLogSink.java b/services/src/main/java/io/grpc/services/BinaryLogSink.java new file mode 100644 index 00000000000..af3204eddff --- /dev/null +++ b/services/src/main/java/io/grpc/services/BinaryLogSink.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import com.google.protobuf.MessageLite; +import io.grpc.ExperimentalApi; +import java.io.Closeable; + +/** + * A class that accepts binary log messages. + * + * @deprecated Use {@link io.grpc.protobuf.services.BinaryLogSink} instead. + */ +@Deprecated +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") +public interface BinaryLogSink extends Closeable { + /** + * Writes the {@code message} to the destination. + */ + void write(MessageLite message); +} diff --git a/services/src/main/java/io/grpc/services/BinaryLogs.java b/services/src/main/java/io/grpc/services/BinaryLogs.java new file mode 100644 index 00000000000..0c4f8b5c7dc --- /dev/null +++ b/services/src/main/java/io/grpc/services/BinaryLogs.java @@ -0,0 +1,57 @@ +/* + * Copyright 2018, gRPC Authors All rights reserved. + * + * 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. + */ + +package io.grpc.services; + +import io.grpc.BinaryLog; +import io.grpc.ExperimentalApi; +import java.io.IOException; + +/** + * @deprecated Use {@link io.grpc.protobuf.services.BinaryLogs} instead. + */ +@Deprecated +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") +public final class BinaryLogs { + /** + * Creates a binary log that writes to a temp file. Warning: this implementation is + * not performance optimized, and RPCs will experience back pressure if disk IO does not keep + * up. + */ + public static BinaryLog createBinaryLog() throws IOException { + return new BinaryLogProviderImpl(); + } + + /** + * Deprecated and will be removed in a future version of gRPC. + */ + @Deprecated + public static BinaryLog createBinaryLog(BinaryLogSink sink) throws IOException { + return new BinaryLogProviderImpl(sink); + } + + /** + * Creates a binary log with a custom {@link BinaryLogSink} for receiving the logged data, + * and a config string as defined by + * + * A16-binary-logging. + */ + public static BinaryLog createBinaryLog(BinaryLogSink sink, String configStr) throws IOException { + return new BinaryLogProviderImpl(sink, configStr); + } + + private BinaryLogs() {} +} diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/services/BinlogHelper.java new file mode 100644 index 00000000000..ba3eef430c1 --- /dev/null +++ b/services/src/main/java/io/grpc/services/BinlogHelper.java @@ -0,0 +1,880 @@ +/* + * Copyright 2017 The 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. + */ + +package io.grpc.services; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.protobuf.ByteString; +import com.google.protobuf.Duration; +import com.google.protobuf.util.Durations; +import com.google.protobuf.util.Timestamps; +import io.grpc.Attributes; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.Context; +import io.grpc.Deadline; +import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; +import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; +import io.grpc.ForwardingServerCall.SimpleForwardingServerCall; +import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener; +import io.grpc.Grpc; +import io.grpc.InternalMetadata; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.Marshaller; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.Status; +import io.grpc.binarylog.v1.Address; +import io.grpc.binarylog.v1.GrpcLogEntry; +import io.grpc.binarylog.v1.GrpcLogEntry.EventType; +import io.grpc.binarylog.v1.Message; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +/** + * A binary log class that is configured for a specific {@link MethodDescriptor}. + */ +@ThreadSafe +final class BinlogHelper { + private static final Logger logger = Logger.getLogger(BinlogHelper.class.getName()); + // Normally 'grpc-' metadata keys are set from within gRPC, and applications are not allowed + // to set them. This key is a special well known key that set from the application layer, but + // represents a com.google.rpc.Status and is given special first class treatment. + // See StatusProto.java + static final Metadata.Key STATUS_DETAILS_KEY = + Metadata.Key.of( + "grpc-status-details-bin", + Metadata.BINARY_BYTE_MARSHALLER); + + @VisibleForTesting + final SinkWriter writer; + + @VisibleForTesting + BinlogHelper(SinkWriter writer) { + this.writer = writer; + } + + // TODO(zpencer): move proto related static helpers into this class + static final class SinkWriterImpl extends SinkWriter { + private final BinaryLogSink sink; + private TimeProvider timeProvider; + private final int maxHeaderBytes; + private final int maxMessageBytes; + + SinkWriterImpl( + BinaryLogSink sink, + TimeProvider timeProvider, + int maxHeaderBytes, + int maxMessageBytes) { + this.sink = sink; + this.timeProvider = timeProvider; + this.maxHeaderBytes = maxHeaderBytes; + this.maxMessageBytes = maxMessageBytes; + } + + GrpcLogEntry.Builder newTimestampedBuilder() { + long epochNanos = timeProvider.currentTimeNanos(); + return GrpcLogEntry.newBuilder().setTimestamp(Timestamps.fromNanos(epochNanos)); + } + + @Override + void logClientHeader( + long seq, + String methodName, + // not all transports have the concept of authority + @Nullable String authority, + @Nullable Duration timeout, + Metadata metadata, + GrpcLogEntry.Logger logger, + long callId, + // null on client side + @Nullable SocketAddress peerAddress) { + Preconditions.checkArgument(methodName != null, "methodName can not be null"); + Preconditions.checkArgument( + !methodName.startsWith("/"), + "in grpc-java method names should not have a leading '/'. However this class will " + + "add one to be consistent with language agnostic conventions."); + Preconditions.checkArgument( + peerAddress == null || logger == GrpcLogEntry.Logger.LOGGER_SERVER, + "peerSocket can only be specified for server"); + + MaybeTruncated pair + = createMetadataProto(metadata, maxHeaderBytes); + io.grpc.binarylog.v1.ClientHeader.Builder clientHeaderBuilder + = io.grpc.binarylog.v1.ClientHeader.newBuilder() + .setMetadata(pair.proto) + .setMethodName("/" + methodName); + if (timeout != null) { + clientHeaderBuilder.setTimeout(timeout); + } + if (authority != null) { + clientHeaderBuilder.setAuthority(authority); + } + + GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() + .setSequenceIdWithinCall(seq) + .setType(EventType.EVENT_TYPE_CLIENT_HEADER) + .setClientHeader(clientHeaderBuilder) + .setPayloadTruncated(pair.truncated) + .setLogger(logger) + .setCallId(callId); + if (peerAddress != null) { + entryBuilder.setPeer(socketToProto(peerAddress)); + } + sink.write(entryBuilder.build()); + } + + @Override + void logServerHeader( + long seq, + Metadata metadata, + GrpcLogEntry.Logger logger, + long callId, + // null on server + @Nullable SocketAddress peerAddress) { + Preconditions.checkArgument( + peerAddress == null || logger == GrpcLogEntry.Logger.LOGGER_CLIENT, + "peerSocket can only be specified for client"); + MaybeTruncated pair + = createMetadataProto(metadata, maxHeaderBytes); + + GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() + .setSequenceIdWithinCall(seq) + .setType(EventType.EVENT_TYPE_SERVER_HEADER) + .setServerHeader( + io.grpc.binarylog.v1.ServerHeader.newBuilder() + .setMetadata(pair.proto)) + .setPayloadTruncated(pair.truncated) + .setLogger(logger) + .setCallId(callId); + if (peerAddress != null) { + entryBuilder.setPeer(socketToProto(peerAddress)); + } + sink.write(entryBuilder.build()); + } + + @Override + void logTrailer( + long seq, + Status status, + Metadata metadata, + GrpcLogEntry.Logger logger, + long callId, + // null on server, can be non null on client if this is a trailer-only response + @Nullable SocketAddress peerAddress) { + Preconditions.checkArgument( + peerAddress == null || logger == GrpcLogEntry.Logger.LOGGER_CLIENT, + "peerSocket can only be specified for client"); + MaybeTruncated pair + = createMetadataProto(metadata, maxHeaderBytes); + + io.grpc.binarylog.v1.Trailer.Builder trailerBuilder + = io.grpc.binarylog.v1.Trailer.newBuilder() + .setStatusCode(status.getCode().value()) + .setMetadata(pair.proto); + String statusDescription = status.getDescription(); + if (statusDescription != null) { + trailerBuilder.setStatusMessage(statusDescription); + } + byte[] statusDetailBytes = metadata.get(STATUS_DETAILS_KEY); + if (statusDetailBytes != null) { + trailerBuilder.setStatusDetails(ByteString.copyFrom(statusDetailBytes)); + } + + GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() + .setSequenceIdWithinCall(seq) + .setType(EventType.EVENT_TYPE_SERVER_TRAILER) + .setTrailer(trailerBuilder) + .setPayloadTruncated(pair.truncated) + .setLogger(logger) + .setCallId(callId); + if (peerAddress != null) { + entryBuilder.setPeer(socketToProto(peerAddress)); + } + sink.write(entryBuilder.build()); + } + + @Override + void logRpcMessage( + long seq, + EventType eventType, + Marshaller marshaller, + T message, + GrpcLogEntry.Logger logger, + long callId) { + Preconditions.checkArgument( + eventType == EventType.EVENT_TYPE_CLIENT_MESSAGE + || eventType == EventType.EVENT_TYPE_SERVER_MESSAGE, + "event type must correspond to client message or server message"); + if (marshaller != BYTEARRAY_MARSHALLER) { + throw new IllegalStateException("Expected the BinaryLog's ByteArrayMarshaller"); + } + MaybeTruncated pair = createMessageProto((byte[]) message, maxMessageBytes); + GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() + .setSequenceIdWithinCall(seq) + .setType(eventType) + .setMessage(pair.proto) + .setPayloadTruncated(pair.truncated) + .setLogger(logger) + .setCallId(callId); + sink.write(entryBuilder.build()); + } + + @Override + void logHalfClose(long seq, GrpcLogEntry.Logger logger, long callId) { + sink.write( + newTimestampedBuilder() + .setSequenceIdWithinCall(seq) + .setType(EventType.EVENT_TYPE_CLIENT_HALF_CLOSE) + .setLogger(logger) + .setCallId(callId) + .build()); + } + + @Override + void logCancel(long seq, GrpcLogEntry.Logger logger, long callId) { + sink.write( + newTimestampedBuilder() + .setSequenceIdWithinCall(seq) + .setType(EventType.EVENT_TYPE_CANCEL) + .setLogger(logger) + .setCallId(callId) + .build()); + } + + @Override + int getMaxHeaderBytes() { + return maxHeaderBytes; + } + + @Override + int getMaxMessageBytes() { + return maxMessageBytes; + } + } + + abstract static class SinkWriter { + /** + * Logs the client header. This method logs the appropriate number of bytes + * as determined by the binary logging configuration. + */ + abstract void logClientHeader( + long seq, + String methodName, + // not all transports have the concept of authority + @Nullable String authority, + @Nullable Duration timeout, + Metadata metadata, + GrpcLogEntry.Logger logger, + long callId, + // null on client side + @Nullable SocketAddress peerAddress); + + /** + * Logs the server header. This method logs the appropriate number of bytes + * as determined by the binary logging configuration. + */ + abstract void logServerHeader( + long seq, + Metadata metadata, + GrpcLogEntry.Logger logger, + long callId, + // null on server + @Nullable SocketAddress peerAddress); + + /** + * Logs the server trailer. This method logs the appropriate number of bytes + * as determined by the binary logging configuration. + */ + abstract void logTrailer( + long seq, + Status status, + Metadata metadata, + GrpcLogEntry.Logger logger, + long callId, + // null on server, can be non null on client if this is a trailer-only response + @Nullable SocketAddress peerAddress); + + /** + * Logs the message message. The number of bytes logged is determined by the binary + * logging configuration. + */ + abstract void logRpcMessage( + long seq, + EventType eventType, + Marshaller marshaller, + T message, + GrpcLogEntry.Logger logger, + long callId); + + abstract void logHalfClose(long seq, GrpcLogEntry.Logger logger, long callId); + + /** + * Logs the cancellation. + */ + abstract void logCancel(long seq, GrpcLogEntry.Logger logger, long callId); + + /** + * Returns the number bytes of the header this writer will log, according to configuration. + */ + abstract int getMaxHeaderBytes(); + + /** + * Returns the number bytes of the message this writer will log, according to configuration. + */ + abstract int getMaxMessageBytes(); + } + + static SocketAddress getPeerSocket(Attributes streamAttributes) { + return streamAttributes.get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR); + } + + private static Deadline min(@Nullable Deadline deadline0, @Nullable Deadline deadline1) { + if (deadline0 == null) { + return deadline1; + } + if (deadline1 == null) { + return deadline0; + } + return deadline0.minimum(deadline1); + } + + interface TimeProvider { + /** Returns the current nano time. */ + long currentTimeNanos(); + + TimeProvider SYSTEM_TIME_PROVIDER = new TimeProvider() { + @Override + public long currentTimeNanos() { + return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()); + } + }; + } + + + public ClientInterceptor getClientInterceptor(final long callId) { + return new ClientInterceptor() { + boolean trailersOnlyResponse = true; + @Override + public ClientCall interceptCall( + final MethodDescriptor method, CallOptions callOptions, Channel next) { + final AtomicLong seq = new AtomicLong(1); + final String methodName = method.getFullMethodName(); + final String authority = next.authority(); + // The timeout should reflect the time remaining when the call is started, so do not + // compute remaining time here. + final Deadline deadline = min(callOptions.getDeadline(), Context.current().getDeadline()); + + return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { + @Override + public void start(final ClientCall.Listener responseListener, Metadata headers) { + final Duration timeout = deadline == null ? null + : Durations.fromNanos(deadline.timeRemaining(TimeUnit.NANOSECONDS)); + writer.logClientHeader( + seq.getAndIncrement(), + methodName, + authority, + timeout, + headers, + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId, + /*peerAddress=*/ null); + ClientCall.Listener wListener = + new SimpleForwardingClientCallListener(responseListener) { + @Override + public void onMessage(RespT message) { + writer.logRpcMessage( + seq.getAndIncrement(), + EventType.EVENT_TYPE_SERVER_MESSAGE, + method.getResponseMarshaller(), + message, + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId); + super.onMessage(message); + } + + @Override + public void onHeaders(Metadata headers) { + trailersOnlyResponse = false; + writer.logServerHeader( + seq.getAndIncrement(), + headers, + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId, + getPeerSocket(getAttributes())); + super.onHeaders(headers); + } + + @Override + public void onClose(Status status, Metadata trailers) { + SocketAddress peer = trailersOnlyResponse + ? getPeerSocket(getAttributes()) : null; + writer.logTrailer( + seq.getAndIncrement(), + status, + trailers, + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId, + peer); + super.onClose(status, trailers); + } + }; + super.start(wListener, headers); + } + + @Override + public void sendMessage(ReqT message) { + writer.logRpcMessage( + seq.getAndIncrement(), + EventType.EVENT_TYPE_CLIENT_MESSAGE, + method.getRequestMarshaller(), + message, + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId); + super.sendMessage(message); + } + + @Override + public void halfClose() { + writer.logHalfClose( + seq.getAndIncrement(), + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId); + super.halfClose(); + } + + @Override + public void cancel(String message, Throwable cause) { + writer.logCancel( + seq.getAndIncrement(), + GrpcLogEntry.Logger.LOGGER_CLIENT, + callId); + super.cancel(message, cause); + } + }; + } + }; + } + + public ServerInterceptor getServerInterceptor(final long callId) { + return new ServerInterceptor() { + @Override + public ServerCall.Listener interceptCall( + final ServerCall call, + Metadata headers, + ServerCallHandler next) { + final AtomicLong seq = new AtomicLong(1); + SocketAddress peer = getPeerSocket(call.getAttributes()); + String methodName = call.getMethodDescriptor().getFullMethodName(); + String authority = call.getAuthority(); + Deadline deadline = Context.current().getDeadline(); + final Duration timeout = deadline == null ? null + : Durations.fromNanos(deadline.timeRemaining(TimeUnit.NANOSECONDS)); + + writer.logClientHeader( + seq.getAndIncrement(), + methodName, + authority, + timeout, + headers, + GrpcLogEntry.Logger.LOGGER_SERVER, + callId, + peer); + ServerCall wCall = new SimpleForwardingServerCall(call) { + @Override + public void sendMessage(RespT message) { + writer.logRpcMessage( + seq.getAndIncrement(), + EventType.EVENT_TYPE_SERVER_MESSAGE, + call.getMethodDescriptor().getResponseMarshaller(), + message, + GrpcLogEntry.Logger.LOGGER_SERVER, + callId); + super.sendMessage(message); + } + + @Override + public void sendHeaders(Metadata headers) { + writer.logServerHeader( + seq.getAndIncrement(), + headers, + GrpcLogEntry.Logger.LOGGER_SERVER, + callId, + /*peerAddress=*/ null); + super.sendHeaders(headers); + } + + @Override + public void close(Status status, Metadata trailers) { + writer.logTrailer( + seq.getAndIncrement(), + status, + trailers, + GrpcLogEntry.Logger.LOGGER_SERVER, + callId, + /*peerAddress=*/ null); + super.close(status, trailers); + } + }; + + return new SimpleForwardingServerCallListener(next.startCall(wCall, headers)) { + @Override + public void onMessage(ReqT message) { + writer.logRpcMessage( + seq.getAndIncrement(), + EventType.EVENT_TYPE_CLIENT_MESSAGE, + call.getMethodDescriptor().getRequestMarshaller(), + message, + GrpcLogEntry.Logger.LOGGER_SERVER, + callId); + super.onMessage(message); + } + + @Override + public void onHalfClose() { + writer.logHalfClose( + seq.getAndIncrement(), + GrpcLogEntry.Logger.LOGGER_SERVER, + callId); + super.onHalfClose(); + } + + @Override + public void onCancel() { + writer.logCancel( + seq.getAndIncrement(), + GrpcLogEntry.Logger.LOGGER_SERVER, + callId); + super.onCancel(); + } + }; + } + }; + } + + interface Factory { + @Nullable + BinlogHelper getLog(String fullMethodName); + } + + static final class FactoryImpl implements Factory { + private final BinlogHelper globalLog; + private final Map perServiceLogs; + private final Map perMethodLogs; + private final Set blacklistedMethods; + + /** + * Accepts a string in the format specified by the binary log spec. + */ + @VisibleForTesting + FactoryImpl(BinaryLogSink sink, String configurationString) { + checkNotNull(sink, "sink"); + BinlogHelper globalLog = null; + Map perServiceLogs = new HashMap<>(); + Map perMethodLogs = new HashMap<>(); + Set blacklistedMethods = new HashSet<>(); + if (configurationString != null && configurationString.length() > 0) { + for (String configuration : Splitter.on(',').split(configurationString)) { + int leftCurly = configuration.indexOf('{'); + // '*' for global, 'service/*' for service glob, or 'service/method' for fully qualified + String methodOrSvc; + // An expression originally wrapped in curly braces; like {m:256,h:256}, {m:256}, {h:256} + String binlogOptionStr; + if (leftCurly == -1) { + methodOrSvc = configuration; + binlogOptionStr = null; + } else { + int rightCurly = configuration.indexOf('}', leftCurly); + if (rightCurly != configuration.length() - 1) { + throw new IllegalArgumentException("Illegal log config pattern: " + configuration); + } + methodOrSvc = configuration.substring(0, leftCurly); + // option without the curly braces + binlogOptionStr = configuration.substring(leftCurly + 1, configuration.length() - 1); + } + if (methodOrSvc.isEmpty()) { + throw new IllegalArgumentException("Illegal log config pattern: " + configuration); + } + if (methodOrSvc.equals("*")) { + // parse config for "*" + checkState( + globalLog == null, + "Duplicate entry, this is fatal: " + configuration); + globalLog = createBinaryLog(sink, binlogOptionStr); + logger.log(Level.INFO, "Global binlog: {0}", binlogOptionStr); + } else if (isServiceGlob(methodOrSvc)) { + // parse config for a service, e.g. "service/*" + String service = MethodDescriptor.extractFullServiceName(methodOrSvc); + checkState( + !perServiceLogs.containsKey(service), + "Duplicate entry, this is fatal: " + configuration); + perServiceLogs.put(service, createBinaryLog(sink, binlogOptionStr)); + logger.log( + Level.INFO, + "Service binlog: service={0} config={1}", + new Object[] {service, binlogOptionStr}); + } else if (methodOrSvc.startsWith("-")) { + // parse config for a method, e.g. "-service/method" + String blacklistedMethod = methodOrSvc.substring(1); + if (blacklistedMethod.length() == 0) { + continue; + } + checkState( + !blacklistedMethods.contains(blacklistedMethod), + "Duplicate entry, this is fatal: " + configuration); + checkState( + !perMethodLogs.containsKey(blacklistedMethod), + "Duplicate entry, this is fatal: " + configuration); + blacklistedMethods.add(blacklistedMethod); + } else { + // parse config for a fully qualified method, e.g "serice/method" + checkState( + !perMethodLogs.containsKey(methodOrSvc), + "Duplicate entry, this is fatal: " + configuration); + checkState( + !blacklistedMethods.contains(methodOrSvc), + "Duplicate entry, this method was blacklisted: " + configuration); + perMethodLogs.put(methodOrSvc, createBinaryLog(sink, binlogOptionStr)); + logger.log( + Level.INFO, + "Method binlog: method={0} config={1}", + new Object[] {methodOrSvc, binlogOptionStr}); + } + } + } + this.globalLog = globalLog; + this.perServiceLogs = Collections.unmodifiableMap(perServiceLogs); + this.perMethodLogs = Collections.unmodifiableMap(perMethodLogs); + this.blacklistedMethods = Collections.unmodifiableSet(blacklistedMethods); + } + + /** + * Accepts a full method name and returns the log that should be used. + */ + @Override + public BinlogHelper getLog(String fullMethodName) { + if (blacklistedMethods.contains(fullMethodName)) { + return null; + } + BinlogHelper methodLog = perMethodLogs.get(fullMethodName); + if (methodLog != null) { + return methodLog; + } + BinlogHelper serviceLog = perServiceLogs.get( + MethodDescriptor.extractFullServiceName(fullMethodName)); + if (serviceLog != null) { + return serviceLog; + } + return globalLog; + } + + /** + * Returns a binlog with the correct header and message limits or {@code null} if the input + * is malformed. The input should be a string that is in one of these forms: + * + *

{@code {h(:\d+)?}, {m(:\d+)?}, {h(:\d+)?,m(:\d+)?}} + * + *

If the {@code logConfig} is null, the returned binlog will have a limit of + * Integer.MAX_VALUE. + */ + @VisibleForTesting + @Nullable + static BinlogHelper createBinaryLog(BinaryLogSink sink, @Nullable String logConfig) { + if (logConfig == null) { + return new BinlogHelper( + new SinkWriterImpl( + sink, TimeProvider.SYSTEM_TIME_PROVIDER, Integer.MAX_VALUE, Integer.MAX_VALUE)); + } + try { + final int maxHeaderBytes; + final int maxMsgBytes; + String[] parts = logConfig.split(";", 2); + if (parts.length == 2) { + if (!(parts[0].startsWith("h") && parts[1].startsWith("m"))) { + throw new IllegalArgumentException("Illegal log config pattern"); + } + maxHeaderBytes = optionalInt(parts[0].substring(1)); + maxMsgBytes = optionalInt(parts[1].substring(1)); + } else if (parts[0].startsWith("h")) { + maxHeaderBytes = optionalInt(parts[0].substring(1)); + maxMsgBytes = 0; + } else if (parts[0].startsWith("m")) { + maxHeaderBytes = 0; + maxMsgBytes = optionalInt(parts[0].substring(1)); + } else { + throw new IllegalArgumentException("Illegal log config pattern"); + } + return new BinlogHelper( + new SinkWriterImpl( + sink, TimeProvider.SYSTEM_TIME_PROVIDER, maxHeaderBytes, maxMsgBytes)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Illegal log config pattern"); + } + } + + /** Returns {@code s}, after verifying it contains only digits. */ + static String checkDigits(String s) { + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c < '0' || '9' < c) { + throw new IllegalArgumentException("Illegal log config pattern"); + } + } + return s; + } + + /** Parses the optional int of the form "" (max int) or ":123" (123). */ + static int optionalInt(String s) { + if (s.isEmpty()) { + return Integer.MAX_VALUE; + } + if (!s.startsWith(":")) { + throw new IllegalArgumentException("Illegal log config pattern"); + } + s = checkDigits(s.substring(1)); + return Integer.parseInt(s); + } + + /** + * Returns true if the input string is a glob of the form: {@code /*}. + */ + static boolean isServiceGlob(String input) { + return input.endsWith("/*"); + } + } + + @VisibleForTesting + static Address socketToProto(SocketAddress address) { + checkNotNull(address, "address"); + + Address.Builder builder = Address.newBuilder(); + if (address instanceof InetSocketAddress) { + InetAddress inetAddress = ((InetSocketAddress) address).getAddress(); + if (inetAddress instanceof Inet4Address) { + builder.setType(Address.Type.TYPE_IPV4) + .setAddress(InetAddressUtil.toAddrString(inetAddress)); + } else if (inetAddress instanceof Inet6Address) { + builder.setType(Address.Type.TYPE_IPV6) + .setAddress(InetAddressUtil.toAddrString(inetAddress)); + } else { + logger.log(Level.SEVERE, "unknown type of InetSocketAddress: {}", address); + builder.setAddress(address.toString()); + } + builder.setIpPort(((InetSocketAddress) address).getPort()); + } else if (address.getClass().getName().equals("io.netty.channel.unix.DomainSocketAddress")) { + // To avoid a compile time dependency on grpc-netty, we check against the runtime class name. + builder.setType(Address.Type.TYPE_UNIX) + .setAddress(address.toString()); + } else { + builder.setType(Address.Type.TYPE_UNKNOWN).setAddress(address.toString()); + } + return builder.build(); + } + + private static final Set NEVER_INCLUDED_METADATA = new HashSet<>( + Collections.singletonList( + // grpc-status-details-bin is already logged in a field of the binlog proto + STATUS_DETAILS_KEY.name())); + private static final Set ALWAYS_INCLUDED_METADATA = new HashSet<>( + Collections.singletonList( + "grpc-trace-bin")); + + static final class MaybeTruncated { + T proto; + boolean truncated; + + private MaybeTruncated(T proto, boolean truncated) { + this.proto = proto; + this.truncated = truncated; + } + } + + @VisibleForTesting + static MaybeTruncated createMetadataProto( + Metadata metadata, int maxHeaderBytes) { + checkNotNull(metadata, "metadata"); + checkArgument(maxHeaderBytes >= 0, "maxHeaderBytes must be non negative"); + io.grpc.binarylog.v1.Metadata.Builder metaBuilder = io.grpc.binarylog.v1.Metadata.newBuilder(); + // This code is tightly coupled with Metadata's implementation + byte[][] serialized = InternalMetadata.serialize(metadata); + boolean truncated = false; + if (serialized != null) { + int curBytes = 0; + for (int i = 0; i < serialized.length; i += 2) { + String key = new String(serialized[i], Charsets.UTF_8); + byte[] value = serialized[i + 1]; + if (NEVER_INCLUDED_METADATA.contains(key)) { + continue; + } + boolean forceInclude = ALWAYS_INCLUDED_METADATA.contains(key); + int bytesAfterAdd = curBytes + key.length() + value.length; + if (!forceInclude && bytesAfterAdd > maxHeaderBytes) { + truncated = true; + continue; + } + metaBuilder.addEntryBuilder() + .setKey(key) + .setValue(ByteString.copyFrom(value)); + if (!forceInclude) { + // force included keys do not count towards the size limit + curBytes = bytesAfterAdd; + } + } + } + return new MaybeTruncated<>(metaBuilder, truncated); + } + + @VisibleForTesting + static MaybeTruncated createMessageProto( + byte[] message, int maxMessageBytes) { + checkNotNull(message, "message"); + checkArgument(maxMessageBytes >= 0, "maxMessageBytes must be non negative"); + Message.Builder msgBuilder = Message + .newBuilder() + .setLength(message.length); + if (maxMessageBytes > 0) { + int desiredBytes = Math.min(maxMessageBytes, message.length); + msgBuilder.setData(ByteString.copyFrom(message, 0, desiredBytes)); + } + return new MaybeTruncated<>(msgBuilder, maxMessageBytes < message.length); + } +} diff --git a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java b/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java new file mode 100644 index 00000000000..349995d9f80 --- /dev/null +++ b/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java @@ -0,0 +1,471 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.protobuf.Any; +import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import com.google.protobuf.util.Durations; +import com.google.protobuf.util.Timestamps; +import io.grpc.ConnectivityState; +import io.grpc.InternalChannelz; +import io.grpc.InternalChannelz.ChannelStats; +import io.grpc.InternalChannelz.ChannelTrace.Event; +import io.grpc.InternalChannelz.RootChannelList; +import io.grpc.InternalChannelz.ServerList; +import io.grpc.InternalChannelz.ServerSocketsList; +import io.grpc.InternalChannelz.ServerStats; +import io.grpc.InternalChannelz.SocketStats; +import io.grpc.InternalChannelz.TransportStats; +import io.grpc.InternalInstrumented; +import io.grpc.InternalWithLogId; +import io.grpc.Status; +import io.grpc.channelz.v1.Address; +import io.grpc.channelz.v1.Address.OtherAddress; +import io.grpc.channelz.v1.Address.TcpIpAddress; +import io.grpc.channelz.v1.Address.UdsAddress; +import io.grpc.channelz.v1.Channel; +import io.grpc.channelz.v1.ChannelConnectivityState; +import io.grpc.channelz.v1.ChannelConnectivityState.State; +import io.grpc.channelz.v1.ChannelData; +import io.grpc.channelz.v1.ChannelRef; +import io.grpc.channelz.v1.ChannelTrace; +import io.grpc.channelz.v1.ChannelTraceEvent; +import io.grpc.channelz.v1.ChannelTraceEvent.Severity; +import io.grpc.channelz.v1.GetServerSocketsResponse; +import io.grpc.channelz.v1.GetServersResponse; +import io.grpc.channelz.v1.GetTopChannelsResponse; +import io.grpc.channelz.v1.Security; +import io.grpc.channelz.v1.Security.OtherSecurity; +import io.grpc.channelz.v1.Security.Tls; +import io.grpc.channelz.v1.Server; +import io.grpc.channelz.v1.ServerData; +import io.grpc.channelz.v1.ServerRef; +import io.grpc.channelz.v1.Socket; +import io.grpc.channelz.v1.SocketData; +import io.grpc.channelz.v1.SocketOption; +import io.grpc.channelz.v1.SocketOptionLinger; +import io.grpc.channelz.v1.SocketOptionTcpInfo; +import io.grpc.channelz.v1.SocketOptionTimeout; +import io.grpc.channelz.v1.SocketRef; +import io.grpc.channelz.v1.Subchannel; +import io.grpc.channelz.v1.SubchannelRef; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.cert.CertificateEncodingException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A static utility class for turning internal data structures into protos. + */ +final class ChannelzProtoUtil { + private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName()); + + private ChannelzProtoUtil() { + // do not instantiate. + } + + static ChannelRef toChannelRef(InternalWithLogId obj) { + return ChannelRef + .newBuilder() + .setChannelId(obj.getLogId().getId()) + .setName(obj.toString()) + .build(); + } + + static SubchannelRef toSubchannelRef(InternalWithLogId obj) { + return SubchannelRef + .newBuilder() + .setSubchannelId(obj.getLogId().getId()) + .setName(obj.toString()) + .build(); + } + + static ServerRef toServerRef(InternalWithLogId obj) { + return ServerRef + .newBuilder() + .setServerId(obj.getLogId().getId()) + .setName(obj.toString()) + .build(); + } + + static SocketRef toSocketRef(InternalWithLogId obj) { + return SocketRef + .newBuilder() + .setSocketId(obj.getLogId().getId()) + .setName(obj.toString()) + .build(); + } + + static Server toServer(InternalInstrumented obj) { + ServerStats stats = getFuture(obj.getStats()); + Server.Builder builder = Server + .newBuilder() + .setRef(toServerRef(obj)) + .setData(toServerData(stats)); + for (InternalInstrumented listenSocket : stats.listenSockets) { + builder.addListenSocket(toSocketRef(listenSocket)); + } + return builder.build(); + } + + static ServerData toServerData(ServerStats stats) { + return ServerData + .newBuilder() + .setCallsStarted(stats.callsStarted) + .setCallsSucceeded(stats.callsSucceeded) + .setCallsFailed(stats.callsFailed) + .setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos)) + .build(); + } + + static Security toSecurity(InternalChannelz.Security security) { + Preconditions.checkNotNull(security); + Preconditions.checkState( + security.tls != null ^ security.other != null, + "one of tls or othersecurity must be non null"); + if (security.tls != null) { + Tls.Builder tlsBuilder + = Tls.newBuilder().setStandardName(security.tls.cipherSuiteStandardName); + try { + if (security.tls.localCert != null) { + tlsBuilder.setLocalCertificate(ByteString.copyFrom( + security.tls.localCert.getEncoded())); + } + if (security.tls.remoteCert != null) { + tlsBuilder.setRemoteCertificate(ByteString.copyFrom( + security.tls.remoteCert.getEncoded())); + } + } catch (CertificateEncodingException e) { + logger.log(Level.FINE, "Caught exception", e); + } + return Security.newBuilder().setTls(tlsBuilder).build(); + } else { + OtherSecurity.Builder builder = OtherSecurity.newBuilder().setName(security.other.name); + if (security.other.any != null) { + builder.setValue((Any) security.other.any); + } + return Security.newBuilder().setOther(builder).build(); + } + } + + static Socket toSocket(InternalInstrumented obj) { + SocketStats socketStats = getFuture(obj.getStats()); + Socket.Builder builder = Socket.newBuilder() + .setRef(toSocketRef(obj)) + .setLocal(toAddress(socketStats.local)); + if (socketStats.security != null) { + builder.setSecurity(toSecurity(socketStats.security)); + } + // listen sockets do not have remote nor data + if (socketStats.remote != null) { + builder.setRemote(toAddress(socketStats.remote)); + } + builder.setData(extractSocketData(socketStats)); + return builder.build(); + } + + static Address toAddress(SocketAddress address) { + Preconditions.checkNotNull(address); + Address.Builder builder = Address.newBuilder(); + if (address instanceof InetSocketAddress) { + InetSocketAddress inetAddress = (InetSocketAddress) address; + builder.setTcpipAddress( + TcpIpAddress + .newBuilder() + .setIpAddress( + ByteString.copyFrom(inetAddress.getAddress().getAddress())) + .setPort(inetAddress.getPort()) + .build()); + } else if (address.getClass().getName().endsWith("io.netty.channel.unix.DomainSocketAddress")) { + builder.setUdsAddress( + UdsAddress + .newBuilder() + .setFilename(address.toString()) // DomainSocketAddress.toString returns filename + .build()); + } else { + builder.setOtherAddress(OtherAddress.newBuilder().setName(address.toString()).build()); + } + return builder.build(); + } + + static SocketData extractSocketData(SocketStats socketStats) { + SocketData.Builder builder = SocketData.newBuilder(); + if (socketStats.data != null) { + TransportStats s = socketStats.data; + builder + .setStreamsStarted(s.streamsStarted) + .setStreamsSucceeded(s.streamsSucceeded) + .setStreamsFailed(s.streamsFailed) + .setMessagesSent(s.messagesSent) + .setMessagesReceived(s.messagesReceived) + .setKeepAlivesSent(s.keepAlivesSent) + .setLastLocalStreamCreatedTimestamp( + Timestamps.fromNanos(s.lastLocalStreamCreatedTimeNanos)) + .setLastRemoteStreamCreatedTimestamp( + Timestamps.fromNanos(s.lastRemoteStreamCreatedTimeNanos)) + .setLastMessageSentTimestamp( + Timestamps.fromNanos(s.lastMessageSentTimeNanos)) + .setLastMessageReceivedTimestamp( + Timestamps.fromNanos(s.lastMessageReceivedTimeNanos)) + .setLocalFlowControlWindow( + Int64Value.of(s.localFlowControlWindow)) + .setRemoteFlowControlWindow( + Int64Value.of(s.remoteFlowControlWindow)); + } + builder.addAllOption(toSocketOptionsList(socketStats.socketOptions)); + return builder.build(); + } + + public static final String SO_LINGER = "SO_LINGER"; + public static final String SO_TIMEOUT = "SO_TIMEOUT"; + public static final String TCP_INFO = "TCP_INFO"; + + static SocketOption toSocketOptionLinger(int lingerSeconds) { + final SocketOptionLinger lingerOpt; + if (lingerSeconds >= 0) { + lingerOpt = SocketOptionLinger + .newBuilder() + .setActive(true) + .setDuration(Durations.fromSeconds(lingerSeconds)) + .build(); + } else { + lingerOpt = SocketOptionLinger.getDefaultInstance(); + } + return SocketOption + .newBuilder() + .setName(SO_LINGER) + .setAdditional(Any.pack(lingerOpt)) + .build(); + } + + static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) { + Preconditions.checkNotNull(name); + return SocketOption + .newBuilder() + .setName(name) + .setAdditional( + Any.pack( + SocketOptionTimeout + .newBuilder() + .setDuration(Durations.fromMillis(timeoutMillis)) + .build())) + .build(); + } + + static SocketOption toSocketOptionTcpInfo(InternalChannelz.TcpInfo i) { + SocketOptionTcpInfo tcpInfo = SocketOptionTcpInfo.newBuilder() + .setTcpiState(i.state) + .setTcpiCaState(i.caState) + .setTcpiRetransmits(i.retransmits) + .setTcpiProbes(i.probes) + .setTcpiBackoff(i.backoff) + .setTcpiOptions(i.options) + .setTcpiSndWscale(i.sndWscale) + .setTcpiRcvWscale(i.rcvWscale) + .setTcpiRto(i.rto) + .setTcpiAto(i.ato) + .setTcpiSndMss(i.sndMss) + .setTcpiRcvMss(i.rcvMss) + .setTcpiUnacked(i.unacked) + .setTcpiSacked(i.sacked) + .setTcpiLost(i.lost) + .setTcpiRetrans(i.retrans) + .setTcpiFackets(i.fackets) + .setTcpiLastDataSent(i.lastDataSent) + .setTcpiLastAckSent(i.lastAckSent) + .setTcpiLastDataRecv(i.lastDataRecv) + .setTcpiLastAckRecv(i.lastAckRecv) + .setTcpiPmtu(i.pmtu) + .setTcpiRcvSsthresh(i.rcvSsthresh) + .setTcpiRtt(i.rtt) + .setTcpiRttvar(i.rttvar) + .setTcpiSndSsthresh(i.sndSsthresh) + .setTcpiSndCwnd(i.sndCwnd) + .setTcpiAdvmss(i.advmss) + .setTcpiReordering(i.reordering) + .build(); + return SocketOption + .newBuilder() + .setName(TCP_INFO) + .setAdditional(Any.pack(tcpInfo)) + .build(); + } + + static SocketOption toSocketOptionAdditional(String name, String value) { + Preconditions.checkNotNull(name); + Preconditions.checkNotNull(value); + return SocketOption.newBuilder().setName(name).setValue(value).build(); + } + + static List toSocketOptionsList(InternalChannelz.SocketOptions options) { + Preconditions.checkNotNull(options); + List ret = new ArrayList<>(); + if (options.lingerSeconds != null) { + ret.add(toSocketOptionLinger(options.lingerSeconds)); + } + if (options.soTimeoutMillis != null) { + ret.add(toSocketOptionTimeout(SO_TIMEOUT, options.soTimeoutMillis)); + } + if (options.tcpInfo != null) { + ret.add(toSocketOptionTcpInfo(options.tcpInfo)); + } + for (Map.Entry entry : options.others.entrySet()) { + ret.add(toSocketOptionAdditional(entry.getKey(), entry.getValue())); + } + return ret; + } + + static Channel toChannel(InternalInstrumented channel) { + ChannelStats stats = getFuture(channel.getStats()); + Channel.Builder channelBuilder = Channel + .newBuilder() + .setRef(toChannelRef(channel)) + .setData(extractChannelData(stats)); + for (InternalWithLogId subchannel : stats.subchannels) { + channelBuilder.addSubchannelRef(toSubchannelRef(subchannel)); + } + + return channelBuilder.build(); + } + + static ChannelData extractChannelData(InternalChannelz.ChannelStats stats) { + ChannelData.Builder builder = ChannelData.newBuilder(); + builder.setTarget(stats.target) + .setState(toChannelConnectivityState(stats.state)) + .setCallsStarted(stats.callsStarted) + .setCallsSucceeded(stats.callsSucceeded) + .setCallsFailed(stats.callsFailed) + .setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos)); + if (stats.channelTrace != null) { + builder.setTrace(toChannelTrace(stats.channelTrace)); + } + return builder.build(); + } + + static ChannelConnectivityState toChannelConnectivityState(ConnectivityState s) { + return ChannelConnectivityState.newBuilder().setState(toState(s)).build(); + } + + private static ChannelTrace toChannelTrace(InternalChannelz.ChannelTrace channelTrace) { + return ChannelTrace.newBuilder() + .setNumEventsLogged(channelTrace.numEventsLogged) + .setCreationTimestamp(Timestamps.fromNanos(channelTrace.creationTimeNanos)) + .addAllEvents(toChannelTraceEvents(channelTrace.events)) + .build(); + } + + private static List toChannelTraceEvents(List events) { + List channelTraceEvents = new ArrayList<>(); + for (Event event : events) { + ChannelTraceEvent.Builder builder = ChannelTraceEvent.newBuilder() + .setDescription(event.description) + .setSeverity(Severity.valueOf(event.severity.name())) + .setTimestamp(Timestamps.fromNanos(event.timestampNanos)); + if (event.channelRef != null) { + builder.setChannelRef(toChannelRef(event.channelRef)); + } + if (event.subchannelRef != null) { + builder.setSubchannelRef(toSubchannelRef(event.subchannelRef)); + } + channelTraceEvents.add(builder.build()); + } + return Collections.unmodifiableList(channelTraceEvents); + } + + static State toState(ConnectivityState state) { + if (state == null) { + return State.UNKNOWN; + } + try { + return Enum.valueOf(State.class, state.name()); + } catch (IllegalArgumentException e) { + return State.UNKNOWN; + } + } + + static Subchannel toSubchannel(InternalInstrumented subchannel) { + ChannelStats stats = getFuture(subchannel.getStats()); + Subchannel.Builder subchannelBuilder = Subchannel + .newBuilder() + .setRef(toSubchannelRef(subchannel)) + .setData(extractChannelData(stats)); + Preconditions.checkState(stats.sockets.isEmpty() || stats.subchannels.isEmpty()); + for (InternalWithLogId childSocket : stats.sockets) { + subchannelBuilder.addSocketRef(toSocketRef(childSocket)); + } + for (InternalWithLogId childSubchannel : stats.subchannels) { + subchannelBuilder.addSubchannelRef(toSubchannelRef(childSubchannel)); + } + return subchannelBuilder.build(); + } + + static GetTopChannelsResponse toGetTopChannelResponse(RootChannelList rootChannels) { + GetTopChannelsResponse.Builder responseBuilder = GetTopChannelsResponse + .newBuilder() + .setEnd(rootChannels.end); + for (InternalInstrumented c : rootChannels.channels) { + responseBuilder.addChannel(ChannelzProtoUtil.toChannel(c)); + } + return responseBuilder.build(); + } + + static GetServersResponse toGetServersResponse(ServerList servers) { + GetServersResponse.Builder responseBuilder = GetServersResponse + .newBuilder() + .setEnd(servers.end); + for (InternalInstrumented s : servers.servers) { + responseBuilder.addServer(ChannelzProtoUtil.toServer(s)); + } + return responseBuilder.build(); + } + + static GetServerSocketsResponse toGetServerSocketsResponse(ServerSocketsList serverSockets) { + GetServerSocketsResponse.Builder responseBuilder = GetServerSocketsResponse + .newBuilder() + .setEnd(serverSockets.end); + for (InternalWithLogId s : serverSockets.sockets) { + responseBuilder.addSocketRef(ChannelzProtoUtil.toSocketRef(s)); + } + return responseBuilder.build(); + } + + private static T getFuture(ListenableFuture future) { + try { + T ret = future.get(); + if (ret == null) { + throw Status.UNIMPLEMENTED + .withDescription("The entity's stats can not be retrieved. " + + "If this is an InProcessTransport this is expected.") + .asRuntimeException(); + } + return ret; + } catch (InterruptedException e) { + throw Status.INTERNAL.withCause(e).asRuntimeException(); + } catch (ExecutionException e) { + throw Status.INTERNAL.withCause(e).asRuntimeException(); + } + } +} diff --git a/services/src/main/java/io/grpc/services/ChannelzService.java b/services/src/main/java/io/grpc/services/ChannelzService.java new file mode 100644 index 00000000000..07e9e7ad035 --- /dev/null +++ b/services/src/main/java/io/grpc/services/ChannelzService.java @@ -0,0 +1,237 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import com.google.common.annotations.VisibleForTesting; +import io.grpc.ExperimentalApi; +import io.grpc.InternalChannelz; +import io.grpc.InternalChannelz.ChannelStats; +import io.grpc.InternalChannelz.ServerList; +import io.grpc.InternalChannelz.ServerSocketsList; +import io.grpc.InternalChannelz.ServerStats; +import io.grpc.InternalChannelz.SocketStats; +import io.grpc.InternalInstrumented; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.channelz.v1.ChannelzGrpc; +import io.grpc.channelz.v1.GetChannelRequest; +import io.grpc.channelz.v1.GetChannelResponse; +import io.grpc.channelz.v1.GetServerRequest; +import io.grpc.channelz.v1.GetServerResponse; +import io.grpc.channelz.v1.GetServerSocketsRequest; +import io.grpc.channelz.v1.GetServerSocketsResponse; +import io.grpc.channelz.v1.GetServersRequest; +import io.grpc.channelz.v1.GetServersResponse; +import io.grpc.channelz.v1.GetSocketRequest; +import io.grpc.channelz.v1.GetSocketResponse; +import io.grpc.channelz.v1.GetSubchannelRequest; +import io.grpc.channelz.v1.GetSubchannelResponse; +import io.grpc.channelz.v1.GetTopChannelsRequest; +import io.grpc.channelz.v1.GetTopChannelsResponse; +import io.grpc.stub.StreamObserver; + +/** + * The channelz service provides stats about a running gRPC process. + * + * @deprecated Use {@link io.grpc.protobuf.services.ChannelzService} instead. + */ +@Deprecated +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4206") +public final class ChannelzService extends ChannelzGrpc.ChannelzImplBase { + private final InternalChannelz channelz; + private final int maxPageSize; + + /** + * Creates an instance. + */ + public static ChannelzService newInstance(int maxPageSize) { + return new ChannelzService(InternalChannelz.instance(), maxPageSize); + } + + @VisibleForTesting + ChannelzService(InternalChannelz channelz, int maxPageSize) { + this.channelz = channelz; + this.maxPageSize = maxPageSize; + } + + /** Returns top level channel aka {@link io.grpc.ManagedChannel}. */ + @Override + public void getTopChannels( + GetTopChannelsRequest request, StreamObserver responseObserver) { + InternalChannelz.RootChannelList rootChannels + = channelz.getRootChannels(request.getStartChannelId(), maxPageSize); + + GetTopChannelsResponse resp; + try { + resp = ChannelzProtoUtil.toGetTopChannelResponse(rootChannels); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } + + /** Returns a top level channel aka {@link io.grpc.ManagedChannel}. */ + @Override + public void getChannel( + GetChannelRequest request, StreamObserver responseObserver) { + InternalInstrumented s = channelz.getRootChannel(request.getChannelId()); + if (s == null) { + responseObserver.onError( + Status.NOT_FOUND.withDescription("Can't find channel " + request.getChannelId()) + .asRuntimeException()); + return; + } + + GetChannelResponse resp; + try { + resp = GetChannelResponse + .newBuilder() + .setChannel(ChannelzProtoUtil.toChannel(s)) + .build(); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } + + /** Returns servers. */ + @Override + public void getServers( + GetServersRequest request, StreamObserver responseObserver) { + ServerList servers = channelz.getServers(request.getStartServerId(), maxPageSize); + + GetServersResponse resp; + try { + resp = ChannelzProtoUtil.toGetServersResponse(servers); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } + + /** Returns a server. */ + @Override + public void getServer( + GetServerRequest request, StreamObserver responseObserver) { + InternalInstrumented s = channelz.getServer(request.getServerId()); + if (s == null) { + responseObserver.onError( + Status.NOT_FOUND.withDescription("Can't find server " + request.getServerId()) + .asRuntimeException()); + return; + } + + GetServerResponse resp; + try { + resp = GetServerResponse + .newBuilder() + .setServer(ChannelzProtoUtil.toServer(s)) + .build(); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } + + /** Returns a subchannel. */ + @Override + public void getSubchannel( + GetSubchannelRequest request, StreamObserver responseObserver) { + InternalInstrumented s = channelz.getSubchannel(request.getSubchannelId()); + if (s == null) { + responseObserver.onError( + Status.NOT_FOUND.withDescription("Can't find subchannel " + request.getSubchannelId()) + .asRuntimeException()); + return; + } + + GetSubchannelResponse resp; + try { + resp = GetSubchannelResponse + .newBuilder() + .setSubchannel(ChannelzProtoUtil.toSubchannel(s)) + .build(); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } + + /** Returns a socket. */ + @Override + public void getSocket( + GetSocketRequest request, StreamObserver responseObserver) { + InternalInstrumented s = channelz.getSocket(request.getSocketId()); + if (s == null) { + responseObserver.onError( + Status.NOT_FOUND.withDescription("Can't find socket " + request.getSocketId()) + .asRuntimeException()); + return; + } + + GetSocketResponse resp; + try { + resp = + GetSocketResponse.newBuilder().setSocket(ChannelzProtoUtil.toSocket(s)).build(); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } + + @Override + public void getServerSockets( + GetServerSocketsRequest request, StreamObserver responseObserver) { + ServerSocketsList serverSockets + = channelz.getServerSockets(request.getServerId(), request.getStartSocketId(), maxPageSize); + if (serverSockets == null) { + responseObserver.onError( + Status.NOT_FOUND.withDescription("Can't find server " + request.getServerId()) + .asRuntimeException()); + return; + } + + GetServerSocketsResponse resp; + try { + resp = ChannelzProtoUtil.toGetServerSocketsResponse(serverSockets); + } catch (StatusRuntimeException e) { + responseObserver.onError(e); + return; + } + + responseObserver.onNext(resp); + responseObserver.onCompleted(); + } +} diff --git a/services/src/main/java/io/grpc/services/InetAddressUtil.java b/services/src/main/java/io/grpc/services/InetAddressUtil.java new file mode 100644 index 00000000000..057a8ccb5e6 --- /dev/null +++ b/services/src/main/java/io/grpc/services/InetAddressUtil.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.primitives.Ints; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.Arrays; + +// This is copied from guava 20.0 because it is a @Beta api +final class InetAddressUtil { + private static final int IPV6_PART_COUNT = 8; + + public static String toAddrString(InetAddress ip) { + checkNotNull(ip); + if (ip instanceof Inet4Address) { + // For IPv4, Java's formatting is good enough. + return ip.getHostAddress(); + } + checkArgument(ip instanceof Inet6Address); + byte[] bytes = ip.getAddress(); + int[] hextets = new int[IPV6_PART_COUNT]; + for (int i = 0; i < hextets.length; i++) { + hextets[i] = Ints.fromBytes((byte) 0, (byte) 0, bytes[2 * i], bytes[2 * i + 1]); + } + compressLongestRunOfZeroes(hextets); + return hextetsToIPv6String(hextets); + } + + private static void compressLongestRunOfZeroes(int[] hextets) { + int bestRunStart = -1; + int bestRunLength = -1; + int runStart = -1; + for (int i = 0; i < hextets.length + 1; i++) { + if (i < hextets.length && hextets[i] == 0) { + if (runStart < 0) { + runStart = i; + } + } else if (runStart >= 0) { + int runLength = i - runStart; + if (runLength > bestRunLength) { + bestRunStart = runStart; + bestRunLength = runLength; + } + runStart = -1; + } + } + if (bestRunLength >= 2) { + Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1); + } + } + + private static String hextetsToIPv6String(int[] hextets) { + // While scanning the array, handle these state transitions: + // start->num => "num" start->gap => "::" + // num->num => ":num" num->gap => "::" + // gap->num => "num" gap->gap => "" + StringBuilder buf = new StringBuilder(39); + boolean lastWasNumber = false; + for (int i = 0; i < hextets.length; i++) { + boolean thisIsNumber = hextets[i] >= 0; + if (thisIsNumber) { + if (lastWasNumber) { + buf.append(':'); + } + buf.append(Integer.toHexString(hextets[i])); + } else { + if (i == 0 || lastWasNumber) { + buf.append("::"); + } + } + lastWasNumber = thisIsNumber; + } + return buf.toString(); + } +} \ No newline at end of file diff --git a/services/src/main/java/io/grpc/services/TempFileSink.java b/services/src/main/java/io/grpc/services/TempFileSink.java new file mode 100644 index 00000000000..c28339d1bd6 --- /dev/null +++ b/services/src/main/java/io/grpc/services/TempFileSink.java @@ -0,0 +1,84 @@ +/* + * Copyright 2018, gRPC Authors All rights reserved. + * + * 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. + */ + +package io.grpc.services; + +import com.google.protobuf.MessageLite; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The output file goes to the JVM's temp dir with a prefix of BINARY_INFO. The proto messages + * are written serially using {@link MessageLite#writeDelimitedTo(OutputStream)}. + */ +class TempFileSink implements BinaryLogSink { + private static final Logger logger = Logger.getLogger(TempFileSink.class.getName()); + + private final String outPath; + private final OutputStream out; + private boolean closed; + + TempFileSink() throws IOException { + File outFile = File.createTempFile("BINARY_INFO.", ""); + outPath = outFile.getPath(); + logger.log(Level.INFO, "Writing binary logs to to {0}", outFile.getAbsolutePath()); + out = new BufferedOutputStream(new FileOutputStream(outFile)); + } + + String getPath() { + return this.outPath; + } + + @Override + public synchronized void write(MessageLite message) { + if (closed) { + logger.log(Level.FINEST, "Attempt to write after TempFileSink is closed."); + return; + } + try { + message.writeDelimitedTo(out); + } catch (IOException e) { + logger.log(Level.SEVERE, "Caught exception while writing", e); + closeQuietly(); + } + } + + @Override + public synchronized void close() throws IOException { + if (closed) { + return; + } + closed = true; + try { + out.flush(); + } finally { + out.close(); + } + } + + private synchronized void closeQuietly() { + try { + close(); + } catch (IOException e) { + logger.log(Level.SEVERE, "Caught exception while closing", e); + } + } +} From c402bf9b8078cddc91bb17c573db298624d30744 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 13 Apr 2021 13:05:12 -0700 Subject: [PATCH 05/20] Forward ProtoReflectionService. --- .../grpc/services/ProtoReflectionService.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 services/src/main/java/io/grpc/services/ProtoReflectionService.java diff --git a/services/src/main/java/io/grpc/services/ProtoReflectionService.java b/services/src/main/java/io/grpc/services/ProtoReflectionService.java new file mode 100644 index 00000000000..b00b68d8991 --- /dev/null +++ b/services/src/main/java/io/grpc/services/ProtoReflectionService.java @@ -0,0 +1,57 @@ +/* + * Copyright 2016 The 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. + */ + +package io.grpc.services; + +import io.grpc.BindableService; +import io.grpc.ExperimentalApi; +import io.grpc.reflection.v1alpha.ServerReflectionGrpc; +import io.grpc.reflection.v1alpha.ServerReflectionRequest; +import io.grpc.reflection.v1alpha.ServerReflectionResponse; +import io.grpc.stub.StreamObserver; + +/** + * Provides a reflection service for Protobuf services (including the reflection service itself). + * + *

Separately tracks mutable and immutable services. Throws an exception if either group of + * services contains multiple Protobuf files with declarations of the same service, method, type, or + * extension. + * + * @deprecated Use {@link io.grpc.protobuf.services.ProtoReflectionService} instead. + */ +@Deprecated +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222") +public final class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { + private final io.grpc.protobuf.services.ProtoReflectionService delegate; + + private ProtoReflectionService() { + delegate = (io.grpc.protobuf.services.ProtoReflectionService) + io.grpc.protobuf.services.ProtoReflectionService.newInstance(); + } + + /** + * Creates a instance of {@link io.grpc.protobuf.services.ProtoReflectionService}. + */ + public static BindableService newInstance() { + return new ProtoReflectionService(); + } + + @Override + public StreamObserver serverReflectionInfo( + StreamObserver responseObserver) { + return delegate.serverReflectionInfo(responseObserver); + } +} From 9989179b22832e1ccd7cd820e2c7d26bbaa4f379 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 13 Apr 2021 13:08:19 -0700 Subject: [PATCH 06/20] Fix bazel build. --- services/BUILD.bazel | 12 +- .../io/grpc/services/BinaryLogProvider.java | 194 ++++++++++++++++++ .../grpc/services/BinaryLogProviderImpl.java | 1 - 3 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 services/src/main/java/io/grpc/services/BinaryLogProvider.java diff --git a/services/BUILD.bazel b/services/BUILD.bazel index 97b5120ccd3..4418862b0fa 100644 --- a/services/BUILD.bazel +++ b/services/BUILD.bazel @@ -5,11 +5,17 @@ package(default_visibility = ["//visibility:public"]) java_library( name = "binarylog", srcs = [ + "src/main/java/io/grpc/protobuf/services/BinaryLogProvider.java", "src/main/java/io/grpc/services/BinaryLogProvider.java", + "src/main/java/io/grpc/protobuf/services/BinaryLogProviderImpl.java", "src/main/java/io/grpc/services/BinaryLogProviderImpl.java", + "src/main/java/io/grpc/protobuf/services/BinaryLogSink.java", "src/main/java/io/grpc/services/BinaryLogSink.java", + "src/main/java/io/grpc/protobuf/services/BinlogHelper.java", "src/main/java/io/grpc/services/BinlogHelper.java", + "src/main/java/io/grpc/protobuf/services/InetAddressUtil.java", "src/main/java/io/grpc/services/InetAddressUtil.java", + "src/main/java/io/grpc/protobuf/services/TempFileSink.java", "src/main/java/io/grpc/services/TempFileSink.java", ], deps = [ @@ -26,7 +32,9 @@ java_library( java_library( name = "channelz", srcs = [ + "src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java", "src/main/java/io/grpc/services/ChannelzProtoUtil.java", + "src/main/java/io/grpc/protobuf/services/ChannelzService.java", "src/main/java/io/grpc/services/ChannelzService.java", ], deps = [ @@ -46,6 +54,7 @@ java_library( name = "reflection", srcs = [ "src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java", + "src/main/java/io/grpc/services/ProtoReflectionService.java", ], deps = [ ":_reflection_java_grpc", @@ -66,7 +75,8 @@ java_library( java_library( name = "health", srcs = [ - "src/main/java/io/grpc/services/HealthServiceImpl.java", + "src/main/java/io/grpc/protobuf/services/HealthServiceImpl.java", + "src/main/java/io/grpc/protobuf/services/HealthStatusManager.java", "src/main/java/io/grpc/services/HealthStatusManager.java", ], deps = [ diff --git a/services/src/main/java/io/grpc/services/BinaryLogProvider.java b/services/src/main/java/io/grpc/services/BinaryLogProvider.java new file mode 100644 index 00000000000..3ca5de65558 --- /dev/null +++ b/services/src/main/java/io/grpc/services/BinaryLogProvider.java @@ -0,0 +1,194 @@ +/* + * Copyright 2017 The 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. + */ + +package io.grpc.services; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import io.grpc.BinaryLog; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ClientInterceptors; +import io.grpc.Internal; +import io.grpc.InternalClientInterceptors; +import io.grpc.InternalServerInterceptors; +import io.grpc.ManagedChannel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.Marshaller; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.ServerMethodDefinition; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.annotation.Nullable; + +// TODO(zpencer): rename class to AbstractBinaryLog +@Internal +public abstract class BinaryLogProvider extends BinaryLog { + @VisibleForTesting + public static final Marshaller BYTEARRAY_MARSHALLER = new ByteArrayMarshaller(); + + private final ClientInterceptor binaryLogShim = new BinaryLogShim(); + + /** + * Wraps a channel to provide binary logging on {@link ClientCall}s as needed. + */ + @Override + public final Channel wrapChannel(Channel channel) { + return ClientInterceptors.intercept(channel, binaryLogShim); + } + + private static MethodDescriptor toByteBufferMethod( + MethodDescriptor method) { + return method.toBuilder(BYTEARRAY_MARSHALLER, BYTEARRAY_MARSHALLER).build(); + } + + /** + * Wraps a {@link ServerMethodDefinition} such that it performs binary logging if needed. + */ + @Override + public final ServerMethodDefinition wrapMethodDefinition( + ServerMethodDefinition oMethodDef) { + ServerInterceptor binlogInterceptor = + getServerInterceptor(oMethodDef.getMethodDescriptor().getFullMethodName()); + if (binlogInterceptor == null) { + return oMethodDef; + } + MethodDescriptor binMethod = + BinaryLogProvider.toByteBufferMethod(oMethodDef.getMethodDescriptor()); + ServerMethodDefinition binDef = + InternalServerInterceptors.wrapMethod(oMethodDef, binMethod); + ServerCallHandler binlogHandler = + InternalServerInterceptors.interceptCallHandlerCreate( + binlogInterceptor, binDef.getServerCallHandler()); + return ServerMethodDefinition.create(binMethod, binlogHandler); + } + + /** + * Returns a {@link ServerInterceptor} for binary logging. gRPC is free to cache the interceptor, + * so the interceptor must be reusable across calls. At runtime, the request and response + * marshallers are always {@code Marshaller}. + * Returns {@code null} if this method is not binary logged. + */ + // TODO(zpencer): ensure the interceptor properly handles retries and hedging + @Nullable + protected abstract ServerInterceptor getServerInterceptor(String fullMethodName); + + /** + * Returns a {@link ClientInterceptor} for binary logging. gRPC is free to cache the interceptor, + * so the interceptor must be reusable across calls. At runtime, the request and response + * marshallers are always {@code Marshaller}. + * Returns {@code null} if this method is not binary logged. + */ + // TODO(zpencer): ensure the interceptor properly handles retries and hedging + @Nullable + protected abstract ClientInterceptor getClientInterceptor( + String fullMethodName, CallOptions callOptions); + + @Override + public void close() throws IOException { + // default impl: noop + // TODO(zpencer): make BinaryLogProvider provide a BinaryLog, and this method belongs there + } + + // Creating a named class makes debugging easier + private static final class ByteArrayMarshaller implements Marshaller { + @Override + public InputStream stream(byte[] value) { + return new ByteArrayInputStream(value); + } + + @Override + public byte[] parse(InputStream stream) { + try { + return parseHelper(stream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private byte[] parseHelper(InputStream stream) throws IOException { + try { + return IoUtils.toByteArray(stream); + } finally { + stream.close(); + } + } + } + + /** + * The pipeline of interceptors is hard coded when the {@link ManagedChannel} is created. + * This shim interceptor should always be installed as a placeholder. When a call starts, + * this interceptor checks with the {@link BinaryLogProvider} to see if logging should happen + * for this particular {@link ClientCall}'s method. + */ + private final class BinaryLogShim implements ClientInterceptor { + @Override + public ClientCall interceptCall( + MethodDescriptor method, + CallOptions callOptions, + Channel next) { + ClientInterceptor binlogInterceptor = getClientInterceptor( + method.getFullMethodName(), callOptions); + if (binlogInterceptor == null) { + return next.newCall(method, callOptions); + } else { + return InternalClientInterceptors + .wrapClientInterceptor( + binlogInterceptor, + BYTEARRAY_MARSHALLER, + BYTEARRAY_MARSHALLER) + .interceptCall(method, callOptions, next); + } + } + } + + // Copied from internal + private static final class IoUtils { + /** maximum buffer to be read is 16 KB. */ + private static final int MAX_BUFFER_LENGTH = 16384; + + /** Returns the byte array. */ + public static byte[] toByteArray(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copy(in, out); + return out.toByteArray(); + } + + /** Copies the data from input stream to output stream. */ + public static long copy(InputStream from, OutputStream to) throws IOException { + // Copied from guava com.google.common.io.ByteStreams because its API is unstable (beta) + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + byte[] buf = new byte[MAX_BUFFER_LENGTH]; + long total = 0; + while (true) { + int r = from.read(buf); + if (r == -1) { + break; + } + to.write(buf, 0, r); + total += r; + } + return total; + } + } +} diff --git a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java index 5c09f8a3ddd..dee13a924c6 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java +++ b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java @@ -20,7 +20,6 @@ import io.grpc.CallOptions; import io.grpc.ClientInterceptor; import io.grpc.ServerInterceptor; -import io.grpc.protobuf.services.BinaryLogProvider; import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nullable; From 15fb5fc325a32c5db8c7886c310b17cc34ad8296 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 13 Apr 2021 13:28:24 -0700 Subject: [PATCH 07/20] Clean up package-info.java in services/protobuf.services. --- .../grpc/protobuf/services/package-info.java | 20 +++++++++++++++++++ .../java/io/grpc/services/package-info.java | 2 ++ 2 files changed, 22 insertions(+) create mode 100644 services/src/main/java/io/grpc/protobuf/services/package-info.java diff --git a/services/src/main/java/io/grpc/protobuf/services/package-info.java b/services/src/main/java/io/grpc/protobuf/services/package-info.java new file mode 100644 index 00000000000..3c87b76b27b --- /dev/null +++ b/services/src/main/java/io/grpc/protobuf/services/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The 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. + */ + +/** + * Service definitions and utilities with protobuf dependency for the pre-defined gRPC services. + */ +package io.grpc.protobuf.services; \ No newline at end of file diff --git a/services/src/main/java/io/grpc/services/package-info.java b/services/src/main/java/io/grpc/services/package-info.java index 3b5b639361c..7e21ae519bb 100644 --- a/services/src/main/java/io/grpc/services/package-info.java +++ b/services/src/main/java/io/grpc/services/package-info.java @@ -14,6 +14,8 @@ * limitations under the License. */ +// TODO(chengyuanzhang): update description after deleting classes with protobuf dependency that +// have already been moved. /** * Service definitions and utilities for the pre-defined gRPC services. */ From 851628c684aca1125ccd1a7aac5223e89d73585c Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 13 Apr 2021 15:54:03 -0700 Subject: [PATCH 08/20] Keep tests for channelz and binlog implementations, as they are kept in-place. --- .../java/io/grpc/services/BinlogHelper.java | 2 +- .../io/grpc/services/InetAddressUtil.java | 2 +- .../services/BinaryLogProviderImplTest.java | 57 + .../grpc/services/BinaryLogProviderTest.java | 437 +++++ .../io/grpc/services/BinlogHelperTest.java | 1581 +++++++++++++++++ .../grpc/services/ChannelzProtoUtilTest.java | 949 ++++++++++ .../io/grpc/services/ChannelzServiceTest.java | 276 +++ .../io/grpc/services/ChannelzTestHelper.java | 183 ++ .../io/grpc/services/TempFileSinkTest.java | 68 + 9 files changed, 3553 insertions(+), 2 deletions(-) create mode 100644 services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java create mode 100644 services/src/test/java/io/grpc/services/BinaryLogProviderTest.java create mode 100644 services/src/test/java/io/grpc/services/BinlogHelperTest.java create mode 100644 services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java create mode 100644 services/src/test/java/io/grpc/services/ChannelzServiceTest.java create mode 100644 services/src/test/java/io/grpc/services/ChannelzTestHelper.java create mode 100644 services/src/test/java/io/grpc/services/TempFileSinkTest.java diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/services/BinlogHelper.java index ba3eef430c1..d8923160e5c 100644 --- a/services/src/main/java/io/grpc/services/BinlogHelper.java +++ b/services/src/main/java/io/grpc/services/BinlogHelper.java @@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; +import static io.grpc.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; diff --git a/services/src/main/java/io/grpc/services/InetAddressUtil.java b/services/src/main/java/io/grpc/services/InetAddressUtil.java index 057a8ccb5e6..e1bc8e9d26b 100644 --- a/services/src/main/java/io/grpc/services/InetAddressUtil.java +++ b/services/src/main/java/io/grpc/services/InetAddressUtil.java @@ -91,4 +91,4 @@ private static String hextetsToIPv6String(int[] hextets) { } return buf.toString(); } -} \ No newline at end of file +} diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java b/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java new file mode 100644 index 00000000000..ae363017438 --- /dev/null +++ b/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import io.grpc.CallOptions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link BinaryLogProviderImpl}. */ +@SuppressWarnings("deprecation") +@RunWith(JUnit4.class) +public class BinaryLogProviderImplTest { + @Test + public void configStrNullTest() throws Exception { + BinaryLogSink sink = mock(BinaryLogSink.class); + BinaryLogProviderImpl binlog = new BinaryLogProviderImpl(sink, /*configStr=*/ null); + assertNull(binlog.getServerInterceptor("package.service/method")); + assertNull(binlog.getClientInterceptor("package.service/method", CallOptions.DEFAULT)); + } + + @Test + public void configStrEmptyTest() throws Exception { + BinaryLogSink sink = mock(BinaryLogSink.class); + BinaryLogProviderImpl binlog = new BinaryLogProviderImpl(sink, ""); + assertNull(binlog.getServerInterceptor("package.service/method")); + assertNull(binlog.getClientInterceptor("package.service/method", CallOptions.DEFAULT)); + } + + @Test + public void closeTest() throws Exception { + BinaryLogSink sink = mock(BinaryLogSink.class); + BinaryLogProviderImpl log = new BinaryLogProviderImpl(sink, "*"); + verify(sink, never()).close(); + log.close(); + verify(sink).close(); + } +} diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java b/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java new file mode 100644 index 00000000000..0f7d41482e0 --- /dev/null +++ b/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java @@ -0,0 +1,437 @@ +/* + * Copyright 2017 The 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. + */ + +package io.grpc.services; + +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import com.google.common.io.ByteStreams; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; +import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; +import io.grpc.ForwardingServerCall.SimpleForwardingServerCall; +import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.Marshaller; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.ServerMethodDefinition; +import io.grpc.internal.NoopClientCall; +import io.grpc.internal.NoopServerCall; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link BinaryLogProvider}. */ +@RunWith(JUnit4.class) +public class BinaryLogProviderTest { + private final InvocationCountMarshaller reqMarshaller = + new InvocationCountMarshaller() { + @Override + Marshaller delegate() { + return StringMarshaller.INSTANCE; + } + }; + private final InvocationCountMarshaller respMarshaller = + new InvocationCountMarshaller() { + @Override + Marshaller delegate() { + return IntegerMarshaller.INSTANCE; + } + }; + private final MethodDescriptor method = + MethodDescriptor + .newBuilder(reqMarshaller, respMarshaller) + .setFullMethodName("myservice/mymethod") + .setType(MethodType.UNARY) + .setSchemaDescriptor(new Object()) + .setIdempotent(true) + .setSafe(true) + .setSampledToLocalTracing(true) + .build(); + private final List binlogReq = new ArrayList<>(); + private final List binlogResp = new ArrayList<>(); + private final BinaryLogProvider binlogProvider = new BinaryLogProvider() { + @Override + public ServerInterceptor getServerInterceptor(String fullMethodName) { + return new TestBinaryLogServerInterceptor(); + } + + @Override + public ClientInterceptor getClientInterceptor( + String fullMethodName, CallOptions callOptions) { + return new TestBinaryLogClientInterceptor(); + } + }; + + @Test + public void wrapChannel_methodDescriptor() throws Exception { + final AtomicReference> methodRef = + new AtomicReference<>(); + Channel channel = new Channel() { + @Override + public ClientCall newCall( + MethodDescriptor method, CallOptions callOptions) { + methodRef.set(method); + return new NoopClientCall<>(); + } + + @Override + public String authority() { + throw new UnsupportedOperationException(); + } + }; + Channel wChannel = binlogProvider.wrapChannel(channel); + ClientCall unusedClientCall = wChannel.newCall(method, CallOptions.DEFAULT); + validateWrappedMethod(methodRef.get()); + } + + @Test + public void wrapChannel_handler() throws Exception { + final List serializedReq = new ArrayList<>(); + final AtomicReference> listener = + new AtomicReference<>(); + Channel channel = new Channel() { + @Override + public ClientCall newCall( + MethodDescriptor methodDescriptor, CallOptions callOptions) { + return new NoopClientCall() { + @Override + public void start(ClientCall.Listener responseListener, Metadata headers) { + listener.set(responseListener); + } + + @Override + public void sendMessage(RequestT message) { + serializedReq.add((byte[]) message); + } + }; + } + + @Override + public String authority() { + throw new UnsupportedOperationException(); + } + }; + Channel wChannel = binlogProvider.wrapChannel(channel); + ClientCall clientCall = wChannel.newCall(method, CallOptions.DEFAULT); + final List observedResponse = new ArrayList<>(); + clientCall.start( + new NoopClientCall.NoopClientCallListener() { + @Override + public void onMessage(Integer message) { + observedResponse.add(message); + } + }, + new Metadata()); + + String expectedRequest = "hello world"; + assertThat(binlogReq).isEmpty(); + assertThat(serializedReq).isEmpty(); + assertEquals(0, reqMarshaller.streamInvocations); + clientCall.sendMessage(expectedRequest); + // it is unacceptably expensive for the binlog to double parse every logged message + assertEquals(1, reqMarshaller.streamInvocations); + assertEquals(0, reqMarshaller.parseInvocations); + assertThat(binlogReq).hasSize(1); + assertThat(serializedReq).hasSize(1); + assertEquals( + expectedRequest, + StringMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogReq.get(0)))); + assertEquals( + expectedRequest, + StringMarshaller.INSTANCE.parse(new ByteArrayInputStream(serializedReq.get(0)))); + + int expectedResponse = 12345; + assertThat(binlogResp).isEmpty(); + assertThat(observedResponse).isEmpty(); + assertEquals(0, respMarshaller.parseInvocations); + onClientMessageHelper(listener.get(), IntegerMarshaller.INSTANCE.stream(expectedResponse)); + // it is unacceptably expensive for the binlog to double parse every logged message + assertEquals(1, respMarshaller.parseInvocations); + assertEquals(0, respMarshaller.streamInvocations); + assertThat(binlogResp).hasSize(1); + assertThat(observedResponse).hasSize(1); + assertEquals( + expectedResponse, + (int) IntegerMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogResp.get(0)))); + assertEquals(expectedResponse, (int) observedResponse.get(0)); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void onClientMessageHelper(ClientCall.Listener listener, Object request) { + listener.onMessage(request); + } + + private void validateWrappedMethod(MethodDescriptor wMethod) { + assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, wMethod.getRequestMarshaller()); + assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, wMethod.getResponseMarshaller()); + assertEquals(method.getType(), wMethod.getType()); + assertEquals(method.getFullMethodName(), wMethod.getFullMethodName()); + assertEquals(method.getSchemaDescriptor(), wMethod.getSchemaDescriptor()); + assertEquals(method.isIdempotent(), wMethod.isIdempotent()); + assertEquals(method.isSafe(), wMethod.isSafe()); + assertEquals(method.isSampledToLocalTracing(), wMethod.isSampledToLocalTracing()); + } + + @Test + public void wrapMethodDefinition_methodDescriptor() throws Exception { + ServerMethodDefinition methodDef = + ServerMethodDefinition.create( + method, + new ServerCallHandler() { + @Override + public ServerCall.Listener startCall( + ServerCall call, Metadata headers) { + throw new UnsupportedOperationException(); + } + }); + ServerMethodDefinition wMethodDef = binlogProvider.wrapMethodDefinition(methodDef); + validateWrappedMethod(wMethodDef.getMethodDescriptor()); + } + + @Test + public void wrapMethodDefinition_handler() throws Exception { + // The request as seen by the user supplied server code + final List observedRequest = new ArrayList<>(); + final AtomicReference> serverCall = + new AtomicReference<>(); + ServerMethodDefinition methodDef = + ServerMethodDefinition.create( + method, + new ServerCallHandler() { + @Override + public ServerCall.Listener startCall( + ServerCall call, Metadata headers) { + serverCall.set(call); + return new ServerCall.Listener() { + @Override + public void onMessage(String message) { + observedRequest.add(message); + } + }; + } + }); + ServerMethodDefinition wDef = binlogProvider.wrapMethodDefinition(methodDef); + List serializedResp = new ArrayList<>(); + ServerCall.Listener wListener = startServerCallHelper(wDef, serializedResp); + + String expectedRequest = "hello world"; + assertThat(binlogReq).isEmpty(); + assertThat(observedRequest).isEmpty(); + assertEquals(0, reqMarshaller.parseInvocations); + onServerMessageHelper(wListener, StringMarshaller.INSTANCE.stream(expectedRequest)); + // it is unacceptably expensive for the binlog to double parse every logged message + assertEquals(1, reqMarshaller.parseInvocations); + assertEquals(0, reqMarshaller.streamInvocations); + assertThat(binlogReq).hasSize(1); + assertThat(observedRequest).hasSize(1); + assertEquals( + expectedRequest, + StringMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogReq.get(0)))); + assertEquals(expectedRequest, observedRequest.get(0)); + + int expectedResponse = 12345; + assertThat(binlogResp).isEmpty(); + assertThat(serializedResp).isEmpty(); + assertEquals(0, respMarshaller.streamInvocations); + serverCall.get().sendMessage(expectedResponse); + // it is unacceptably expensive for the binlog to double parse every logged message + assertEquals(0, respMarshaller.parseInvocations); + assertEquals(1, respMarshaller.streamInvocations); + assertThat(binlogResp).hasSize(1); + assertThat(serializedResp).hasSize(1); + assertEquals( + expectedResponse, + (int) IntegerMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogResp.get(0)))); + assertEquals(expectedResponse, + (int) method.parseResponse(new ByteArrayInputStream((byte[]) serializedResp.get(0)))); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void onServerMessageHelper(ServerCall.Listener listener, Object request) { + listener.onMessage(request); + } + + private static ServerCall.Listener startServerCallHelper( + final ServerMethodDefinition methodDef, + final List serializedResp) { + ServerCall serverCall = new NoopServerCall() { + @Override + public void sendMessage(RespT message) { + serializedResp.add(message); + } + + @Override + public MethodDescriptor getMethodDescriptor() { + return methodDef.getMethodDescriptor(); + } + }; + return methodDef.getServerCallHandler().startCall(serverCall, new Metadata()); + } + + private final class TestBinaryLogClientInterceptor implements ClientInterceptor { + @Override + public ClientCall interceptCall( + final MethodDescriptor method, + CallOptions callOptions, + Channel next) { + assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, method.getRequestMarshaller()); + assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, method.getResponseMarshaller()); + return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { + @Override + public void start(ClientCall.Listener responseListener, Metadata headers) { + super.start( + new SimpleForwardingClientCallListener(responseListener) { + @Override + public void onMessage(RespT message) { + assertTrue(message instanceof InputStream); + try { + byte[] bytes = ByteStreams.toByteArray((InputStream) message); + binlogResp.add(bytes); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + RespT dup = method.parseResponse(input); + super.onMessage(dup); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }, + headers); + } + + @Override + public void sendMessage(ReqT message) { + byte[] bytes = (byte[]) message; + binlogReq.add(bytes); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + ReqT dup = method.parseRequest(input); + super.sendMessage(dup); + } + }; + } + } + + private final class TestBinaryLogServerInterceptor implements ServerInterceptor { + @Override + public ServerCall.Listener interceptCall( + final ServerCall call, + Metadata headers, + ServerCallHandler next) { + assertSame( + BinaryLogProvider.BYTEARRAY_MARSHALLER, + call.getMethodDescriptor().getRequestMarshaller()); + assertSame( + BinaryLogProvider.BYTEARRAY_MARSHALLER, + call.getMethodDescriptor().getResponseMarshaller()); + ServerCall wCall = new SimpleForwardingServerCall(call) { + @Override + public void sendMessage(RespT message) { + byte[] bytes = (byte[]) message; + binlogResp.add(bytes); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + RespT dup = call.getMethodDescriptor().parseResponse(input); + super.sendMessage(dup); + } + }; + final ServerCall.Listener oListener = next.startCall(wCall, headers); + return new SimpleForwardingServerCallListener(oListener) { + @Override + public void onMessage(ReqT message) { + assertTrue(message instanceof InputStream); + try { + byte[] bytes = ByteStreams.toByteArray((InputStream) message); + binlogReq.add(bytes); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + ReqT dup = call.getMethodDescriptor().parseRequest(input); + super.onMessage(dup); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + } + } + + private abstract static class InvocationCountMarshaller + implements MethodDescriptor.Marshaller { + private int streamInvocations = 0; + private int parseInvocations = 0; + + abstract MethodDescriptor.Marshaller delegate(); + + @Override + public InputStream stream(T value) { + streamInvocations++; + return delegate().stream(value); + } + + @Override + public T parse(InputStream stream) { + parseInvocations++; + return delegate().parse(stream); + } + } + + + private static class StringMarshaller implements MethodDescriptor.Marshaller { + public static final StringMarshaller INSTANCE = new StringMarshaller(); + + @Override + public InputStream stream(String value) { + return new ByteArrayInputStream(value.getBytes(UTF_8)); + } + + @Override + public String parse(InputStream stream) { + try { + return new String(ByteStreams.toByteArray(stream), UTF_8); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } + + private static class IntegerMarshaller implements MethodDescriptor.Marshaller { + public static final IntegerMarshaller INSTANCE = new IntegerMarshaller(); + + @Override + public InputStream stream(Integer value) { + return StringMarshaller.INSTANCE.stream(value.toString()); + } + + @Override + public Integer parse(InputStream stream) { + return Integer.valueOf(StringMarshaller.INSTANCE.parse(stream)); + } + } +} diff --git a/services/src/test/java/io/grpc/services/BinlogHelperTest.java b/services/src/test/java/io/grpc/services/BinlogHelperTest.java new file mode 100644 index 00000000000..fd11f4a86ac --- /dev/null +++ b/services/src/test/java/io/grpc/services/BinlogHelperTest.java @@ -0,0 +1,1581 @@ +/* + * Copyright 2017 The 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. + */ + +package io.grpc.services; + +import static com.google.common.truth.Truth.assertThat; +import static io.grpc.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; +import static io.grpc.services.BinlogHelper.createMetadataProto; +import static io.grpc.services.BinlogHelper.getPeerSocket; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.SettableFuture; +import com.google.protobuf.Any; +import com.google.protobuf.ByteString; +import com.google.protobuf.Duration; +import com.google.protobuf.StringValue; +import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Durations; +import io.grpc.Attributes; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.Context; +import io.grpc.Deadline; +import io.grpc.Grpc; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.Status; +import io.grpc.StatusException; +import io.grpc.binarylog.v1.Address; +import io.grpc.binarylog.v1.ClientHeader; +import io.grpc.binarylog.v1.GrpcLogEntry; +import io.grpc.binarylog.v1.GrpcLogEntry.EventType; +import io.grpc.binarylog.v1.GrpcLogEntry.Logger; +import io.grpc.binarylog.v1.Message; +import io.grpc.binarylog.v1.MetadataEntry; +import io.grpc.binarylog.v1.ServerHeader; +import io.grpc.binarylog.v1.Trailer; +import io.grpc.internal.NoopClientCall; +import io.grpc.internal.NoopServerCall; +import io.grpc.protobuf.StatusProto; +import io.grpc.services.BinlogHelper.FactoryImpl; +import io.grpc.services.BinlogHelper.MaybeTruncated; +import io.grpc.services.BinlogHelper.SinkWriter; +import io.grpc.services.BinlogHelper.SinkWriterImpl; +import io.grpc.services.BinlogHelper.TimeProvider; +import io.netty.channel.unix.DomainSocketAddress; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.charset.Charset; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.AdditionalMatchers; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; + +/** Tests for {@link BinlogHelper}. */ +@SuppressWarnings("deprecation") +@RunWith(JUnit4.class) +public final class BinlogHelperTest { + private static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final BinlogHelper HEADER_FULL = new Builder().header(Integer.MAX_VALUE).build(); + private static final BinlogHelper HEADER_256 = new Builder().header(256).build(); + private static final BinlogHelper MSG_FULL = new Builder().msg(Integer.MAX_VALUE).build(); + private static final BinlogHelper MSG_256 = new Builder().msg(256).build(); + private static final BinlogHelper BOTH_256 = new Builder().header(256).msg(256).build(); + private static final BinlogHelper BOTH_FULL = + new Builder().header(Integer.MAX_VALUE).msg(Integer.MAX_VALUE).build(); + + private static final String DATA_A = "aaaaaaaaa"; + private static final String DATA_B = "bbbbbbbbb"; + private static final String DATA_C = "ccccccccc"; + private static final Metadata.Key KEY_A = + Metadata.Key.of("a", Metadata.ASCII_STRING_MARSHALLER); + private static final Metadata.Key KEY_B = + Metadata.Key.of("b", Metadata.ASCII_STRING_MARSHALLER); + private static final Metadata.Key KEY_C = + Metadata.Key.of("c", Metadata.ASCII_STRING_MARSHALLER); + private static final MetadataEntry ENTRY_A = + MetadataEntry + .newBuilder() + .setKey(KEY_A.name()) + .setValue(ByteString.copyFrom(DATA_A.getBytes(US_ASCII))) + .build(); + private static final MetadataEntry ENTRY_B = + MetadataEntry + .newBuilder() + .setKey(KEY_B.name()) + .setValue(ByteString.copyFrom(DATA_B.getBytes(US_ASCII))) + .build(); + private static final MetadataEntry ENTRY_C = + MetadataEntry + .newBuilder() + .setKey(KEY_C.name()) + .setValue(ByteString.copyFrom(DATA_C.getBytes(US_ASCII))) + .build(); + private static final long CALL_ID = 0x1112131415161718L; + private static final int HEADER_LIMIT = 10; + private static final int MESSAGE_LIMIT = Integer.MAX_VALUE; + + private final Metadata nonEmptyMetadata = new Metadata(); + private final BinaryLogSink sink = mock(BinaryLogSink.class); + private final Timestamp timestamp + = Timestamp.newBuilder().setSeconds(9876).setNanos(54321).build(); + private final BinlogHelper.TimeProvider timeProvider = new TimeProvider() { + @Override + public long currentTimeNanos() { + return TimeUnit.SECONDS.toNanos(9876) + 54321; + } + }; + private final SinkWriter sinkWriterImpl = + new SinkWriterImpl( + sink, + timeProvider, + HEADER_LIMIT, + MESSAGE_LIMIT); + private final SinkWriter mockSinkWriter = mock(SinkWriter.class); + private final byte[] message = new byte[100]; + private SocketAddress peer; + + @Before + public void setUp() throws Exception { + nonEmptyMetadata.put(KEY_A, DATA_A); + nonEmptyMetadata.put(KEY_B, DATA_B); + nonEmptyMetadata.put(KEY_C, DATA_C); + peer = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 1234); + } + + @Test + public void configBinLog_global() throws Exception { + assertSameLimits(BOTH_FULL, makeLog("*", "p.s/m")); + assertSameLimits(BOTH_FULL, makeLog("*{h;m}", "p.s/m")); + assertSameLimits(HEADER_FULL, makeLog("*{h}", "p.s/m")); + assertSameLimits(MSG_FULL, makeLog("*{m}", "p.s/m")); + assertSameLimits(HEADER_256, makeLog("*{h:256}", "p.s/m")); + assertSameLimits(MSG_256, makeLog("*{m:256}", "p.s/m")); + assertSameLimits(BOTH_256, makeLog("*{h:256;m:256}", "p.s/m")); + assertSameLimits( + new Builder().header(Integer.MAX_VALUE).msg(256).build(), + makeLog("*{h;m:256}", "p.s/m")); + assertSameLimits( + new Builder().header(256).msg(Integer.MAX_VALUE).build(), + makeLog("*{h:256;m}", "p.s/m")); + } + + @Test + public void configBinLog_method() throws Exception { + assertSameLimits(BOTH_FULL, makeLog("p.s/m", "p.s/m")); + assertSameLimits(BOTH_FULL, makeLog("p.s/m{h;m}", "p.s/m")); + assertSameLimits(HEADER_FULL, makeLog("p.s/m{h}", "p.s/m")); + assertSameLimits(MSG_FULL, makeLog("p.s/m{m}", "p.s/m")); + assertSameLimits(HEADER_256, makeLog("p.s/m{h:256}", "p.s/m")); + assertSameLimits(MSG_256, makeLog("p.s/m{m:256}", "p.s/m")); + assertSameLimits(BOTH_256, makeLog("p.s/m{h:256;m:256}", "p.s/m")); + assertSameLimits( + new Builder().header(Integer.MAX_VALUE).msg(256).build(), + makeLog("p.s/m{h;m:256}", "p.s/m")); + assertSameLimits( + new Builder().header(256).msg(Integer.MAX_VALUE).build(), + makeLog("p.s/m{h:256;m}", "p.s/m")); + } + + @Test + public void configBinLog_method_absent() throws Exception { + assertNull(makeLog("p.s/m", "p.s/absent")); + } + + @Test + public void configBinLog_service() throws Exception { + assertSameLimits(BOTH_FULL, makeLog("p.s/*", "p.s/m")); + assertSameLimits(BOTH_FULL, makeLog("p.s/*{h;m}", "p.s/m")); + assertSameLimits(HEADER_FULL, makeLog("p.s/*{h}", "p.s/m")); + assertSameLimits(MSG_FULL, makeLog("p.s/*{m}", "p.s/m")); + assertSameLimits(HEADER_256, makeLog("p.s/*{h:256}", "p.s/m")); + assertSameLimits(MSG_256, makeLog("p.s/*{m:256}", "p.s/m")); + assertSameLimits(BOTH_256, makeLog("p.s/*{h:256;m:256}", "p.s/m")); + assertSameLimits( + new Builder().header(Integer.MAX_VALUE).msg(256).build(), + makeLog("p.s/*{h;m:256}", "p.s/m")); + assertSameLimits( + new Builder().header(256).msg(Integer.MAX_VALUE).build(), + makeLog("p.s/*{h:256;m}", "p.s/m")); + } + + @Test + public void configBinLog_service_absent() throws Exception { + assertNull(makeLog("p.s/*", "p.other/m")); + } + + @Test + public void createLogFromOptionString() throws Exception { + assertSameLimits(BOTH_FULL, makeOptions(null)); + assertSameLimits(HEADER_FULL, makeOptions("h")); + assertSameLimits(MSG_FULL, makeOptions("m")); + assertSameLimits(HEADER_256, makeOptions("h:256")); + assertSameLimits(MSG_256, makeOptions("m:256")); + assertSameLimits(BOTH_256, makeOptions("h:256;m:256")); + assertSameLimits( + new Builder().header(Integer.MAX_VALUE).msg(256).build(), + makeOptions("h;m:256")); + assertSameLimits( + new Builder().header(256).msg(Integer.MAX_VALUE).build(), + makeOptions("h:256;m")); + } + + private void assertIllegalPatternDetected(String perSvcOrMethodConfig) { + try { + FactoryImpl.createBinaryLog(sink, perSvcOrMethodConfig); + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().startsWith("Illegal log config pattern"); + } + } + + @Test + public void badFactoryConfigStrDetected() throws Exception { + try { + new FactoryImpl(sink, "obviouslybad{"); + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().startsWith("Illegal log config pattern"); + } + } + + @Test + public void badFactoryConfigStrDetected_empty() throws Exception { + try { + new FactoryImpl(sink, "*,"); + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().startsWith("Illegal log config pattern"); + } + } + + @Test + public void createLogFromOptionString_malformed() throws Exception { + assertIllegalPatternDetected(""); + assertIllegalPatternDetected("bad"); + assertIllegalPatternDetected("mad"); + assertIllegalPatternDetected("x;y"); + assertIllegalPatternDetected("h:abc"); + assertIllegalPatternDetected("h:1e8"); + assertIllegalPatternDetected("2"); + assertIllegalPatternDetected("2;2"); + // The grammar specifies that if both h and m are present, h comes before m + assertIllegalPatternDetected("m:123;h:123"); + // NumberFormatException + assertIllegalPatternDetected("h:99999999999999"); + } + + @Test + public void configBinLog_multiConfig_withGlobal() throws Exception { + String configStr = + "*{h}," + + "package.both256/*{h:256;m:256}," + + "package.service1/both128{h:128;m:128}," + + "package.service2/method_messageOnly{m}"; + assertSameLimits(HEADER_FULL, makeLog(configStr, "otherpackage.service/method")); + + assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method1")); + assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method2")); + assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method3")); + + assertSameLimits( + new Builder().header(128).msg(128).build(), makeLog(configStr, "package.service1/both128")); + // the global config is in effect + assertSameLimits(HEADER_FULL, makeLog(configStr, "package.service1/absent")); + + assertSameLimits(MSG_FULL, makeLog(configStr, "package.service2/method_messageOnly")); + // the global config is in effect + assertSameLimits(HEADER_FULL, makeLog(configStr, "package.service2/absent")); + } + + @Test + public void configBinLog_multiConfig_noGlobal() throws Exception { + String configStr = + "package.both256/*{h:256;m:256}," + + "package.service1/both128{h:128;m:128}," + + "package.service2/method_messageOnly{m}"; + assertNull(makeLog(configStr, "otherpackage.service/method")); + + assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method1")); + assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method2")); + assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method3")); + + assertSameLimits( + new Builder().header(128).msg(128).build(), makeLog(configStr, "package.service1/both128")); + // no global config in effect + assertNull(makeLog(configStr, "package.service1/absent")); + + assertSameLimits(MSG_FULL, makeLog(configStr, "package.service2/method_messageOnly")); + // no global config in effect + assertNull(makeLog(configStr, "package.service2/absent")); + } + + @Test + public void configBinLog_blacklist() { + assertNull(makeLog("*,-p.s/blacklisted", "p.s/blacklisted")); + assertNull(makeLog("-p.s/blacklisted,*", "p.s/blacklisted")); + assertNotNull(makeLog("-p.s/method,*", "p.s/allowed")); + + assertNull(makeLog("p.s/*,-p.s/blacklisted", "p.s/blacklisted")); + assertNull(makeLog("-p.s/blacklisted,p.s/*", "p.s/blacklisted")); + assertNotNull(makeLog("-p.s/blacklisted,p.s/*", "p.s/allowed")); + } + + private void assertDuplicatelPatternDetected(String factoryConfigStr) { + try { + new BinlogHelper.FactoryImpl(sink, factoryConfigStr); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected).hasMessageThat().startsWith("Duplicate entry"); + } + } + + @Test + public void configBinLog_duplicates_global() throws Exception { + assertDuplicatelPatternDetected("*{h},*{h:256}"); + } + + @Test + public void configBinLog_duplicates_service() throws Exception { + assertDuplicatelPatternDetected("p.s/*,p.s/*{h}"); + + } + + @Test + public void configBinLog_duplicates_method() throws Exception { + assertDuplicatelPatternDetected("p.s/*,p.s/*{h:1;m:2}"); + assertDuplicatelPatternDetected("p.s/m,-p.s/m"); + assertDuplicatelPatternDetected("-p.s/m,p.s/m"); + assertDuplicatelPatternDetected("-p.s/m,-p.s/m"); + } + + @Test + public void socketToProto_ipv4() throws Exception { + InetAddress address = InetAddress.getByName("127.0.0.1"); + int port = 12345; + InetSocketAddress socketAddress = new InetSocketAddress(address, port); + assertEquals( + Address + .newBuilder() + .setType(Address.Type.TYPE_IPV4) + .setAddress("127.0.0.1") + .setIpPort(12345) + .build(), + BinlogHelper.socketToProto(socketAddress)); + } + + @Test + public void socketToProto_ipv6() throws Exception { + // this is a ipv6 link local address + InetAddress address = InetAddress.getByName("2001:db8:0:0:0:0:2:1"); + int port = 12345; + InetSocketAddress socketAddress = new InetSocketAddress(address, port); + assertEquals( + Address + .newBuilder() + .setType(Address.Type.TYPE_IPV6) + .setAddress("2001:db8::2:1") // RFC 5952 section 4: ipv6 canonical form required + .setIpPort(12345) + .build(), + BinlogHelper.socketToProto(socketAddress)); + } + + @Test + public void socketToProto_unix() throws Exception { + String path = "/some/path"; + DomainSocketAddress socketAddress = new DomainSocketAddress(path); + assertEquals( + Address + .newBuilder() + .setType(Address.Type.TYPE_UNIX) + .setAddress("/some/path") + .build(), + BinlogHelper.socketToProto(socketAddress) + ); + } + + @Test + public void socketToProto_unknown() throws Exception { + SocketAddress unknownSocket = new SocketAddress() { + @Override + public String toString() { + return "some-socket-address"; + } + }; + assertEquals( + Address.newBuilder() + .setType(Address.Type.TYPE_UNKNOWN) + .setAddress("some-socket-address") + .build(), + BinlogHelper.socketToProto(unknownSocket)); + } + + @Test + public void metadataToProto_empty() throws Exception { + assertEquals( + GrpcLogEntry.newBuilder() + .setType(EventType.EVENT_TYPE_CLIENT_HEADER) + .setClientHeader( + ClientHeader.newBuilder().setMetadata( + io.grpc.binarylog.v1.Metadata.getDefaultInstance())) + .build(), + metadataToProtoTestHelper( + EventType.EVENT_TYPE_CLIENT_HEADER, new Metadata(), Integer.MAX_VALUE)); + } + + @Test + public void metadataToProto() throws Exception { + assertEquals( + GrpcLogEntry.newBuilder() + .setType(EventType.EVENT_TYPE_CLIENT_HEADER) + .setClientHeader( + ClientHeader.newBuilder().setMetadata( + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .addEntry(ENTRY_B) + .addEntry(ENTRY_C) + .build())) + .build(), + metadataToProtoTestHelper( + EventType.EVENT_TYPE_CLIENT_HEADER, nonEmptyMetadata, Integer.MAX_VALUE)); + } + + @Test + public void metadataToProto_setsTruncated() throws Exception { + assertTrue(BinlogHelper.createMetadataProto(nonEmptyMetadata, 0).truncated); + } + + @Test + public void metadataToProto_truncated() throws Exception { + // 0 byte limit not enough for any metadata + assertEquals( + io.grpc.binarylog.v1.Metadata.getDefaultInstance(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 0).proto.build()); + // not enough bytes for first key value + assertEquals( + io.grpc.binarylog.v1.Metadata.getDefaultInstance(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 9).proto.build()); + // enough for first key value + assertEquals( + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .build(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 10).proto.build()); + // Test edge cases for >= 2 key values + assertEquals( + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .build(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 19).proto.build()); + assertEquals( + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .addEntry(ENTRY_B) + .build(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 20).proto.build()); + assertEquals( + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .addEntry(ENTRY_B) + .build(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 29).proto.build()); + + // not truncated: enough for all keys + assertEquals( + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .addEntry(ENTRY_B) + .addEntry(ENTRY_C) + .build(), + BinlogHelper.createMetadataProto(nonEmptyMetadata, 30).proto.build()); + } + + @Test + public void messageToProto() throws Exception { + byte[] bytes + = "this is a long message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes(US_ASCII); + assertEquals( + GrpcLogEntry.newBuilder() + .setMessage( + Message + .newBuilder() + .setData(ByteString.copyFrom(bytes)) + .setLength(bytes.length) + .build()) + .build(), + messageToProtoTestHelper(bytes, Integer.MAX_VALUE)); + } + + @Test + public void messageToProto_truncated() throws Exception { + byte[] bytes + = "this is a long message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes(US_ASCII); + assertEquals( + GrpcLogEntry.newBuilder() + .setMessage( + Message + .newBuilder() + .setLength(bytes.length) + .build()) + .setPayloadTruncated(true) + .build(), + messageToProtoTestHelper(bytes, 0)); + + int limit = 10; + String truncatedMessage = "this is a "; + assertEquals( + GrpcLogEntry.newBuilder() + .setMessage( + Message + .newBuilder() + .setData(ByteString.copyFrom(truncatedMessage.getBytes(US_ASCII))) + .setLength(bytes.length) + .build()) + .setPayloadTruncated(true) + .build(), + messageToProtoTestHelper(bytes, limit)); + } + + @Test + public void logClientHeader() throws Exception { + long seq = 1; + String authority = "authority"; + String methodName = "service/method"; + Duration timeout = Durations.fromMillis(1234); + InetAddress address = InetAddress.getByName("127.0.0.1"); + int port = 12345; + InetSocketAddress peerAddress = new InetSocketAddress(address, port); + long callId = 1000; + + GrpcLogEntry.Builder builder = + metadataToProtoTestHelper(EventType.EVENT_TYPE_CLIENT_HEADER, nonEmptyMetadata, 10) + .toBuilder() + .setTimestamp(timestamp) + .setSequenceIdWithinCall(seq) + .setLogger(Logger.LOGGER_CLIENT) + .setCallId(callId); + builder.getClientHeaderBuilder() + .setMethodName("/" + methodName) + .setAuthority(authority) + .setTimeout(timeout); + GrpcLogEntry base = builder.build(); + { + sinkWriterImpl.logClientHeader( + seq, + methodName, + authority, + timeout, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + /*peerAddress=*/ null); + verify(sink).write(base); + } + + // logger is server + { + sinkWriterImpl.logClientHeader( + seq, + methodName, + authority, + timeout, + nonEmptyMetadata, + Logger.LOGGER_SERVER, + callId, + peerAddress); + verify(sink).write( + base.toBuilder() + .setPeer(BinlogHelper.socketToProto(peerAddress)) + .setLogger(Logger.LOGGER_SERVER) + .build()); + } + + // authority is null + { + sinkWriterImpl.logClientHeader( + seq, + methodName, + /*authority=*/ null, + timeout, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + /*peerAddress=*/ null); + + verify(sink).write( + base.toBuilder() + .setClientHeader(builder.getClientHeader().toBuilder().clearAuthority().build()) + .build()); + } + + // timeout is null + { + sinkWriterImpl.logClientHeader( + seq, + methodName, + authority, + /*timeout=*/ null, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + /*peerAddress=*/ null); + + verify(sink).write( + base.toBuilder() + .setClientHeader(builder.getClientHeader().toBuilder().clearTimeout().build()) + .build()); + } + + // peerAddress is non null (error for client side) + try { + sinkWriterImpl.logClientHeader( + seq, + methodName, + authority, + timeout, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + peerAddress); + fail(); + } catch (IllegalArgumentException expected) { + // noop + } + } + + @Test + public void logServerHeader() throws Exception { + long seq = 1; + InetAddress address = InetAddress.getByName("127.0.0.1"); + int port = 12345; + InetSocketAddress peerAddress = new InetSocketAddress(address, port); + long callId = 1000; + + GrpcLogEntry.Builder builder = + metadataToProtoTestHelper(EventType.EVENT_TYPE_SERVER_HEADER, nonEmptyMetadata, 10) + .toBuilder() + .setTimestamp(timestamp) + .setSequenceIdWithinCall(seq) + .setLogger(Logger.LOGGER_CLIENT) + .setCallId(callId) + .setPeer(BinlogHelper.socketToProto(peerAddress)); + + { + sinkWriterImpl.logServerHeader( + seq, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + peerAddress); + verify(sink).write(builder.build()); + } + + // logger is server + // null peerAddress is required for server side + { + sinkWriterImpl.logServerHeader( + seq, + nonEmptyMetadata, + Logger.LOGGER_SERVER, + callId, + /*peerAddress=*/ null); + verify(sink).write( + builder + .setLogger(Logger.LOGGER_SERVER) + .clearPeer() + .build()); + } + + // logger is server + // non null peerAddress is an error + try { + sinkWriterImpl.logServerHeader( + seq, + nonEmptyMetadata, + Logger.LOGGER_SERVER, + callId, + peerAddress); + fail(); + } catch (IllegalArgumentException expected) { + // noop + } + } + + @Test + public void logTrailer() throws Exception { + long seq = 1; + InetAddress address = InetAddress.getByName("127.0.0.1"); + int port = 12345; + InetSocketAddress peerAddress = new InetSocketAddress(address, port); + long callId = 1000; + Status statusDescription = Status.INTERNAL.withDescription("my description"); + + GrpcLogEntry.Builder builder = + metadataToProtoTestHelper(EventType.EVENT_TYPE_SERVER_TRAILER, nonEmptyMetadata, 10) + .toBuilder() + .setTimestamp(timestamp) + .setSequenceIdWithinCall(seq) + .setLogger(Logger.LOGGER_CLIENT) + .setCallId(callId) + .setPeer(BinlogHelper.socketToProto(peerAddress)); + + builder.getTrailerBuilder() + .setStatusCode(Status.INTERNAL.getCode().value()) + .setStatusMessage("my description"); + GrpcLogEntry base = builder.build(); + + { + sinkWriterImpl.logTrailer( + seq, + statusDescription, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + peerAddress); + verify(sink).write(base); + } + + // logger is server + { + sinkWriterImpl.logTrailer( + seq, + statusDescription, + nonEmptyMetadata, + Logger.LOGGER_SERVER, + callId, + /*peerAddress=*/ null); + verify(sink).write( + base.toBuilder() + .clearPeer() + .setLogger(Logger.LOGGER_SERVER) + .build()); + } + + // peerAddress is null + { + sinkWriterImpl.logTrailer( + seq, + statusDescription, + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + /*peerAddress=*/ null); + verify(sink).write( + base.toBuilder() + .clearPeer() + .build()); + } + + // status code is present but description is null + { + sinkWriterImpl.logTrailer( + seq, + statusDescription.getCode().toStatus(), // strip the description + nonEmptyMetadata, + Logger.LOGGER_CLIENT, + callId, + peerAddress); + verify(sink).write( + base.toBuilder() + .setTrailer(base.getTrailer().toBuilder().clearStatusMessage()) + .build()); + } + + // status proto always logged if present (com.google.rpc.Status), + { + int zeroHeaderBytes = 0; + SinkWriterImpl truncatingWriter = new SinkWriterImpl( + sink, timeProvider, zeroHeaderBytes, MESSAGE_LIMIT); + com.google.rpc.Status statusProto = com.google.rpc.Status.newBuilder() + .addDetails( + Any.pack(StringValue.newBuilder().setValue("arbitrarypayload").build())) + .setCode(Status.INTERNAL.getCode().value()) + .setMessage("status detail string") + .build(); + StatusException statusException + = StatusProto.toStatusException(statusProto, nonEmptyMetadata); + truncatingWriter.logTrailer( + seq, + statusException.getStatus(), + statusException.getTrailers(), + Logger.LOGGER_CLIENT, + callId, + peerAddress); + verify(sink).write( + base.toBuilder() + .setTrailer( + builder.getTrailerBuilder() + .setStatusMessage("status detail string") + .setStatusDetails(ByteString.copyFrom(statusProto.toByteArray())) + .setMetadata(io.grpc.binarylog.v1.Metadata.getDefaultInstance())) + .build()); + } + } + + @Test + public void alwaysLoggedMetadata_grpcTraceBin() throws Exception { + Metadata.Key key + = Metadata.Key.of("grpc-trace-bin", Metadata.BINARY_BYTE_MARSHALLER); + Metadata metadata = new Metadata(); + metadata.put(key, new byte[1]); + int zeroHeaderBytes = 0; + MaybeTruncated pair = + createMetadataProto(metadata, zeroHeaderBytes); + assertEquals( + key.name(), + Iterables.getOnlyElement(pair.proto.getEntryBuilderList()).getKey()); + assertFalse(pair.truncated); + } + + @Test + public void neverLoggedMetadata_grpcStatusDetilsBin() throws Exception { + Metadata.Key key + = Metadata.Key.of("grpc-status-details-bin", Metadata.BINARY_BYTE_MARSHALLER); + Metadata metadata = new Metadata(); + metadata.put(key, new byte[1]); + int unlimitedHeaderBytes = Integer.MAX_VALUE; + MaybeTruncated pair + = createMetadataProto(metadata, unlimitedHeaderBytes); + assertThat(pair.proto.getEntryBuilderList()).isEmpty(); + assertFalse(pair.truncated); + } + + @Test + public void logRpcMessage() throws Exception { + long seq = 1; + long callId = 1000; + GrpcLogEntry base = messageToProtoTestHelper(message, MESSAGE_LIMIT).toBuilder() + .setTimestamp(timestamp) + .setType(EventType.EVENT_TYPE_CLIENT_MESSAGE) + .setLogger(Logger.LOGGER_CLIENT) + .setSequenceIdWithinCall(1) + .setCallId(callId) + .build(); + { + sinkWriterImpl.logRpcMessage( + seq, + EventType.EVENT_TYPE_CLIENT_MESSAGE, + BYTEARRAY_MARSHALLER, + message, + Logger.LOGGER_CLIENT, + callId); + verify(sink).write(base); + } + + // server messsage + { + sinkWriterImpl.logRpcMessage( + seq, + EventType.EVENT_TYPE_SERVER_MESSAGE, + BYTEARRAY_MARSHALLER, + message, + Logger.LOGGER_CLIENT, + callId); + verify(sink).write( + base.toBuilder() + .setType(EventType.EVENT_TYPE_SERVER_MESSAGE) + .build()); + } + + // logger is server + { + sinkWriterImpl.logRpcMessage( + seq, + EventType.EVENT_TYPE_CLIENT_MESSAGE, + BYTEARRAY_MARSHALLER, + message, + Logger.LOGGER_SERVER, + callId); + verify(sink).write( + base.toBuilder() + .setLogger(Logger.LOGGER_SERVER) + .build()); + } + } + + @Test + public void getPeerSocketTest() { + assertNull(getPeerSocket(Attributes.EMPTY)); + assertSame( + peer, + getPeerSocket(Attributes.newBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer).build())); + } + + @Test + public void serverDeadlineLogged() { + final AtomicReference> interceptedCall = + new AtomicReference<>(); + @SuppressWarnings("unchecked") + final ServerCall.Listener mockListener = mock(ServerCall.Listener.class); + + final MethodDescriptor method = + MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("service/method") + .setRequestMarshaller(BYTEARRAY_MARSHALLER) + .setResponseMarshaller(BYTEARRAY_MARSHALLER) + .build(); + + // We expect the contents of the "grpc-timeout" header to be installed the context + Context.current() + .withDeadlineAfter(1, TimeUnit.SECONDS, Executors.newSingleThreadScheduledExecutor()) + .run(new Runnable() { + @Override + public void run() { + ServerCall.Listener unused = + new BinlogHelper(mockSinkWriter) + .getServerInterceptor(CALL_ID) + .interceptCall( + new NoopServerCall() { + @Override + public MethodDescriptor getMethodDescriptor() { + return method; + } + }, + new Metadata(), + new ServerCallHandler() { + @Override + public ServerCall.Listener startCall( + ServerCall call, + Metadata headers) { + interceptedCall.set(call); + return mockListener; + } + }); + } + }); + ArgumentCaptor timeoutCaptor = ArgumentCaptor.forClass(Duration.class); + verify(mockSinkWriter).logClientHeader( + /*seq=*/ eq(1L), + eq("service/method"), + ArgumentMatchers.isNull(), + timeoutCaptor.capture(), + any(Metadata.class), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID), + ArgumentMatchers.isNull()); + verifyNoMoreInteractions(mockSinkWriter); + Duration timeout = timeoutCaptor.getValue(); + assertThat(TimeUnit.SECONDS.toNanos(1) - Durations.toNanos(timeout)) + .isAtMost(TimeUnit.MILLISECONDS.toNanos(250)); + } + + @Test + public void clientDeadlineLogged_deadlineSetViaCallOption() { + MethodDescriptor method = + MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("service/method") + .setRequestMarshaller(BYTEARRAY_MARSHALLER) + .setResponseMarshaller(BYTEARRAY_MARSHALLER) + .build(); + @SuppressWarnings("unchecked") + ClientCall.Listener mockListener = mock(ClientCall.Listener.class); + + ClientCall call = + new BinlogHelper(mockSinkWriter) + .getClientInterceptor(CALL_ID) + .interceptCall( + method, + CallOptions.DEFAULT.withDeadlineAfter(1, TimeUnit.SECONDS), + new Channel() { + @Override + public ClientCall newCall( + MethodDescriptor methodDescriptor, + CallOptions callOptions) { + return new NoopClientCall<>(); + } + + @Override + public String authority() { + return null; + } + }); + call.start(mockListener, new Metadata()); + ArgumentCaptor callOptTimeoutCaptor = ArgumentCaptor.forClass(Duration.class); + verify(mockSinkWriter) + .logClientHeader( + anyLong(), + AdditionalMatchers.or(ArgumentMatchers.isNull(), anyString()), + AdditionalMatchers.or(ArgumentMatchers.isNull(), anyString()), + callOptTimeoutCaptor.capture(), + any(Metadata.class), + any(GrpcLogEntry.Logger.class), + anyLong(), + AdditionalMatchers.or(ArgumentMatchers.isNull(), + ArgumentMatchers.any())); + Duration timeout = callOptTimeoutCaptor.getValue(); + assertThat(TimeUnit.SECONDS.toNanos(1) - Durations.toNanos(timeout)) + .isAtMost(TimeUnit.MILLISECONDS.toNanos(250)); + } + + @Test + public void clientDeadlineLogged_deadlineSetViaContext() throws Exception { + // important: deadline is read from the ctx where call was created + final SettableFuture> callFuture = SettableFuture.create(); + Context.current() + .withDeadline( + Deadline.after(1, TimeUnit.SECONDS), Executors.newSingleThreadScheduledExecutor()) + .run(new Runnable() { + @Override + public void run() { + MethodDescriptor method = + MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("service/method") + .setRequestMarshaller(BYTEARRAY_MARSHALLER) + .setResponseMarshaller(BYTEARRAY_MARSHALLER) + .build(); + + callFuture.set(new BinlogHelper(mockSinkWriter) + .getClientInterceptor(CALL_ID) + .interceptCall( + method, + CallOptions.DEFAULT.withDeadlineAfter(1, TimeUnit.SECONDS), + new Channel() { + @Override + public ClientCall newCall( + MethodDescriptor methodDescriptor, + CallOptions callOptions) { + return new NoopClientCall<>(); + } + + @Override + public String authority() { + return null; + } + })); + } + }); + @SuppressWarnings("unchecked") + ClientCall.Listener mockListener = mock(ClientCall.Listener.class); + callFuture.get().start(mockListener, new Metadata()); + ArgumentCaptor callOptTimeoutCaptor = ArgumentCaptor.forClass(Duration.class); + verify(mockSinkWriter) + .logClientHeader( + anyLong(), + anyString(), + ArgumentMatchers.any(), + callOptTimeoutCaptor.capture(), + any(Metadata.class), + any(GrpcLogEntry.Logger.class), + anyLong(), + AdditionalMatchers.or(ArgumentMatchers.isNull(), + ArgumentMatchers.any())); + Duration timeout = callOptTimeoutCaptor.getValue(); + assertThat(TimeUnit.SECONDS.toNanos(1) - Durations.toNanos(timeout)) + .isAtMost(TimeUnit.MILLISECONDS.toNanos(250)); + } + + @Test + public void clientInterceptor() throws Exception { + final AtomicReference> interceptedListener = + new AtomicReference<>(); + // capture these manually because ClientCall can not be mocked + final AtomicReference actualClientInitial = new AtomicReference<>(); + final AtomicReference actualRequest = new AtomicReference<>(); + + final SettableFuture halfCloseCalled = SettableFuture.create(); + final SettableFuture cancelCalled = SettableFuture.create(); + Channel channel = new Channel() { + @Override + public ClientCall newCall( + MethodDescriptor methodDescriptor, CallOptions callOptions) { + return new NoopClientCall() { + @Override + @SuppressWarnings("unchecked") + public void start(Listener responseListener, Metadata headers) { + interceptedListener.set((Listener) responseListener); + actualClientInitial.set(headers); + } + + @Override + public void sendMessage(RequestT message) { + actualRequest.set(message); + } + + @Override + public void cancel(String message, Throwable cause) { + cancelCalled.set(null); + } + + @Override + public void halfClose() { + halfCloseCalled.set(null); + } + + @Override + public Attributes getAttributes() { + return Attributes.newBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer).build(); + } + }; + } + + @Override + public String authority() { + return "the-authority"; + } + }; + + @SuppressWarnings("unchecked") + ClientCall.Listener mockListener = mock(ClientCall.Listener.class); + + MethodDescriptor method = + MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("service/method") + .setRequestMarshaller(BYTEARRAY_MARSHALLER) + .setResponseMarshaller(BYTEARRAY_MARSHALLER) + .build(); + ClientCall interceptedCall = + new BinlogHelper(mockSinkWriter) + .getClientInterceptor(CALL_ID) + .interceptCall( + method, + CallOptions.DEFAULT, + channel); + + // send client header + { + Metadata clientInitial = new Metadata(); + interceptedCall.start(mockListener, clientInitial); + verify(mockSinkWriter).logClientHeader( + /*seq=*/ eq(1L), + eq("service/method"), + eq("the-authority"), + ArgumentMatchers.isNull(), + same(clientInitial), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID), + ArgumentMatchers.isNull()); + verifyNoMoreInteractions(mockSinkWriter); + assertSame(clientInitial, actualClientInitial.get()); + } + + // receive server header + { + Metadata serverInitial = new Metadata(); + interceptedListener.get().onHeaders(serverInitial); + verify(mockSinkWriter).logServerHeader( + /*seq=*/ eq(2L), + same(serverInitial), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID), + same(peer)); + verifyNoMoreInteractions(mockSinkWriter); + verify(mockListener).onHeaders(same(serverInitial)); + } + + // send client msg + { + byte[] request = "this is a request".getBytes(US_ASCII); + interceptedCall.sendMessage(request); + verify(mockSinkWriter).logRpcMessage( + /*seq=*/ eq(3L), + eq(EventType.EVENT_TYPE_CLIENT_MESSAGE), + same(BYTEARRAY_MARSHALLER), + same(request), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID)); + verifyNoMoreInteractions(mockSinkWriter); + assertSame(request, actualRequest.get()); + } + + // client half close + { + interceptedCall.halfClose(); + verify(mockSinkWriter).logHalfClose( + /*seq=*/ eq(4L), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID)); + halfCloseCalled.get(1, TimeUnit.SECONDS); + verifyNoMoreInteractions(mockSinkWriter); + } + + // receive server msg + { + byte[] response = "this is a response".getBytes(US_ASCII); + interceptedListener.get().onMessage(response); + verify(mockSinkWriter).logRpcMessage( + /*seq=*/ eq(5L), + eq(EventType.EVENT_TYPE_SERVER_MESSAGE), + same(BYTEARRAY_MARSHALLER), + same(response), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID)); + verifyNoMoreInteractions(mockSinkWriter); + verify(mockListener).onMessage(same(response)); + } + + // receive trailer + { + Status status = Status.INTERNAL.withDescription("some description"); + Metadata trailers = new Metadata(); + + interceptedListener.get().onClose(status, trailers); + verify(mockSinkWriter).logTrailer( + /*seq=*/ eq(6L), + same(status), + same(trailers), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID), + ArgumentMatchers.isNull()); + verifyNoMoreInteractions(mockSinkWriter); + verify(mockListener).onClose(same(status), same(trailers)); + } + + // cancel + { + interceptedCall.cancel(null, null); + verify(mockSinkWriter).logCancel( + /*seq=*/ eq(7L), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID)); + cancelCalled.get(1, TimeUnit.SECONDS); + } + } + + @Test + public void clientInterceptor_trailersOnlyResponseLogsPeerAddress() throws Exception { + final AtomicReference> interceptedListener = + new AtomicReference<>(); + // capture these manually because ClientCall can not be mocked + final AtomicReference actualClientInitial = new AtomicReference<>(); + final AtomicReference actualRequest = new AtomicReference<>(); + + Channel channel = new Channel() { + @Override + public ClientCall newCall( + MethodDescriptor methodDescriptor, CallOptions callOptions) { + return new NoopClientCall() { + @Override + @SuppressWarnings("unchecked") + public void start(Listener responseListener, Metadata headers) { + interceptedListener.set((Listener) responseListener); + actualClientInitial.set(headers); + } + + @Override + public void sendMessage(RequestT message) { + actualRequest.set(message); + } + + @Override + public Attributes getAttributes() { + return Attributes.newBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer).build(); + } + }; + } + + @Override + public String authority() { + return "the-authority"; + } + }; + + @SuppressWarnings("unchecked") + ClientCall.Listener mockListener = mock(ClientCall.Listener.class); + + MethodDescriptor method = + MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("service/method") + .setRequestMarshaller(BYTEARRAY_MARSHALLER) + .setResponseMarshaller(BYTEARRAY_MARSHALLER) + .build(); + ClientCall interceptedCall = + new BinlogHelper(mockSinkWriter) + .getClientInterceptor(CALL_ID) + .interceptCall( + method, + CallOptions.DEFAULT.withDeadlineAfter(1, TimeUnit.SECONDS), + channel); + Metadata clientInitial = new Metadata(); + interceptedCall.start(mockListener, clientInitial); + verify(mockSinkWriter).logClientHeader( + /*seq=*/ eq(1L), + anyString(), + anyString(), + any(Duration.class), + any(Metadata.class), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID), + ArgumentMatchers.isNull()); + verifyNoMoreInteractions(mockSinkWriter); + + // trailer only response + { + Status status = Status.INTERNAL.withDescription("some description"); + Metadata trailers = new Metadata(); + + interceptedListener.get().onClose(status, trailers); + verify(mockSinkWriter).logTrailer( + /*seq=*/ eq(2L), + same(status), + same(trailers), + eq(Logger.LOGGER_CLIENT), + eq(CALL_ID), + same(peer)); + verifyNoMoreInteractions(mockSinkWriter); + verify(mockListener).onClose(same(status), same(trailers)); + } + } + + @Test + public void serverInterceptor() throws Exception { + final AtomicReference> interceptedCall = + new AtomicReference<>(); + ServerCall.Listener capturedListener; + @SuppressWarnings("unchecked") + final ServerCall.Listener mockListener = mock(ServerCall.Listener.class); + // capture these manually because ServerCall can not be mocked + final AtomicReference actualServerInitial = new AtomicReference<>(); + final AtomicReference actualResponse = new AtomicReference<>(); + final AtomicReference actualStatus = new AtomicReference<>(); + final AtomicReference actualTrailers = new AtomicReference<>(); + + // begin call and receive client header + { + Metadata clientInitial = new Metadata(); + final MethodDescriptor method = + MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("service/method") + .setRequestMarshaller(BYTEARRAY_MARSHALLER) + .setResponseMarshaller(BYTEARRAY_MARSHALLER) + .build(); + capturedListener = + new BinlogHelper(mockSinkWriter) + .getServerInterceptor(CALL_ID) + .interceptCall( + new NoopServerCall() { + @Override + public void sendHeaders(Metadata headers) { + actualServerInitial.set(headers); + } + + @Override + public void sendMessage(byte[] message) { + actualResponse.set(message); + } + + @Override + public void close(Status status, Metadata trailers) { + actualStatus.set(status); + actualTrailers.set(trailers); + } + + @Override + public MethodDescriptor getMethodDescriptor() { + return method; + } + + @Override + public Attributes getAttributes() { + return Attributes + .newBuilder() + .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer) + .build(); + } + + @Override + public String getAuthority() { + return "the-authority"; + } + }, + clientInitial, + new ServerCallHandler() { + @Override + public ServerCall.Listener startCall( + ServerCall call, + Metadata headers) { + interceptedCall.set(call); + return mockListener; + } + }); + verify(mockSinkWriter).logClientHeader( + /*seq=*/ eq(1L), + eq("service/method"), + eq("the-authority"), + ArgumentMatchers.isNull(), + same(clientInitial), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID), + same(peer)); + verifyNoMoreInteractions(mockSinkWriter); + } + + // send server header + { + Metadata serverInital = new Metadata(); + interceptedCall.get().sendHeaders(serverInital); + verify(mockSinkWriter).logServerHeader( + /*seq=*/ eq(2L), + same(serverInital), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID), + ArgumentMatchers.isNull()); + verifyNoMoreInteractions(mockSinkWriter); + assertSame(serverInital, actualServerInitial.get()); + } + + // receive client msg + { + byte[] request = "this is a request".getBytes(US_ASCII); + capturedListener.onMessage(request); + verify(mockSinkWriter).logRpcMessage( + /*seq=*/ eq(3L), + eq(EventType.EVENT_TYPE_CLIENT_MESSAGE), + same(BYTEARRAY_MARSHALLER), + same(request), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID)); + verifyNoMoreInteractions(mockSinkWriter); + verify(mockListener).onMessage(same(request)); + } + + // client half close + { + capturedListener.onHalfClose(); + verify(mockSinkWriter).logHalfClose( + eq(4L), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID)); + verifyNoMoreInteractions(mockSinkWriter); + verify(mockListener).onHalfClose(); + } + + // send server msg + { + byte[] response = "this is a response".getBytes(US_ASCII); + interceptedCall.get().sendMessage(response); + verify(mockSinkWriter).logRpcMessage( + /*seq=*/ eq(5L), + eq(EventType.EVENT_TYPE_SERVER_MESSAGE), + same(BYTEARRAY_MARSHALLER), + same(response), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID)); + verifyNoMoreInteractions(mockSinkWriter); + assertSame(response, actualResponse.get()); + } + + // send trailer + { + Status status = Status.INTERNAL.withDescription("some description"); + Metadata trailers = new Metadata(); + interceptedCall.get().close(status, trailers); + verify(mockSinkWriter).logTrailer( + /*seq=*/ eq(6L), + same(status), + same(trailers), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID), + ArgumentMatchers.isNull()); + verifyNoMoreInteractions(mockSinkWriter); + assertSame(status, actualStatus.get()); + assertSame(trailers, actualTrailers.get()); + } + + // cancel + { + capturedListener.onCancel(); + verify(mockSinkWriter).logCancel( + /*seq=*/ eq(7L), + eq(Logger.LOGGER_SERVER), + eq(CALL_ID)); + verify(mockListener).onCancel(); + } + } + + /** A builder class to make unit test code more readable. */ + private static final class Builder { + int maxHeaderBytes = 0; + int maxMessageBytes = 0; + + Builder header(int bytes) { + maxHeaderBytes = bytes; + return this; + } + + Builder msg(int bytes) { + maxMessageBytes = bytes; + return this; + } + + BinlogHelper build() { + return new BinlogHelper( + new SinkWriterImpl(mock(BinaryLogSink.class), null, maxHeaderBytes, maxMessageBytes)); + } + } + + private static void assertSameLimits(BinlogHelper a, BinlogHelper b) { + assertEquals(a.writer.getMaxMessageBytes(), b.writer.getMaxMessageBytes()); + assertEquals(a.writer.getMaxHeaderBytes(), b.writer.getMaxHeaderBytes()); + } + + private BinlogHelper makeLog(String factoryConfigStr, String lookup) { + return new BinlogHelper.FactoryImpl(sink, factoryConfigStr).getLog(lookup); + } + + private BinlogHelper makeOptions(String logConfigStr) { + return FactoryImpl.createBinaryLog(sink, logConfigStr); + } + + private static GrpcLogEntry metadataToProtoTestHelper( + EventType type, Metadata metadata, int maxHeaderBytes) { + GrpcLogEntry.Builder builder = GrpcLogEntry.newBuilder(); + MaybeTruncated pair + = BinlogHelper.createMetadataProto(metadata, maxHeaderBytes); + switch (type) { + case EVENT_TYPE_CLIENT_HEADER: + builder.setClientHeader(ClientHeader.newBuilder().setMetadata(pair.proto)); + break; + case EVENT_TYPE_SERVER_HEADER: + builder.setServerHeader(ServerHeader.newBuilder().setMetadata(pair.proto)); + break; + case EVENT_TYPE_SERVER_TRAILER: + builder.setTrailer(Trailer.newBuilder().setMetadata(pair.proto)); + break; + default: + throw new IllegalArgumentException(); + } + builder.setType(type).setPayloadTruncated(pair.truncated); + return builder.build(); + } + + private static GrpcLogEntry messageToProtoTestHelper( + byte[] message, int maxMessageBytes) { + GrpcLogEntry.Builder builder = GrpcLogEntry.newBuilder(); + MaybeTruncated pair + = BinlogHelper.createMessageProto(message, maxMessageBytes); + builder.setMessage(pair.proto).setPayloadTruncated(pair.truncated); + return builder.build(); + } +} diff --git a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java new file mode 100644 index 00000000000..8692f6fcde0 --- /dev/null +++ b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java @@ -0,0 +1,949 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import static com.google.common.truth.Truth.assertThat; +import static io.grpc.InternalChannelz.id; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.Any; +import com.google.protobuf.ByteString; +import com.google.protobuf.Int64Value; +import com.google.protobuf.Message; +import com.google.protobuf.util.Durations; +import com.google.protobuf.util.Timestamps; +import io.grpc.ConnectivityState; +import io.grpc.InternalChannelz; +import io.grpc.InternalChannelz.ChannelStats; +import io.grpc.InternalChannelz.ChannelTrace.Event; +import io.grpc.InternalChannelz.ChannelTrace.Event.Severity; +import io.grpc.InternalChannelz.RootChannelList; +import io.grpc.InternalChannelz.ServerList; +import io.grpc.InternalChannelz.ServerSocketsList; +import io.grpc.InternalChannelz.ServerStats; +import io.grpc.InternalChannelz.SocketOptions; +import io.grpc.InternalChannelz.SocketStats; +import io.grpc.InternalInstrumented; +import io.grpc.InternalWithLogId; +import io.grpc.channelz.v1.Address; +import io.grpc.channelz.v1.Address.OtherAddress; +import io.grpc.channelz.v1.Address.TcpIpAddress; +import io.grpc.channelz.v1.Address.UdsAddress; +import io.grpc.channelz.v1.Channel; +import io.grpc.channelz.v1.ChannelConnectivityState; +import io.grpc.channelz.v1.ChannelConnectivityState.State; +import io.grpc.channelz.v1.ChannelData; +import io.grpc.channelz.v1.ChannelRef; +import io.grpc.channelz.v1.ChannelTrace; +import io.grpc.channelz.v1.ChannelTraceEvent; +import io.grpc.channelz.v1.GetChannelRequest; +import io.grpc.channelz.v1.GetServerSocketsResponse; +import io.grpc.channelz.v1.GetServersResponse; +import io.grpc.channelz.v1.GetTopChannelsResponse; +import io.grpc.channelz.v1.Security; +import io.grpc.channelz.v1.Security.OtherSecurity; +import io.grpc.channelz.v1.Security.Tls; +import io.grpc.channelz.v1.Server; +import io.grpc.channelz.v1.ServerData; +import io.grpc.channelz.v1.ServerRef; +import io.grpc.channelz.v1.Socket; +import io.grpc.channelz.v1.SocketData; +import io.grpc.channelz.v1.SocketOption; +import io.grpc.channelz.v1.SocketOptionLinger; +import io.grpc.channelz.v1.SocketOptionTcpInfo; +import io.grpc.channelz.v1.SocketOptionTimeout; +import io.grpc.channelz.v1.SocketRef; +import io.grpc.channelz.v1.Subchannel; +import io.grpc.channelz.v1.SubchannelRef; +import io.grpc.services.ChannelzTestHelper.TestChannel; +import io.grpc.services.ChannelzTestHelper.TestListenSocket; +import io.grpc.services.ChannelzTestHelper.TestServer; +import io.grpc.services.ChannelzTestHelper.TestSocket; +import io.netty.channel.unix.DomainSocketAddress; +import java.net.Inet4Address; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class ChannelzProtoUtilTest { + + private final TestChannel channel = new TestChannel(); + private final ChannelRef channelRef = ChannelRef + .newBuilder() + .setName(channel.toString()) + .setChannelId(channel.getLogId().getId()) + .build(); + private final ChannelData channelData = ChannelData + .newBuilder() + .setTarget("sometarget") + .setState(ChannelConnectivityState.newBuilder().setState(State.READY)) + .setCallsStarted(1) + .setCallsSucceeded(2) + .setCallsFailed(3) + .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) + .build(); + private final Channel channelProto = Channel + .newBuilder() + .setRef(channelRef) + .setData(channelData) + .build(); + + private final TestChannel subchannel = new TestChannel(); + private final SubchannelRef subchannelRef = SubchannelRef + .newBuilder() + .setName(subchannel.toString()) + .setSubchannelId(subchannel.getLogId().getId()) + .build(); + private final ChannelData subchannelData = ChannelData + .newBuilder() + .setTarget("sometarget") + .setState(ChannelConnectivityState.newBuilder().setState(State.READY)) + .setCallsStarted(1) + .setCallsSucceeded(2) + .setCallsFailed(3) + .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) + .build(); + private final Subchannel subchannelProto = Subchannel + .newBuilder() + .setRef(subchannelRef) + .setData(subchannelData) + .build(); + + private final TestServer server = new TestServer(); + private final ServerRef serverRef = ServerRef + .newBuilder() + .setName(server.toString()) + .setServerId(server.getLogId().getId()) + .build(); + private final ServerData serverData = ServerData + .newBuilder() + .setCallsStarted(1) + .setCallsSucceeded(2) + .setCallsFailed(3) + .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) + .build(); + private final Server serverProto = Server + .newBuilder() + .setRef(serverRef) + .setData(serverData) + .build(); + + private final SocketOption sockOptLingerDisabled = SocketOption + .newBuilder() + .setName("SO_LINGER") + .setAdditional( + Any.pack(SocketOptionLinger.getDefaultInstance())) + .build(); + + private final SocketOption sockOptlinger10s = SocketOption + .newBuilder() + .setName("SO_LINGER") + .setAdditional( + Any.pack(SocketOptionLinger + .newBuilder() + .setActive(true) + .setDuration(Durations.fromSeconds(10)) + .build())) + .build(); + + private final SocketOption sockOptTimeout200ms = SocketOption + .newBuilder() + .setName("SO_TIMEOUT") + .setAdditional( + Any.pack(SocketOptionTimeout + .newBuilder() + .setDuration(Durations.fromMillis(200)) + .build()) + ).build(); + + private final SocketOption sockOptAdditional = SocketOption + .newBuilder() + .setName("SO_MADE_UP_OPTION") + .setValue("some-made-up-value") + .build(); + + private final InternalChannelz.TcpInfo channelzTcpInfo + = new InternalChannelz.TcpInfo.Builder() + .setState(70) + .setCaState(71) + .setRetransmits(72) + .setProbes(73) + .setBackoff(74) + .setOptions(75) + .setSndWscale(76) + .setRcvWscale(77) + .setRto(78) + .setAto(79) + .setSndMss(710) + .setRcvMss(711) + .setUnacked(712) + .setSacked(713) + .setLost(714) + .setRetrans(715) + .setFackets(716) + .setLastDataSent(717) + .setLastAckSent(718) + .setLastDataRecv(719) + .setLastAckRecv(720) + .setPmtu(721) + .setRcvSsthresh(722) + .setRtt(723) + .setRttvar(724) + .setSndSsthresh(725) + .setSndCwnd(726) + .setAdvmss(727) + .setReordering(728) + .build(); + + private final SocketOption socketOptionTcpInfo = SocketOption + .newBuilder() + .setName("TCP_INFO") + .setAdditional( + Any.pack( + SocketOptionTcpInfo.newBuilder() + .setTcpiState(70) + .setTcpiCaState(71) + .setTcpiRetransmits(72) + .setTcpiProbes(73) + .setTcpiBackoff(74) + .setTcpiOptions(75) + .setTcpiSndWscale(76) + .setTcpiRcvWscale(77) + .setTcpiRto(78) + .setTcpiAto(79) + .setTcpiSndMss(710) + .setTcpiRcvMss(711) + .setTcpiUnacked(712) + .setTcpiSacked(713) + .setTcpiLost(714) + .setTcpiRetrans(715) + .setTcpiFackets(716) + .setTcpiLastDataSent(717) + .setTcpiLastAckSent(718) + .setTcpiLastDataRecv(719) + .setTcpiLastAckRecv(720) + .setTcpiPmtu(721) + .setTcpiRcvSsthresh(722) + .setTcpiRtt(723) + .setTcpiRttvar(724) + .setTcpiSndSsthresh(725) + .setTcpiSndCwnd(726) + .setTcpiAdvmss(727) + .setTcpiReordering(728) + .build())) + .build(); + + private final TestListenSocket listenSocket = new TestListenSocket(); + private final SocketRef listenSocketRef = SocketRef + .newBuilder() + .setName(listenSocket.toString()) + .setSocketId(id(listenSocket)) + .build(); + private final Address listenAddress = Address + .newBuilder() + .setTcpipAddress( + TcpIpAddress + .newBuilder() + .setIpAddress(ByteString.copyFrom( + ((InetSocketAddress) listenSocket.listenAddress).getAddress().getAddress())) + .setPort(1234)) + .build(); + + private final TestSocket socket = new TestSocket(); + private final SocketRef socketRef = SocketRef + .newBuilder() + .setName(socket.toString()) + .setSocketId(socket.getLogId().getId()) + .build(); + private final SocketData socketDataWithDataNoSockOpts = SocketData + .newBuilder() + .setStreamsStarted(1) + .setLastLocalStreamCreatedTimestamp(Timestamps.fromNanos(2)) + .setLastRemoteStreamCreatedTimestamp(Timestamps.fromNanos(3)) + .setStreamsSucceeded(4) + .setStreamsFailed(5) + .setMessagesSent(6) + .setMessagesReceived(7) + .setKeepAlivesSent(8) + .setLastMessageSentTimestamp(Timestamps.fromNanos(9)) + .setLastMessageReceivedTimestamp(Timestamps.fromNanos(10)) + .setLocalFlowControlWindow(Int64Value.newBuilder().setValue(11)) + .setRemoteFlowControlWindow(Int64Value.newBuilder().setValue(12)) + .build(); + private final Address localAddress = Address + .newBuilder() + .setTcpipAddress( + TcpIpAddress + .newBuilder() + .setIpAddress(ByteString.copyFrom( + ((InetSocketAddress) socket.local).getAddress().getAddress())) + .setPort(1000)) + .build(); + private final Address remoteAddress = Address + .newBuilder() + .setTcpipAddress( + TcpIpAddress + .newBuilder() + .setIpAddress(ByteString.copyFrom( + ((InetSocketAddress) socket.remote).getAddress().getAddress())) + .setPort(1000)) + .build(); + + private final ChannelTrace channelTrace = ChannelTrace + .newBuilder() + .setNumEventsLogged(1234) + .setCreationTimestamp(Timestamps.fromNanos(1000)) + .build(); + + @Test + public void toChannelRef() { + assertEquals(channelRef, ChannelzProtoUtil.toChannelRef(channel)); + } + + @Test + public void toSubchannelRef() { + assertEquals(subchannelRef, ChannelzProtoUtil.toSubchannelRef(subchannel)); + } + + @Test + public void toServerRef() { + assertEquals(serverRef, ChannelzProtoUtil.toServerRef(server)); + } + + @Test + public void toSocketRef() { + assertEquals(socketRef, ChannelzProtoUtil.toSocketRef(socket)); + } + + @Test + public void toState() { + for (ConnectivityState connectivityState : ConnectivityState.values()) { + assertEquals( + connectivityState.name(), + ChannelzProtoUtil.toState(connectivityState).getValueDescriptor().getName()); + } + assertEquals(State.UNKNOWN, ChannelzProtoUtil.toState(null)); + } + + @Test + public void toSocket_withDataNoOptions() throws Exception { + assertEquals( + Socket + .newBuilder() + .setRef(socketRef) + .setLocal(localAddress) + .setRemote(remoteAddress) + .setData(socketDataWithDataNoSockOpts) + .build(), + ChannelzProtoUtil.toSocket(socket)); + } + + @Test + public void toSocket_noDataWithOptions() throws Exception { + assertEquals( + Socket + .newBuilder() + .setRef(listenSocketRef) + .setLocal(listenAddress) + .setData( + SocketData + .newBuilder() + .addOption( + SocketOption + .newBuilder() + .setName("listen_option") + .setValue("listen_option_value"))) + .build(), + ChannelzProtoUtil.toSocket(listenSocket)); + } + + @Test + public void toSocket_withDataWithOptions() throws Exception { + socket.socketOptions + = new SocketOptions(null, null, null, ImmutableMap.of("test_name", "test_value")); + assertEquals( + Socket + .newBuilder() + .setRef(socketRef) + .setLocal(localAddress) + .setRemote(remoteAddress) + .setData( + SocketData + .newBuilder(socketDataWithDataNoSockOpts) + .addOption( + SocketOption.newBuilder() + .setName("test_name").setValue("test_value"))) + .build(), + ChannelzProtoUtil.toSocket(socket)); + } + + @Test + public void extractSocketData() throws Exception { + // no options + assertEquals( + socketDataWithDataNoSockOpts, + ChannelzProtoUtil.extractSocketData(socket.getStats().get())); + + // with options + socket.socketOptions = toBuilder(socket.socketOptions) + .setSocketOptionLingerSeconds(10) + .setTcpInfo(channelzTcpInfo) + .build(); + assertEquals( + socketDataWithDataNoSockOpts + .toBuilder() + .addOption(sockOptlinger10s) + .addOption(socketOptionTcpInfo) + .build(), + ChannelzProtoUtil.extractSocketData(socket.getStats().get())); + } + + @Test + public void toSocketData() throws Exception { + assertEquals( + socketDataWithDataNoSockOpts + .toBuilder() + .build(), + ChannelzProtoUtil.extractSocketData(socket.getStats().get())); + } + + @Test + public void socketSecurityTls() throws Exception { + Certificate local = mock(Certificate.class); + Certificate remote = mock(Certificate.class); + when(local.getEncoded()).thenReturn("localcert".getBytes(Charsets.UTF_8)); + when(remote.getEncoded()).thenReturn("remotecert".getBytes(Charsets.UTF_8)); + + socket.security = new InternalChannelz.Security( + new InternalChannelz.Tls("TLS_NULL_WITH_NULL_NULL", local, remote)); + assertEquals( + Security.newBuilder().setTls( + Tls.newBuilder() + .setStandardName("TLS_NULL_WITH_NULL_NULL") + .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8)) + .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) + .build(), + ChannelzProtoUtil.toSocket(socket).getSecurity()); + + socket.security = new InternalChannelz.Security( + new InternalChannelz.Tls("TLS_NULL_WITH_NULL_NULL", /*localCert=*/ null, remote)); + assertEquals( + Security.newBuilder().setTls( + Tls.newBuilder() + .setStandardName("TLS_NULL_WITH_NULL_NULL") + .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) + .build(), + ChannelzProtoUtil.toSocket(socket).getSecurity()); + + socket.security = new InternalChannelz.Security( + new InternalChannelz.Tls("TLS_NULL_WITH_NULL_NULL", local, /*remoteCert=*/ null)); + assertEquals( + Security.newBuilder().setTls( + Tls.newBuilder() + .setStandardName("TLS_NULL_WITH_NULL_NULL") + .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8))) + .build(), + ChannelzProtoUtil.toSocket(socket).getSecurity()); + } + + @Test + public void socketSecurityOther() throws Exception { + // what is packed here is not important, just pick some proto message + Message contents = GetChannelRequest.newBuilder().setChannelId(1).build(); + Any packed = Any.pack(contents); + socket.security + = new InternalChannelz.Security( + new InternalChannelz.OtherSecurity("other_security", packed)); + assertEquals( + Security.newBuilder().setOther( + OtherSecurity.newBuilder().setName("other_security").setValue(packed)) + .build(), + ChannelzProtoUtil.toSocket(socket).getSecurity()); + } + + @Test + public void toAddress_inet() throws Exception { + InetSocketAddress inet4 = new InetSocketAddress(Inet4Address.getByName("10.0.0.1"), 1000); + assertEquals( + Address.newBuilder().setTcpipAddress( + TcpIpAddress + .newBuilder() + .setIpAddress(ByteString.copyFrom(inet4.getAddress().getAddress())) + .setPort(1000)) + .build(), + ChannelzProtoUtil.toAddress(inet4)); + } + + @Test + public void toAddress_uds() throws Exception { + String path = "/tmp/foo"; + DomainSocketAddress uds = new DomainSocketAddress(path); + assertEquals( + Address.newBuilder().setUdsAddress( + UdsAddress + .newBuilder() + .setFilename(path)) + .build(), + ChannelzProtoUtil.toAddress(uds)); + } + + @Test + public void toAddress_other() throws Exception { + final String name = "my name"; + SocketAddress other = new SocketAddress() { + @Override + public String toString() { + return name; + } + }; + assertEquals( + Address.newBuilder().setOtherAddress( + OtherAddress + .newBuilder() + .setName(name)) + .build(), + ChannelzProtoUtil.toAddress(other)); + } + + @Test + public void toServer() throws Exception { + // no listen sockets + assertEquals(serverProto, ChannelzProtoUtil.toServer(server)); + + // 1 listen socket + server.serverStats = toBuilder(server.serverStats) + .addListenSockets(ImmutableList.>of(listenSocket)) + .build(); + assertEquals( + serverProto + .toBuilder() + .addListenSocket(listenSocketRef) + .build(), + ChannelzProtoUtil.toServer(server)); + + // multiple listen sockets + TestListenSocket otherListenSocket = new TestListenSocket(); + SocketRef otherListenSocketRef = ChannelzProtoUtil.toSocketRef(otherListenSocket); + server.serverStats = toBuilder(server.serverStats) + .addListenSockets( + ImmutableList.>of(otherListenSocket)) + .build(); + assertEquals( + serverProto + .toBuilder() + .addListenSocket(listenSocketRef) + .addListenSocket(otherListenSocketRef) + .build(), + ChannelzProtoUtil.toServer(server)); + } + + @Test + public void toServerData() throws Exception { + assertEquals(serverData, ChannelzProtoUtil.toServerData(server.serverStats)); + } + + @Test + public void toChannel() throws Exception { + assertEquals(channelProto, ChannelzProtoUtil.toChannel(channel)); + + channel.stats = toBuilder(channel.stats) + .setSubchannels(ImmutableList.of(subchannel)) + .build(); + + assertEquals( + channelProto + .toBuilder() + .addSubchannelRef(subchannelRef) + .build(), + ChannelzProtoUtil.toChannel(channel)); + + TestChannel otherSubchannel = new TestChannel(); + channel.stats = toBuilder(channel.stats) + .setSubchannels(ImmutableList.of(subchannel, otherSubchannel)) + .build(); + assertEquals( + channelProto + .toBuilder() + .addSubchannelRef(subchannelRef) + .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(otherSubchannel)) + .build(), + ChannelzProtoUtil.toChannel(channel)); + } + + @Test + public void extractChannelData() { + assertEquals(channelData, ChannelzProtoUtil.extractChannelData(channel.stats)); + } + + @Test + public void toSubchannel_noChildren() throws Exception { + assertEquals( + subchannelProto, + ChannelzProtoUtil.toSubchannel(subchannel)); + } + + @Test + public void toSubchannel_socketChildren() throws Exception { + subchannel.stats = toBuilder(subchannel.stats) + .setSockets(ImmutableList.of(socket)) + .build(); + + assertEquals( + subchannelProto.toBuilder() + .addSocketRef(socketRef) + .build(), + ChannelzProtoUtil.toSubchannel(subchannel)); + + TestSocket otherSocket = new TestSocket(); + subchannel.stats = toBuilder(subchannel.stats) + .setSockets(ImmutableList.of(socket, otherSocket)) + .build(); + assertEquals( + subchannelProto + .toBuilder() + .addSocketRef(socketRef) + .addSocketRef(ChannelzProtoUtil.toSocketRef(otherSocket)) + .build(), + ChannelzProtoUtil.toSubchannel(subchannel)); + } + + @Test + public void toSubchannel_subchannelChildren() throws Exception { + TestChannel subchannel1 = new TestChannel(); + subchannel.stats = toBuilder(subchannel.stats) + .setSubchannels(ImmutableList.of(subchannel1)) + .build(); + assertEquals( + subchannelProto.toBuilder() + .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(subchannel1)) + .build(), + ChannelzProtoUtil.toSubchannel(subchannel)); + + TestChannel subchannel2 = new TestChannel(); + subchannel.stats = toBuilder(subchannel.stats) + .setSubchannels(ImmutableList.of(subchannel1, subchannel2)) + .build(); + assertEquals( + subchannelProto + .toBuilder() + .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(subchannel1)) + .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(subchannel2)) + .build(), + ChannelzProtoUtil.toSubchannel(subchannel)); + } + + @Test + public void toGetTopChannelsResponse() { + // empty results + assertEquals( + GetTopChannelsResponse.newBuilder().setEnd(true).build(), + ChannelzProtoUtil.toGetTopChannelResponse( + new RootChannelList( + Collections.>emptyList(), true))); + + // 1 result, paginated + assertEquals( + GetTopChannelsResponse + .newBuilder() + .addChannel(channelProto) + .build(), + ChannelzProtoUtil.toGetTopChannelResponse( + new RootChannelList( + ImmutableList.>of(channel), false))); + + // 1 result, end + assertEquals( + GetTopChannelsResponse + .newBuilder() + .addChannel(channelProto) + .setEnd(true) + .build(), + ChannelzProtoUtil.toGetTopChannelResponse( + new RootChannelList( + ImmutableList.>of(channel), true))); + + // 2 results, end + TestChannel channel2 = new TestChannel(); + assertEquals( + GetTopChannelsResponse + .newBuilder() + .addChannel(channelProto) + .addChannel(ChannelzProtoUtil.toChannel(channel2)) + .setEnd(true) + .build(), + ChannelzProtoUtil.toGetTopChannelResponse( + new RootChannelList( + ImmutableList.>of(channel, channel2), true))); + } + + @Test + public void toGetServersResponse() { + // empty results + assertEquals( + GetServersResponse.getDefaultInstance(), + ChannelzProtoUtil.toGetServersResponse( + new ServerList(Collections.>emptyList(), false))); + + // 1 result, paginated + assertEquals( + GetServersResponse + .newBuilder() + .addServer(serverProto) + .build(), + ChannelzProtoUtil.toGetServersResponse( + new ServerList(ImmutableList.>of(server), false))); + + // 1 result, end + assertEquals( + GetServersResponse + .newBuilder() + .addServer(serverProto) + .setEnd(true) + .build(), + ChannelzProtoUtil.toGetServersResponse( + new ServerList(ImmutableList.>of(server), true))); + + TestServer server2 = new TestServer(); + // 2 results, end + assertEquals( + GetServersResponse + .newBuilder() + .addServer(serverProto) + .addServer(ChannelzProtoUtil.toServer(server2)) + .setEnd(true) + .build(), + ChannelzProtoUtil.toGetServersResponse( + new ServerList( + ImmutableList.>of(server, server2), true))); + } + + @Test + public void toGetServerSocketsResponse() { + // empty results + assertEquals( + GetServerSocketsResponse.getDefaultInstance(), + ChannelzProtoUtil.toGetServerSocketsResponse( + new ServerSocketsList(Collections.emptyList(), false))); + + // 1 result, paginated + assertEquals( + GetServerSocketsResponse + .newBuilder() + .addSocketRef(socketRef) + .build(), + ChannelzProtoUtil.toGetServerSocketsResponse( + new ServerSocketsList(ImmutableList.of(socket), false))); + + // 1 result, end + assertEquals( + GetServerSocketsResponse + .newBuilder() + .addSocketRef(socketRef) + .setEnd(true) + .build(), + ChannelzProtoUtil.toGetServerSocketsResponse( + new ServerSocketsList(ImmutableList.of(socket), true))); + + TestSocket socket2 = new TestSocket(); + // 2 results, end + assertEquals( + GetServerSocketsResponse + .newBuilder() + .addSocketRef(socketRef) + .addSocketRef(ChannelzProtoUtil.toSocketRef(socket2)) + .setEnd(true) + .build(), + ChannelzProtoUtil.toGetServerSocketsResponse( + new ServerSocketsList(ImmutableList.of(socket, socket2), true))); + } + + @Test + public void toSocketOptionLinger() { + assertEquals(sockOptLingerDisabled, ChannelzProtoUtil.toSocketOptionLinger(-1)); + assertEquals(sockOptlinger10s, ChannelzProtoUtil.toSocketOptionLinger(10)); + } + + @Test + public void toSocketOptionTimeout() { + assertEquals( + sockOptTimeout200ms, ChannelzProtoUtil.toSocketOptionTimeout("SO_TIMEOUT", 200)); + } + + @Test + public void toSocketOptionAdditional() { + assertEquals( + sockOptAdditional, + ChannelzProtoUtil.toSocketOptionAdditional("SO_MADE_UP_OPTION", "some-made-up-value")); + } + + @Test + public void toSocketOptionTcpInfo() { + assertEquals( + socketOptionTcpInfo, + ChannelzProtoUtil.toSocketOptionTcpInfo(channelzTcpInfo)); + } + + @Test + public void toSocketOptionsList() { + assertThat( + ChannelzProtoUtil.toSocketOptionsList( + new InternalChannelz.SocketOptions.Builder().build())) + .isEmpty(); + + assertThat( + ChannelzProtoUtil.toSocketOptionsList( + new InternalChannelz.SocketOptions.Builder().setSocketOptionLingerSeconds(10).build())) + .containsExactly(sockOptlinger10s); + + assertThat( + ChannelzProtoUtil.toSocketOptionsList( + new InternalChannelz.SocketOptions.Builder().setSocketOptionTimeoutMillis(200).build())) + .containsExactly(sockOptTimeout200ms); + + assertThat( + ChannelzProtoUtil.toSocketOptionsList( + new InternalChannelz.SocketOptions + .Builder() + .addOption("SO_MADE_UP_OPTION", "some-made-up-value") + .build())) + .containsExactly(sockOptAdditional); + + SocketOption otherOption = SocketOption + .newBuilder() + .setName("SO_MADE_UP_OPTION2") + .setValue("some-made-up-value2") + .build(); + assertThat( + ChannelzProtoUtil.toSocketOptionsList( + new InternalChannelz.SocketOptions.Builder() + .addOption("SO_MADE_UP_OPTION", "some-made-up-value") + .addOption("SO_MADE_UP_OPTION2", "some-made-up-value2") + .build())) + .containsExactly(sockOptAdditional, otherOption); + } + + @Test + public void channelTrace_withoutEvents() { + ChannelStats stats = toBuilder(channel.stats) + .setChannelTrace(new InternalChannelz.ChannelTrace.Builder() + .setNumEventsLogged(1234) + .setCreationTimeNanos(1000) + .build()) + .build(); + + ChannelData protoStats = channelData.toBuilder().setTrace(channelTrace).build(); + assertEquals(ChannelzProtoUtil.extractChannelData(stats), protoStats); + } + + @Test + public void channelTrace_withEvents() { + Event event1 = new Event.Builder() + .setDescription("event1") + .setSeverity(Severity.CT_ERROR) + .setTimestampNanos(12) + .setSubchannelRef(subchannel) + .build(); + Event event2 = new Event.Builder() + .setDescription("event2") + .setTimestampNanos(34) + .setSeverity(Severity.CT_INFO) + .setChannelRef(channel) + .build(); + + ChannelStats stats = + toBuilder(channel.stats) + .setChannelTrace( + new InternalChannelz.ChannelTrace.Builder() + .setNumEventsLogged(1234) + .setCreationTimeNanos(1000) + .setEvents(Arrays.asList(event1, event2)) + .build()) + .build(); + + ChannelTraceEvent protoEvent1 = ChannelTraceEvent + .newBuilder() + .setDescription("event1") + .setTimestamp(Timestamps.fromNanos(12)) + .setSeverity(ChannelTraceEvent.Severity.CT_ERROR) + .setSubchannelRef(subchannelRef) + .build(); + ChannelTraceEvent protoEvent2 = ChannelTraceEvent + .newBuilder() + .setDescription("event2") + .setTimestamp(Timestamps.fromNanos(34)) + .setSeverity(ChannelTraceEvent.Severity.CT_INFO) + .setChannelRef(channelRef) + .build(); + ChannelData protoStats = channelData + .toBuilder() + .setTrace(channelTrace + .toBuilder() + .addAllEvents(Arrays.asList(protoEvent1, protoEvent2)) + .build()) + .build(); + assertEquals(ChannelzProtoUtil.extractChannelData(stats), protoStats); + } + + private static ChannelStats.Builder toBuilder(ChannelStats stats) { + ChannelStats.Builder builder = new ChannelStats.Builder() + .setTarget(stats.target) + .setState(stats.state) + .setCallsStarted(stats.callsStarted) + .setCallsSucceeded(stats.callsSucceeded) + .setCallsFailed(stats.callsFailed) + .setLastCallStartedNanos(stats.lastCallStartedNanos); + if (!stats.subchannels.isEmpty()) { + builder.setSubchannels(stats.subchannels); + } + if (!stats.sockets.isEmpty()) { + builder.setSockets(stats.sockets); + } + return builder; + } + + + private static SocketOptions.Builder toBuilder(SocketOptions options) { + SocketOptions.Builder builder = new SocketOptions.Builder() + .setSocketOptionTimeoutMillis(options.soTimeoutMillis) + .setSocketOptionLingerSeconds(options.lingerSeconds); + for (Map.Entry entry : options.others.entrySet()) { + builder.addOption(entry.getKey(), entry.getValue()); + } + return builder; + } + + private static ServerStats.Builder toBuilder(ServerStats stats) { + return new ServerStats.Builder() + .setCallsStarted(stats.callsStarted) + .setCallsSucceeded(stats.callsSucceeded) + .setCallsFailed(stats.callsFailed) + .setLastCallStartedNanos(stats.lastCallStartedNanos) + .addListenSockets(stats.listenSockets); + } +} diff --git a/services/src/test/java/io/grpc/services/ChannelzServiceTest.java b/services/src/test/java/io/grpc/services/ChannelzServiceTest.java new file mode 100644 index 00000000000..0849f8611ad --- /dev/null +++ b/services/src/test/java/io/grpc/services/ChannelzServiceTest.java @@ -0,0 +1,276 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import io.grpc.InternalChannelz; +import io.grpc.Status; +import io.grpc.channelz.v1.GetChannelRequest; +import io.grpc.channelz.v1.GetChannelResponse; +import io.grpc.channelz.v1.GetServerRequest; +import io.grpc.channelz.v1.GetServerResponse; +import io.grpc.channelz.v1.GetServersRequest; +import io.grpc.channelz.v1.GetServersResponse; +import io.grpc.channelz.v1.GetSocketRequest; +import io.grpc.channelz.v1.GetSocketResponse; +import io.grpc.channelz.v1.GetSubchannelRequest; +import io.grpc.channelz.v1.GetSubchannelResponse; +import io.grpc.channelz.v1.GetTopChannelsRequest; +import io.grpc.channelz.v1.GetTopChannelsResponse; +import io.grpc.services.ChannelzTestHelper.TestChannel; +import io.grpc.services.ChannelzTestHelper.TestServer; +import io.grpc.services.ChannelzTestHelper.TestSocket; +import io.grpc.stub.StreamObserver; +import java.util.concurrent.ExecutionException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; + +@SuppressWarnings("deprecation") +@RunWith(JUnit4.class) +public class ChannelzServiceTest { + // small value to force pagination + private static final int MAX_PAGE_SIZE = 1; + + private final InternalChannelz channelz = new InternalChannelz(); + private ChannelzService service = new ChannelzService(channelz, MAX_PAGE_SIZE); + + @Test + public void getTopChannels_empty() { + assertEquals( + GetTopChannelsResponse.newBuilder().setEnd(true).build(), + getTopChannelHelper(0)); + } + + @Test + public void getTopChannels_onePage() throws Exception { + TestChannel root = new TestChannel(); + channelz.addRootChannel(root); + + assertEquals( + GetTopChannelsResponse + .newBuilder() + .addChannel(ChannelzProtoUtil.toChannel(root)) + .setEnd(true) + .build(), + getTopChannelHelper(0)); + } + + @Test + public void getChannel() throws ExecutionException, InterruptedException { + TestChannel root = new TestChannel(); + assertChannelNotFound(root.getLogId().getId()); + + channelz.addRootChannel(root); + assertEquals( + GetChannelResponse + .newBuilder() + .setChannel(ChannelzProtoUtil.toChannel(root)) + .build(), + getChannelHelper(root.getLogId().getId())); + + channelz.removeRootChannel(root); + assertChannelNotFound(root.getLogId().getId()); + } + + @Test + public void getSubchannel() throws Exception { + TestChannel subchannel = new TestChannel(); + assertSubchannelNotFound(subchannel.getLogId().getId()); + + channelz.addSubchannel(subchannel); + assertEquals( + GetSubchannelResponse + .newBuilder() + .setSubchannel(ChannelzProtoUtil.toSubchannel(subchannel)) + .build(), + getSubchannelHelper(subchannel.getLogId().getId())); + + channelz.removeSubchannel(subchannel); + assertSubchannelNotFound(subchannel.getLogId().getId()); + } + + @Test + public void getServers_empty() { + assertEquals( + GetServersResponse.newBuilder().setEnd(true).build(), + getServersHelper(0)); + } + + @Test + public void getServers_onePage() throws Exception { + TestServer server = new TestServer(); + channelz.addServer(server); + + assertEquals( + GetServersResponse + .newBuilder() + .addServer(ChannelzProtoUtil.toServer(server)) + .setEnd(true) + .build(), + getServersHelper(0)); + } + + @Test + public void getServer() throws ExecutionException, InterruptedException { + TestServer server = new TestServer(); + assertServerNotFound(server.getLogId().getId()); + + channelz.addServer(server); + assertEquals( + GetServerResponse + .newBuilder() + .setServer(ChannelzProtoUtil.toServer(server)) + .build(), + getServerHelper(server.getLogId().getId())); + + channelz.removeServer(server); + assertServerNotFound(server.getLogId().getId()); + } + + + @Test + public void getSocket() throws Exception { + TestSocket socket = new TestSocket(); + assertSocketNotFound(socket.getLogId().getId()); + + channelz.addClientSocket(socket); + assertEquals( + GetSocketResponse + .newBuilder() + .setSocket(ChannelzProtoUtil.toSocket(socket)) + .build(), + getSocketHelper(socket.getLogId().getId())); + + channelz.removeClientSocket(socket); + assertSocketNotFound(socket.getLogId().getId()); + } + + private GetTopChannelsResponse getTopChannelHelper(long startId) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor responseCaptor + = ArgumentCaptor.forClass(GetTopChannelsResponse.class); + service.getTopChannels( + GetTopChannelsRequest.newBuilder().setStartChannelId(startId).build(), + observer); + verify(observer).onNext(responseCaptor.capture()); + verify(observer).onCompleted(); + return responseCaptor.getValue(); + } + + private GetChannelResponse getChannelHelper(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor response + = ArgumentCaptor.forClass(GetChannelResponse.class); + service.getChannel(GetChannelRequest.newBuilder().setChannelId(id).build(), observer); + verify(observer).onNext(response.capture()); + verify(observer).onCompleted(); + return response.getValue(); + } + + private void assertChannelNotFound(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); + service.getChannel(GetChannelRequest.newBuilder().setChannelId(id).build(), observer); + verify(observer).onError(exceptionCaptor.capture()); + Status s = Status.fromThrowable(exceptionCaptor.getValue()); + assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); + } + + private GetSubchannelResponse getSubchannelHelper(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor response + = ArgumentCaptor.forClass(GetSubchannelResponse.class); + service.getSubchannel(GetSubchannelRequest.newBuilder().setSubchannelId(id).build(), observer); + verify(observer).onNext(response.capture()); + verify(observer).onCompleted(); + return response.getValue(); + } + + private void assertSubchannelNotFound(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); + service.getSubchannel(GetSubchannelRequest.newBuilder().setSubchannelId(id).build(), observer); + verify(observer).onError(exceptionCaptor.capture()); + Status s = Status.fromThrowable(exceptionCaptor.getValue()); + assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); + } + + private GetServersResponse getServersHelper(long startId) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor responseCaptor + = ArgumentCaptor.forClass(GetServersResponse.class); + service.getServers( + GetServersRequest.newBuilder().setStartServerId(startId).build(), + observer); + verify(observer).onNext(responseCaptor.capture()); + verify(observer).onCompleted(); + return responseCaptor.getValue(); + } + + private void assertServerNotFound(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); + service.getServer(GetServerRequest.newBuilder().setServerId(id).build(), observer); + verify(observer).onError(exceptionCaptor.capture()); + Status s = Status.fromThrowable(exceptionCaptor.getValue()); + assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); + } + + private GetServerResponse getServerHelper(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor response = ArgumentCaptor.forClass(GetServerResponse.class); + service.getServer(GetServerRequest.newBuilder().setServerId(id).build(), observer); + verify(observer).onNext(response.capture()); + verify(observer).onCompleted(); + return response.getValue(); + } + + private void assertSocketNotFound(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); + service.getSocket(GetSocketRequest.newBuilder().setSocketId(id).build(), observer); + verify(observer).onError(exceptionCaptor.capture()); + Status s = Status.fromThrowable(exceptionCaptor.getValue()); + assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); + } + + private GetSocketResponse getSocketHelper(long id) { + @SuppressWarnings("unchecked") + StreamObserver observer = mock(StreamObserver.class); + ArgumentCaptor response + = ArgumentCaptor.forClass(GetSocketResponse.class); + service.getSocket(GetSocketRequest.newBuilder().setSocketId(id).build(), observer); + verify(observer).onNext(response.capture()); + verify(observer).onCompleted(); + return response.getValue(); + } +} diff --git a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java b/services/src/test/java/io/grpc/services/ChannelzTestHelper.java new file mode 100644 index 00000000000..6bd8e8bceb5 --- /dev/null +++ b/services/src/test/java/io/grpc/services/ChannelzTestHelper.java @@ -0,0 +1,183 @@ +/* + * Copyright 2018 The 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. + */ + +package io.grpc.services; + +import com.google.common.base.MoreObjects; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import io.grpc.ConnectivityState; +import io.grpc.InternalChannelz; +import io.grpc.InternalChannelz.ChannelStats; +import io.grpc.InternalChannelz.Security; +import io.grpc.InternalChannelz.ServerStats; +import io.grpc.InternalChannelz.SocketOptions; +import io.grpc.InternalChannelz.SocketStats; +import io.grpc.InternalChannelz.TransportStats; +import io.grpc.InternalInstrumented; +import io.grpc.InternalLogId; +import io.grpc.InternalWithLogId; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Collections; + +/** + * Test class definitions that will be used in the proto utils test as well as + * channelz service test. + */ +final class ChannelzTestHelper { + + static final class TestSocket implements InternalInstrumented { + private final InternalLogId id = InternalLogId.allocate("socket", /*details=*/ null); + TransportStats transportStats = new TransportStats( + /*streamsStarted=*/ 1, + /*lastLocalStreamCreatedTimeNanos=*/ 2, + /*lastRemoteStreamCreatedTimeNanos=*/ 3, + /*streamsSucceeded=*/ 4, + /*streamsFailed=*/ 5, + /*messagesSent=*/ 6, + /*messagesReceived=*/ 7, + /*keepAlivesSent=*/ 8, + /*lastMessageSentTimeNanos=*/ 9, + /*lastMessageReceivedTimeNanos=*/ 10, + /*localFlowControlWindow=*/ 11, + /*remoteFlowControlWindow=*/ 12); + SocketAddress local = new InetSocketAddress("10.0.0.1", 1000); + SocketAddress remote = new InetSocketAddress("10.0.0.2", 1000); + InternalChannelz.SocketOptions socketOptions + = new InternalChannelz.SocketOptions.Builder().build(); + Security security = null; + + @Override + public ListenableFuture getStats() { + SettableFuture ret = SettableFuture.create(); + ret.set( + new SocketStats( + transportStats, + local, + remote, + socketOptions, + security)); + return ret; + } + + @Override + public InternalLogId getLogId() { + return id; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("logId", getLogId()) + .toString(); + } + } + + static final class TestListenSocket implements InternalInstrumented { + private final InternalLogId id = InternalLogId.allocate("listensocket", /*details=*/ null); + SocketAddress listenAddress = new InetSocketAddress("10.0.0.1", 1234); + + @Override + public ListenableFuture getStats() { + SettableFuture ret = SettableFuture.create(); + ret.set( + new SocketStats( + /*data=*/ null, + listenAddress, + /*remote=*/ null, + new SocketOptions.Builder().addOption("listen_option", "listen_option_value").build(), + /*security=*/ null)); + return ret; + } + + @Override + public InternalLogId getLogId() { + return id; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("logId", getLogId()) + .toString(); + } + } + + static final class TestServer implements InternalInstrumented { + private final InternalLogId id = InternalLogId.allocate("server", /*details=*/ null); + ServerStats serverStats = new ServerStats( + /*callsStarted=*/ 1, + /*callsSucceeded=*/ 2, + /*callsFailed=*/ 3, + /*lastCallStartedNanos=*/ 4, + Collections.>emptyList()); + + @Override + public ListenableFuture getStats() { + SettableFuture ret = SettableFuture.create(); + ret.set(serverStats); + return ret; + } + + @Override + public InternalLogId getLogId() { + return id; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("logId", getLogId()) + .toString(); + } + } + + static final class TestChannel implements InternalInstrumented { + private final InternalLogId id = + InternalLogId.allocate("channel-or-subchannel", /*details=*/ null); + + ChannelStats stats = new ChannelStats.Builder() + .setTarget("sometarget") + .setState(ConnectivityState.READY) + .setCallsStarted(1) + .setCallsSucceeded(2) + .setCallsFailed(3) + .setLastCallStartedNanos(4) + .setSubchannels(Collections.emptyList()) + .setSockets(Collections.emptyList()) + .build(); + + @Override + public ListenableFuture getStats() { + SettableFuture ret = SettableFuture.create(); + ret.set(stats); + return ret; + } + + @Override + public InternalLogId getLogId() { + return id; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("logId", getLogId()) + .toString(); + } + } +} diff --git a/services/src/test/java/io/grpc/services/TempFileSinkTest.java b/services/src/test/java/io/grpc/services/TempFileSinkTest.java new file mode 100644 index 00000000000..e0c5e200c58 --- /dev/null +++ b/services/src/test/java/io/grpc/services/TempFileSinkTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018, gRPC Authors All rights reserved. + * + * 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. + */ + +package io.grpc.services; + +import static org.junit.Assert.assertEquals; + +import io.grpc.binarylog.v1.GrpcLogEntry; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for {@link TempFileSink}. + */ +@RunWith(JUnit4.class) +public class TempFileSinkTest { + @Test + public void readMyWrite() throws Exception { + TempFileSink sink = new TempFileSink(); + GrpcLogEntry e1 = GrpcLogEntry.newBuilder() + .setCallId(1234) + .build(); + GrpcLogEntry e2 = GrpcLogEntry.newBuilder() + .setCallId(5678) + .build(); + sink.write(e1); + sink.write(e2); + sink.close(); + + DataInputStream input = new DataInputStream(new FileInputStream(sink.getPath())); + try { + GrpcLogEntry read1 = GrpcLogEntry.parseDelimitedFrom(input); + GrpcLogEntry read2 = GrpcLogEntry.parseDelimitedFrom(input); + + assertEquals(e1, read1); + assertEquals(e2, read2); + assertEquals(-1, input.read()); + } finally { + input.close(); + } + } + + @Test + public void writeAfterCloseIsSilent() throws IOException { + TempFileSink sink = new TempFileSink(); + sink.close(); + sink.write(GrpcLogEntry.newBuilder() + .setCallId(1234) + .build()); + } +} From b6a5c184cb645ddee28f11ab0aa0f9b99fdebf59 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Thu, 15 Apr 2021 15:59:26 -0700 Subject: [PATCH 09/20] Restore unnecessary changes. --- .../io/grpc/services/HealthStatusManager.java | 2 +- .../grpc/services/BinaryLogProviderTest.java | 20 ++++---- .../io/grpc/services/BinlogHelperTest.java | 50 +++++++++---------- .../grpc/services/ChannelzProtoUtilTest.java | 34 ++++++------- .../io/grpc/services/TempFileSinkTest.java | 2 +- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/services/src/main/java/io/grpc/services/HealthStatusManager.java b/services/src/main/java/io/grpc/services/HealthStatusManager.java index fe1b7a8e08a..136a44264c9 100644 --- a/services/src/main/java/io/grpc/services/HealthStatusManager.java +++ b/services/src/main/java/io/grpc/services/HealthStatusManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 The gRPC Authors + * Copyright 2016 The gRPC Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java b/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java index 0f7d41482e0..6905cbb603d 100644 --- a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java +++ b/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java @@ -331,11 +331,11 @@ public void onMessage(RespT message) { @Override public void sendMessage(ReqT message) { - byte[] bytes = (byte[]) message; - binlogReq.add(bytes); - ByteArrayInputStream input = new ByteArrayInputStream(bytes); - ReqT dup = method.parseRequest(input); - super.sendMessage(dup); + byte[] bytes = (byte[]) message; + binlogReq.add(bytes); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + ReqT dup = method.parseRequest(input); + super.sendMessage(dup); } }; } @@ -356,11 +356,11 @@ public ServerCall.Listener interceptCall( ServerCall wCall = new SimpleForwardingServerCall(call) { @Override public void sendMessage(RespT message) { - byte[] bytes = (byte[]) message; - binlogResp.add(bytes); - ByteArrayInputStream input = new ByteArrayInputStream(bytes); - RespT dup = call.getMethodDescriptor().parseResponse(input); - super.sendMessage(dup); + byte[] bytes = (byte[]) message; + binlogResp.add(bytes); + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + RespT dup = call.getMethodDescriptor().parseResponse(input); + super.sendMessage(dup); } }; final ServerCall.Listener oListener = next.startCall(wCall, headers); diff --git a/services/src/test/java/io/grpc/services/BinlogHelperTest.java b/services/src/test/java/io/grpc/services/BinlogHelperTest.java index fd11f4a86ac..d5ea0aec181 100644 --- a/services/src/test/java/io/grpc/services/BinlogHelperTest.java +++ b/services/src/test/java/io/grpc/services/BinlogHelperTest.java @@ -115,22 +115,22 @@ public final class BinlogHelperTest { Metadata.Key.of("c", Metadata.ASCII_STRING_MARSHALLER); private static final MetadataEntry ENTRY_A = MetadataEntry - .newBuilder() - .setKey(KEY_A.name()) - .setValue(ByteString.copyFrom(DATA_A.getBytes(US_ASCII))) - .build(); + .newBuilder() + .setKey(KEY_A.name()) + .setValue(ByteString.copyFrom(DATA_A.getBytes(US_ASCII))) + .build(); private static final MetadataEntry ENTRY_B = - MetadataEntry - .newBuilder() - .setKey(KEY_B.name()) - .setValue(ByteString.copyFrom(DATA_B.getBytes(US_ASCII))) - .build(); + MetadataEntry + .newBuilder() + .setKey(KEY_B.name()) + .setValue(ByteString.copyFrom(DATA_B.getBytes(US_ASCII))) + .build(); private static final MetadataEntry ENTRY_C = - MetadataEntry - .newBuilder() - .setKey(KEY_C.name()) - .setValue(ByteString.copyFrom(DATA_C.getBytes(US_ASCII))) - .build(); + MetadataEntry + .newBuilder() + .setKey(KEY_C.name()) + .setValue(ByteString.copyFrom(DATA_C.getBytes(US_ASCII))) + .build(); private static final long CALL_ID = 0x1112131415161718L; private static final int HEADER_LIMIT = 10; private static final int MESSAGE_LIMIT = Integer.MAX_VALUE; @@ -289,9 +289,9 @@ public void createLogFromOptionString_malformed() throws Exception { public void configBinLog_multiConfig_withGlobal() throws Exception { String configStr = "*{h}," - + "package.both256/*{h:256;m:256}," - + "package.service1/both128{h:128;m:128}," - + "package.service2/method_messageOnly{m}"; + + "package.both256/*{h:256;m:256}," + + "package.service1/both128{h:128;m:128}," + + "package.service2/method_messageOnly{m}"; assertSameLimits(HEADER_FULL, makeLog(configStr, "otherpackage.service/method")); assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method1")); @@ -312,8 +312,8 @@ public void configBinLog_multiConfig_withGlobal() throws Exception { public void configBinLog_multiConfig_noGlobal() throws Exception { String configStr = "package.both256/*{h:256;m:256}," - + "package.service1/both128{h:128;m:128}," - + "package.service2/method_messageOnly{m}"; + + "package.service1/both128{h:128;m:128}," + + "package.service2/method_messageOnly{m}"; assertNull(makeLog(configStr, "otherpackage.service/method")); assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method1")); @@ -450,12 +450,12 @@ public void metadataToProto() throws Exception { .setType(EventType.EVENT_TYPE_CLIENT_HEADER) .setClientHeader( ClientHeader.newBuilder().setMetadata( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .addEntry(ENTRY_B) - .addEntry(ENTRY_C) - .build())) + io.grpc.binarylog.v1.Metadata + .newBuilder() + .addEntry(ENTRY_A) + .addEntry(ENTRY_B) + .addEntry(ENTRY_C) + .build())) .build(), metadataToProtoTestHelper( EventType.EVENT_TYPE_CLIENT_HEADER, nonEmptyMetadata, Integer.MAX_VALUE)); diff --git a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java index 8692f6fcde0..45dd8056ddf 100644 --- a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java +++ b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java @@ -130,10 +130,10 @@ public final class ChannelzProtoUtilTest { .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) .build(); private final Subchannel subchannelProto = Subchannel - .newBuilder() - .setRef(subchannelRef) - .setData(subchannelData) - .build(); + .newBuilder() + .setRef(subchannelRef) + .setData(subchannelData) + .build(); private final TestServer server = new TestServer(); private final ServerRef serverRef = ServerRef @@ -177,9 +177,9 @@ public final class ChannelzProtoUtilTest { .setName("SO_TIMEOUT") .setAdditional( Any.pack(SocketOptionTimeout - .newBuilder() - .setDuration(Durations.fromMillis(200)) - .build()) + .newBuilder() + .setDuration(Durations.fromMillis(200)) + .build()) ).build(); private final SocketOption sockOptAdditional = SocketOption @@ -445,10 +445,10 @@ public void socketSecurityTls() throws Exception { assertEquals( Security.newBuilder().setTls( Tls.newBuilder() - .setStandardName("TLS_NULL_WITH_NULL_NULL") - .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8)) - .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) - .build(), + .setStandardName("TLS_NULL_WITH_NULL_NULL") + .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8)) + .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) + .build(), ChannelzProtoUtil.toSocket(socket).getSecurity()); socket.security = new InternalChannelz.Security( @@ -456,9 +456,9 @@ public void socketSecurityTls() throws Exception { assertEquals( Security.newBuilder().setTls( Tls.newBuilder() - .setStandardName("TLS_NULL_WITH_NULL_NULL") - .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) - .build(), + .setStandardName("TLS_NULL_WITH_NULL_NULL") + .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) + .build(), ChannelzProtoUtil.toSocket(socket).getSecurity()); socket.security = new InternalChannelz.Security( @@ -469,7 +469,7 @@ public void socketSecurityTls() throws Exception { .setStandardName("TLS_NULL_WITH_NULL_NULL") .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8))) .build(), - ChannelzProtoUtil.toSocket(socket).getSecurity()); + ChannelzProtoUtil.toSocket(socket).getSecurity()); } @Test @@ -479,11 +479,11 @@ public void socketSecurityOther() throws Exception { Any packed = Any.pack(contents); socket.security = new InternalChannelz.Security( - new InternalChannelz.OtherSecurity("other_security", packed)); + new InternalChannelz.OtherSecurity("other_security", packed)); assertEquals( Security.newBuilder().setOther( OtherSecurity.newBuilder().setName("other_security").setValue(packed)) - .build(), + .build(), ChannelzProtoUtil.toSocket(socket).getSecurity()); } diff --git a/services/src/test/java/io/grpc/services/TempFileSinkTest.java b/services/src/test/java/io/grpc/services/TempFileSinkTest.java index e0c5e200c58..f47f10569d6 100644 --- a/services/src/test/java/io/grpc/services/TempFileSinkTest.java +++ b/services/src/test/java/io/grpc/services/TempFileSinkTest.java @@ -27,7 +27,7 @@ import org.junit.runners.JUnit4; /** - * Tests for {@link TempFileSink}. + * Tests for {@link io.grpc.services.TempFileSink}. */ @RunWith(JUnit4.class) public class TempFileSinkTest { From c505f40e97591c78c92501cd68227f238da41ba2 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 15:08:17 -0700 Subject: [PATCH 10/20] Change to forwarding by inheritance instead of by delegating. --- .../io/grpc/protobuf/services/BinaryLogs.java | 4 +- .../protobuf/services/ChannelzService.java | 6 +- .../services/HealthStatusManager.java | 2 +- .../services/ProtoReflectionService.java | 4 +- .../io/grpc/services/BinaryLogProvider.java | 171 +------ .../java/io/grpc/services/BinaryLogSink.java | 8 +- .../java/io/grpc/services/BinaryLogs.java | 31 +- .../io/grpc/services/ChannelzService.java | 202 +------- .../HealthCheckingLoadBalancerUtil.java | 68 --- .../io/grpc/services/HealthStatusManager.java | 54 +-- .../grpc/services/ProtoReflectionService.java | 28 +- .../grpc/services/BinaryLogProviderTest.java | 437 ------------------ .../io/grpc/services/ChannelzServiceTest.java | 276 ----------- 13 files changed, 17 insertions(+), 1274 deletions(-) delete mode 100644 services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java delete mode 100644 services/src/test/java/io/grpc/services/BinaryLogProviderTest.java delete mode 100644 services/src/test/java/io/grpc/services/ChannelzServiceTest.java diff --git a/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java b/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java index 4a7757c8617..c468fc318d8 100644 --- a/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java @@ -21,7 +21,7 @@ import java.io.IOException; @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") -public final class BinaryLogs { +public class BinaryLogs { /** * Creates a binary log that writes to a temp file. Warning: this implementation is * not performance optimized, and RPCs will experience back pressure if disk IO does not keep @@ -49,5 +49,5 @@ public static BinaryLog createBinaryLog(BinaryLogSink sink, String configStr) th return new BinaryLogProviderImpl(sink, configStr); } - private BinaryLogs() {} + protected BinaryLogs() {} } diff --git a/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java b/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java index 66daf559d32..cc8d663e2b7 100644 --- a/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java +++ b/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java @@ -16,7 +16,6 @@ package io.grpc.protobuf.services; -import com.google.common.annotations.VisibleForTesting; import io.grpc.ExperimentalApi; import io.grpc.InternalChannelz; import io.grpc.InternalChannelz.ChannelStats; @@ -48,7 +47,7 @@ * The channelz service provides stats about a running gRPC process. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4206") -public final class ChannelzService extends ChannelzGrpc.ChannelzImplBase { +public class ChannelzService extends ChannelzGrpc.ChannelzImplBase { private final InternalChannelz channelz; private final int maxPageSize; @@ -59,8 +58,7 @@ public static ChannelzService newInstance(int maxPageSize) { return new ChannelzService(InternalChannelz.instance(), maxPageSize); } - @VisibleForTesting - ChannelzService(InternalChannelz channelz, int maxPageSize) { + protected ChannelzService(InternalChannelz channelz, int maxPageSize) { this.channelz = channelz; this.maxPageSize = maxPageSize; } diff --git a/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java b/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java index 56cff68f3aa..38f3b9c22d5 100644 --- a/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java +++ b/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java @@ -31,7 +31,7 @@ * {@link ServingStatus#SERVING}. */ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/4696") -public final class HealthStatusManager { +public class HealthStatusManager { /** * The special "service name" that represent all services on a GRPC server. It is an empty * string. diff --git a/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java b/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java index 4a7840a3ad9..38a9ea23148 100644 --- a/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java +++ b/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java @@ -62,14 +62,14 @@ * extension. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222") -public final class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { +public class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { private final Object lock = new Object(); @GuardedBy("lock") private final Map serverReflectionIndexes = new WeakHashMap<>(); - private ProtoReflectionService() {} + protected ProtoReflectionService() {} /** * Creates a instance of {@link ProtoReflectionService}. diff --git a/services/src/main/java/io/grpc/services/BinaryLogProvider.java b/services/src/main/java/io/grpc/services/BinaryLogProvider.java index 3ca5de65558..66266d21725 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogProvider.java +++ b/services/src/main/java/io/grpc/services/BinaryLogProvider.java @@ -16,179 +16,10 @@ package io.grpc.services; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import io.grpc.BinaryLog; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.ClientInterceptors; import io.grpc.Internal; -import io.grpc.InternalClientInterceptors; -import io.grpc.InternalServerInterceptors; -import io.grpc.ManagedChannel; -import io.grpc.MethodDescriptor; -import io.grpc.MethodDescriptor.Marshaller; -import io.grpc.ServerCallHandler; -import io.grpc.ServerInterceptor; -import io.grpc.ServerMethodDefinition; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import javax.annotation.Nullable; // TODO(zpencer): rename class to AbstractBinaryLog @Internal -public abstract class BinaryLogProvider extends BinaryLog { - @VisibleForTesting - public static final Marshaller BYTEARRAY_MARSHALLER = new ByteArrayMarshaller(); +public abstract class BinaryLogProvider extends io.grpc.protobuf.services.BinaryLogProvider { - private final ClientInterceptor binaryLogShim = new BinaryLogShim(); - - /** - * Wraps a channel to provide binary logging on {@link ClientCall}s as needed. - */ - @Override - public final Channel wrapChannel(Channel channel) { - return ClientInterceptors.intercept(channel, binaryLogShim); - } - - private static MethodDescriptor toByteBufferMethod( - MethodDescriptor method) { - return method.toBuilder(BYTEARRAY_MARSHALLER, BYTEARRAY_MARSHALLER).build(); - } - - /** - * Wraps a {@link ServerMethodDefinition} such that it performs binary logging if needed. - */ - @Override - public final ServerMethodDefinition wrapMethodDefinition( - ServerMethodDefinition oMethodDef) { - ServerInterceptor binlogInterceptor = - getServerInterceptor(oMethodDef.getMethodDescriptor().getFullMethodName()); - if (binlogInterceptor == null) { - return oMethodDef; - } - MethodDescriptor binMethod = - BinaryLogProvider.toByteBufferMethod(oMethodDef.getMethodDescriptor()); - ServerMethodDefinition binDef = - InternalServerInterceptors.wrapMethod(oMethodDef, binMethod); - ServerCallHandler binlogHandler = - InternalServerInterceptors.interceptCallHandlerCreate( - binlogInterceptor, binDef.getServerCallHandler()); - return ServerMethodDefinition.create(binMethod, binlogHandler); - } - - /** - * Returns a {@link ServerInterceptor} for binary logging. gRPC is free to cache the interceptor, - * so the interceptor must be reusable across calls. At runtime, the request and response - * marshallers are always {@code Marshaller}. - * Returns {@code null} if this method is not binary logged. - */ - // TODO(zpencer): ensure the interceptor properly handles retries and hedging - @Nullable - protected abstract ServerInterceptor getServerInterceptor(String fullMethodName); - - /** - * Returns a {@link ClientInterceptor} for binary logging. gRPC is free to cache the interceptor, - * so the interceptor must be reusable across calls. At runtime, the request and response - * marshallers are always {@code Marshaller}. - * Returns {@code null} if this method is not binary logged. - */ - // TODO(zpencer): ensure the interceptor properly handles retries and hedging - @Nullable - protected abstract ClientInterceptor getClientInterceptor( - String fullMethodName, CallOptions callOptions); - - @Override - public void close() throws IOException { - // default impl: noop - // TODO(zpencer): make BinaryLogProvider provide a BinaryLog, and this method belongs there - } - - // Creating a named class makes debugging easier - private static final class ByteArrayMarshaller implements Marshaller { - @Override - public InputStream stream(byte[] value) { - return new ByteArrayInputStream(value); - } - - @Override - public byte[] parse(InputStream stream) { - try { - return parseHelper(stream); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private byte[] parseHelper(InputStream stream) throws IOException { - try { - return IoUtils.toByteArray(stream); - } finally { - stream.close(); - } - } - } - - /** - * The pipeline of interceptors is hard coded when the {@link ManagedChannel} is created. - * This shim interceptor should always be installed as a placeholder. When a call starts, - * this interceptor checks with the {@link BinaryLogProvider} to see if logging should happen - * for this particular {@link ClientCall}'s method. - */ - private final class BinaryLogShim implements ClientInterceptor { - @Override - public ClientCall interceptCall( - MethodDescriptor method, - CallOptions callOptions, - Channel next) { - ClientInterceptor binlogInterceptor = getClientInterceptor( - method.getFullMethodName(), callOptions); - if (binlogInterceptor == null) { - return next.newCall(method, callOptions); - } else { - return InternalClientInterceptors - .wrapClientInterceptor( - binlogInterceptor, - BYTEARRAY_MARSHALLER, - BYTEARRAY_MARSHALLER) - .interceptCall(method, callOptions, next); - } - } - } - - // Copied from internal - private static final class IoUtils { - /** maximum buffer to be read is 16 KB. */ - private static final int MAX_BUFFER_LENGTH = 16384; - - /** Returns the byte array. */ - public static byte[] toByteArray(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - copy(in, out); - return out.toByteArray(); - } - - /** Copies the data from input stream to output stream. */ - public static long copy(InputStream from, OutputStream to) throws IOException { - // Copied from guava com.google.common.io.ByteStreams because its API is unstable (beta) - Preconditions.checkNotNull(from); - Preconditions.checkNotNull(to); - byte[] buf = new byte[MAX_BUFFER_LENGTH]; - long total = 0; - while (true) { - int r = from.read(buf); - if (r == -1) { - break; - } - to.write(buf, 0, r); - total += r; - } - return total; - } - } } diff --git a/services/src/main/java/io/grpc/services/BinaryLogSink.java b/services/src/main/java/io/grpc/services/BinaryLogSink.java index af3204eddff..7ac2246e907 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogSink.java +++ b/services/src/main/java/io/grpc/services/BinaryLogSink.java @@ -16,9 +16,7 @@ package io.grpc.services; -import com.google.protobuf.MessageLite; import io.grpc.ExperimentalApi; -import java.io.Closeable; /** * A class that accepts binary log messages. @@ -27,9 +25,5 @@ */ @Deprecated @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") -public interface BinaryLogSink extends Closeable { - /** - * Writes the {@code message} to the destination. - */ - void write(MessageLite message); +public interface BinaryLogSink extends io.grpc.protobuf.services.BinaryLogSink { } diff --git a/services/src/main/java/io/grpc/services/BinaryLogs.java b/services/src/main/java/io/grpc/services/BinaryLogs.java index 0c4f8b5c7dc..07950f75908 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogs.java +++ b/services/src/main/java/io/grpc/services/BinaryLogs.java @@ -16,42 +16,13 @@ package io.grpc.services; -import io.grpc.BinaryLog; import io.grpc.ExperimentalApi; -import java.io.IOException; /** * @deprecated Use {@link io.grpc.protobuf.services.BinaryLogs} instead. */ @Deprecated @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") -public final class BinaryLogs { - /** - * Creates a binary log that writes to a temp file. Warning: this implementation is - * not performance optimized, and RPCs will experience back pressure if disk IO does not keep - * up. - */ - public static BinaryLog createBinaryLog() throws IOException { - return new BinaryLogProviderImpl(); - } - - /** - * Deprecated and will be removed in a future version of gRPC. - */ - @Deprecated - public static BinaryLog createBinaryLog(BinaryLogSink sink) throws IOException { - return new BinaryLogProviderImpl(sink); - } - - /** - * Creates a binary log with a custom {@link BinaryLogSink} for receiving the logged data, - * and a config string as defined by - * - * A16-binary-logging. - */ - public static BinaryLog createBinaryLog(BinaryLogSink sink, String configStr) throws IOException { - return new BinaryLogProviderImpl(sink, configStr); - } - +public final class BinaryLogs extends io.grpc.protobuf.services.BinaryLogs { private BinaryLogs() {} } diff --git a/services/src/main/java/io/grpc/services/ChannelzService.java b/services/src/main/java/io/grpc/services/ChannelzService.java index 07e9e7ad035..e41bdd7213d 100644 --- a/services/src/main/java/io/grpc/services/ChannelzService.java +++ b/services/src/main/java/io/grpc/services/ChannelzService.java @@ -16,33 +16,8 @@ package io.grpc.services; -import com.google.common.annotations.VisibleForTesting; import io.grpc.ExperimentalApi; import io.grpc.InternalChannelz; -import io.grpc.InternalChannelz.ChannelStats; -import io.grpc.InternalChannelz.ServerList; -import io.grpc.InternalChannelz.ServerSocketsList; -import io.grpc.InternalChannelz.ServerStats; -import io.grpc.InternalChannelz.SocketStats; -import io.grpc.InternalInstrumented; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.grpc.channelz.v1.ChannelzGrpc; -import io.grpc.channelz.v1.GetChannelRequest; -import io.grpc.channelz.v1.GetChannelResponse; -import io.grpc.channelz.v1.GetServerRequest; -import io.grpc.channelz.v1.GetServerResponse; -import io.grpc.channelz.v1.GetServerSocketsRequest; -import io.grpc.channelz.v1.GetServerSocketsResponse; -import io.grpc.channelz.v1.GetServersRequest; -import io.grpc.channelz.v1.GetServersResponse; -import io.grpc.channelz.v1.GetSocketRequest; -import io.grpc.channelz.v1.GetSocketResponse; -import io.grpc.channelz.v1.GetSubchannelRequest; -import io.grpc.channelz.v1.GetSubchannelResponse; -import io.grpc.channelz.v1.GetTopChannelsRequest; -import io.grpc.channelz.v1.GetTopChannelsResponse; -import io.grpc.stub.StreamObserver; /** * The channelz service provides stats about a running gRPC process. @@ -51,9 +26,7 @@ */ @Deprecated @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4206") -public final class ChannelzService extends ChannelzGrpc.ChannelzImplBase { - private final InternalChannelz channelz; - private final int maxPageSize; +public final class ChannelzService extends io.grpc.protobuf.services.ChannelzService { /** * Creates an instance. @@ -62,176 +35,7 @@ public static ChannelzService newInstance(int maxPageSize) { return new ChannelzService(InternalChannelz.instance(), maxPageSize); } - @VisibleForTesting - ChannelzService(InternalChannelz channelz, int maxPageSize) { - this.channelz = channelz; - this.maxPageSize = maxPageSize; - } - - /** Returns top level channel aka {@link io.grpc.ManagedChannel}. */ - @Override - public void getTopChannels( - GetTopChannelsRequest request, StreamObserver responseObserver) { - InternalChannelz.RootChannelList rootChannels - = channelz.getRootChannels(request.getStartChannelId(), maxPageSize); - - GetTopChannelsResponse resp; - try { - resp = ChannelzProtoUtil.toGetTopChannelResponse(rootChannels); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); - } - - /** Returns a top level channel aka {@link io.grpc.ManagedChannel}. */ - @Override - public void getChannel( - GetChannelRequest request, StreamObserver responseObserver) { - InternalInstrumented s = channelz.getRootChannel(request.getChannelId()); - if (s == null) { - responseObserver.onError( - Status.NOT_FOUND.withDescription("Can't find channel " + request.getChannelId()) - .asRuntimeException()); - return; - } - - GetChannelResponse resp; - try { - resp = GetChannelResponse - .newBuilder() - .setChannel(ChannelzProtoUtil.toChannel(s)) - .build(); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); - } - - /** Returns servers. */ - @Override - public void getServers( - GetServersRequest request, StreamObserver responseObserver) { - ServerList servers = channelz.getServers(request.getStartServerId(), maxPageSize); - - GetServersResponse resp; - try { - resp = ChannelzProtoUtil.toGetServersResponse(servers); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); - } - - /** Returns a server. */ - @Override - public void getServer( - GetServerRequest request, StreamObserver responseObserver) { - InternalInstrumented s = channelz.getServer(request.getServerId()); - if (s == null) { - responseObserver.onError( - Status.NOT_FOUND.withDescription("Can't find server " + request.getServerId()) - .asRuntimeException()); - return; - } - - GetServerResponse resp; - try { - resp = GetServerResponse - .newBuilder() - .setServer(ChannelzProtoUtil.toServer(s)) - .build(); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); - } - - /** Returns a subchannel. */ - @Override - public void getSubchannel( - GetSubchannelRequest request, StreamObserver responseObserver) { - InternalInstrumented s = channelz.getSubchannel(request.getSubchannelId()); - if (s == null) { - responseObserver.onError( - Status.NOT_FOUND.withDescription("Can't find subchannel " + request.getSubchannelId()) - .asRuntimeException()); - return; - } - - GetSubchannelResponse resp; - try { - resp = GetSubchannelResponse - .newBuilder() - .setSubchannel(ChannelzProtoUtil.toSubchannel(s)) - .build(); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); - } - - /** Returns a socket. */ - @Override - public void getSocket( - GetSocketRequest request, StreamObserver responseObserver) { - InternalInstrumented s = channelz.getSocket(request.getSocketId()); - if (s == null) { - responseObserver.onError( - Status.NOT_FOUND.withDescription("Can't find socket " + request.getSocketId()) - .asRuntimeException()); - return; - } - - GetSocketResponse resp; - try { - resp = - GetSocketResponse.newBuilder().setSocket(ChannelzProtoUtil.toSocket(s)).build(); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); - } - - @Override - public void getServerSockets( - GetServerSocketsRequest request, StreamObserver responseObserver) { - ServerSocketsList serverSockets - = channelz.getServerSockets(request.getServerId(), request.getStartSocketId(), maxPageSize); - if (serverSockets == null) { - responseObserver.onError( - Status.NOT_FOUND.withDescription("Can't find server " + request.getServerId()) - .asRuntimeException()); - return; - } - - GetServerSocketsResponse resp; - try { - resp = ChannelzProtoUtil.toGetServerSocketsResponse(serverSockets); - } catch (StatusRuntimeException e) { - responseObserver.onError(e); - return; - } - - responseObserver.onNext(resp); - responseObserver.onCompleted(); + private ChannelzService(InternalChannelz channelz, int maxPageSize) { + super(channelz, maxPageSize); } } diff --git a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java b/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java deleted file mode 100644 index 75373f0a810..00000000000 --- a/services/src/main/java/io/grpc/services/HealthCheckingLoadBalancerUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import io.grpc.ExperimentalApi; -import io.grpc.LoadBalancer; -import io.grpc.LoadBalancer.Helper; - -/** - * Utility for enabling - * - * client-side health checking for {@link LoadBalancer}s. - * - * @deprecated Use {@link io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil} instead. - */ -@Deprecated -@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5025") -public class HealthCheckingLoadBalancerUtil { - private HealthCheckingLoadBalancerUtil() { - } - - /** - * Creates a health-checking-capable LoadBalancer. This method is used to implement - * health-checking-capable {@link io.grpc.LoadBalancer.Factory}s, which will typically written - * this way: - * - *
-   * public class HealthCheckingFooLbFactory extends LoadBalancer.Factory {
-   *   // This is the original balancer implementation that doesn't have health checking
-   *   private final LoadBalancer.Factory fooLbFactory;
-   *
-   *   ...
-   *
-   *   // Returns the health-checking-capable version of FooLb
-   *   public LoadBalancer newLoadBalancer(Helper helper) {
-   *     return HealthCheckingLoadBalancerUtil.newHealthCheckingLoadBalancer(fooLbFactory, helper);
-   *   }
-   * }
-   * 
- * - *

As a requirement for the original LoadBalancer, it must call - * {@code Helper.createSubchannel()} from the {@link Helper#getSynchronizationContext() - * Synchronization Context}, or {@code createSubchannel()} will throw. - * - * @param factory the original factory that implements load-balancing logic without health - * checking - * @param helper the helper passed to the resulting health-checking LoadBalancer. - */ - public static LoadBalancer newHealthCheckingLoadBalancer( - LoadBalancer.Factory factory, Helper helper) { - return io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil.newHealthCheckingLoadBalancer( - factory, helper); - } -} diff --git a/services/src/main/java/io/grpc/services/HealthStatusManager.java b/services/src/main/java/io/grpc/services/HealthStatusManager.java index 136a44264c9..3b730439640 100644 --- a/services/src/main/java/io/grpc/services/HealthStatusManager.java +++ b/services/src/main/java/io/grpc/services/HealthStatusManager.java @@ -16,7 +16,6 @@ package io.grpc.services; -import io.grpc.BindableService; import io.grpc.health.v1.HealthCheckResponse.ServingStatus; /** @@ -32,56 +31,5 @@ */ @Deprecated @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/4696") -public final class HealthStatusManager { - /** - * The special "service name" that represent all services on a GRPC server. It is an empty - * string. - */ - public static final String SERVICE_NAME_ALL_SERVICES = - io.grpc.protobuf.services.HealthStatusManager.SERVICE_NAME_ALL_SERVICES; - - private final io.grpc.protobuf.services.HealthStatusManager delegate; - - public HealthStatusManager() { - this.delegate = new io.grpc.protobuf.services.HealthStatusManager(); - } - - /** - * Gets the health check service created in the constructor. - */ - public BindableService getHealthService() { - return delegate.getHealthService(); - } - - /** - * Updates the status of the server. - * @param service the name of some aspect of the server that is associated with a health status. - * This name can have no relation with the gRPC services that the server is running with. - * It can also be an empty String {@code ""} per the gRPC specification. - * @param status is one of the values {@link ServingStatus#SERVING}, - * {@link ServingStatus#NOT_SERVING} and {@link ServingStatus#UNKNOWN}. - */ - public void setStatus(String service, ServingStatus status) { - delegate.setStatus(service, status); - } - - /** - * Clears the health status record of a service. The health service will respond with NOT_FOUND - * error on checking the status of a cleared service. - * @param service the name of some aspect of the server that is associated with a health status. - * This name can have no relation with the gRPC services that the server is running with. - * It can also be an empty String {@code ""} per the gRPC specification. - */ - public void clearStatus(String service) { - delegate.clearStatus(service); - } - - /** - * enterTerminalState causes the health status manager to mark all services as not serving, and - * prevents future updates to services. This method is meant to be called prior to server - * shutdown as a way to indicate that clients should redirect their traffic elsewhere. - */ - public void enterTerminalState() { - delegate.enterTerminalState(); - } +public final class HealthStatusManager extends io.grpc.protobuf.services.HealthStatusManager { } diff --git a/services/src/main/java/io/grpc/services/ProtoReflectionService.java b/services/src/main/java/io/grpc/services/ProtoReflectionService.java index b00b68d8991..eb86500e222 100644 --- a/services/src/main/java/io/grpc/services/ProtoReflectionService.java +++ b/services/src/main/java/io/grpc/services/ProtoReflectionService.java @@ -16,12 +16,7 @@ package io.grpc.services; -import io.grpc.BindableService; import io.grpc.ExperimentalApi; -import io.grpc.reflection.v1alpha.ServerReflectionGrpc; -import io.grpc.reflection.v1alpha.ServerReflectionRequest; -import io.grpc.reflection.v1alpha.ServerReflectionResponse; -import io.grpc.stub.StreamObserver; /** * Provides a reflection service for Protobuf services (including the reflection service itself). @@ -34,24 +29,7 @@ */ @Deprecated @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222") -public final class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { - private final io.grpc.protobuf.services.ProtoReflectionService delegate; - - private ProtoReflectionService() { - delegate = (io.grpc.protobuf.services.ProtoReflectionService) - io.grpc.protobuf.services.ProtoReflectionService.newInstance(); - } - - /** - * Creates a instance of {@link io.grpc.protobuf.services.ProtoReflectionService}. - */ - public static BindableService newInstance() { - return new ProtoReflectionService(); - } - - @Override - public StreamObserver serverReflectionInfo( - StreamObserver responseObserver) { - return delegate.serverReflectionInfo(responseObserver); - } +public final class ProtoReflectionService + extends io.grpc.protobuf.services.ProtoReflectionService { + private ProtoReflectionService() {} } diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java b/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java deleted file mode 100644 index 6905cbb603d..00000000000 --- a/services/src/test/java/io/grpc/services/BinaryLogProviderTest.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright 2017 The 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. - */ - -package io.grpc.services; - -import static com.google.common.base.Charsets.UTF_8; -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import com.google.common.io.ByteStreams; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; -import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; -import io.grpc.ForwardingServerCall.SimpleForwardingServerCall; -import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener; -import io.grpc.Metadata; -import io.grpc.MethodDescriptor; -import io.grpc.MethodDescriptor.Marshaller; -import io.grpc.MethodDescriptor.MethodType; -import io.grpc.ServerCall; -import io.grpc.ServerCallHandler; -import io.grpc.ServerInterceptor; -import io.grpc.ServerMethodDefinition; -import io.grpc.internal.NoopClientCall; -import io.grpc.internal.NoopServerCall; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link BinaryLogProvider}. */ -@RunWith(JUnit4.class) -public class BinaryLogProviderTest { - private final InvocationCountMarshaller reqMarshaller = - new InvocationCountMarshaller() { - @Override - Marshaller delegate() { - return StringMarshaller.INSTANCE; - } - }; - private final InvocationCountMarshaller respMarshaller = - new InvocationCountMarshaller() { - @Override - Marshaller delegate() { - return IntegerMarshaller.INSTANCE; - } - }; - private final MethodDescriptor method = - MethodDescriptor - .newBuilder(reqMarshaller, respMarshaller) - .setFullMethodName("myservice/mymethod") - .setType(MethodType.UNARY) - .setSchemaDescriptor(new Object()) - .setIdempotent(true) - .setSafe(true) - .setSampledToLocalTracing(true) - .build(); - private final List binlogReq = new ArrayList<>(); - private final List binlogResp = new ArrayList<>(); - private final BinaryLogProvider binlogProvider = new BinaryLogProvider() { - @Override - public ServerInterceptor getServerInterceptor(String fullMethodName) { - return new TestBinaryLogServerInterceptor(); - } - - @Override - public ClientInterceptor getClientInterceptor( - String fullMethodName, CallOptions callOptions) { - return new TestBinaryLogClientInterceptor(); - } - }; - - @Test - public void wrapChannel_methodDescriptor() throws Exception { - final AtomicReference> methodRef = - new AtomicReference<>(); - Channel channel = new Channel() { - @Override - public ClientCall newCall( - MethodDescriptor method, CallOptions callOptions) { - methodRef.set(method); - return new NoopClientCall<>(); - } - - @Override - public String authority() { - throw new UnsupportedOperationException(); - } - }; - Channel wChannel = binlogProvider.wrapChannel(channel); - ClientCall unusedClientCall = wChannel.newCall(method, CallOptions.DEFAULT); - validateWrappedMethod(methodRef.get()); - } - - @Test - public void wrapChannel_handler() throws Exception { - final List serializedReq = new ArrayList<>(); - final AtomicReference> listener = - new AtomicReference<>(); - Channel channel = new Channel() { - @Override - public ClientCall newCall( - MethodDescriptor methodDescriptor, CallOptions callOptions) { - return new NoopClientCall() { - @Override - public void start(ClientCall.Listener responseListener, Metadata headers) { - listener.set(responseListener); - } - - @Override - public void sendMessage(RequestT message) { - serializedReq.add((byte[]) message); - } - }; - } - - @Override - public String authority() { - throw new UnsupportedOperationException(); - } - }; - Channel wChannel = binlogProvider.wrapChannel(channel); - ClientCall clientCall = wChannel.newCall(method, CallOptions.DEFAULT); - final List observedResponse = new ArrayList<>(); - clientCall.start( - new NoopClientCall.NoopClientCallListener() { - @Override - public void onMessage(Integer message) { - observedResponse.add(message); - } - }, - new Metadata()); - - String expectedRequest = "hello world"; - assertThat(binlogReq).isEmpty(); - assertThat(serializedReq).isEmpty(); - assertEquals(0, reqMarshaller.streamInvocations); - clientCall.sendMessage(expectedRequest); - // it is unacceptably expensive for the binlog to double parse every logged message - assertEquals(1, reqMarshaller.streamInvocations); - assertEquals(0, reqMarshaller.parseInvocations); - assertThat(binlogReq).hasSize(1); - assertThat(serializedReq).hasSize(1); - assertEquals( - expectedRequest, - StringMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogReq.get(0)))); - assertEquals( - expectedRequest, - StringMarshaller.INSTANCE.parse(new ByteArrayInputStream(serializedReq.get(0)))); - - int expectedResponse = 12345; - assertThat(binlogResp).isEmpty(); - assertThat(observedResponse).isEmpty(); - assertEquals(0, respMarshaller.parseInvocations); - onClientMessageHelper(listener.get(), IntegerMarshaller.INSTANCE.stream(expectedResponse)); - // it is unacceptably expensive for the binlog to double parse every logged message - assertEquals(1, respMarshaller.parseInvocations); - assertEquals(0, respMarshaller.streamInvocations); - assertThat(binlogResp).hasSize(1); - assertThat(observedResponse).hasSize(1); - assertEquals( - expectedResponse, - (int) IntegerMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogResp.get(0)))); - assertEquals(expectedResponse, (int) observedResponse.get(0)); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private static void onClientMessageHelper(ClientCall.Listener listener, Object request) { - listener.onMessage(request); - } - - private void validateWrappedMethod(MethodDescriptor wMethod) { - assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, wMethod.getRequestMarshaller()); - assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, wMethod.getResponseMarshaller()); - assertEquals(method.getType(), wMethod.getType()); - assertEquals(method.getFullMethodName(), wMethod.getFullMethodName()); - assertEquals(method.getSchemaDescriptor(), wMethod.getSchemaDescriptor()); - assertEquals(method.isIdempotent(), wMethod.isIdempotent()); - assertEquals(method.isSafe(), wMethod.isSafe()); - assertEquals(method.isSampledToLocalTracing(), wMethod.isSampledToLocalTracing()); - } - - @Test - public void wrapMethodDefinition_methodDescriptor() throws Exception { - ServerMethodDefinition methodDef = - ServerMethodDefinition.create( - method, - new ServerCallHandler() { - @Override - public ServerCall.Listener startCall( - ServerCall call, Metadata headers) { - throw new UnsupportedOperationException(); - } - }); - ServerMethodDefinition wMethodDef = binlogProvider.wrapMethodDefinition(methodDef); - validateWrappedMethod(wMethodDef.getMethodDescriptor()); - } - - @Test - public void wrapMethodDefinition_handler() throws Exception { - // The request as seen by the user supplied server code - final List observedRequest = new ArrayList<>(); - final AtomicReference> serverCall = - new AtomicReference<>(); - ServerMethodDefinition methodDef = - ServerMethodDefinition.create( - method, - new ServerCallHandler() { - @Override - public ServerCall.Listener startCall( - ServerCall call, Metadata headers) { - serverCall.set(call); - return new ServerCall.Listener() { - @Override - public void onMessage(String message) { - observedRequest.add(message); - } - }; - } - }); - ServerMethodDefinition wDef = binlogProvider.wrapMethodDefinition(methodDef); - List serializedResp = new ArrayList<>(); - ServerCall.Listener wListener = startServerCallHelper(wDef, serializedResp); - - String expectedRequest = "hello world"; - assertThat(binlogReq).isEmpty(); - assertThat(observedRequest).isEmpty(); - assertEquals(0, reqMarshaller.parseInvocations); - onServerMessageHelper(wListener, StringMarshaller.INSTANCE.stream(expectedRequest)); - // it is unacceptably expensive for the binlog to double parse every logged message - assertEquals(1, reqMarshaller.parseInvocations); - assertEquals(0, reqMarshaller.streamInvocations); - assertThat(binlogReq).hasSize(1); - assertThat(observedRequest).hasSize(1); - assertEquals( - expectedRequest, - StringMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogReq.get(0)))); - assertEquals(expectedRequest, observedRequest.get(0)); - - int expectedResponse = 12345; - assertThat(binlogResp).isEmpty(); - assertThat(serializedResp).isEmpty(); - assertEquals(0, respMarshaller.streamInvocations); - serverCall.get().sendMessage(expectedResponse); - // it is unacceptably expensive for the binlog to double parse every logged message - assertEquals(0, respMarshaller.parseInvocations); - assertEquals(1, respMarshaller.streamInvocations); - assertThat(binlogResp).hasSize(1); - assertThat(serializedResp).hasSize(1); - assertEquals( - expectedResponse, - (int) IntegerMarshaller.INSTANCE.parse(new ByteArrayInputStream(binlogResp.get(0)))); - assertEquals(expectedResponse, - (int) method.parseResponse(new ByteArrayInputStream((byte[]) serializedResp.get(0)))); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private static void onServerMessageHelper(ServerCall.Listener listener, Object request) { - listener.onMessage(request); - } - - private static ServerCall.Listener startServerCallHelper( - final ServerMethodDefinition methodDef, - final List serializedResp) { - ServerCall serverCall = new NoopServerCall() { - @Override - public void sendMessage(RespT message) { - serializedResp.add(message); - } - - @Override - public MethodDescriptor getMethodDescriptor() { - return methodDef.getMethodDescriptor(); - } - }; - return methodDef.getServerCallHandler().startCall(serverCall, new Metadata()); - } - - private final class TestBinaryLogClientInterceptor implements ClientInterceptor { - @Override - public ClientCall interceptCall( - final MethodDescriptor method, - CallOptions callOptions, - Channel next) { - assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, method.getRequestMarshaller()); - assertSame(BinaryLogProvider.BYTEARRAY_MARSHALLER, method.getResponseMarshaller()); - return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { - @Override - public void start(ClientCall.Listener responseListener, Metadata headers) { - super.start( - new SimpleForwardingClientCallListener(responseListener) { - @Override - public void onMessage(RespT message) { - assertTrue(message instanceof InputStream); - try { - byte[] bytes = ByteStreams.toByteArray((InputStream) message); - binlogResp.add(bytes); - ByteArrayInputStream input = new ByteArrayInputStream(bytes); - RespT dup = method.parseResponse(input); - super.onMessage(dup); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }, - headers); - } - - @Override - public void sendMessage(ReqT message) { - byte[] bytes = (byte[]) message; - binlogReq.add(bytes); - ByteArrayInputStream input = new ByteArrayInputStream(bytes); - ReqT dup = method.parseRequest(input); - super.sendMessage(dup); - } - }; - } - } - - private final class TestBinaryLogServerInterceptor implements ServerInterceptor { - @Override - public ServerCall.Listener interceptCall( - final ServerCall call, - Metadata headers, - ServerCallHandler next) { - assertSame( - BinaryLogProvider.BYTEARRAY_MARSHALLER, - call.getMethodDescriptor().getRequestMarshaller()); - assertSame( - BinaryLogProvider.BYTEARRAY_MARSHALLER, - call.getMethodDescriptor().getResponseMarshaller()); - ServerCall wCall = new SimpleForwardingServerCall(call) { - @Override - public void sendMessage(RespT message) { - byte[] bytes = (byte[]) message; - binlogResp.add(bytes); - ByteArrayInputStream input = new ByteArrayInputStream(bytes); - RespT dup = call.getMethodDescriptor().parseResponse(input); - super.sendMessage(dup); - } - }; - final ServerCall.Listener oListener = next.startCall(wCall, headers); - return new SimpleForwardingServerCallListener(oListener) { - @Override - public void onMessage(ReqT message) { - assertTrue(message instanceof InputStream); - try { - byte[] bytes = ByteStreams.toByteArray((InputStream) message); - binlogReq.add(bytes); - ByteArrayInputStream input = new ByteArrayInputStream(bytes); - ReqT dup = call.getMethodDescriptor().parseRequest(input); - super.onMessage(dup); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }; - } - } - - private abstract static class InvocationCountMarshaller - implements MethodDescriptor.Marshaller { - private int streamInvocations = 0; - private int parseInvocations = 0; - - abstract MethodDescriptor.Marshaller delegate(); - - @Override - public InputStream stream(T value) { - streamInvocations++; - return delegate().stream(value); - } - - @Override - public T parse(InputStream stream) { - parseInvocations++; - return delegate().parse(stream); - } - } - - - private static class StringMarshaller implements MethodDescriptor.Marshaller { - public static final StringMarshaller INSTANCE = new StringMarshaller(); - - @Override - public InputStream stream(String value) { - return new ByteArrayInputStream(value.getBytes(UTF_8)); - } - - @Override - public String parse(InputStream stream) { - try { - return new String(ByteStreams.toByteArray(stream), UTF_8); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - } - - private static class IntegerMarshaller implements MethodDescriptor.Marshaller { - public static final IntegerMarshaller INSTANCE = new IntegerMarshaller(); - - @Override - public InputStream stream(Integer value) { - return StringMarshaller.INSTANCE.stream(value.toString()); - } - - @Override - public Integer parse(InputStream stream) { - return Integer.valueOf(StringMarshaller.INSTANCE.parse(stream)); - } - } -} diff --git a/services/src/test/java/io/grpc/services/ChannelzServiceTest.java b/services/src/test/java/io/grpc/services/ChannelzServiceTest.java deleted file mode 100644 index 0849f8611ad..00000000000 --- a/services/src/test/java/io/grpc/services/ChannelzServiceTest.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import io.grpc.InternalChannelz; -import io.grpc.Status; -import io.grpc.channelz.v1.GetChannelRequest; -import io.grpc.channelz.v1.GetChannelResponse; -import io.grpc.channelz.v1.GetServerRequest; -import io.grpc.channelz.v1.GetServerResponse; -import io.grpc.channelz.v1.GetServersRequest; -import io.grpc.channelz.v1.GetServersResponse; -import io.grpc.channelz.v1.GetSocketRequest; -import io.grpc.channelz.v1.GetSocketResponse; -import io.grpc.channelz.v1.GetSubchannelRequest; -import io.grpc.channelz.v1.GetSubchannelResponse; -import io.grpc.channelz.v1.GetTopChannelsRequest; -import io.grpc.channelz.v1.GetTopChannelsResponse; -import io.grpc.services.ChannelzTestHelper.TestChannel; -import io.grpc.services.ChannelzTestHelper.TestServer; -import io.grpc.services.ChannelzTestHelper.TestSocket; -import io.grpc.stub.StreamObserver; -import java.util.concurrent.ExecutionException; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; - -@SuppressWarnings("deprecation") -@RunWith(JUnit4.class) -public class ChannelzServiceTest { - // small value to force pagination - private static final int MAX_PAGE_SIZE = 1; - - private final InternalChannelz channelz = new InternalChannelz(); - private ChannelzService service = new ChannelzService(channelz, MAX_PAGE_SIZE); - - @Test - public void getTopChannels_empty() { - assertEquals( - GetTopChannelsResponse.newBuilder().setEnd(true).build(), - getTopChannelHelper(0)); - } - - @Test - public void getTopChannels_onePage() throws Exception { - TestChannel root = new TestChannel(); - channelz.addRootChannel(root); - - assertEquals( - GetTopChannelsResponse - .newBuilder() - .addChannel(ChannelzProtoUtil.toChannel(root)) - .setEnd(true) - .build(), - getTopChannelHelper(0)); - } - - @Test - public void getChannel() throws ExecutionException, InterruptedException { - TestChannel root = new TestChannel(); - assertChannelNotFound(root.getLogId().getId()); - - channelz.addRootChannel(root); - assertEquals( - GetChannelResponse - .newBuilder() - .setChannel(ChannelzProtoUtil.toChannel(root)) - .build(), - getChannelHelper(root.getLogId().getId())); - - channelz.removeRootChannel(root); - assertChannelNotFound(root.getLogId().getId()); - } - - @Test - public void getSubchannel() throws Exception { - TestChannel subchannel = new TestChannel(); - assertSubchannelNotFound(subchannel.getLogId().getId()); - - channelz.addSubchannel(subchannel); - assertEquals( - GetSubchannelResponse - .newBuilder() - .setSubchannel(ChannelzProtoUtil.toSubchannel(subchannel)) - .build(), - getSubchannelHelper(subchannel.getLogId().getId())); - - channelz.removeSubchannel(subchannel); - assertSubchannelNotFound(subchannel.getLogId().getId()); - } - - @Test - public void getServers_empty() { - assertEquals( - GetServersResponse.newBuilder().setEnd(true).build(), - getServersHelper(0)); - } - - @Test - public void getServers_onePage() throws Exception { - TestServer server = new TestServer(); - channelz.addServer(server); - - assertEquals( - GetServersResponse - .newBuilder() - .addServer(ChannelzProtoUtil.toServer(server)) - .setEnd(true) - .build(), - getServersHelper(0)); - } - - @Test - public void getServer() throws ExecutionException, InterruptedException { - TestServer server = new TestServer(); - assertServerNotFound(server.getLogId().getId()); - - channelz.addServer(server); - assertEquals( - GetServerResponse - .newBuilder() - .setServer(ChannelzProtoUtil.toServer(server)) - .build(), - getServerHelper(server.getLogId().getId())); - - channelz.removeServer(server); - assertServerNotFound(server.getLogId().getId()); - } - - - @Test - public void getSocket() throws Exception { - TestSocket socket = new TestSocket(); - assertSocketNotFound(socket.getLogId().getId()); - - channelz.addClientSocket(socket); - assertEquals( - GetSocketResponse - .newBuilder() - .setSocket(ChannelzProtoUtil.toSocket(socket)) - .build(), - getSocketHelper(socket.getLogId().getId())); - - channelz.removeClientSocket(socket); - assertSocketNotFound(socket.getLogId().getId()); - } - - private GetTopChannelsResponse getTopChannelHelper(long startId) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor responseCaptor - = ArgumentCaptor.forClass(GetTopChannelsResponse.class); - service.getTopChannels( - GetTopChannelsRequest.newBuilder().setStartChannelId(startId).build(), - observer); - verify(observer).onNext(responseCaptor.capture()); - verify(observer).onCompleted(); - return responseCaptor.getValue(); - } - - private GetChannelResponse getChannelHelper(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor response - = ArgumentCaptor.forClass(GetChannelResponse.class); - service.getChannel(GetChannelRequest.newBuilder().setChannelId(id).build(), observer); - verify(observer).onNext(response.capture()); - verify(observer).onCompleted(); - return response.getValue(); - } - - private void assertChannelNotFound(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); - service.getChannel(GetChannelRequest.newBuilder().setChannelId(id).build(), observer); - verify(observer).onError(exceptionCaptor.capture()); - Status s = Status.fromThrowable(exceptionCaptor.getValue()); - assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); - } - - private GetSubchannelResponse getSubchannelHelper(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor response - = ArgumentCaptor.forClass(GetSubchannelResponse.class); - service.getSubchannel(GetSubchannelRequest.newBuilder().setSubchannelId(id).build(), observer); - verify(observer).onNext(response.capture()); - verify(observer).onCompleted(); - return response.getValue(); - } - - private void assertSubchannelNotFound(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); - service.getSubchannel(GetSubchannelRequest.newBuilder().setSubchannelId(id).build(), observer); - verify(observer).onError(exceptionCaptor.capture()); - Status s = Status.fromThrowable(exceptionCaptor.getValue()); - assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); - } - - private GetServersResponse getServersHelper(long startId) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor responseCaptor - = ArgumentCaptor.forClass(GetServersResponse.class); - service.getServers( - GetServersRequest.newBuilder().setStartServerId(startId).build(), - observer); - verify(observer).onNext(responseCaptor.capture()); - verify(observer).onCompleted(); - return responseCaptor.getValue(); - } - - private void assertServerNotFound(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); - service.getServer(GetServerRequest.newBuilder().setServerId(id).build(), observer); - verify(observer).onError(exceptionCaptor.capture()); - Status s = Status.fromThrowable(exceptionCaptor.getValue()); - assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); - } - - private GetServerResponse getServerHelper(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor response = ArgumentCaptor.forClass(GetServerResponse.class); - service.getServer(GetServerRequest.newBuilder().setServerId(id).build(), observer); - verify(observer).onNext(response.capture()); - verify(observer).onCompleted(); - return response.getValue(); - } - - private void assertSocketNotFound(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); - service.getSocket(GetSocketRequest.newBuilder().setSocketId(id).build(), observer); - verify(observer).onError(exceptionCaptor.capture()); - Status s = Status.fromThrowable(exceptionCaptor.getValue()); - assertWithMessage(s.toString()).that(s.getCode()).isEqualTo(Status.Code.NOT_FOUND); - } - - private GetSocketResponse getSocketHelper(long id) { - @SuppressWarnings("unchecked") - StreamObserver observer = mock(StreamObserver.class); - ArgumentCaptor response - = ArgumentCaptor.forClass(GetSocketResponse.class); - service.getSocket(GetSocketRequest.newBuilder().setSocketId(id).build(), observer); - verify(observer).onNext(response.capture()); - verify(observer).onCompleted(); - return response.getValue(); - } -} From a9ce1388b4a1915e1f6d3cc85bb7c99c95b0c063 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:04:22 -0700 Subject: [PATCH 11/20] Forward with delegation, delete necessary abstract class forwarding. --- .../io/grpc/protobuf/services/BinaryLogs.java | 4 +- .../protobuf/services/ChannelzService.java | 6 +- .../services/HealthStatusManager.java | 2 +- .../io/grpc/services/BinaryLogProvider.java | 25 ------- .../grpc/services/BinaryLogProviderImpl.java | 4 +- .../java/io/grpc/services/BinaryLogs.java | 31 +++++++- .../java/io/grpc/services/BinlogHelper.java | 3 +- .../io/grpc/services/ChannelzService.java | 74 +++++++++++++++++-- .../io/grpc/services/HealthStatusManager.java | 57 +++++++++++++- .../grpc/services/ProtoReflectionService.java | 35 --------- .../io/grpc/services/BinlogHelperTest.java | 2 +- 11 files changed, 166 insertions(+), 77 deletions(-) delete mode 100644 services/src/main/java/io/grpc/services/BinaryLogProvider.java delete mode 100644 services/src/main/java/io/grpc/services/ProtoReflectionService.java diff --git a/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java b/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java index c468fc318d8..4a7757c8617 100644 --- a/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java +++ b/services/src/main/java/io/grpc/protobuf/services/BinaryLogs.java @@ -21,7 +21,7 @@ import java.io.IOException; @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") -public class BinaryLogs { +public final class BinaryLogs { /** * Creates a binary log that writes to a temp file. Warning: this implementation is * not performance optimized, and RPCs will experience back pressure if disk IO does not keep @@ -49,5 +49,5 @@ public static BinaryLog createBinaryLog(BinaryLogSink sink, String configStr) th return new BinaryLogProviderImpl(sink, configStr); } - protected BinaryLogs() {} + private BinaryLogs() {} } diff --git a/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java b/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java index cc8d663e2b7..66daf559d32 100644 --- a/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java +++ b/services/src/main/java/io/grpc/protobuf/services/ChannelzService.java @@ -16,6 +16,7 @@ package io.grpc.protobuf.services; +import com.google.common.annotations.VisibleForTesting; import io.grpc.ExperimentalApi; import io.grpc.InternalChannelz; import io.grpc.InternalChannelz.ChannelStats; @@ -47,7 +48,7 @@ * The channelz service provides stats about a running gRPC process. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4206") -public class ChannelzService extends ChannelzGrpc.ChannelzImplBase { +public final class ChannelzService extends ChannelzGrpc.ChannelzImplBase { private final InternalChannelz channelz; private final int maxPageSize; @@ -58,7 +59,8 @@ public static ChannelzService newInstance(int maxPageSize) { return new ChannelzService(InternalChannelz.instance(), maxPageSize); } - protected ChannelzService(InternalChannelz channelz, int maxPageSize) { + @VisibleForTesting + ChannelzService(InternalChannelz channelz, int maxPageSize) { this.channelz = channelz; this.maxPageSize = maxPageSize; } diff --git a/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java b/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java index 38f3b9c22d5..56cff68f3aa 100644 --- a/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java +++ b/services/src/main/java/io/grpc/protobuf/services/HealthStatusManager.java @@ -31,7 +31,7 @@ * {@link ServingStatus#SERVING}. */ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/4696") -public class HealthStatusManager { +public final class HealthStatusManager { /** * The special "service name" that represent all services on a GRPC server. It is an empty * string. diff --git a/services/src/main/java/io/grpc/services/BinaryLogProvider.java b/services/src/main/java/io/grpc/services/BinaryLogProvider.java deleted file mode 100644 index 66266d21725..00000000000 --- a/services/src/main/java/io/grpc/services/BinaryLogProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 The 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. - */ - -package io.grpc.services; - -import io.grpc.Internal; - -// TODO(zpencer): rename class to AbstractBinaryLog -@Internal -public abstract class BinaryLogProvider extends io.grpc.protobuf.services.BinaryLogProvider { - -} diff --git a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java index dee13a924c6..01e521359ff 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java +++ b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java @@ -25,9 +25,9 @@ import javax.annotation.Nullable; /** - * The default implementation of a {@link BinaryLogProvider}. + * The default implementation of a {@link io.grpc.protobuf.services.BinaryLogProvider}. */ -class BinaryLogProviderImpl extends BinaryLogProvider { +class BinaryLogProviderImpl extends io.grpc.protobuf.services.BinaryLogProvider { // avoid using 0 because proto3 long fields default to 0 when unset private static final AtomicLong counter = new AtomicLong(1); diff --git a/services/src/main/java/io/grpc/services/BinaryLogs.java b/services/src/main/java/io/grpc/services/BinaryLogs.java index 07950f75908..234a58b7706 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogs.java +++ b/services/src/main/java/io/grpc/services/BinaryLogs.java @@ -16,13 +16,42 @@ package io.grpc.services; +import io.grpc.BinaryLog; import io.grpc.ExperimentalApi; +import java.io.IOException; /** * @deprecated Use {@link io.grpc.protobuf.services.BinaryLogs} instead. */ @Deprecated @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") -public final class BinaryLogs extends io.grpc.protobuf.services.BinaryLogs { +public final class BinaryLogs { + /** + * Creates a binary log that writes to a temp file. Warning: this implementation is + * not performance optimized, and RPCs will experience back pressure if disk IO does not keep + * up. + */ + public static BinaryLog createBinaryLog() throws IOException { + return io.grpc.protobuf.services.BinaryLogs.createBinaryLog(); + } + + /** + * Deprecated and will be removed in a future version of gRPC. + */ + @Deprecated + public static BinaryLog createBinaryLog(BinaryLogSink sink) throws IOException { + return io.grpc.protobuf.services.BinaryLogs.createBinaryLog(sink); + } + + /** + * Creates a binary log with a custom {@link BinaryLogSink} for receiving the logged data, + * and a config string as defined by + * + * A16-binary-logging. + */ + public static BinaryLog createBinaryLog(BinaryLogSink sink, String configStr) throws IOException { + return io.grpc.protobuf.services.BinaryLogs.createBinaryLog(sink, configStr); + } + private BinaryLogs() {} } diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/services/BinlogHelper.java index d8923160e5c..c4d61bac737 100644 --- a/services/src/main/java/io/grpc/services/BinlogHelper.java +++ b/services/src/main/java/io/grpc/services/BinlogHelper.java @@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static io.grpc.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; @@ -245,7 +244,7 @@ void logRpcMessage( eventType == EventType.EVENT_TYPE_CLIENT_MESSAGE || eventType == EventType.EVENT_TYPE_SERVER_MESSAGE, "event type must correspond to client message or server message"); - if (marshaller != BYTEARRAY_MARSHALLER) { + if (marshaller != io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER) { throw new IllegalStateException("Expected the BinaryLog's ByteArrayMarshaller"); } MaybeTruncated pair = createMessageProto((byte[]) message, maxMessageBytes); diff --git a/services/src/main/java/io/grpc/services/ChannelzService.java b/services/src/main/java/io/grpc/services/ChannelzService.java index e41bdd7213d..e8ef2bb8a9a 100644 --- a/services/src/main/java/io/grpc/services/ChannelzService.java +++ b/services/src/main/java/io/grpc/services/ChannelzService.java @@ -17,7 +17,22 @@ package io.grpc.services; import io.grpc.ExperimentalApi; -import io.grpc.InternalChannelz; +import io.grpc.channelz.v1.ChannelzGrpc; +import io.grpc.channelz.v1.GetChannelRequest; +import io.grpc.channelz.v1.GetChannelResponse; +import io.grpc.channelz.v1.GetServerRequest; +import io.grpc.channelz.v1.GetServerResponse; +import io.grpc.channelz.v1.GetServerSocketsRequest; +import io.grpc.channelz.v1.GetServerSocketsResponse; +import io.grpc.channelz.v1.GetServersRequest; +import io.grpc.channelz.v1.GetServersResponse; +import io.grpc.channelz.v1.GetSocketRequest; +import io.grpc.channelz.v1.GetSocketResponse; +import io.grpc.channelz.v1.GetSubchannelRequest; +import io.grpc.channelz.v1.GetSubchannelResponse; +import io.grpc.channelz.v1.GetTopChannelsRequest; +import io.grpc.channelz.v1.GetTopChannelsResponse; +import io.grpc.stub.StreamObserver; /** * The channelz service provides stats about a running gRPC process. @@ -26,16 +41,65 @@ */ @Deprecated @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4206") -public final class ChannelzService extends io.grpc.protobuf.services.ChannelzService { +public final class ChannelzService extends ChannelzGrpc.ChannelzImplBase { + private final io.grpc.protobuf.services.ChannelzService delegate; /** * Creates an instance. */ public static ChannelzService newInstance(int maxPageSize) { - return new ChannelzService(InternalChannelz.instance(), maxPageSize); + return new ChannelzService(maxPageSize); } - private ChannelzService(InternalChannelz channelz, int maxPageSize) { - super(channelz, maxPageSize); + private ChannelzService(int maxPageSize) { + delegate = io.grpc.protobuf.services.ChannelzService.newInstance(maxPageSize); + } + + /** Returns top level channel aka {@link io.grpc.ManagedChannel}. */ + @Override + public void getTopChannels( + GetTopChannelsRequest request, StreamObserver responseObserver) { + delegate.getTopChannels(request, responseObserver); + } + + /** Returns a top level channel aka {@link io.grpc.ManagedChannel}. */ + @Override + public void getChannel( + GetChannelRequest request, StreamObserver responseObserver) { + delegate.getChannel(request, responseObserver); + } + + /** Returns servers. */ + @Override + public void getServers( + GetServersRequest request, StreamObserver responseObserver) { + delegate.getServers(request, responseObserver); + } + + /** Returns a server. */ + @Override + public void getServer( + GetServerRequest request, StreamObserver responseObserver) { + delegate.getServer(request, responseObserver); + } + + /** Returns a subchannel. */ + @Override + public void getSubchannel( + GetSubchannelRequest request, StreamObserver responseObserver) { + delegate.getSubchannel(request, responseObserver); + } + + /** Returns a socket. */ + @Override + public void getSocket( + GetSocketRequest request, StreamObserver responseObserver) { + delegate.getSocket(request, responseObserver); + } + + @Override + public void getServerSockets( + GetServerSocketsRequest request, StreamObserver responseObserver) { + delegate.getServerSockets(request, responseObserver); } } diff --git a/services/src/main/java/io/grpc/services/HealthStatusManager.java b/services/src/main/java/io/grpc/services/HealthStatusManager.java index 3b730439640..7bcaf52da52 100644 --- a/services/src/main/java/io/grpc/services/HealthStatusManager.java +++ b/services/src/main/java/io/grpc/services/HealthStatusManager.java @@ -16,6 +16,7 @@ package io.grpc.services; +import io.grpc.BindableService; import io.grpc.health.v1.HealthCheckResponse.ServingStatus; /** @@ -31,5 +32,59 @@ */ @Deprecated @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/4696") -public final class HealthStatusManager extends io.grpc.protobuf.services.HealthStatusManager { +public final class HealthStatusManager { + /** + * The special "service name" that represent all services on a GRPC server. It is an empty + * string. + */ + public static final String SERVICE_NAME_ALL_SERVICES = + io.grpc.protobuf.services.HealthStatusManager.SERVICE_NAME_ALL_SERVICES; + + private io.grpc.protobuf.services.HealthStatusManager delegate; + + /** + * Creates a new health service instance. + */ + public HealthStatusManager() { + delegate = new io.grpc.protobuf.services.HealthStatusManager(); + } + + /** + * Gets the health check service created in the constructor. + */ + public BindableService getHealthService() { + return delegate.getHealthService(); + } + + /** + * Updates the status of the server. + * @param service the name of some aspect of the server that is associated with a health status. + * This name can have no relation with the gRPC services that the server is running with. + * It can also be an empty String {@code ""} per the gRPC specification. + * @param status is one of the values {@link ServingStatus#SERVING}, + * {@link ServingStatus#NOT_SERVING} and {@link ServingStatus#UNKNOWN}. + */ + public void setStatus(String service, ServingStatus status) { + delegate.setStatus(service, status); + } + + /** + * Clears the health status record of a service. The health service will respond with NOT_FOUND + * error on checking the status of a cleared service. + * @param service the name of some aspect of the server that is associated with a health status. + * This name can have no relation with the gRPC services that the server is running with. + * It can also be an empty String {@code ""} per the gRPC specification. + */ + public void clearStatus(String service) { + delegate.clearStatus(service); + } + + /** + * enterTerminalState causes the health status manager to mark all services as not serving, and + * prevents future updates to services. This method is meant to be called prior to server + * shutdown as a way to indicate that clients should redirect their traffic elsewhere. + */ + public void enterTerminalState() { + delegate.enterTerminalState(); + } } diff --git a/services/src/main/java/io/grpc/services/ProtoReflectionService.java b/services/src/main/java/io/grpc/services/ProtoReflectionService.java deleted file mode 100644 index eb86500e222..00000000000 --- a/services/src/main/java/io/grpc/services/ProtoReflectionService.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2016 The 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. - */ - -package io.grpc.services; - -import io.grpc.ExperimentalApi; - -/** - * Provides a reflection service for Protobuf services (including the reflection service itself). - * - *

Separately tracks mutable and immutable services. Throws an exception if either group of - * services contains multiple Protobuf files with declarations of the same service, method, type, or - * extension. - * - * @deprecated Use {@link io.grpc.protobuf.services.ProtoReflectionService} instead. - */ -@Deprecated -@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222") -public final class ProtoReflectionService - extends io.grpc.protobuf.services.ProtoReflectionService { - private ProtoReflectionService() {} -} diff --git a/services/src/test/java/io/grpc/services/BinlogHelperTest.java b/services/src/test/java/io/grpc/services/BinlogHelperTest.java index d5ea0aec181..2596f0dd0de 100644 --- a/services/src/test/java/io/grpc/services/BinlogHelperTest.java +++ b/services/src/test/java/io/grpc/services/BinlogHelperTest.java @@ -17,7 +17,7 @@ package io.grpc.services; import static com.google.common.truth.Truth.assertThat; -import static io.grpc.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; +import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; import static io.grpc.services.BinlogHelper.createMetadataProto; import static io.grpc.services.BinlogHelper.getPeerSocket; import static org.junit.Assert.assertEquals; From b592d20de9d01cb871491de0ee2208d1e84acee7 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:07:29 -0700 Subject: [PATCH 12/20] Clean up bazel. --- services/BUILD.bazel | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/BUILD.bazel b/services/BUILD.bazel index 4418862b0fa..4673f54a28f 100644 --- a/services/BUILD.bazel +++ b/services/BUILD.bazel @@ -6,7 +6,6 @@ java_library( name = "binarylog", srcs = [ "src/main/java/io/grpc/protobuf/services/BinaryLogProvider.java", - "src/main/java/io/grpc/services/BinaryLogProvider.java", "src/main/java/io/grpc/protobuf/services/BinaryLogProviderImpl.java", "src/main/java/io/grpc/services/BinaryLogProviderImpl.java", "src/main/java/io/grpc/protobuf/services/BinaryLogSink.java", @@ -54,7 +53,6 @@ java_library( name = "reflection", srcs = [ "src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java", - "src/main/java/io/grpc/services/ProtoReflectionService.java", ], deps = [ ":_reflection_java_grpc", From 1951bc22a121c36b413f4341fa70ebdbe4e40520 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:09:20 -0700 Subject: [PATCH 13/20] Fix style. --- .../main/java/io/grpc/protobuf/services/InetAddressUtil.java | 2 +- .../io/grpc/protobuf/services/ProtoReflectionService.java | 4 ++-- .../src/main/java/io/grpc/protobuf/services/package-info.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java b/services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java index 2ba09c41b26..b8bacbc2bb3 100644 --- a/services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java +++ b/services/src/main/java/io/grpc/protobuf/services/InetAddressUtil.java @@ -91,4 +91,4 @@ private static String hextetsToIPv6String(int[] hextets) { } return buf.toString(); } -} \ No newline at end of file +} diff --git a/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java b/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java index 38a9ea23148..4a7840a3ad9 100644 --- a/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java +++ b/services/src/main/java/io/grpc/protobuf/services/ProtoReflectionService.java @@ -62,14 +62,14 @@ * extension. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222") -public class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { +public final class ProtoReflectionService extends ServerReflectionGrpc.ServerReflectionImplBase { private final Object lock = new Object(); @GuardedBy("lock") private final Map serverReflectionIndexes = new WeakHashMap<>(); - protected ProtoReflectionService() {} + private ProtoReflectionService() {} /** * Creates a instance of {@link ProtoReflectionService}. diff --git a/services/src/main/java/io/grpc/protobuf/services/package-info.java b/services/src/main/java/io/grpc/protobuf/services/package-info.java index 3c87b76b27b..6fb88cc4756 100644 --- a/services/src/main/java/io/grpc/protobuf/services/package-info.java +++ b/services/src/main/java/io/grpc/protobuf/services/package-info.java @@ -17,4 +17,4 @@ /** * Service definitions and utilities with protobuf dependency for the pre-defined gRPC services. */ -package io.grpc.protobuf.services; \ No newline at end of file +package io.grpc.protobuf.services; From ba9fb8debd821948d8ba3b5c3c30113f5713280d Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:14:48 -0700 Subject: [PATCH 14/20] Do not extend public interface that is already accessible. --- .../grpc/services/BinaryLogProviderImpl.java | 1 + .../java/io/grpc/services/BinaryLogSink.java | 29 ------------------- .../java/io/grpc/services/BinlogHelper.java | 1 + .../java/io/grpc/services/TempFileSink.java | 2 +- .../io/grpc/services/BinlogHelperTest.java | 1 + 5 files changed, 4 insertions(+), 30 deletions(-) delete mode 100644 services/src/main/java/io/grpc/services/BinaryLogSink.java diff --git a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java index 01e521359ff..58560cbf2d1 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java +++ b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java @@ -20,6 +20,7 @@ import io.grpc.CallOptions; import io.grpc.ClientInterceptor; import io.grpc.ServerInterceptor; +import io.grpc.protobuf.services.BinaryLogSink; import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nullable; diff --git a/services/src/main/java/io/grpc/services/BinaryLogSink.java b/services/src/main/java/io/grpc/services/BinaryLogSink.java deleted file mode 100644 index 7ac2246e907..00000000000 --- a/services/src/main/java/io/grpc/services/BinaryLogSink.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import io.grpc.ExperimentalApi; - -/** - * A class that accepts binary log messages. - * - * @deprecated Use {@link io.grpc.protobuf.services.BinaryLogSink} instead. - */ -@Deprecated -@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017") -public interface BinaryLogSink extends io.grpc.protobuf.services.BinaryLogSink { -} diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/services/BinlogHelper.java index c4d61bac737..5405b1f95cc 100644 --- a/services/src/main/java/io/grpc/services/BinlogHelper.java +++ b/services/src/main/java/io/grpc/services/BinlogHelper.java @@ -52,6 +52,7 @@ import io.grpc.binarylog.v1.GrpcLogEntry; import io.grpc.binarylog.v1.GrpcLogEntry.EventType; import io.grpc.binarylog.v1.Message; +import io.grpc.protobuf.services.BinaryLogSink; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; diff --git a/services/src/main/java/io/grpc/services/TempFileSink.java b/services/src/main/java/io/grpc/services/TempFileSink.java index c28339d1bd6..f21cb7d1977 100644 --- a/services/src/main/java/io/grpc/services/TempFileSink.java +++ b/services/src/main/java/io/grpc/services/TempFileSink.java @@ -29,7 +29,7 @@ * The output file goes to the JVM's temp dir with a prefix of BINARY_INFO. The proto messages * are written serially using {@link MessageLite#writeDelimitedTo(OutputStream)}. */ -class TempFileSink implements BinaryLogSink { +class TempFileSink implements io.grpc.protobuf.services.BinaryLogSink { private static final Logger logger = Logger.getLogger(TempFileSink.class.getName()); private final String outPath; diff --git a/services/src/test/java/io/grpc/services/BinlogHelperTest.java b/services/src/test/java/io/grpc/services/BinlogHelperTest.java index 2596f0dd0de..e5e9224f9c8 100644 --- a/services/src/test/java/io/grpc/services/BinlogHelperTest.java +++ b/services/src/test/java/io/grpc/services/BinlogHelperTest.java @@ -70,6 +70,7 @@ import io.grpc.internal.NoopClientCall; import io.grpc.internal.NoopServerCall; import io.grpc.protobuf.StatusProto; +import io.grpc.protobuf.services.BinaryLogSink; import io.grpc.services.BinlogHelper.FactoryImpl; import io.grpc.services.BinlogHelper.MaybeTruncated; import io.grpc.services.BinlogHelper.SinkWriter; From 6477050a8214ab42ee280abd8d2c67f1767e1f04 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:16:57 -0700 Subject: [PATCH 15/20] Statically import constant. --- services/src/main/java/io/grpc/services/BinlogHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/services/BinlogHelper.java index 5405b1f95cc..d7460c00b02 100644 --- a/services/src/main/java/io/grpc/services/BinlogHelper.java +++ b/services/src/main/java/io/grpc/services/BinlogHelper.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; @@ -245,7 +246,7 @@ void logRpcMessage( eventType == EventType.EVENT_TYPE_CLIENT_MESSAGE || eventType == EventType.EVENT_TYPE_SERVER_MESSAGE, "event type must correspond to client message or server message"); - if (marshaller != io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER) { + if (marshaller != BYTEARRAY_MARSHALLER) { throw new IllegalStateException("Expected the BinaryLog's ByteArrayMarshaller"); } MaybeTruncated pair = createMessageProto((byte[]) message, maxMessageBytes); From 3f4dd9410c22ba1101134c0816d185bc2a0e4059 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:18:39 -0700 Subject: [PATCH 16/20] Reestore excessive empty line change. --- .../resources/META-INF/services/io.grpc.LoadBalancerProvider | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider b/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider index d78ce9b58a2..d3e5f85ffbb 100644 --- a/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider +++ b/services/src/main/resources/META-INF/services/io.grpc.LoadBalancerProvider @@ -1 +1 @@ -io.grpc.protobuf.services.internal.HealthCheckingRoundRobinLoadBalancerProvider \ No newline at end of file +io.grpc.protobuf.services.internal.HealthCheckingRoundRobinLoadBalancerProvider From 1f5dcba39840d72d05b8d82abce9871c8e63f966 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:23:03 -0700 Subject: [PATCH 17/20] Fix interface reference. --- services/src/main/java/io/grpc/services/BinaryLogs.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/src/main/java/io/grpc/services/BinaryLogs.java b/services/src/main/java/io/grpc/services/BinaryLogs.java index 234a58b7706..e4831dc5bfa 100644 --- a/services/src/main/java/io/grpc/services/BinaryLogs.java +++ b/services/src/main/java/io/grpc/services/BinaryLogs.java @@ -18,6 +18,7 @@ import io.grpc.BinaryLog; import io.grpc.ExperimentalApi; +import io.grpc.protobuf.services.BinaryLogSink; import java.io.IOException; /** From 4dae3bbdd7e467cd7891793834c23f7d50189950 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:24:19 -0700 Subject: [PATCH 18/20] Empty line. --- services/src/main/java/io/grpc/services/InetAddressUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/io/grpc/services/InetAddressUtil.java b/services/src/main/java/io/grpc/services/InetAddressUtil.java index e1bc8e9d26b..057a8ccb5e6 100644 --- a/services/src/main/java/io/grpc/services/InetAddressUtil.java +++ b/services/src/main/java/io/grpc/services/InetAddressUtil.java @@ -91,4 +91,4 @@ private static String hextetsToIPv6String(int[] hextets) { } return buf.toString(); } -} +} \ No newline at end of file From de38d171e2c7bddefa3a8b7b658e3798b1866cf3 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:38:05 -0700 Subject: [PATCH 19/20] Delete all unnecessary classes. --- .../grpc/services/BinaryLogProviderImpl.java | 93 - .../java/io/grpc/services/BinlogHelper.java | 881 --------- .../io/grpc/services/ChannelzProtoUtil.java | 471 ----- .../io/grpc/services/InetAddressUtil.java | 94 - .../java/io/grpc/services/TempFileSink.java | 84 - .../services/BinaryLogProviderImplTest.java | 57 - .../io/grpc/services/BinlogHelperTest.java | 1582 ----------------- .../grpc/services/ChannelzProtoUtilTest.java | 949 ---------- .../io/grpc/services/ChannelzTestHelper.java | 183 -- .../io/grpc/services/TempFileSinkTest.java | 68 - 10 files changed, 4462 deletions(-) delete mode 100644 services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java delete mode 100644 services/src/main/java/io/grpc/services/BinlogHelper.java delete mode 100644 services/src/main/java/io/grpc/services/ChannelzProtoUtil.java delete mode 100644 services/src/main/java/io/grpc/services/InetAddressUtil.java delete mode 100644 services/src/main/java/io/grpc/services/TempFileSink.java delete mode 100644 services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java delete mode 100644 services/src/test/java/io/grpc/services/BinlogHelperTest.java delete mode 100644 services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java delete mode 100644 services/src/test/java/io/grpc/services/ChannelzTestHelper.java delete mode 100644 services/src/test/java/io/grpc/services/TempFileSinkTest.java diff --git a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java b/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java deleted file mode 100644 index 58560cbf2d1..00000000000 --- a/services/src/main/java/io/grpc/services/BinaryLogProviderImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import com.google.common.base.Preconditions; -import io.grpc.CallOptions; -import io.grpc.ClientInterceptor; -import io.grpc.ServerInterceptor; -import io.grpc.protobuf.services.BinaryLogSink; -import java.io.IOException; -import java.util.concurrent.atomic.AtomicLong; -import javax.annotation.Nullable; - -/** - * The default implementation of a {@link io.grpc.protobuf.services.BinaryLogProvider}. - */ -class BinaryLogProviderImpl extends io.grpc.protobuf.services.BinaryLogProvider { - // avoid using 0 because proto3 long fields default to 0 when unset - private static final AtomicLong counter = new AtomicLong(1); - - private final BinlogHelper.Factory factory; - private final BinaryLogSink sink; - - public BinaryLogProviderImpl() throws IOException { - this(new TempFileSink(), System.getenv("GRPC_BINARY_LOG_CONFIG")); - } - - /** - * Deprecated and will be removed in a future version of gRPC. - */ - @Deprecated - public BinaryLogProviderImpl(BinaryLogSink sink) throws IOException { - this(sink, System.getenv("GRPC_BINARY_LOG_CONFIG")); - } - - /** - * Creates an instance. - * @param sink ownership is transferred to this class. - * @param configStr config string to parse to determine logged methods and msg size limits. - * @throws IOException if initialization failed. - */ - public BinaryLogProviderImpl(BinaryLogSink sink, String configStr) throws IOException { - this.sink = Preconditions.checkNotNull(sink); - try { - factory = new BinlogHelper.FactoryImpl(sink, configStr); - } catch (RuntimeException e) { - sink.close(); - // parsing the conf string may throw if it is blank or contains errors - throw new IOException( - "Can not initialize. The env variable GRPC_BINARY_LOG_CONFIG must be valid.", e); - } - } - - @Nullable - @Override - public ServerInterceptor getServerInterceptor(String fullMethodName) { - BinlogHelper helperForMethod = factory.getLog(fullMethodName); - if (helperForMethod == null) { - return null; - } - return helperForMethod.getServerInterceptor(counter.getAndIncrement()); - } - - @Nullable - @Override - public ClientInterceptor getClientInterceptor( - String fullMethodName, CallOptions callOptions) { - BinlogHelper helperForMethod = factory.getLog(fullMethodName); - if (helperForMethod == null) { - return null; - } - return helperForMethod.getClientInterceptor(counter.getAndIncrement()); - } - - @Override - public void close() throws IOException { - sink.close(); - } -} diff --git a/services/src/main/java/io/grpc/services/BinlogHelper.java b/services/src/main/java/io/grpc/services/BinlogHelper.java deleted file mode 100644 index d7460c00b02..00000000000 --- a/services/src/main/java/io/grpc/services/BinlogHelper.java +++ /dev/null @@ -1,881 +0,0 @@ -/* - * Copyright 2017 The 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. - */ - -package io.grpc.services; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.protobuf.ByteString; -import com.google.protobuf.Duration; -import com.google.protobuf.util.Durations; -import com.google.protobuf.util.Timestamps; -import io.grpc.Attributes; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.Context; -import io.grpc.Deadline; -import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; -import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; -import io.grpc.ForwardingServerCall.SimpleForwardingServerCall; -import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener; -import io.grpc.Grpc; -import io.grpc.InternalMetadata; -import io.grpc.Metadata; -import io.grpc.MethodDescriptor; -import io.grpc.MethodDescriptor.Marshaller; -import io.grpc.ServerCall; -import io.grpc.ServerCallHandler; -import io.grpc.ServerInterceptor; -import io.grpc.Status; -import io.grpc.binarylog.v1.Address; -import io.grpc.binarylog.v1.GrpcLogEntry; -import io.grpc.binarylog.v1.GrpcLogEntry.EventType; -import io.grpc.binarylog.v1.Message; -import io.grpc.protobuf.services.BinaryLogSink; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.Nullable; -import javax.annotation.concurrent.ThreadSafe; - -/** - * A binary log class that is configured for a specific {@link MethodDescriptor}. - */ -@ThreadSafe -final class BinlogHelper { - private static final Logger logger = Logger.getLogger(BinlogHelper.class.getName()); - // Normally 'grpc-' metadata keys are set from within gRPC, and applications are not allowed - // to set them. This key is a special well known key that set from the application layer, but - // represents a com.google.rpc.Status and is given special first class treatment. - // See StatusProto.java - static final Metadata.Key STATUS_DETAILS_KEY = - Metadata.Key.of( - "grpc-status-details-bin", - Metadata.BINARY_BYTE_MARSHALLER); - - @VisibleForTesting - final SinkWriter writer; - - @VisibleForTesting - BinlogHelper(SinkWriter writer) { - this.writer = writer; - } - - // TODO(zpencer): move proto related static helpers into this class - static final class SinkWriterImpl extends SinkWriter { - private final BinaryLogSink sink; - private TimeProvider timeProvider; - private final int maxHeaderBytes; - private final int maxMessageBytes; - - SinkWriterImpl( - BinaryLogSink sink, - TimeProvider timeProvider, - int maxHeaderBytes, - int maxMessageBytes) { - this.sink = sink; - this.timeProvider = timeProvider; - this.maxHeaderBytes = maxHeaderBytes; - this.maxMessageBytes = maxMessageBytes; - } - - GrpcLogEntry.Builder newTimestampedBuilder() { - long epochNanos = timeProvider.currentTimeNanos(); - return GrpcLogEntry.newBuilder().setTimestamp(Timestamps.fromNanos(epochNanos)); - } - - @Override - void logClientHeader( - long seq, - String methodName, - // not all transports have the concept of authority - @Nullable String authority, - @Nullable Duration timeout, - Metadata metadata, - GrpcLogEntry.Logger logger, - long callId, - // null on client side - @Nullable SocketAddress peerAddress) { - Preconditions.checkArgument(methodName != null, "methodName can not be null"); - Preconditions.checkArgument( - !methodName.startsWith("/"), - "in grpc-java method names should not have a leading '/'. However this class will " - + "add one to be consistent with language agnostic conventions."); - Preconditions.checkArgument( - peerAddress == null || logger == GrpcLogEntry.Logger.LOGGER_SERVER, - "peerSocket can only be specified for server"); - - MaybeTruncated pair - = createMetadataProto(metadata, maxHeaderBytes); - io.grpc.binarylog.v1.ClientHeader.Builder clientHeaderBuilder - = io.grpc.binarylog.v1.ClientHeader.newBuilder() - .setMetadata(pair.proto) - .setMethodName("/" + methodName); - if (timeout != null) { - clientHeaderBuilder.setTimeout(timeout); - } - if (authority != null) { - clientHeaderBuilder.setAuthority(authority); - } - - GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() - .setSequenceIdWithinCall(seq) - .setType(EventType.EVENT_TYPE_CLIENT_HEADER) - .setClientHeader(clientHeaderBuilder) - .setPayloadTruncated(pair.truncated) - .setLogger(logger) - .setCallId(callId); - if (peerAddress != null) { - entryBuilder.setPeer(socketToProto(peerAddress)); - } - sink.write(entryBuilder.build()); - } - - @Override - void logServerHeader( - long seq, - Metadata metadata, - GrpcLogEntry.Logger logger, - long callId, - // null on server - @Nullable SocketAddress peerAddress) { - Preconditions.checkArgument( - peerAddress == null || logger == GrpcLogEntry.Logger.LOGGER_CLIENT, - "peerSocket can only be specified for client"); - MaybeTruncated pair - = createMetadataProto(metadata, maxHeaderBytes); - - GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() - .setSequenceIdWithinCall(seq) - .setType(EventType.EVENT_TYPE_SERVER_HEADER) - .setServerHeader( - io.grpc.binarylog.v1.ServerHeader.newBuilder() - .setMetadata(pair.proto)) - .setPayloadTruncated(pair.truncated) - .setLogger(logger) - .setCallId(callId); - if (peerAddress != null) { - entryBuilder.setPeer(socketToProto(peerAddress)); - } - sink.write(entryBuilder.build()); - } - - @Override - void logTrailer( - long seq, - Status status, - Metadata metadata, - GrpcLogEntry.Logger logger, - long callId, - // null on server, can be non null on client if this is a trailer-only response - @Nullable SocketAddress peerAddress) { - Preconditions.checkArgument( - peerAddress == null || logger == GrpcLogEntry.Logger.LOGGER_CLIENT, - "peerSocket can only be specified for client"); - MaybeTruncated pair - = createMetadataProto(metadata, maxHeaderBytes); - - io.grpc.binarylog.v1.Trailer.Builder trailerBuilder - = io.grpc.binarylog.v1.Trailer.newBuilder() - .setStatusCode(status.getCode().value()) - .setMetadata(pair.proto); - String statusDescription = status.getDescription(); - if (statusDescription != null) { - trailerBuilder.setStatusMessage(statusDescription); - } - byte[] statusDetailBytes = metadata.get(STATUS_DETAILS_KEY); - if (statusDetailBytes != null) { - trailerBuilder.setStatusDetails(ByteString.copyFrom(statusDetailBytes)); - } - - GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() - .setSequenceIdWithinCall(seq) - .setType(EventType.EVENT_TYPE_SERVER_TRAILER) - .setTrailer(trailerBuilder) - .setPayloadTruncated(pair.truncated) - .setLogger(logger) - .setCallId(callId); - if (peerAddress != null) { - entryBuilder.setPeer(socketToProto(peerAddress)); - } - sink.write(entryBuilder.build()); - } - - @Override - void logRpcMessage( - long seq, - EventType eventType, - Marshaller marshaller, - T message, - GrpcLogEntry.Logger logger, - long callId) { - Preconditions.checkArgument( - eventType == EventType.EVENT_TYPE_CLIENT_MESSAGE - || eventType == EventType.EVENT_TYPE_SERVER_MESSAGE, - "event type must correspond to client message or server message"); - if (marshaller != BYTEARRAY_MARSHALLER) { - throw new IllegalStateException("Expected the BinaryLog's ByteArrayMarshaller"); - } - MaybeTruncated pair = createMessageProto((byte[]) message, maxMessageBytes); - GrpcLogEntry.Builder entryBuilder = newTimestampedBuilder() - .setSequenceIdWithinCall(seq) - .setType(eventType) - .setMessage(pair.proto) - .setPayloadTruncated(pair.truncated) - .setLogger(logger) - .setCallId(callId); - sink.write(entryBuilder.build()); - } - - @Override - void logHalfClose(long seq, GrpcLogEntry.Logger logger, long callId) { - sink.write( - newTimestampedBuilder() - .setSequenceIdWithinCall(seq) - .setType(EventType.EVENT_TYPE_CLIENT_HALF_CLOSE) - .setLogger(logger) - .setCallId(callId) - .build()); - } - - @Override - void logCancel(long seq, GrpcLogEntry.Logger logger, long callId) { - sink.write( - newTimestampedBuilder() - .setSequenceIdWithinCall(seq) - .setType(EventType.EVENT_TYPE_CANCEL) - .setLogger(logger) - .setCallId(callId) - .build()); - } - - @Override - int getMaxHeaderBytes() { - return maxHeaderBytes; - } - - @Override - int getMaxMessageBytes() { - return maxMessageBytes; - } - } - - abstract static class SinkWriter { - /** - * Logs the client header. This method logs the appropriate number of bytes - * as determined by the binary logging configuration. - */ - abstract void logClientHeader( - long seq, - String methodName, - // not all transports have the concept of authority - @Nullable String authority, - @Nullable Duration timeout, - Metadata metadata, - GrpcLogEntry.Logger logger, - long callId, - // null on client side - @Nullable SocketAddress peerAddress); - - /** - * Logs the server header. This method logs the appropriate number of bytes - * as determined by the binary logging configuration. - */ - abstract void logServerHeader( - long seq, - Metadata metadata, - GrpcLogEntry.Logger logger, - long callId, - // null on server - @Nullable SocketAddress peerAddress); - - /** - * Logs the server trailer. This method logs the appropriate number of bytes - * as determined by the binary logging configuration. - */ - abstract void logTrailer( - long seq, - Status status, - Metadata metadata, - GrpcLogEntry.Logger logger, - long callId, - // null on server, can be non null on client if this is a trailer-only response - @Nullable SocketAddress peerAddress); - - /** - * Logs the message message. The number of bytes logged is determined by the binary - * logging configuration. - */ - abstract void logRpcMessage( - long seq, - EventType eventType, - Marshaller marshaller, - T message, - GrpcLogEntry.Logger logger, - long callId); - - abstract void logHalfClose(long seq, GrpcLogEntry.Logger logger, long callId); - - /** - * Logs the cancellation. - */ - abstract void logCancel(long seq, GrpcLogEntry.Logger logger, long callId); - - /** - * Returns the number bytes of the header this writer will log, according to configuration. - */ - abstract int getMaxHeaderBytes(); - - /** - * Returns the number bytes of the message this writer will log, according to configuration. - */ - abstract int getMaxMessageBytes(); - } - - static SocketAddress getPeerSocket(Attributes streamAttributes) { - return streamAttributes.get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR); - } - - private static Deadline min(@Nullable Deadline deadline0, @Nullable Deadline deadline1) { - if (deadline0 == null) { - return deadline1; - } - if (deadline1 == null) { - return deadline0; - } - return deadline0.minimum(deadline1); - } - - interface TimeProvider { - /** Returns the current nano time. */ - long currentTimeNanos(); - - TimeProvider SYSTEM_TIME_PROVIDER = new TimeProvider() { - @Override - public long currentTimeNanos() { - return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()); - } - }; - } - - - public ClientInterceptor getClientInterceptor(final long callId) { - return new ClientInterceptor() { - boolean trailersOnlyResponse = true; - @Override - public ClientCall interceptCall( - final MethodDescriptor method, CallOptions callOptions, Channel next) { - final AtomicLong seq = new AtomicLong(1); - final String methodName = method.getFullMethodName(); - final String authority = next.authority(); - // The timeout should reflect the time remaining when the call is started, so do not - // compute remaining time here. - final Deadline deadline = min(callOptions.getDeadline(), Context.current().getDeadline()); - - return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { - @Override - public void start(final ClientCall.Listener responseListener, Metadata headers) { - final Duration timeout = deadline == null ? null - : Durations.fromNanos(deadline.timeRemaining(TimeUnit.NANOSECONDS)); - writer.logClientHeader( - seq.getAndIncrement(), - methodName, - authority, - timeout, - headers, - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId, - /*peerAddress=*/ null); - ClientCall.Listener wListener = - new SimpleForwardingClientCallListener(responseListener) { - @Override - public void onMessage(RespT message) { - writer.logRpcMessage( - seq.getAndIncrement(), - EventType.EVENT_TYPE_SERVER_MESSAGE, - method.getResponseMarshaller(), - message, - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId); - super.onMessage(message); - } - - @Override - public void onHeaders(Metadata headers) { - trailersOnlyResponse = false; - writer.logServerHeader( - seq.getAndIncrement(), - headers, - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId, - getPeerSocket(getAttributes())); - super.onHeaders(headers); - } - - @Override - public void onClose(Status status, Metadata trailers) { - SocketAddress peer = trailersOnlyResponse - ? getPeerSocket(getAttributes()) : null; - writer.logTrailer( - seq.getAndIncrement(), - status, - trailers, - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId, - peer); - super.onClose(status, trailers); - } - }; - super.start(wListener, headers); - } - - @Override - public void sendMessage(ReqT message) { - writer.logRpcMessage( - seq.getAndIncrement(), - EventType.EVENT_TYPE_CLIENT_MESSAGE, - method.getRequestMarshaller(), - message, - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId); - super.sendMessage(message); - } - - @Override - public void halfClose() { - writer.logHalfClose( - seq.getAndIncrement(), - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId); - super.halfClose(); - } - - @Override - public void cancel(String message, Throwable cause) { - writer.logCancel( - seq.getAndIncrement(), - GrpcLogEntry.Logger.LOGGER_CLIENT, - callId); - super.cancel(message, cause); - } - }; - } - }; - } - - public ServerInterceptor getServerInterceptor(final long callId) { - return new ServerInterceptor() { - @Override - public ServerCall.Listener interceptCall( - final ServerCall call, - Metadata headers, - ServerCallHandler next) { - final AtomicLong seq = new AtomicLong(1); - SocketAddress peer = getPeerSocket(call.getAttributes()); - String methodName = call.getMethodDescriptor().getFullMethodName(); - String authority = call.getAuthority(); - Deadline deadline = Context.current().getDeadline(); - final Duration timeout = deadline == null ? null - : Durations.fromNanos(deadline.timeRemaining(TimeUnit.NANOSECONDS)); - - writer.logClientHeader( - seq.getAndIncrement(), - methodName, - authority, - timeout, - headers, - GrpcLogEntry.Logger.LOGGER_SERVER, - callId, - peer); - ServerCall wCall = new SimpleForwardingServerCall(call) { - @Override - public void sendMessage(RespT message) { - writer.logRpcMessage( - seq.getAndIncrement(), - EventType.EVENT_TYPE_SERVER_MESSAGE, - call.getMethodDescriptor().getResponseMarshaller(), - message, - GrpcLogEntry.Logger.LOGGER_SERVER, - callId); - super.sendMessage(message); - } - - @Override - public void sendHeaders(Metadata headers) { - writer.logServerHeader( - seq.getAndIncrement(), - headers, - GrpcLogEntry.Logger.LOGGER_SERVER, - callId, - /*peerAddress=*/ null); - super.sendHeaders(headers); - } - - @Override - public void close(Status status, Metadata trailers) { - writer.logTrailer( - seq.getAndIncrement(), - status, - trailers, - GrpcLogEntry.Logger.LOGGER_SERVER, - callId, - /*peerAddress=*/ null); - super.close(status, trailers); - } - }; - - return new SimpleForwardingServerCallListener(next.startCall(wCall, headers)) { - @Override - public void onMessage(ReqT message) { - writer.logRpcMessage( - seq.getAndIncrement(), - EventType.EVENT_TYPE_CLIENT_MESSAGE, - call.getMethodDescriptor().getRequestMarshaller(), - message, - GrpcLogEntry.Logger.LOGGER_SERVER, - callId); - super.onMessage(message); - } - - @Override - public void onHalfClose() { - writer.logHalfClose( - seq.getAndIncrement(), - GrpcLogEntry.Logger.LOGGER_SERVER, - callId); - super.onHalfClose(); - } - - @Override - public void onCancel() { - writer.logCancel( - seq.getAndIncrement(), - GrpcLogEntry.Logger.LOGGER_SERVER, - callId); - super.onCancel(); - } - }; - } - }; - } - - interface Factory { - @Nullable - BinlogHelper getLog(String fullMethodName); - } - - static final class FactoryImpl implements Factory { - private final BinlogHelper globalLog; - private final Map perServiceLogs; - private final Map perMethodLogs; - private final Set blacklistedMethods; - - /** - * Accepts a string in the format specified by the binary log spec. - */ - @VisibleForTesting - FactoryImpl(BinaryLogSink sink, String configurationString) { - checkNotNull(sink, "sink"); - BinlogHelper globalLog = null; - Map perServiceLogs = new HashMap<>(); - Map perMethodLogs = new HashMap<>(); - Set blacklistedMethods = new HashSet<>(); - if (configurationString != null && configurationString.length() > 0) { - for (String configuration : Splitter.on(',').split(configurationString)) { - int leftCurly = configuration.indexOf('{'); - // '*' for global, 'service/*' for service glob, or 'service/method' for fully qualified - String methodOrSvc; - // An expression originally wrapped in curly braces; like {m:256,h:256}, {m:256}, {h:256} - String binlogOptionStr; - if (leftCurly == -1) { - methodOrSvc = configuration; - binlogOptionStr = null; - } else { - int rightCurly = configuration.indexOf('}', leftCurly); - if (rightCurly != configuration.length() - 1) { - throw new IllegalArgumentException("Illegal log config pattern: " + configuration); - } - methodOrSvc = configuration.substring(0, leftCurly); - // option without the curly braces - binlogOptionStr = configuration.substring(leftCurly + 1, configuration.length() - 1); - } - if (methodOrSvc.isEmpty()) { - throw new IllegalArgumentException("Illegal log config pattern: " + configuration); - } - if (methodOrSvc.equals("*")) { - // parse config for "*" - checkState( - globalLog == null, - "Duplicate entry, this is fatal: " + configuration); - globalLog = createBinaryLog(sink, binlogOptionStr); - logger.log(Level.INFO, "Global binlog: {0}", binlogOptionStr); - } else if (isServiceGlob(methodOrSvc)) { - // parse config for a service, e.g. "service/*" - String service = MethodDescriptor.extractFullServiceName(methodOrSvc); - checkState( - !perServiceLogs.containsKey(service), - "Duplicate entry, this is fatal: " + configuration); - perServiceLogs.put(service, createBinaryLog(sink, binlogOptionStr)); - logger.log( - Level.INFO, - "Service binlog: service={0} config={1}", - new Object[] {service, binlogOptionStr}); - } else if (methodOrSvc.startsWith("-")) { - // parse config for a method, e.g. "-service/method" - String blacklistedMethod = methodOrSvc.substring(1); - if (blacklistedMethod.length() == 0) { - continue; - } - checkState( - !blacklistedMethods.contains(blacklistedMethod), - "Duplicate entry, this is fatal: " + configuration); - checkState( - !perMethodLogs.containsKey(blacklistedMethod), - "Duplicate entry, this is fatal: " + configuration); - blacklistedMethods.add(blacklistedMethod); - } else { - // parse config for a fully qualified method, e.g "serice/method" - checkState( - !perMethodLogs.containsKey(methodOrSvc), - "Duplicate entry, this is fatal: " + configuration); - checkState( - !blacklistedMethods.contains(methodOrSvc), - "Duplicate entry, this method was blacklisted: " + configuration); - perMethodLogs.put(methodOrSvc, createBinaryLog(sink, binlogOptionStr)); - logger.log( - Level.INFO, - "Method binlog: method={0} config={1}", - new Object[] {methodOrSvc, binlogOptionStr}); - } - } - } - this.globalLog = globalLog; - this.perServiceLogs = Collections.unmodifiableMap(perServiceLogs); - this.perMethodLogs = Collections.unmodifiableMap(perMethodLogs); - this.blacklistedMethods = Collections.unmodifiableSet(blacklistedMethods); - } - - /** - * Accepts a full method name and returns the log that should be used. - */ - @Override - public BinlogHelper getLog(String fullMethodName) { - if (blacklistedMethods.contains(fullMethodName)) { - return null; - } - BinlogHelper methodLog = perMethodLogs.get(fullMethodName); - if (methodLog != null) { - return methodLog; - } - BinlogHelper serviceLog = perServiceLogs.get( - MethodDescriptor.extractFullServiceName(fullMethodName)); - if (serviceLog != null) { - return serviceLog; - } - return globalLog; - } - - /** - * Returns a binlog with the correct header and message limits or {@code null} if the input - * is malformed. The input should be a string that is in one of these forms: - * - *

{@code {h(:\d+)?}, {m(:\d+)?}, {h(:\d+)?,m(:\d+)?}} - * - *

If the {@code logConfig} is null, the returned binlog will have a limit of - * Integer.MAX_VALUE. - */ - @VisibleForTesting - @Nullable - static BinlogHelper createBinaryLog(BinaryLogSink sink, @Nullable String logConfig) { - if (logConfig == null) { - return new BinlogHelper( - new SinkWriterImpl( - sink, TimeProvider.SYSTEM_TIME_PROVIDER, Integer.MAX_VALUE, Integer.MAX_VALUE)); - } - try { - final int maxHeaderBytes; - final int maxMsgBytes; - String[] parts = logConfig.split(";", 2); - if (parts.length == 2) { - if (!(parts[0].startsWith("h") && parts[1].startsWith("m"))) { - throw new IllegalArgumentException("Illegal log config pattern"); - } - maxHeaderBytes = optionalInt(parts[0].substring(1)); - maxMsgBytes = optionalInt(parts[1].substring(1)); - } else if (parts[0].startsWith("h")) { - maxHeaderBytes = optionalInt(parts[0].substring(1)); - maxMsgBytes = 0; - } else if (parts[0].startsWith("m")) { - maxHeaderBytes = 0; - maxMsgBytes = optionalInt(parts[0].substring(1)); - } else { - throw new IllegalArgumentException("Illegal log config pattern"); - } - return new BinlogHelper( - new SinkWriterImpl( - sink, TimeProvider.SYSTEM_TIME_PROVIDER, maxHeaderBytes, maxMsgBytes)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Illegal log config pattern"); - } - } - - /** Returns {@code s}, after verifying it contains only digits. */ - static String checkDigits(String s) { - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c < '0' || '9' < c) { - throw new IllegalArgumentException("Illegal log config pattern"); - } - } - return s; - } - - /** Parses the optional int of the form "" (max int) or ":123" (123). */ - static int optionalInt(String s) { - if (s.isEmpty()) { - return Integer.MAX_VALUE; - } - if (!s.startsWith(":")) { - throw new IllegalArgumentException("Illegal log config pattern"); - } - s = checkDigits(s.substring(1)); - return Integer.parseInt(s); - } - - /** - * Returns true if the input string is a glob of the form: {@code /*}. - */ - static boolean isServiceGlob(String input) { - return input.endsWith("/*"); - } - } - - @VisibleForTesting - static Address socketToProto(SocketAddress address) { - checkNotNull(address, "address"); - - Address.Builder builder = Address.newBuilder(); - if (address instanceof InetSocketAddress) { - InetAddress inetAddress = ((InetSocketAddress) address).getAddress(); - if (inetAddress instanceof Inet4Address) { - builder.setType(Address.Type.TYPE_IPV4) - .setAddress(InetAddressUtil.toAddrString(inetAddress)); - } else if (inetAddress instanceof Inet6Address) { - builder.setType(Address.Type.TYPE_IPV6) - .setAddress(InetAddressUtil.toAddrString(inetAddress)); - } else { - logger.log(Level.SEVERE, "unknown type of InetSocketAddress: {}", address); - builder.setAddress(address.toString()); - } - builder.setIpPort(((InetSocketAddress) address).getPort()); - } else if (address.getClass().getName().equals("io.netty.channel.unix.DomainSocketAddress")) { - // To avoid a compile time dependency on grpc-netty, we check against the runtime class name. - builder.setType(Address.Type.TYPE_UNIX) - .setAddress(address.toString()); - } else { - builder.setType(Address.Type.TYPE_UNKNOWN).setAddress(address.toString()); - } - return builder.build(); - } - - private static final Set NEVER_INCLUDED_METADATA = new HashSet<>( - Collections.singletonList( - // grpc-status-details-bin is already logged in a field of the binlog proto - STATUS_DETAILS_KEY.name())); - private static final Set ALWAYS_INCLUDED_METADATA = new HashSet<>( - Collections.singletonList( - "grpc-trace-bin")); - - static final class MaybeTruncated { - T proto; - boolean truncated; - - private MaybeTruncated(T proto, boolean truncated) { - this.proto = proto; - this.truncated = truncated; - } - } - - @VisibleForTesting - static MaybeTruncated createMetadataProto( - Metadata metadata, int maxHeaderBytes) { - checkNotNull(metadata, "metadata"); - checkArgument(maxHeaderBytes >= 0, "maxHeaderBytes must be non negative"); - io.grpc.binarylog.v1.Metadata.Builder metaBuilder = io.grpc.binarylog.v1.Metadata.newBuilder(); - // This code is tightly coupled with Metadata's implementation - byte[][] serialized = InternalMetadata.serialize(metadata); - boolean truncated = false; - if (serialized != null) { - int curBytes = 0; - for (int i = 0; i < serialized.length; i += 2) { - String key = new String(serialized[i], Charsets.UTF_8); - byte[] value = serialized[i + 1]; - if (NEVER_INCLUDED_METADATA.contains(key)) { - continue; - } - boolean forceInclude = ALWAYS_INCLUDED_METADATA.contains(key); - int bytesAfterAdd = curBytes + key.length() + value.length; - if (!forceInclude && bytesAfterAdd > maxHeaderBytes) { - truncated = true; - continue; - } - metaBuilder.addEntryBuilder() - .setKey(key) - .setValue(ByteString.copyFrom(value)); - if (!forceInclude) { - // force included keys do not count towards the size limit - curBytes = bytesAfterAdd; - } - } - } - return new MaybeTruncated<>(metaBuilder, truncated); - } - - @VisibleForTesting - static MaybeTruncated createMessageProto( - byte[] message, int maxMessageBytes) { - checkNotNull(message, "message"); - checkArgument(maxMessageBytes >= 0, "maxMessageBytes must be non negative"); - Message.Builder msgBuilder = Message - .newBuilder() - .setLength(message.length); - if (maxMessageBytes > 0) { - int desiredBytes = Math.min(maxMessageBytes, message.length); - msgBuilder.setData(ByteString.copyFrom(message, 0, desiredBytes)); - } - return new MaybeTruncated<>(msgBuilder, maxMessageBytes < message.length); - } -} diff --git a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java b/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java deleted file mode 100644 index 349995d9f80..00000000000 --- a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; -import com.google.protobuf.Int64Value; -import com.google.protobuf.util.Durations; -import com.google.protobuf.util.Timestamps; -import io.grpc.ConnectivityState; -import io.grpc.InternalChannelz; -import io.grpc.InternalChannelz.ChannelStats; -import io.grpc.InternalChannelz.ChannelTrace.Event; -import io.grpc.InternalChannelz.RootChannelList; -import io.grpc.InternalChannelz.ServerList; -import io.grpc.InternalChannelz.ServerSocketsList; -import io.grpc.InternalChannelz.ServerStats; -import io.grpc.InternalChannelz.SocketStats; -import io.grpc.InternalChannelz.TransportStats; -import io.grpc.InternalInstrumented; -import io.grpc.InternalWithLogId; -import io.grpc.Status; -import io.grpc.channelz.v1.Address; -import io.grpc.channelz.v1.Address.OtherAddress; -import io.grpc.channelz.v1.Address.TcpIpAddress; -import io.grpc.channelz.v1.Address.UdsAddress; -import io.grpc.channelz.v1.Channel; -import io.grpc.channelz.v1.ChannelConnectivityState; -import io.grpc.channelz.v1.ChannelConnectivityState.State; -import io.grpc.channelz.v1.ChannelData; -import io.grpc.channelz.v1.ChannelRef; -import io.grpc.channelz.v1.ChannelTrace; -import io.grpc.channelz.v1.ChannelTraceEvent; -import io.grpc.channelz.v1.ChannelTraceEvent.Severity; -import io.grpc.channelz.v1.GetServerSocketsResponse; -import io.grpc.channelz.v1.GetServersResponse; -import io.grpc.channelz.v1.GetTopChannelsResponse; -import io.grpc.channelz.v1.Security; -import io.grpc.channelz.v1.Security.OtherSecurity; -import io.grpc.channelz.v1.Security.Tls; -import io.grpc.channelz.v1.Server; -import io.grpc.channelz.v1.ServerData; -import io.grpc.channelz.v1.ServerRef; -import io.grpc.channelz.v1.Socket; -import io.grpc.channelz.v1.SocketData; -import io.grpc.channelz.v1.SocketOption; -import io.grpc.channelz.v1.SocketOptionLinger; -import io.grpc.channelz.v1.SocketOptionTcpInfo; -import io.grpc.channelz.v1.SocketOptionTimeout; -import io.grpc.channelz.v1.SocketRef; -import io.grpc.channelz.v1.Subchannel; -import io.grpc.channelz.v1.SubchannelRef; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.cert.CertificateEncodingException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A static utility class for turning internal data structures into protos. - */ -final class ChannelzProtoUtil { - private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName()); - - private ChannelzProtoUtil() { - // do not instantiate. - } - - static ChannelRef toChannelRef(InternalWithLogId obj) { - return ChannelRef - .newBuilder() - .setChannelId(obj.getLogId().getId()) - .setName(obj.toString()) - .build(); - } - - static SubchannelRef toSubchannelRef(InternalWithLogId obj) { - return SubchannelRef - .newBuilder() - .setSubchannelId(obj.getLogId().getId()) - .setName(obj.toString()) - .build(); - } - - static ServerRef toServerRef(InternalWithLogId obj) { - return ServerRef - .newBuilder() - .setServerId(obj.getLogId().getId()) - .setName(obj.toString()) - .build(); - } - - static SocketRef toSocketRef(InternalWithLogId obj) { - return SocketRef - .newBuilder() - .setSocketId(obj.getLogId().getId()) - .setName(obj.toString()) - .build(); - } - - static Server toServer(InternalInstrumented obj) { - ServerStats stats = getFuture(obj.getStats()); - Server.Builder builder = Server - .newBuilder() - .setRef(toServerRef(obj)) - .setData(toServerData(stats)); - for (InternalInstrumented listenSocket : stats.listenSockets) { - builder.addListenSocket(toSocketRef(listenSocket)); - } - return builder.build(); - } - - static ServerData toServerData(ServerStats stats) { - return ServerData - .newBuilder() - .setCallsStarted(stats.callsStarted) - .setCallsSucceeded(stats.callsSucceeded) - .setCallsFailed(stats.callsFailed) - .setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos)) - .build(); - } - - static Security toSecurity(InternalChannelz.Security security) { - Preconditions.checkNotNull(security); - Preconditions.checkState( - security.tls != null ^ security.other != null, - "one of tls or othersecurity must be non null"); - if (security.tls != null) { - Tls.Builder tlsBuilder - = Tls.newBuilder().setStandardName(security.tls.cipherSuiteStandardName); - try { - if (security.tls.localCert != null) { - tlsBuilder.setLocalCertificate(ByteString.copyFrom( - security.tls.localCert.getEncoded())); - } - if (security.tls.remoteCert != null) { - tlsBuilder.setRemoteCertificate(ByteString.copyFrom( - security.tls.remoteCert.getEncoded())); - } - } catch (CertificateEncodingException e) { - logger.log(Level.FINE, "Caught exception", e); - } - return Security.newBuilder().setTls(tlsBuilder).build(); - } else { - OtherSecurity.Builder builder = OtherSecurity.newBuilder().setName(security.other.name); - if (security.other.any != null) { - builder.setValue((Any) security.other.any); - } - return Security.newBuilder().setOther(builder).build(); - } - } - - static Socket toSocket(InternalInstrumented obj) { - SocketStats socketStats = getFuture(obj.getStats()); - Socket.Builder builder = Socket.newBuilder() - .setRef(toSocketRef(obj)) - .setLocal(toAddress(socketStats.local)); - if (socketStats.security != null) { - builder.setSecurity(toSecurity(socketStats.security)); - } - // listen sockets do not have remote nor data - if (socketStats.remote != null) { - builder.setRemote(toAddress(socketStats.remote)); - } - builder.setData(extractSocketData(socketStats)); - return builder.build(); - } - - static Address toAddress(SocketAddress address) { - Preconditions.checkNotNull(address); - Address.Builder builder = Address.newBuilder(); - if (address instanceof InetSocketAddress) { - InetSocketAddress inetAddress = (InetSocketAddress) address; - builder.setTcpipAddress( - TcpIpAddress - .newBuilder() - .setIpAddress( - ByteString.copyFrom(inetAddress.getAddress().getAddress())) - .setPort(inetAddress.getPort()) - .build()); - } else if (address.getClass().getName().endsWith("io.netty.channel.unix.DomainSocketAddress")) { - builder.setUdsAddress( - UdsAddress - .newBuilder() - .setFilename(address.toString()) // DomainSocketAddress.toString returns filename - .build()); - } else { - builder.setOtherAddress(OtherAddress.newBuilder().setName(address.toString()).build()); - } - return builder.build(); - } - - static SocketData extractSocketData(SocketStats socketStats) { - SocketData.Builder builder = SocketData.newBuilder(); - if (socketStats.data != null) { - TransportStats s = socketStats.data; - builder - .setStreamsStarted(s.streamsStarted) - .setStreamsSucceeded(s.streamsSucceeded) - .setStreamsFailed(s.streamsFailed) - .setMessagesSent(s.messagesSent) - .setMessagesReceived(s.messagesReceived) - .setKeepAlivesSent(s.keepAlivesSent) - .setLastLocalStreamCreatedTimestamp( - Timestamps.fromNanos(s.lastLocalStreamCreatedTimeNanos)) - .setLastRemoteStreamCreatedTimestamp( - Timestamps.fromNanos(s.lastRemoteStreamCreatedTimeNanos)) - .setLastMessageSentTimestamp( - Timestamps.fromNanos(s.lastMessageSentTimeNanos)) - .setLastMessageReceivedTimestamp( - Timestamps.fromNanos(s.lastMessageReceivedTimeNanos)) - .setLocalFlowControlWindow( - Int64Value.of(s.localFlowControlWindow)) - .setRemoteFlowControlWindow( - Int64Value.of(s.remoteFlowControlWindow)); - } - builder.addAllOption(toSocketOptionsList(socketStats.socketOptions)); - return builder.build(); - } - - public static final String SO_LINGER = "SO_LINGER"; - public static final String SO_TIMEOUT = "SO_TIMEOUT"; - public static final String TCP_INFO = "TCP_INFO"; - - static SocketOption toSocketOptionLinger(int lingerSeconds) { - final SocketOptionLinger lingerOpt; - if (lingerSeconds >= 0) { - lingerOpt = SocketOptionLinger - .newBuilder() - .setActive(true) - .setDuration(Durations.fromSeconds(lingerSeconds)) - .build(); - } else { - lingerOpt = SocketOptionLinger.getDefaultInstance(); - } - return SocketOption - .newBuilder() - .setName(SO_LINGER) - .setAdditional(Any.pack(lingerOpt)) - .build(); - } - - static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) { - Preconditions.checkNotNull(name); - return SocketOption - .newBuilder() - .setName(name) - .setAdditional( - Any.pack( - SocketOptionTimeout - .newBuilder() - .setDuration(Durations.fromMillis(timeoutMillis)) - .build())) - .build(); - } - - static SocketOption toSocketOptionTcpInfo(InternalChannelz.TcpInfo i) { - SocketOptionTcpInfo tcpInfo = SocketOptionTcpInfo.newBuilder() - .setTcpiState(i.state) - .setTcpiCaState(i.caState) - .setTcpiRetransmits(i.retransmits) - .setTcpiProbes(i.probes) - .setTcpiBackoff(i.backoff) - .setTcpiOptions(i.options) - .setTcpiSndWscale(i.sndWscale) - .setTcpiRcvWscale(i.rcvWscale) - .setTcpiRto(i.rto) - .setTcpiAto(i.ato) - .setTcpiSndMss(i.sndMss) - .setTcpiRcvMss(i.rcvMss) - .setTcpiUnacked(i.unacked) - .setTcpiSacked(i.sacked) - .setTcpiLost(i.lost) - .setTcpiRetrans(i.retrans) - .setTcpiFackets(i.fackets) - .setTcpiLastDataSent(i.lastDataSent) - .setTcpiLastAckSent(i.lastAckSent) - .setTcpiLastDataRecv(i.lastDataRecv) - .setTcpiLastAckRecv(i.lastAckRecv) - .setTcpiPmtu(i.pmtu) - .setTcpiRcvSsthresh(i.rcvSsthresh) - .setTcpiRtt(i.rtt) - .setTcpiRttvar(i.rttvar) - .setTcpiSndSsthresh(i.sndSsthresh) - .setTcpiSndCwnd(i.sndCwnd) - .setTcpiAdvmss(i.advmss) - .setTcpiReordering(i.reordering) - .build(); - return SocketOption - .newBuilder() - .setName(TCP_INFO) - .setAdditional(Any.pack(tcpInfo)) - .build(); - } - - static SocketOption toSocketOptionAdditional(String name, String value) { - Preconditions.checkNotNull(name); - Preconditions.checkNotNull(value); - return SocketOption.newBuilder().setName(name).setValue(value).build(); - } - - static List toSocketOptionsList(InternalChannelz.SocketOptions options) { - Preconditions.checkNotNull(options); - List ret = new ArrayList<>(); - if (options.lingerSeconds != null) { - ret.add(toSocketOptionLinger(options.lingerSeconds)); - } - if (options.soTimeoutMillis != null) { - ret.add(toSocketOptionTimeout(SO_TIMEOUT, options.soTimeoutMillis)); - } - if (options.tcpInfo != null) { - ret.add(toSocketOptionTcpInfo(options.tcpInfo)); - } - for (Map.Entry entry : options.others.entrySet()) { - ret.add(toSocketOptionAdditional(entry.getKey(), entry.getValue())); - } - return ret; - } - - static Channel toChannel(InternalInstrumented channel) { - ChannelStats stats = getFuture(channel.getStats()); - Channel.Builder channelBuilder = Channel - .newBuilder() - .setRef(toChannelRef(channel)) - .setData(extractChannelData(stats)); - for (InternalWithLogId subchannel : stats.subchannels) { - channelBuilder.addSubchannelRef(toSubchannelRef(subchannel)); - } - - return channelBuilder.build(); - } - - static ChannelData extractChannelData(InternalChannelz.ChannelStats stats) { - ChannelData.Builder builder = ChannelData.newBuilder(); - builder.setTarget(stats.target) - .setState(toChannelConnectivityState(stats.state)) - .setCallsStarted(stats.callsStarted) - .setCallsSucceeded(stats.callsSucceeded) - .setCallsFailed(stats.callsFailed) - .setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos)); - if (stats.channelTrace != null) { - builder.setTrace(toChannelTrace(stats.channelTrace)); - } - return builder.build(); - } - - static ChannelConnectivityState toChannelConnectivityState(ConnectivityState s) { - return ChannelConnectivityState.newBuilder().setState(toState(s)).build(); - } - - private static ChannelTrace toChannelTrace(InternalChannelz.ChannelTrace channelTrace) { - return ChannelTrace.newBuilder() - .setNumEventsLogged(channelTrace.numEventsLogged) - .setCreationTimestamp(Timestamps.fromNanos(channelTrace.creationTimeNanos)) - .addAllEvents(toChannelTraceEvents(channelTrace.events)) - .build(); - } - - private static List toChannelTraceEvents(List events) { - List channelTraceEvents = new ArrayList<>(); - for (Event event : events) { - ChannelTraceEvent.Builder builder = ChannelTraceEvent.newBuilder() - .setDescription(event.description) - .setSeverity(Severity.valueOf(event.severity.name())) - .setTimestamp(Timestamps.fromNanos(event.timestampNanos)); - if (event.channelRef != null) { - builder.setChannelRef(toChannelRef(event.channelRef)); - } - if (event.subchannelRef != null) { - builder.setSubchannelRef(toSubchannelRef(event.subchannelRef)); - } - channelTraceEvents.add(builder.build()); - } - return Collections.unmodifiableList(channelTraceEvents); - } - - static State toState(ConnectivityState state) { - if (state == null) { - return State.UNKNOWN; - } - try { - return Enum.valueOf(State.class, state.name()); - } catch (IllegalArgumentException e) { - return State.UNKNOWN; - } - } - - static Subchannel toSubchannel(InternalInstrumented subchannel) { - ChannelStats stats = getFuture(subchannel.getStats()); - Subchannel.Builder subchannelBuilder = Subchannel - .newBuilder() - .setRef(toSubchannelRef(subchannel)) - .setData(extractChannelData(stats)); - Preconditions.checkState(stats.sockets.isEmpty() || stats.subchannels.isEmpty()); - for (InternalWithLogId childSocket : stats.sockets) { - subchannelBuilder.addSocketRef(toSocketRef(childSocket)); - } - for (InternalWithLogId childSubchannel : stats.subchannels) { - subchannelBuilder.addSubchannelRef(toSubchannelRef(childSubchannel)); - } - return subchannelBuilder.build(); - } - - static GetTopChannelsResponse toGetTopChannelResponse(RootChannelList rootChannels) { - GetTopChannelsResponse.Builder responseBuilder = GetTopChannelsResponse - .newBuilder() - .setEnd(rootChannels.end); - for (InternalInstrumented c : rootChannels.channels) { - responseBuilder.addChannel(ChannelzProtoUtil.toChannel(c)); - } - return responseBuilder.build(); - } - - static GetServersResponse toGetServersResponse(ServerList servers) { - GetServersResponse.Builder responseBuilder = GetServersResponse - .newBuilder() - .setEnd(servers.end); - for (InternalInstrumented s : servers.servers) { - responseBuilder.addServer(ChannelzProtoUtil.toServer(s)); - } - return responseBuilder.build(); - } - - static GetServerSocketsResponse toGetServerSocketsResponse(ServerSocketsList serverSockets) { - GetServerSocketsResponse.Builder responseBuilder = GetServerSocketsResponse - .newBuilder() - .setEnd(serverSockets.end); - for (InternalWithLogId s : serverSockets.sockets) { - responseBuilder.addSocketRef(ChannelzProtoUtil.toSocketRef(s)); - } - return responseBuilder.build(); - } - - private static T getFuture(ListenableFuture future) { - try { - T ret = future.get(); - if (ret == null) { - throw Status.UNIMPLEMENTED - .withDescription("The entity's stats can not be retrieved. " - + "If this is an InProcessTransport this is expected.") - .asRuntimeException(); - } - return ret; - } catch (InterruptedException e) { - throw Status.INTERNAL.withCause(e).asRuntimeException(); - } catch (ExecutionException e) { - throw Status.INTERNAL.withCause(e).asRuntimeException(); - } - } -} diff --git a/services/src/main/java/io/grpc/services/InetAddressUtil.java b/services/src/main/java/io/grpc/services/InetAddressUtil.java deleted file mode 100644 index 057a8ccb5e6..00000000000 --- a/services/src/main/java/io/grpc/services/InetAddressUtil.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.primitives.Ints; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.Arrays; - -// This is copied from guava 20.0 because it is a @Beta api -final class InetAddressUtil { - private static final int IPV6_PART_COUNT = 8; - - public static String toAddrString(InetAddress ip) { - checkNotNull(ip); - if (ip instanceof Inet4Address) { - // For IPv4, Java's formatting is good enough. - return ip.getHostAddress(); - } - checkArgument(ip instanceof Inet6Address); - byte[] bytes = ip.getAddress(); - int[] hextets = new int[IPV6_PART_COUNT]; - for (int i = 0; i < hextets.length; i++) { - hextets[i] = Ints.fromBytes((byte) 0, (byte) 0, bytes[2 * i], bytes[2 * i + 1]); - } - compressLongestRunOfZeroes(hextets); - return hextetsToIPv6String(hextets); - } - - private static void compressLongestRunOfZeroes(int[] hextets) { - int bestRunStart = -1; - int bestRunLength = -1; - int runStart = -1; - for (int i = 0; i < hextets.length + 1; i++) { - if (i < hextets.length && hextets[i] == 0) { - if (runStart < 0) { - runStart = i; - } - } else if (runStart >= 0) { - int runLength = i - runStart; - if (runLength > bestRunLength) { - bestRunStart = runStart; - bestRunLength = runLength; - } - runStart = -1; - } - } - if (bestRunLength >= 2) { - Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1); - } - } - - private static String hextetsToIPv6String(int[] hextets) { - // While scanning the array, handle these state transitions: - // start->num => "num" start->gap => "::" - // num->num => ":num" num->gap => "::" - // gap->num => "num" gap->gap => "" - StringBuilder buf = new StringBuilder(39); - boolean lastWasNumber = false; - for (int i = 0; i < hextets.length; i++) { - boolean thisIsNumber = hextets[i] >= 0; - if (thisIsNumber) { - if (lastWasNumber) { - buf.append(':'); - } - buf.append(Integer.toHexString(hextets[i])); - } else { - if (i == 0 || lastWasNumber) { - buf.append("::"); - } - } - lastWasNumber = thisIsNumber; - } - return buf.toString(); - } -} \ No newline at end of file diff --git a/services/src/main/java/io/grpc/services/TempFileSink.java b/services/src/main/java/io/grpc/services/TempFileSink.java deleted file mode 100644 index f21cb7d1977..00000000000 --- a/services/src/main/java/io/grpc/services/TempFileSink.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2018, gRPC Authors All rights reserved. - * - * 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. - */ - -package io.grpc.services; - -import com.google.protobuf.MessageLite; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The output file goes to the JVM's temp dir with a prefix of BINARY_INFO. The proto messages - * are written serially using {@link MessageLite#writeDelimitedTo(OutputStream)}. - */ -class TempFileSink implements io.grpc.protobuf.services.BinaryLogSink { - private static final Logger logger = Logger.getLogger(TempFileSink.class.getName()); - - private final String outPath; - private final OutputStream out; - private boolean closed; - - TempFileSink() throws IOException { - File outFile = File.createTempFile("BINARY_INFO.", ""); - outPath = outFile.getPath(); - logger.log(Level.INFO, "Writing binary logs to to {0}", outFile.getAbsolutePath()); - out = new BufferedOutputStream(new FileOutputStream(outFile)); - } - - String getPath() { - return this.outPath; - } - - @Override - public synchronized void write(MessageLite message) { - if (closed) { - logger.log(Level.FINEST, "Attempt to write after TempFileSink is closed."); - return; - } - try { - message.writeDelimitedTo(out); - } catch (IOException e) { - logger.log(Level.SEVERE, "Caught exception while writing", e); - closeQuietly(); - } - } - - @Override - public synchronized void close() throws IOException { - if (closed) { - return; - } - closed = true; - try { - out.flush(); - } finally { - out.close(); - } - } - - private synchronized void closeQuietly() { - try { - close(); - } catch (IOException e) { - logger.log(Level.SEVERE, "Caught exception while closing", e); - } - } -} diff --git a/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java b/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java deleted file mode 100644 index ae363017438..00000000000 --- a/services/src/test/java/io/grpc/services/BinaryLogProviderImplTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -import io.grpc.CallOptions; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for {@link BinaryLogProviderImpl}. */ -@SuppressWarnings("deprecation") -@RunWith(JUnit4.class) -public class BinaryLogProviderImplTest { - @Test - public void configStrNullTest() throws Exception { - BinaryLogSink sink = mock(BinaryLogSink.class); - BinaryLogProviderImpl binlog = new BinaryLogProviderImpl(sink, /*configStr=*/ null); - assertNull(binlog.getServerInterceptor("package.service/method")); - assertNull(binlog.getClientInterceptor("package.service/method", CallOptions.DEFAULT)); - } - - @Test - public void configStrEmptyTest() throws Exception { - BinaryLogSink sink = mock(BinaryLogSink.class); - BinaryLogProviderImpl binlog = new BinaryLogProviderImpl(sink, ""); - assertNull(binlog.getServerInterceptor("package.service/method")); - assertNull(binlog.getClientInterceptor("package.service/method", CallOptions.DEFAULT)); - } - - @Test - public void closeTest() throws Exception { - BinaryLogSink sink = mock(BinaryLogSink.class); - BinaryLogProviderImpl log = new BinaryLogProviderImpl(sink, "*"); - verify(sink, never()).close(); - log.close(); - verify(sink).close(); - } -} diff --git a/services/src/test/java/io/grpc/services/BinlogHelperTest.java b/services/src/test/java/io/grpc/services/BinlogHelperTest.java deleted file mode 100644 index e5e9224f9c8..00000000000 --- a/services/src/test/java/io/grpc/services/BinlogHelperTest.java +++ /dev/null @@ -1,1582 +0,0 @@ -/* - * Copyright 2017 The 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. - */ - -package io.grpc.services; - -import static com.google.common.truth.Truth.assertThat; -import static io.grpc.protobuf.services.BinaryLogProvider.BYTEARRAY_MARSHALLER; -import static io.grpc.services.BinlogHelper.createMetadataProto; -import static io.grpc.services.BinlogHelper.getPeerSocket; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import com.google.common.collect.Iterables; -import com.google.common.util.concurrent.SettableFuture; -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; -import com.google.protobuf.Duration; -import com.google.protobuf.StringValue; -import com.google.protobuf.Timestamp; -import com.google.protobuf.util.Durations; -import io.grpc.Attributes; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.Context; -import io.grpc.Deadline; -import io.grpc.Grpc; -import io.grpc.Metadata; -import io.grpc.MethodDescriptor; -import io.grpc.MethodDescriptor.MethodType; -import io.grpc.ServerCall; -import io.grpc.ServerCallHandler; -import io.grpc.Status; -import io.grpc.StatusException; -import io.grpc.binarylog.v1.Address; -import io.grpc.binarylog.v1.ClientHeader; -import io.grpc.binarylog.v1.GrpcLogEntry; -import io.grpc.binarylog.v1.GrpcLogEntry.EventType; -import io.grpc.binarylog.v1.GrpcLogEntry.Logger; -import io.grpc.binarylog.v1.Message; -import io.grpc.binarylog.v1.MetadataEntry; -import io.grpc.binarylog.v1.ServerHeader; -import io.grpc.binarylog.v1.Trailer; -import io.grpc.internal.NoopClientCall; -import io.grpc.internal.NoopServerCall; -import io.grpc.protobuf.StatusProto; -import io.grpc.protobuf.services.BinaryLogSink; -import io.grpc.services.BinlogHelper.FactoryImpl; -import io.grpc.services.BinlogHelper.MaybeTruncated; -import io.grpc.services.BinlogHelper.SinkWriter; -import io.grpc.services.BinlogHelper.SinkWriterImpl; -import io.grpc.services.BinlogHelper.TimeProvider; -import io.netty.channel.unix.DomainSocketAddress; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.charset.Charset; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.AdditionalMatchers; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; - -/** Tests for {@link BinlogHelper}. */ -@SuppressWarnings("deprecation") -@RunWith(JUnit4.class) -public final class BinlogHelperTest { - private static final Charset US_ASCII = Charset.forName("US-ASCII"); - private static final BinlogHelper HEADER_FULL = new Builder().header(Integer.MAX_VALUE).build(); - private static final BinlogHelper HEADER_256 = new Builder().header(256).build(); - private static final BinlogHelper MSG_FULL = new Builder().msg(Integer.MAX_VALUE).build(); - private static final BinlogHelper MSG_256 = new Builder().msg(256).build(); - private static final BinlogHelper BOTH_256 = new Builder().header(256).msg(256).build(); - private static final BinlogHelper BOTH_FULL = - new Builder().header(Integer.MAX_VALUE).msg(Integer.MAX_VALUE).build(); - - private static final String DATA_A = "aaaaaaaaa"; - private static final String DATA_B = "bbbbbbbbb"; - private static final String DATA_C = "ccccccccc"; - private static final Metadata.Key KEY_A = - Metadata.Key.of("a", Metadata.ASCII_STRING_MARSHALLER); - private static final Metadata.Key KEY_B = - Metadata.Key.of("b", Metadata.ASCII_STRING_MARSHALLER); - private static final Metadata.Key KEY_C = - Metadata.Key.of("c", Metadata.ASCII_STRING_MARSHALLER); - private static final MetadataEntry ENTRY_A = - MetadataEntry - .newBuilder() - .setKey(KEY_A.name()) - .setValue(ByteString.copyFrom(DATA_A.getBytes(US_ASCII))) - .build(); - private static final MetadataEntry ENTRY_B = - MetadataEntry - .newBuilder() - .setKey(KEY_B.name()) - .setValue(ByteString.copyFrom(DATA_B.getBytes(US_ASCII))) - .build(); - private static final MetadataEntry ENTRY_C = - MetadataEntry - .newBuilder() - .setKey(KEY_C.name()) - .setValue(ByteString.copyFrom(DATA_C.getBytes(US_ASCII))) - .build(); - private static final long CALL_ID = 0x1112131415161718L; - private static final int HEADER_LIMIT = 10; - private static final int MESSAGE_LIMIT = Integer.MAX_VALUE; - - private final Metadata nonEmptyMetadata = new Metadata(); - private final BinaryLogSink sink = mock(BinaryLogSink.class); - private final Timestamp timestamp - = Timestamp.newBuilder().setSeconds(9876).setNanos(54321).build(); - private final BinlogHelper.TimeProvider timeProvider = new TimeProvider() { - @Override - public long currentTimeNanos() { - return TimeUnit.SECONDS.toNanos(9876) + 54321; - } - }; - private final SinkWriter sinkWriterImpl = - new SinkWriterImpl( - sink, - timeProvider, - HEADER_LIMIT, - MESSAGE_LIMIT); - private final SinkWriter mockSinkWriter = mock(SinkWriter.class); - private final byte[] message = new byte[100]; - private SocketAddress peer; - - @Before - public void setUp() throws Exception { - nonEmptyMetadata.put(KEY_A, DATA_A); - nonEmptyMetadata.put(KEY_B, DATA_B); - nonEmptyMetadata.put(KEY_C, DATA_C); - peer = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 1234); - } - - @Test - public void configBinLog_global() throws Exception { - assertSameLimits(BOTH_FULL, makeLog("*", "p.s/m")); - assertSameLimits(BOTH_FULL, makeLog("*{h;m}", "p.s/m")); - assertSameLimits(HEADER_FULL, makeLog("*{h}", "p.s/m")); - assertSameLimits(MSG_FULL, makeLog("*{m}", "p.s/m")); - assertSameLimits(HEADER_256, makeLog("*{h:256}", "p.s/m")); - assertSameLimits(MSG_256, makeLog("*{m:256}", "p.s/m")); - assertSameLimits(BOTH_256, makeLog("*{h:256;m:256}", "p.s/m")); - assertSameLimits( - new Builder().header(Integer.MAX_VALUE).msg(256).build(), - makeLog("*{h;m:256}", "p.s/m")); - assertSameLimits( - new Builder().header(256).msg(Integer.MAX_VALUE).build(), - makeLog("*{h:256;m}", "p.s/m")); - } - - @Test - public void configBinLog_method() throws Exception { - assertSameLimits(BOTH_FULL, makeLog("p.s/m", "p.s/m")); - assertSameLimits(BOTH_FULL, makeLog("p.s/m{h;m}", "p.s/m")); - assertSameLimits(HEADER_FULL, makeLog("p.s/m{h}", "p.s/m")); - assertSameLimits(MSG_FULL, makeLog("p.s/m{m}", "p.s/m")); - assertSameLimits(HEADER_256, makeLog("p.s/m{h:256}", "p.s/m")); - assertSameLimits(MSG_256, makeLog("p.s/m{m:256}", "p.s/m")); - assertSameLimits(BOTH_256, makeLog("p.s/m{h:256;m:256}", "p.s/m")); - assertSameLimits( - new Builder().header(Integer.MAX_VALUE).msg(256).build(), - makeLog("p.s/m{h;m:256}", "p.s/m")); - assertSameLimits( - new Builder().header(256).msg(Integer.MAX_VALUE).build(), - makeLog("p.s/m{h:256;m}", "p.s/m")); - } - - @Test - public void configBinLog_method_absent() throws Exception { - assertNull(makeLog("p.s/m", "p.s/absent")); - } - - @Test - public void configBinLog_service() throws Exception { - assertSameLimits(BOTH_FULL, makeLog("p.s/*", "p.s/m")); - assertSameLimits(BOTH_FULL, makeLog("p.s/*{h;m}", "p.s/m")); - assertSameLimits(HEADER_FULL, makeLog("p.s/*{h}", "p.s/m")); - assertSameLimits(MSG_FULL, makeLog("p.s/*{m}", "p.s/m")); - assertSameLimits(HEADER_256, makeLog("p.s/*{h:256}", "p.s/m")); - assertSameLimits(MSG_256, makeLog("p.s/*{m:256}", "p.s/m")); - assertSameLimits(BOTH_256, makeLog("p.s/*{h:256;m:256}", "p.s/m")); - assertSameLimits( - new Builder().header(Integer.MAX_VALUE).msg(256).build(), - makeLog("p.s/*{h;m:256}", "p.s/m")); - assertSameLimits( - new Builder().header(256).msg(Integer.MAX_VALUE).build(), - makeLog("p.s/*{h:256;m}", "p.s/m")); - } - - @Test - public void configBinLog_service_absent() throws Exception { - assertNull(makeLog("p.s/*", "p.other/m")); - } - - @Test - public void createLogFromOptionString() throws Exception { - assertSameLimits(BOTH_FULL, makeOptions(null)); - assertSameLimits(HEADER_FULL, makeOptions("h")); - assertSameLimits(MSG_FULL, makeOptions("m")); - assertSameLimits(HEADER_256, makeOptions("h:256")); - assertSameLimits(MSG_256, makeOptions("m:256")); - assertSameLimits(BOTH_256, makeOptions("h:256;m:256")); - assertSameLimits( - new Builder().header(Integer.MAX_VALUE).msg(256).build(), - makeOptions("h;m:256")); - assertSameLimits( - new Builder().header(256).msg(Integer.MAX_VALUE).build(), - makeOptions("h:256;m")); - } - - private void assertIllegalPatternDetected(String perSvcOrMethodConfig) { - try { - FactoryImpl.createBinaryLog(sink, perSvcOrMethodConfig); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().startsWith("Illegal log config pattern"); - } - } - - @Test - public void badFactoryConfigStrDetected() throws Exception { - try { - new FactoryImpl(sink, "obviouslybad{"); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().startsWith("Illegal log config pattern"); - } - } - - @Test - public void badFactoryConfigStrDetected_empty() throws Exception { - try { - new FactoryImpl(sink, "*,"); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().startsWith("Illegal log config pattern"); - } - } - - @Test - public void createLogFromOptionString_malformed() throws Exception { - assertIllegalPatternDetected(""); - assertIllegalPatternDetected("bad"); - assertIllegalPatternDetected("mad"); - assertIllegalPatternDetected("x;y"); - assertIllegalPatternDetected("h:abc"); - assertIllegalPatternDetected("h:1e8"); - assertIllegalPatternDetected("2"); - assertIllegalPatternDetected("2;2"); - // The grammar specifies that if both h and m are present, h comes before m - assertIllegalPatternDetected("m:123;h:123"); - // NumberFormatException - assertIllegalPatternDetected("h:99999999999999"); - } - - @Test - public void configBinLog_multiConfig_withGlobal() throws Exception { - String configStr = - "*{h}," - + "package.both256/*{h:256;m:256}," - + "package.service1/both128{h:128;m:128}," - + "package.service2/method_messageOnly{m}"; - assertSameLimits(HEADER_FULL, makeLog(configStr, "otherpackage.service/method")); - - assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method1")); - assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method2")); - assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method3")); - - assertSameLimits( - new Builder().header(128).msg(128).build(), makeLog(configStr, "package.service1/both128")); - // the global config is in effect - assertSameLimits(HEADER_FULL, makeLog(configStr, "package.service1/absent")); - - assertSameLimits(MSG_FULL, makeLog(configStr, "package.service2/method_messageOnly")); - // the global config is in effect - assertSameLimits(HEADER_FULL, makeLog(configStr, "package.service2/absent")); - } - - @Test - public void configBinLog_multiConfig_noGlobal() throws Exception { - String configStr = - "package.both256/*{h:256;m:256}," - + "package.service1/both128{h:128;m:128}," - + "package.service2/method_messageOnly{m}"; - assertNull(makeLog(configStr, "otherpackage.service/method")); - - assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method1")); - assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method2")); - assertSameLimits(BOTH_256, makeLog(configStr, "package.both256/method3")); - - assertSameLimits( - new Builder().header(128).msg(128).build(), makeLog(configStr, "package.service1/both128")); - // no global config in effect - assertNull(makeLog(configStr, "package.service1/absent")); - - assertSameLimits(MSG_FULL, makeLog(configStr, "package.service2/method_messageOnly")); - // no global config in effect - assertNull(makeLog(configStr, "package.service2/absent")); - } - - @Test - public void configBinLog_blacklist() { - assertNull(makeLog("*,-p.s/blacklisted", "p.s/blacklisted")); - assertNull(makeLog("-p.s/blacklisted,*", "p.s/blacklisted")); - assertNotNull(makeLog("-p.s/method,*", "p.s/allowed")); - - assertNull(makeLog("p.s/*,-p.s/blacklisted", "p.s/blacklisted")); - assertNull(makeLog("-p.s/blacklisted,p.s/*", "p.s/blacklisted")); - assertNotNull(makeLog("-p.s/blacklisted,p.s/*", "p.s/allowed")); - } - - private void assertDuplicatelPatternDetected(String factoryConfigStr) { - try { - new BinlogHelper.FactoryImpl(sink, factoryConfigStr); - fail(); - } catch (IllegalStateException expected) { - assertThat(expected).hasMessageThat().startsWith("Duplicate entry"); - } - } - - @Test - public void configBinLog_duplicates_global() throws Exception { - assertDuplicatelPatternDetected("*{h},*{h:256}"); - } - - @Test - public void configBinLog_duplicates_service() throws Exception { - assertDuplicatelPatternDetected("p.s/*,p.s/*{h}"); - - } - - @Test - public void configBinLog_duplicates_method() throws Exception { - assertDuplicatelPatternDetected("p.s/*,p.s/*{h:1;m:2}"); - assertDuplicatelPatternDetected("p.s/m,-p.s/m"); - assertDuplicatelPatternDetected("-p.s/m,p.s/m"); - assertDuplicatelPatternDetected("-p.s/m,-p.s/m"); - } - - @Test - public void socketToProto_ipv4() throws Exception { - InetAddress address = InetAddress.getByName("127.0.0.1"); - int port = 12345; - InetSocketAddress socketAddress = new InetSocketAddress(address, port); - assertEquals( - Address - .newBuilder() - .setType(Address.Type.TYPE_IPV4) - .setAddress("127.0.0.1") - .setIpPort(12345) - .build(), - BinlogHelper.socketToProto(socketAddress)); - } - - @Test - public void socketToProto_ipv6() throws Exception { - // this is a ipv6 link local address - InetAddress address = InetAddress.getByName("2001:db8:0:0:0:0:2:1"); - int port = 12345; - InetSocketAddress socketAddress = new InetSocketAddress(address, port); - assertEquals( - Address - .newBuilder() - .setType(Address.Type.TYPE_IPV6) - .setAddress("2001:db8::2:1") // RFC 5952 section 4: ipv6 canonical form required - .setIpPort(12345) - .build(), - BinlogHelper.socketToProto(socketAddress)); - } - - @Test - public void socketToProto_unix() throws Exception { - String path = "/some/path"; - DomainSocketAddress socketAddress = new DomainSocketAddress(path); - assertEquals( - Address - .newBuilder() - .setType(Address.Type.TYPE_UNIX) - .setAddress("/some/path") - .build(), - BinlogHelper.socketToProto(socketAddress) - ); - } - - @Test - public void socketToProto_unknown() throws Exception { - SocketAddress unknownSocket = new SocketAddress() { - @Override - public String toString() { - return "some-socket-address"; - } - }; - assertEquals( - Address.newBuilder() - .setType(Address.Type.TYPE_UNKNOWN) - .setAddress("some-socket-address") - .build(), - BinlogHelper.socketToProto(unknownSocket)); - } - - @Test - public void metadataToProto_empty() throws Exception { - assertEquals( - GrpcLogEntry.newBuilder() - .setType(EventType.EVENT_TYPE_CLIENT_HEADER) - .setClientHeader( - ClientHeader.newBuilder().setMetadata( - io.grpc.binarylog.v1.Metadata.getDefaultInstance())) - .build(), - metadataToProtoTestHelper( - EventType.EVENT_TYPE_CLIENT_HEADER, new Metadata(), Integer.MAX_VALUE)); - } - - @Test - public void metadataToProto() throws Exception { - assertEquals( - GrpcLogEntry.newBuilder() - .setType(EventType.EVENT_TYPE_CLIENT_HEADER) - .setClientHeader( - ClientHeader.newBuilder().setMetadata( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .addEntry(ENTRY_B) - .addEntry(ENTRY_C) - .build())) - .build(), - metadataToProtoTestHelper( - EventType.EVENT_TYPE_CLIENT_HEADER, nonEmptyMetadata, Integer.MAX_VALUE)); - } - - @Test - public void metadataToProto_setsTruncated() throws Exception { - assertTrue(BinlogHelper.createMetadataProto(nonEmptyMetadata, 0).truncated); - } - - @Test - public void metadataToProto_truncated() throws Exception { - // 0 byte limit not enough for any metadata - assertEquals( - io.grpc.binarylog.v1.Metadata.getDefaultInstance(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 0).proto.build()); - // not enough bytes for first key value - assertEquals( - io.grpc.binarylog.v1.Metadata.getDefaultInstance(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 9).proto.build()); - // enough for first key value - assertEquals( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .build(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 10).proto.build()); - // Test edge cases for >= 2 key values - assertEquals( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .build(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 19).proto.build()); - assertEquals( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .addEntry(ENTRY_B) - .build(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 20).proto.build()); - assertEquals( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .addEntry(ENTRY_B) - .build(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 29).proto.build()); - - // not truncated: enough for all keys - assertEquals( - io.grpc.binarylog.v1.Metadata - .newBuilder() - .addEntry(ENTRY_A) - .addEntry(ENTRY_B) - .addEntry(ENTRY_C) - .build(), - BinlogHelper.createMetadataProto(nonEmptyMetadata, 30).proto.build()); - } - - @Test - public void messageToProto() throws Exception { - byte[] bytes - = "this is a long message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes(US_ASCII); - assertEquals( - GrpcLogEntry.newBuilder() - .setMessage( - Message - .newBuilder() - .setData(ByteString.copyFrom(bytes)) - .setLength(bytes.length) - .build()) - .build(), - messageToProtoTestHelper(bytes, Integer.MAX_VALUE)); - } - - @Test - public void messageToProto_truncated() throws Exception { - byte[] bytes - = "this is a long message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes(US_ASCII); - assertEquals( - GrpcLogEntry.newBuilder() - .setMessage( - Message - .newBuilder() - .setLength(bytes.length) - .build()) - .setPayloadTruncated(true) - .build(), - messageToProtoTestHelper(bytes, 0)); - - int limit = 10; - String truncatedMessage = "this is a "; - assertEquals( - GrpcLogEntry.newBuilder() - .setMessage( - Message - .newBuilder() - .setData(ByteString.copyFrom(truncatedMessage.getBytes(US_ASCII))) - .setLength(bytes.length) - .build()) - .setPayloadTruncated(true) - .build(), - messageToProtoTestHelper(bytes, limit)); - } - - @Test - public void logClientHeader() throws Exception { - long seq = 1; - String authority = "authority"; - String methodName = "service/method"; - Duration timeout = Durations.fromMillis(1234); - InetAddress address = InetAddress.getByName("127.0.0.1"); - int port = 12345; - InetSocketAddress peerAddress = new InetSocketAddress(address, port); - long callId = 1000; - - GrpcLogEntry.Builder builder = - metadataToProtoTestHelper(EventType.EVENT_TYPE_CLIENT_HEADER, nonEmptyMetadata, 10) - .toBuilder() - .setTimestamp(timestamp) - .setSequenceIdWithinCall(seq) - .setLogger(Logger.LOGGER_CLIENT) - .setCallId(callId); - builder.getClientHeaderBuilder() - .setMethodName("/" + methodName) - .setAuthority(authority) - .setTimeout(timeout); - GrpcLogEntry base = builder.build(); - { - sinkWriterImpl.logClientHeader( - seq, - methodName, - authority, - timeout, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - /*peerAddress=*/ null); - verify(sink).write(base); - } - - // logger is server - { - sinkWriterImpl.logClientHeader( - seq, - methodName, - authority, - timeout, - nonEmptyMetadata, - Logger.LOGGER_SERVER, - callId, - peerAddress); - verify(sink).write( - base.toBuilder() - .setPeer(BinlogHelper.socketToProto(peerAddress)) - .setLogger(Logger.LOGGER_SERVER) - .build()); - } - - // authority is null - { - sinkWriterImpl.logClientHeader( - seq, - methodName, - /*authority=*/ null, - timeout, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - /*peerAddress=*/ null); - - verify(sink).write( - base.toBuilder() - .setClientHeader(builder.getClientHeader().toBuilder().clearAuthority().build()) - .build()); - } - - // timeout is null - { - sinkWriterImpl.logClientHeader( - seq, - methodName, - authority, - /*timeout=*/ null, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - /*peerAddress=*/ null); - - verify(sink).write( - base.toBuilder() - .setClientHeader(builder.getClientHeader().toBuilder().clearTimeout().build()) - .build()); - } - - // peerAddress is non null (error for client side) - try { - sinkWriterImpl.logClientHeader( - seq, - methodName, - authority, - timeout, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - peerAddress); - fail(); - } catch (IllegalArgumentException expected) { - // noop - } - } - - @Test - public void logServerHeader() throws Exception { - long seq = 1; - InetAddress address = InetAddress.getByName("127.0.0.1"); - int port = 12345; - InetSocketAddress peerAddress = new InetSocketAddress(address, port); - long callId = 1000; - - GrpcLogEntry.Builder builder = - metadataToProtoTestHelper(EventType.EVENT_TYPE_SERVER_HEADER, nonEmptyMetadata, 10) - .toBuilder() - .setTimestamp(timestamp) - .setSequenceIdWithinCall(seq) - .setLogger(Logger.LOGGER_CLIENT) - .setCallId(callId) - .setPeer(BinlogHelper.socketToProto(peerAddress)); - - { - sinkWriterImpl.logServerHeader( - seq, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - peerAddress); - verify(sink).write(builder.build()); - } - - // logger is server - // null peerAddress is required for server side - { - sinkWriterImpl.logServerHeader( - seq, - nonEmptyMetadata, - Logger.LOGGER_SERVER, - callId, - /*peerAddress=*/ null); - verify(sink).write( - builder - .setLogger(Logger.LOGGER_SERVER) - .clearPeer() - .build()); - } - - // logger is server - // non null peerAddress is an error - try { - sinkWriterImpl.logServerHeader( - seq, - nonEmptyMetadata, - Logger.LOGGER_SERVER, - callId, - peerAddress); - fail(); - } catch (IllegalArgumentException expected) { - // noop - } - } - - @Test - public void logTrailer() throws Exception { - long seq = 1; - InetAddress address = InetAddress.getByName("127.0.0.1"); - int port = 12345; - InetSocketAddress peerAddress = new InetSocketAddress(address, port); - long callId = 1000; - Status statusDescription = Status.INTERNAL.withDescription("my description"); - - GrpcLogEntry.Builder builder = - metadataToProtoTestHelper(EventType.EVENT_TYPE_SERVER_TRAILER, nonEmptyMetadata, 10) - .toBuilder() - .setTimestamp(timestamp) - .setSequenceIdWithinCall(seq) - .setLogger(Logger.LOGGER_CLIENT) - .setCallId(callId) - .setPeer(BinlogHelper.socketToProto(peerAddress)); - - builder.getTrailerBuilder() - .setStatusCode(Status.INTERNAL.getCode().value()) - .setStatusMessage("my description"); - GrpcLogEntry base = builder.build(); - - { - sinkWriterImpl.logTrailer( - seq, - statusDescription, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - peerAddress); - verify(sink).write(base); - } - - // logger is server - { - sinkWriterImpl.logTrailer( - seq, - statusDescription, - nonEmptyMetadata, - Logger.LOGGER_SERVER, - callId, - /*peerAddress=*/ null); - verify(sink).write( - base.toBuilder() - .clearPeer() - .setLogger(Logger.LOGGER_SERVER) - .build()); - } - - // peerAddress is null - { - sinkWriterImpl.logTrailer( - seq, - statusDescription, - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - /*peerAddress=*/ null); - verify(sink).write( - base.toBuilder() - .clearPeer() - .build()); - } - - // status code is present but description is null - { - sinkWriterImpl.logTrailer( - seq, - statusDescription.getCode().toStatus(), // strip the description - nonEmptyMetadata, - Logger.LOGGER_CLIENT, - callId, - peerAddress); - verify(sink).write( - base.toBuilder() - .setTrailer(base.getTrailer().toBuilder().clearStatusMessage()) - .build()); - } - - // status proto always logged if present (com.google.rpc.Status), - { - int zeroHeaderBytes = 0; - SinkWriterImpl truncatingWriter = new SinkWriterImpl( - sink, timeProvider, zeroHeaderBytes, MESSAGE_LIMIT); - com.google.rpc.Status statusProto = com.google.rpc.Status.newBuilder() - .addDetails( - Any.pack(StringValue.newBuilder().setValue("arbitrarypayload").build())) - .setCode(Status.INTERNAL.getCode().value()) - .setMessage("status detail string") - .build(); - StatusException statusException - = StatusProto.toStatusException(statusProto, nonEmptyMetadata); - truncatingWriter.logTrailer( - seq, - statusException.getStatus(), - statusException.getTrailers(), - Logger.LOGGER_CLIENT, - callId, - peerAddress); - verify(sink).write( - base.toBuilder() - .setTrailer( - builder.getTrailerBuilder() - .setStatusMessage("status detail string") - .setStatusDetails(ByteString.copyFrom(statusProto.toByteArray())) - .setMetadata(io.grpc.binarylog.v1.Metadata.getDefaultInstance())) - .build()); - } - } - - @Test - public void alwaysLoggedMetadata_grpcTraceBin() throws Exception { - Metadata.Key key - = Metadata.Key.of("grpc-trace-bin", Metadata.BINARY_BYTE_MARSHALLER); - Metadata metadata = new Metadata(); - metadata.put(key, new byte[1]); - int zeroHeaderBytes = 0; - MaybeTruncated pair = - createMetadataProto(metadata, zeroHeaderBytes); - assertEquals( - key.name(), - Iterables.getOnlyElement(pair.proto.getEntryBuilderList()).getKey()); - assertFalse(pair.truncated); - } - - @Test - public void neverLoggedMetadata_grpcStatusDetilsBin() throws Exception { - Metadata.Key key - = Metadata.Key.of("grpc-status-details-bin", Metadata.BINARY_BYTE_MARSHALLER); - Metadata metadata = new Metadata(); - metadata.put(key, new byte[1]); - int unlimitedHeaderBytes = Integer.MAX_VALUE; - MaybeTruncated pair - = createMetadataProto(metadata, unlimitedHeaderBytes); - assertThat(pair.proto.getEntryBuilderList()).isEmpty(); - assertFalse(pair.truncated); - } - - @Test - public void logRpcMessage() throws Exception { - long seq = 1; - long callId = 1000; - GrpcLogEntry base = messageToProtoTestHelper(message, MESSAGE_LIMIT).toBuilder() - .setTimestamp(timestamp) - .setType(EventType.EVENT_TYPE_CLIENT_MESSAGE) - .setLogger(Logger.LOGGER_CLIENT) - .setSequenceIdWithinCall(1) - .setCallId(callId) - .build(); - { - sinkWriterImpl.logRpcMessage( - seq, - EventType.EVENT_TYPE_CLIENT_MESSAGE, - BYTEARRAY_MARSHALLER, - message, - Logger.LOGGER_CLIENT, - callId); - verify(sink).write(base); - } - - // server messsage - { - sinkWriterImpl.logRpcMessage( - seq, - EventType.EVENT_TYPE_SERVER_MESSAGE, - BYTEARRAY_MARSHALLER, - message, - Logger.LOGGER_CLIENT, - callId); - verify(sink).write( - base.toBuilder() - .setType(EventType.EVENT_TYPE_SERVER_MESSAGE) - .build()); - } - - // logger is server - { - sinkWriterImpl.logRpcMessage( - seq, - EventType.EVENT_TYPE_CLIENT_MESSAGE, - BYTEARRAY_MARSHALLER, - message, - Logger.LOGGER_SERVER, - callId); - verify(sink).write( - base.toBuilder() - .setLogger(Logger.LOGGER_SERVER) - .build()); - } - } - - @Test - public void getPeerSocketTest() { - assertNull(getPeerSocket(Attributes.EMPTY)); - assertSame( - peer, - getPeerSocket(Attributes.newBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer).build())); - } - - @Test - public void serverDeadlineLogged() { - final AtomicReference> interceptedCall = - new AtomicReference<>(); - @SuppressWarnings("unchecked") - final ServerCall.Listener mockListener = mock(ServerCall.Listener.class); - - final MethodDescriptor method = - MethodDescriptor.newBuilder() - .setType(MethodType.UNKNOWN) - .setFullMethodName("service/method") - .setRequestMarshaller(BYTEARRAY_MARSHALLER) - .setResponseMarshaller(BYTEARRAY_MARSHALLER) - .build(); - - // We expect the contents of the "grpc-timeout" header to be installed the context - Context.current() - .withDeadlineAfter(1, TimeUnit.SECONDS, Executors.newSingleThreadScheduledExecutor()) - .run(new Runnable() { - @Override - public void run() { - ServerCall.Listener unused = - new BinlogHelper(mockSinkWriter) - .getServerInterceptor(CALL_ID) - .interceptCall( - new NoopServerCall() { - @Override - public MethodDescriptor getMethodDescriptor() { - return method; - } - }, - new Metadata(), - new ServerCallHandler() { - @Override - public ServerCall.Listener startCall( - ServerCall call, - Metadata headers) { - interceptedCall.set(call); - return mockListener; - } - }); - } - }); - ArgumentCaptor timeoutCaptor = ArgumentCaptor.forClass(Duration.class); - verify(mockSinkWriter).logClientHeader( - /*seq=*/ eq(1L), - eq("service/method"), - ArgumentMatchers.isNull(), - timeoutCaptor.capture(), - any(Metadata.class), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID), - ArgumentMatchers.isNull()); - verifyNoMoreInteractions(mockSinkWriter); - Duration timeout = timeoutCaptor.getValue(); - assertThat(TimeUnit.SECONDS.toNanos(1) - Durations.toNanos(timeout)) - .isAtMost(TimeUnit.MILLISECONDS.toNanos(250)); - } - - @Test - public void clientDeadlineLogged_deadlineSetViaCallOption() { - MethodDescriptor method = - MethodDescriptor.newBuilder() - .setType(MethodType.UNKNOWN) - .setFullMethodName("service/method") - .setRequestMarshaller(BYTEARRAY_MARSHALLER) - .setResponseMarshaller(BYTEARRAY_MARSHALLER) - .build(); - @SuppressWarnings("unchecked") - ClientCall.Listener mockListener = mock(ClientCall.Listener.class); - - ClientCall call = - new BinlogHelper(mockSinkWriter) - .getClientInterceptor(CALL_ID) - .interceptCall( - method, - CallOptions.DEFAULT.withDeadlineAfter(1, TimeUnit.SECONDS), - new Channel() { - @Override - public ClientCall newCall( - MethodDescriptor methodDescriptor, - CallOptions callOptions) { - return new NoopClientCall<>(); - } - - @Override - public String authority() { - return null; - } - }); - call.start(mockListener, new Metadata()); - ArgumentCaptor callOptTimeoutCaptor = ArgumentCaptor.forClass(Duration.class); - verify(mockSinkWriter) - .logClientHeader( - anyLong(), - AdditionalMatchers.or(ArgumentMatchers.isNull(), anyString()), - AdditionalMatchers.or(ArgumentMatchers.isNull(), anyString()), - callOptTimeoutCaptor.capture(), - any(Metadata.class), - any(GrpcLogEntry.Logger.class), - anyLong(), - AdditionalMatchers.or(ArgumentMatchers.isNull(), - ArgumentMatchers.any())); - Duration timeout = callOptTimeoutCaptor.getValue(); - assertThat(TimeUnit.SECONDS.toNanos(1) - Durations.toNanos(timeout)) - .isAtMost(TimeUnit.MILLISECONDS.toNanos(250)); - } - - @Test - public void clientDeadlineLogged_deadlineSetViaContext() throws Exception { - // important: deadline is read from the ctx where call was created - final SettableFuture> callFuture = SettableFuture.create(); - Context.current() - .withDeadline( - Deadline.after(1, TimeUnit.SECONDS), Executors.newSingleThreadScheduledExecutor()) - .run(new Runnable() { - @Override - public void run() { - MethodDescriptor method = - MethodDescriptor.newBuilder() - .setType(MethodType.UNKNOWN) - .setFullMethodName("service/method") - .setRequestMarshaller(BYTEARRAY_MARSHALLER) - .setResponseMarshaller(BYTEARRAY_MARSHALLER) - .build(); - - callFuture.set(new BinlogHelper(mockSinkWriter) - .getClientInterceptor(CALL_ID) - .interceptCall( - method, - CallOptions.DEFAULT.withDeadlineAfter(1, TimeUnit.SECONDS), - new Channel() { - @Override - public ClientCall newCall( - MethodDescriptor methodDescriptor, - CallOptions callOptions) { - return new NoopClientCall<>(); - } - - @Override - public String authority() { - return null; - } - })); - } - }); - @SuppressWarnings("unchecked") - ClientCall.Listener mockListener = mock(ClientCall.Listener.class); - callFuture.get().start(mockListener, new Metadata()); - ArgumentCaptor callOptTimeoutCaptor = ArgumentCaptor.forClass(Duration.class); - verify(mockSinkWriter) - .logClientHeader( - anyLong(), - anyString(), - ArgumentMatchers.any(), - callOptTimeoutCaptor.capture(), - any(Metadata.class), - any(GrpcLogEntry.Logger.class), - anyLong(), - AdditionalMatchers.or(ArgumentMatchers.isNull(), - ArgumentMatchers.any())); - Duration timeout = callOptTimeoutCaptor.getValue(); - assertThat(TimeUnit.SECONDS.toNanos(1) - Durations.toNanos(timeout)) - .isAtMost(TimeUnit.MILLISECONDS.toNanos(250)); - } - - @Test - public void clientInterceptor() throws Exception { - final AtomicReference> interceptedListener = - new AtomicReference<>(); - // capture these manually because ClientCall can not be mocked - final AtomicReference actualClientInitial = new AtomicReference<>(); - final AtomicReference actualRequest = new AtomicReference<>(); - - final SettableFuture halfCloseCalled = SettableFuture.create(); - final SettableFuture cancelCalled = SettableFuture.create(); - Channel channel = new Channel() { - @Override - public ClientCall newCall( - MethodDescriptor methodDescriptor, CallOptions callOptions) { - return new NoopClientCall() { - @Override - @SuppressWarnings("unchecked") - public void start(Listener responseListener, Metadata headers) { - interceptedListener.set((Listener) responseListener); - actualClientInitial.set(headers); - } - - @Override - public void sendMessage(RequestT message) { - actualRequest.set(message); - } - - @Override - public void cancel(String message, Throwable cause) { - cancelCalled.set(null); - } - - @Override - public void halfClose() { - halfCloseCalled.set(null); - } - - @Override - public Attributes getAttributes() { - return Attributes.newBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer).build(); - } - }; - } - - @Override - public String authority() { - return "the-authority"; - } - }; - - @SuppressWarnings("unchecked") - ClientCall.Listener mockListener = mock(ClientCall.Listener.class); - - MethodDescriptor method = - MethodDescriptor.newBuilder() - .setType(MethodType.UNKNOWN) - .setFullMethodName("service/method") - .setRequestMarshaller(BYTEARRAY_MARSHALLER) - .setResponseMarshaller(BYTEARRAY_MARSHALLER) - .build(); - ClientCall interceptedCall = - new BinlogHelper(mockSinkWriter) - .getClientInterceptor(CALL_ID) - .interceptCall( - method, - CallOptions.DEFAULT, - channel); - - // send client header - { - Metadata clientInitial = new Metadata(); - interceptedCall.start(mockListener, clientInitial); - verify(mockSinkWriter).logClientHeader( - /*seq=*/ eq(1L), - eq("service/method"), - eq("the-authority"), - ArgumentMatchers.isNull(), - same(clientInitial), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID), - ArgumentMatchers.isNull()); - verifyNoMoreInteractions(mockSinkWriter); - assertSame(clientInitial, actualClientInitial.get()); - } - - // receive server header - { - Metadata serverInitial = new Metadata(); - interceptedListener.get().onHeaders(serverInitial); - verify(mockSinkWriter).logServerHeader( - /*seq=*/ eq(2L), - same(serverInitial), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID), - same(peer)); - verifyNoMoreInteractions(mockSinkWriter); - verify(mockListener).onHeaders(same(serverInitial)); - } - - // send client msg - { - byte[] request = "this is a request".getBytes(US_ASCII); - interceptedCall.sendMessage(request); - verify(mockSinkWriter).logRpcMessage( - /*seq=*/ eq(3L), - eq(EventType.EVENT_TYPE_CLIENT_MESSAGE), - same(BYTEARRAY_MARSHALLER), - same(request), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID)); - verifyNoMoreInteractions(mockSinkWriter); - assertSame(request, actualRequest.get()); - } - - // client half close - { - interceptedCall.halfClose(); - verify(mockSinkWriter).logHalfClose( - /*seq=*/ eq(4L), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID)); - halfCloseCalled.get(1, TimeUnit.SECONDS); - verifyNoMoreInteractions(mockSinkWriter); - } - - // receive server msg - { - byte[] response = "this is a response".getBytes(US_ASCII); - interceptedListener.get().onMessage(response); - verify(mockSinkWriter).logRpcMessage( - /*seq=*/ eq(5L), - eq(EventType.EVENT_TYPE_SERVER_MESSAGE), - same(BYTEARRAY_MARSHALLER), - same(response), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID)); - verifyNoMoreInteractions(mockSinkWriter); - verify(mockListener).onMessage(same(response)); - } - - // receive trailer - { - Status status = Status.INTERNAL.withDescription("some description"); - Metadata trailers = new Metadata(); - - interceptedListener.get().onClose(status, trailers); - verify(mockSinkWriter).logTrailer( - /*seq=*/ eq(6L), - same(status), - same(trailers), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID), - ArgumentMatchers.isNull()); - verifyNoMoreInteractions(mockSinkWriter); - verify(mockListener).onClose(same(status), same(trailers)); - } - - // cancel - { - interceptedCall.cancel(null, null); - verify(mockSinkWriter).logCancel( - /*seq=*/ eq(7L), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID)); - cancelCalled.get(1, TimeUnit.SECONDS); - } - } - - @Test - public void clientInterceptor_trailersOnlyResponseLogsPeerAddress() throws Exception { - final AtomicReference> interceptedListener = - new AtomicReference<>(); - // capture these manually because ClientCall can not be mocked - final AtomicReference actualClientInitial = new AtomicReference<>(); - final AtomicReference actualRequest = new AtomicReference<>(); - - Channel channel = new Channel() { - @Override - public ClientCall newCall( - MethodDescriptor methodDescriptor, CallOptions callOptions) { - return new NoopClientCall() { - @Override - @SuppressWarnings("unchecked") - public void start(Listener responseListener, Metadata headers) { - interceptedListener.set((Listener) responseListener); - actualClientInitial.set(headers); - } - - @Override - public void sendMessage(RequestT message) { - actualRequest.set(message); - } - - @Override - public Attributes getAttributes() { - return Attributes.newBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer).build(); - } - }; - } - - @Override - public String authority() { - return "the-authority"; - } - }; - - @SuppressWarnings("unchecked") - ClientCall.Listener mockListener = mock(ClientCall.Listener.class); - - MethodDescriptor method = - MethodDescriptor.newBuilder() - .setType(MethodType.UNKNOWN) - .setFullMethodName("service/method") - .setRequestMarshaller(BYTEARRAY_MARSHALLER) - .setResponseMarshaller(BYTEARRAY_MARSHALLER) - .build(); - ClientCall interceptedCall = - new BinlogHelper(mockSinkWriter) - .getClientInterceptor(CALL_ID) - .interceptCall( - method, - CallOptions.DEFAULT.withDeadlineAfter(1, TimeUnit.SECONDS), - channel); - Metadata clientInitial = new Metadata(); - interceptedCall.start(mockListener, clientInitial); - verify(mockSinkWriter).logClientHeader( - /*seq=*/ eq(1L), - anyString(), - anyString(), - any(Duration.class), - any(Metadata.class), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID), - ArgumentMatchers.isNull()); - verifyNoMoreInteractions(mockSinkWriter); - - // trailer only response - { - Status status = Status.INTERNAL.withDescription("some description"); - Metadata trailers = new Metadata(); - - interceptedListener.get().onClose(status, trailers); - verify(mockSinkWriter).logTrailer( - /*seq=*/ eq(2L), - same(status), - same(trailers), - eq(Logger.LOGGER_CLIENT), - eq(CALL_ID), - same(peer)); - verifyNoMoreInteractions(mockSinkWriter); - verify(mockListener).onClose(same(status), same(trailers)); - } - } - - @Test - public void serverInterceptor() throws Exception { - final AtomicReference> interceptedCall = - new AtomicReference<>(); - ServerCall.Listener capturedListener; - @SuppressWarnings("unchecked") - final ServerCall.Listener mockListener = mock(ServerCall.Listener.class); - // capture these manually because ServerCall can not be mocked - final AtomicReference actualServerInitial = new AtomicReference<>(); - final AtomicReference actualResponse = new AtomicReference<>(); - final AtomicReference actualStatus = new AtomicReference<>(); - final AtomicReference actualTrailers = new AtomicReference<>(); - - // begin call and receive client header - { - Metadata clientInitial = new Metadata(); - final MethodDescriptor method = - MethodDescriptor.newBuilder() - .setType(MethodType.UNKNOWN) - .setFullMethodName("service/method") - .setRequestMarshaller(BYTEARRAY_MARSHALLER) - .setResponseMarshaller(BYTEARRAY_MARSHALLER) - .build(); - capturedListener = - new BinlogHelper(mockSinkWriter) - .getServerInterceptor(CALL_ID) - .interceptCall( - new NoopServerCall() { - @Override - public void sendHeaders(Metadata headers) { - actualServerInitial.set(headers); - } - - @Override - public void sendMessage(byte[] message) { - actualResponse.set(message); - } - - @Override - public void close(Status status, Metadata trailers) { - actualStatus.set(status); - actualTrailers.set(trailers); - } - - @Override - public MethodDescriptor getMethodDescriptor() { - return method; - } - - @Override - public Attributes getAttributes() { - return Attributes - .newBuilder() - .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, peer) - .build(); - } - - @Override - public String getAuthority() { - return "the-authority"; - } - }, - clientInitial, - new ServerCallHandler() { - @Override - public ServerCall.Listener startCall( - ServerCall call, - Metadata headers) { - interceptedCall.set(call); - return mockListener; - } - }); - verify(mockSinkWriter).logClientHeader( - /*seq=*/ eq(1L), - eq("service/method"), - eq("the-authority"), - ArgumentMatchers.isNull(), - same(clientInitial), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID), - same(peer)); - verifyNoMoreInteractions(mockSinkWriter); - } - - // send server header - { - Metadata serverInital = new Metadata(); - interceptedCall.get().sendHeaders(serverInital); - verify(mockSinkWriter).logServerHeader( - /*seq=*/ eq(2L), - same(serverInital), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID), - ArgumentMatchers.isNull()); - verifyNoMoreInteractions(mockSinkWriter); - assertSame(serverInital, actualServerInitial.get()); - } - - // receive client msg - { - byte[] request = "this is a request".getBytes(US_ASCII); - capturedListener.onMessage(request); - verify(mockSinkWriter).logRpcMessage( - /*seq=*/ eq(3L), - eq(EventType.EVENT_TYPE_CLIENT_MESSAGE), - same(BYTEARRAY_MARSHALLER), - same(request), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID)); - verifyNoMoreInteractions(mockSinkWriter); - verify(mockListener).onMessage(same(request)); - } - - // client half close - { - capturedListener.onHalfClose(); - verify(mockSinkWriter).logHalfClose( - eq(4L), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID)); - verifyNoMoreInteractions(mockSinkWriter); - verify(mockListener).onHalfClose(); - } - - // send server msg - { - byte[] response = "this is a response".getBytes(US_ASCII); - interceptedCall.get().sendMessage(response); - verify(mockSinkWriter).logRpcMessage( - /*seq=*/ eq(5L), - eq(EventType.EVENT_TYPE_SERVER_MESSAGE), - same(BYTEARRAY_MARSHALLER), - same(response), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID)); - verifyNoMoreInteractions(mockSinkWriter); - assertSame(response, actualResponse.get()); - } - - // send trailer - { - Status status = Status.INTERNAL.withDescription("some description"); - Metadata trailers = new Metadata(); - interceptedCall.get().close(status, trailers); - verify(mockSinkWriter).logTrailer( - /*seq=*/ eq(6L), - same(status), - same(trailers), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID), - ArgumentMatchers.isNull()); - verifyNoMoreInteractions(mockSinkWriter); - assertSame(status, actualStatus.get()); - assertSame(trailers, actualTrailers.get()); - } - - // cancel - { - capturedListener.onCancel(); - verify(mockSinkWriter).logCancel( - /*seq=*/ eq(7L), - eq(Logger.LOGGER_SERVER), - eq(CALL_ID)); - verify(mockListener).onCancel(); - } - } - - /** A builder class to make unit test code more readable. */ - private static final class Builder { - int maxHeaderBytes = 0; - int maxMessageBytes = 0; - - Builder header(int bytes) { - maxHeaderBytes = bytes; - return this; - } - - Builder msg(int bytes) { - maxMessageBytes = bytes; - return this; - } - - BinlogHelper build() { - return new BinlogHelper( - new SinkWriterImpl(mock(BinaryLogSink.class), null, maxHeaderBytes, maxMessageBytes)); - } - } - - private static void assertSameLimits(BinlogHelper a, BinlogHelper b) { - assertEquals(a.writer.getMaxMessageBytes(), b.writer.getMaxMessageBytes()); - assertEquals(a.writer.getMaxHeaderBytes(), b.writer.getMaxHeaderBytes()); - } - - private BinlogHelper makeLog(String factoryConfigStr, String lookup) { - return new BinlogHelper.FactoryImpl(sink, factoryConfigStr).getLog(lookup); - } - - private BinlogHelper makeOptions(String logConfigStr) { - return FactoryImpl.createBinaryLog(sink, logConfigStr); - } - - private static GrpcLogEntry metadataToProtoTestHelper( - EventType type, Metadata metadata, int maxHeaderBytes) { - GrpcLogEntry.Builder builder = GrpcLogEntry.newBuilder(); - MaybeTruncated pair - = BinlogHelper.createMetadataProto(metadata, maxHeaderBytes); - switch (type) { - case EVENT_TYPE_CLIENT_HEADER: - builder.setClientHeader(ClientHeader.newBuilder().setMetadata(pair.proto)); - break; - case EVENT_TYPE_SERVER_HEADER: - builder.setServerHeader(ServerHeader.newBuilder().setMetadata(pair.proto)); - break; - case EVENT_TYPE_SERVER_TRAILER: - builder.setTrailer(Trailer.newBuilder().setMetadata(pair.proto)); - break; - default: - throw new IllegalArgumentException(); - } - builder.setType(type).setPayloadTruncated(pair.truncated); - return builder.build(); - } - - private static GrpcLogEntry messageToProtoTestHelper( - byte[] message, int maxMessageBytes) { - GrpcLogEntry.Builder builder = GrpcLogEntry.newBuilder(); - MaybeTruncated pair - = BinlogHelper.createMessageProto(message, maxMessageBytes); - builder.setMessage(pair.proto).setPayloadTruncated(pair.truncated); - return builder.build(); - } -} diff --git a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java deleted file mode 100644 index 45dd8056ddf..00000000000 --- a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java +++ /dev/null @@ -1,949 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import static com.google.common.truth.Truth.assertThat; -import static io.grpc.InternalChannelz.id; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; -import com.google.protobuf.Int64Value; -import com.google.protobuf.Message; -import com.google.protobuf.util.Durations; -import com.google.protobuf.util.Timestamps; -import io.grpc.ConnectivityState; -import io.grpc.InternalChannelz; -import io.grpc.InternalChannelz.ChannelStats; -import io.grpc.InternalChannelz.ChannelTrace.Event; -import io.grpc.InternalChannelz.ChannelTrace.Event.Severity; -import io.grpc.InternalChannelz.RootChannelList; -import io.grpc.InternalChannelz.ServerList; -import io.grpc.InternalChannelz.ServerSocketsList; -import io.grpc.InternalChannelz.ServerStats; -import io.grpc.InternalChannelz.SocketOptions; -import io.grpc.InternalChannelz.SocketStats; -import io.grpc.InternalInstrumented; -import io.grpc.InternalWithLogId; -import io.grpc.channelz.v1.Address; -import io.grpc.channelz.v1.Address.OtherAddress; -import io.grpc.channelz.v1.Address.TcpIpAddress; -import io.grpc.channelz.v1.Address.UdsAddress; -import io.grpc.channelz.v1.Channel; -import io.grpc.channelz.v1.ChannelConnectivityState; -import io.grpc.channelz.v1.ChannelConnectivityState.State; -import io.grpc.channelz.v1.ChannelData; -import io.grpc.channelz.v1.ChannelRef; -import io.grpc.channelz.v1.ChannelTrace; -import io.grpc.channelz.v1.ChannelTraceEvent; -import io.grpc.channelz.v1.GetChannelRequest; -import io.grpc.channelz.v1.GetServerSocketsResponse; -import io.grpc.channelz.v1.GetServersResponse; -import io.grpc.channelz.v1.GetTopChannelsResponse; -import io.grpc.channelz.v1.Security; -import io.grpc.channelz.v1.Security.OtherSecurity; -import io.grpc.channelz.v1.Security.Tls; -import io.grpc.channelz.v1.Server; -import io.grpc.channelz.v1.ServerData; -import io.grpc.channelz.v1.ServerRef; -import io.grpc.channelz.v1.Socket; -import io.grpc.channelz.v1.SocketData; -import io.grpc.channelz.v1.SocketOption; -import io.grpc.channelz.v1.SocketOptionLinger; -import io.grpc.channelz.v1.SocketOptionTcpInfo; -import io.grpc.channelz.v1.SocketOptionTimeout; -import io.grpc.channelz.v1.SocketRef; -import io.grpc.channelz.v1.Subchannel; -import io.grpc.channelz.v1.SubchannelRef; -import io.grpc.services.ChannelzTestHelper.TestChannel; -import io.grpc.services.ChannelzTestHelper.TestListenSocket; -import io.grpc.services.ChannelzTestHelper.TestServer; -import io.grpc.services.ChannelzTestHelper.TestSocket; -import io.netty.channel.unix.DomainSocketAddress; -import java.net.Inet4Address; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.cert.Certificate; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public final class ChannelzProtoUtilTest { - - private final TestChannel channel = new TestChannel(); - private final ChannelRef channelRef = ChannelRef - .newBuilder() - .setName(channel.toString()) - .setChannelId(channel.getLogId().getId()) - .build(); - private final ChannelData channelData = ChannelData - .newBuilder() - .setTarget("sometarget") - .setState(ChannelConnectivityState.newBuilder().setState(State.READY)) - .setCallsStarted(1) - .setCallsSucceeded(2) - .setCallsFailed(3) - .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) - .build(); - private final Channel channelProto = Channel - .newBuilder() - .setRef(channelRef) - .setData(channelData) - .build(); - - private final TestChannel subchannel = new TestChannel(); - private final SubchannelRef subchannelRef = SubchannelRef - .newBuilder() - .setName(subchannel.toString()) - .setSubchannelId(subchannel.getLogId().getId()) - .build(); - private final ChannelData subchannelData = ChannelData - .newBuilder() - .setTarget("sometarget") - .setState(ChannelConnectivityState.newBuilder().setState(State.READY)) - .setCallsStarted(1) - .setCallsSucceeded(2) - .setCallsFailed(3) - .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) - .build(); - private final Subchannel subchannelProto = Subchannel - .newBuilder() - .setRef(subchannelRef) - .setData(subchannelData) - .build(); - - private final TestServer server = new TestServer(); - private final ServerRef serverRef = ServerRef - .newBuilder() - .setName(server.toString()) - .setServerId(server.getLogId().getId()) - .build(); - private final ServerData serverData = ServerData - .newBuilder() - .setCallsStarted(1) - .setCallsSucceeded(2) - .setCallsFailed(3) - .setLastCallStartedTimestamp(Timestamps.fromNanos(4)) - .build(); - private final Server serverProto = Server - .newBuilder() - .setRef(serverRef) - .setData(serverData) - .build(); - - private final SocketOption sockOptLingerDisabled = SocketOption - .newBuilder() - .setName("SO_LINGER") - .setAdditional( - Any.pack(SocketOptionLinger.getDefaultInstance())) - .build(); - - private final SocketOption sockOptlinger10s = SocketOption - .newBuilder() - .setName("SO_LINGER") - .setAdditional( - Any.pack(SocketOptionLinger - .newBuilder() - .setActive(true) - .setDuration(Durations.fromSeconds(10)) - .build())) - .build(); - - private final SocketOption sockOptTimeout200ms = SocketOption - .newBuilder() - .setName("SO_TIMEOUT") - .setAdditional( - Any.pack(SocketOptionTimeout - .newBuilder() - .setDuration(Durations.fromMillis(200)) - .build()) - ).build(); - - private final SocketOption sockOptAdditional = SocketOption - .newBuilder() - .setName("SO_MADE_UP_OPTION") - .setValue("some-made-up-value") - .build(); - - private final InternalChannelz.TcpInfo channelzTcpInfo - = new InternalChannelz.TcpInfo.Builder() - .setState(70) - .setCaState(71) - .setRetransmits(72) - .setProbes(73) - .setBackoff(74) - .setOptions(75) - .setSndWscale(76) - .setRcvWscale(77) - .setRto(78) - .setAto(79) - .setSndMss(710) - .setRcvMss(711) - .setUnacked(712) - .setSacked(713) - .setLost(714) - .setRetrans(715) - .setFackets(716) - .setLastDataSent(717) - .setLastAckSent(718) - .setLastDataRecv(719) - .setLastAckRecv(720) - .setPmtu(721) - .setRcvSsthresh(722) - .setRtt(723) - .setRttvar(724) - .setSndSsthresh(725) - .setSndCwnd(726) - .setAdvmss(727) - .setReordering(728) - .build(); - - private final SocketOption socketOptionTcpInfo = SocketOption - .newBuilder() - .setName("TCP_INFO") - .setAdditional( - Any.pack( - SocketOptionTcpInfo.newBuilder() - .setTcpiState(70) - .setTcpiCaState(71) - .setTcpiRetransmits(72) - .setTcpiProbes(73) - .setTcpiBackoff(74) - .setTcpiOptions(75) - .setTcpiSndWscale(76) - .setTcpiRcvWscale(77) - .setTcpiRto(78) - .setTcpiAto(79) - .setTcpiSndMss(710) - .setTcpiRcvMss(711) - .setTcpiUnacked(712) - .setTcpiSacked(713) - .setTcpiLost(714) - .setTcpiRetrans(715) - .setTcpiFackets(716) - .setTcpiLastDataSent(717) - .setTcpiLastAckSent(718) - .setTcpiLastDataRecv(719) - .setTcpiLastAckRecv(720) - .setTcpiPmtu(721) - .setTcpiRcvSsthresh(722) - .setTcpiRtt(723) - .setTcpiRttvar(724) - .setTcpiSndSsthresh(725) - .setTcpiSndCwnd(726) - .setTcpiAdvmss(727) - .setTcpiReordering(728) - .build())) - .build(); - - private final TestListenSocket listenSocket = new TestListenSocket(); - private final SocketRef listenSocketRef = SocketRef - .newBuilder() - .setName(listenSocket.toString()) - .setSocketId(id(listenSocket)) - .build(); - private final Address listenAddress = Address - .newBuilder() - .setTcpipAddress( - TcpIpAddress - .newBuilder() - .setIpAddress(ByteString.copyFrom( - ((InetSocketAddress) listenSocket.listenAddress).getAddress().getAddress())) - .setPort(1234)) - .build(); - - private final TestSocket socket = new TestSocket(); - private final SocketRef socketRef = SocketRef - .newBuilder() - .setName(socket.toString()) - .setSocketId(socket.getLogId().getId()) - .build(); - private final SocketData socketDataWithDataNoSockOpts = SocketData - .newBuilder() - .setStreamsStarted(1) - .setLastLocalStreamCreatedTimestamp(Timestamps.fromNanos(2)) - .setLastRemoteStreamCreatedTimestamp(Timestamps.fromNanos(3)) - .setStreamsSucceeded(4) - .setStreamsFailed(5) - .setMessagesSent(6) - .setMessagesReceived(7) - .setKeepAlivesSent(8) - .setLastMessageSentTimestamp(Timestamps.fromNanos(9)) - .setLastMessageReceivedTimestamp(Timestamps.fromNanos(10)) - .setLocalFlowControlWindow(Int64Value.newBuilder().setValue(11)) - .setRemoteFlowControlWindow(Int64Value.newBuilder().setValue(12)) - .build(); - private final Address localAddress = Address - .newBuilder() - .setTcpipAddress( - TcpIpAddress - .newBuilder() - .setIpAddress(ByteString.copyFrom( - ((InetSocketAddress) socket.local).getAddress().getAddress())) - .setPort(1000)) - .build(); - private final Address remoteAddress = Address - .newBuilder() - .setTcpipAddress( - TcpIpAddress - .newBuilder() - .setIpAddress(ByteString.copyFrom( - ((InetSocketAddress) socket.remote).getAddress().getAddress())) - .setPort(1000)) - .build(); - - private final ChannelTrace channelTrace = ChannelTrace - .newBuilder() - .setNumEventsLogged(1234) - .setCreationTimestamp(Timestamps.fromNanos(1000)) - .build(); - - @Test - public void toChannelRef() { - assertEquals(channelRef, ChannelzProtoUtil.toChannelRef(channel)); - } - - @Test - public void toSubchannelRef() { - assertEquals(subchannelRef, ChannelzProtoUtil.toSubchannelRef(subchannel)); - } - - @Test - public void toServerRef() { - assertEquals(serverRef, ChannelzProtoUtil.toServerRef(server)); - } - - @Test - public void toSocketRef() { - assertEquals(socketRef, ChannelzProtoUtil.toSocketRef(socket)); - } - - @Test - public void toState() { - for (ConnectivityState connectivityState : ConnectivityState.values()) { - assertEquals( - connectivityState.name(), - ChannelzProtoUtil.toState(connectivityState).getValueDescriptor().getName()); - } - assertEquals(State.UNKNOWN, ChannelzProtoUtil.toState(null)); - } - - @Test - public void toSocket_withDataNoOptions() throws Exception { - assertEquals( - Socket - .newBuilder() - .setRef(socketRef) - .setLocal(localAddress) - .setRemote(remoteAddress) - .setData(socketDataWithDataNoSockOpts) - .build(), - ChannelzProtoUtil.toSocket(socket)); - } - - @Test - public void toSocket_noDataWithOptions() throws Exception { - assertEquals( - Socket - .newBuilder() - .setRef(listenSocketRef) - .setLocal(listenAddress) - .setData( - SocketData - .newBuilder() - .addOption( - SocketOption - .newBuilder() - .setName("listen_option") - .setValue("listen_option_value"))) - .build(), - ChannelzProtoUtil.toSocket(listenSocket)); - } - - @Test - public void toSocket_withDataWithOptions() throws Exception { - socket.socketOptions - = new SocketOptions(null, null, null, ImmutableMap.of("test_name", "test_value")); - assertEquals( - Socket - .newBuilder() - .setRef(socketRef) - .setLocal(localAddress) - .setRemote(remoteAddress) - .setData( - SocketData - .newBuilder(socketDataWithDataNoSockOpts) - .addOption( - SocketOption.newBuilder() - .setName("test_name").setValue("test_value"))) - .build(), - ChannelzProtoUtil.toSocket(socket)); - } - - @Test - public void extractSocketData() throws Exception { - // no options - assertEquals( - socketDataWithDataNoSockOpts, - ChannelzProtoUtil.extractSocketData(socket.getStats().get())); - - // with options - socket.socketOptions = toBuilder(socket.socketOptions) - .setSocketOptionLingerSeconds(10) - .setTcpInfo(channelzTcpInfo) - .build(); - assertEquals( - socketDataWithDataNoSockOpts - .toBuilder() - .addOption(sockOptlinger10s) - .addOption(socketOptionTcpInfo) - .build(), - ChannelzProtoUtil.extractSocketData(socket.getStats().get())); - } - - @Test - public void toSocketData() throws Exception { - assertEquals( - socketDataWithDataNoSockOpts - .toBuilder() - .build(), - ChannelzProtoUtil.extractSocketData(socket.getStats().get())); - } - - @Test - public void socketSecurityTls() throws Exception { - Certificate local = mock(Certificate.class); - Certificate remote = mock(Certificate.class); - when(local.getEncoded()).thenReturn("localcert".getBytes(Charsets.UTF_8)); - when(remote.getEncoded()).thenReturn("remotecert".getBytes(Charsets.UTF_8)); - - socket.security = new InternalChannelz.Security( - new InternalChannelz.Tls("TLS_NULL_WITH_NULL_NULL", local, remote)); - assertEquals( - Security.newBuilder().setTls( - Tls.newBuilder() - .setStandardName("TLS_NULL_WITH_NULL_NULL") - .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8)) - .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) - .build(), - ChannelzProtoUtil.toSocket(socket).getSecurity()); - - socket.security = new InternalChannelz.Security( - new InternalChannelz.Tls("TLS_NULL_WITH_NULL_NULL", /*localCert=*/ null, remote)); - assertEquals( - Security.newBuilder().setTls( - Tls.newBuilder() - .setStandardName("TLS_NULL_WITH_NULL_NULL") - .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8))) - .build(), - ChannelzProtoUtil.toSocket(socket).getSecurity()); - - socket.security = new InternalChannelz.Security( - new InternalChannelz.Tls("TLS_NULL_WITH_NULL_NULL", local, /*remoteCert=*/ null)); - assertEquals( - Security.newBuilder().setTls( - Tls.newBuilder() - .setStandardName("TLS_NULL_WITH_NULL_NULL") - .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8))) - .build(), - ChannelzProtoUtil.toSocket(socket).getSecurity()); - } - - @Test - public void socketSecurityOther() throws Exception { - // what is packed here is not important, just pick some proto message - Message contents = GetChannelRequest.newBuilder().setChannelId(1).build(); - Any packed = Any.pack(contents); - socket.security - = new InternalChannelz.Security( - new InternalChannelz.OtherSecurity("other_security", packed)); - assertEquals( - Security.newBuilder().setOther( - OtherSecurity.newBuilder().setName("other_security").setValue(packed)) - .build(), - ChannelzProtoUtil.toSocket(socket).getSecurity()); - } - - @Test - public void toAddress_inet() throws Exception { - InetSocketAddress inet4 = new InetSocketAddress(Inet4Address.getByName("10.0.0.1"), 1000); - assertEquals( - Address.newBuilder().setTcpipAddress( - TcpIpAddress - .newBuilder() - .setIpAddress(ByteString.copyFrom(inet4.getAddress().getAddress())) - .setPort(1000)) - .build(), - ChannelzProtoUtil.toAddress(inet4)); - } - - @Test - public void toAddress_uds() throws Exception { - String path = "/tmp/foo"; - DomainSocketAddress uds = new DomainSocketAddress(path); - assertEquals( - Address.newBuilder().setUdsAddress( - UdsAddress - .newBuilder() - .setFilename(path)) - .build(), - ChannelzProtoUtil.toAddress(uds)); - } - - @Test - public void toAddress_other() throws Exception { - final String name = "my name"; - SocketAddress other = new SocketAddress() { - @Override - public String toString() { - return name; - } - }; - assertEquals( - Address.newBuilder().setOtherAddress( - OtherAddress - .newBuilder() - .setName(name)) - .build(), - ChannelzProtoUtil.toAddress(other)); - } - - @Test - public void toServer() throws Exception { - // no listen sockets - assertEquals(serverProto, ChannelzProtoUtil.toServer(server)); - - // 1 listen socket - server.serverStats = toBuilder(server.serverStats) - .addListenSockets(ImmutableList.>of(listenSocket)) - .build(); - assertEquals( - serverProto - .toBuilder() - .addListenSocket(listenSocketRef) - .build(), - ChannelzProtoUtil.toServer(server)); - - // multiple listen sockets - TestListenSocket otherListenSocket = new TestListenSocket(); - SocketRef otherListenSocketRef = ChannelzProtoUtil.toSocketRef(otherListenSocket); - server.serverStats = toBuilder(server.serverStats) - .addListenSockets( - ImmutableList.>of(otherListenSocket)) - .build(); - assertEquals( - serverProto - .toBuilder() - .addListenSocket(listenSocketRef) - .addListenSocket(otherListenSocketRef) - .build(), - ChannelzProtoUtil.toServer(server)); - } - - @Test - public void toServerData() throws Exception { - assertEquals(serverData, ChannelzProtoUtil.toServerData(server.serverStats)); - } - - @Test - public void toChannel() throws Exception { - assertEquals(channelProto, ChannelzProtoUtil.toChannel(channel)); - - channel.stats = toBuilder(channel.stats) - .setSubchannels(ImmutableList.of(subchannel)) - .build(); - - assertEquals( - channelProto - .toBuilder() - .addSubchannelRef(subchannelRef) - .build(), - ChannelzProtoUtil.toChannel(channel)); - - TestChannel otherSubchannel = new TestChannel(); - channel.stats = toBuilder(channel.stats) - .setSubchannels(ImmutableList.of(subchannel, otherSubchannel)) - .build(); - assertEquals( - channelProto - .toBuilder() - .addSubchannelRef(subchannelRef) - .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(otherSubchannel)) - .build(), - ChannelzProtoUtil.toChannel(channel)); - } - - @Test - public void extractChannelData() { - assertEquals(channelData, ChannelzProtoUtil.extractChannelData(channel.stats)); - } - - @Test - public void toSubchannel_noChildren() throws Exception { - assertEquals( - subchannelProto, - ChannelzProtoUtil.toSubchannel(subchannel)); - } - - @Test - public void toSubchannel_socketChildren() throws Exception { - subchannel.stats = toBuilder(subchannel.stats) - .setSockets(ImmutableList.of(socket)) - .build(); - - assertEquals( - subchannelProto.toBuilder() - .addSocketRef(socketRef) - .build(), - ChannelzProtoUtil.toSubchannel(subchannel)); - - TestSocket otherSocket = new TestSocket(); - subchannel.stats = toBuilder(subchannel.stats) - .setSockets(ImmutableList.of(socket, otherSocket)) - .build(); - assertEquals( - subchannelProto - .toBuilder() - .addSocketRef(socketRef) - .addSocketRef(ChannelzProtoUtil.toSocketRef(otherSocket)) - .build(), - ChannelzProtoUtil.toSubchannel(subchannel)); - } - - @Test - public void toSubchannel_subchannelChildren() throws Exception { - TestChannel subchannel1 = new TestChannel(); - subchannel.stats = toBuilder(subchannel.stats) - .setSubchannels(ImmutableList.of(subchannel1)) - .build(); - assertEquals( - subchannelProto.toBuilder() - .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(subchannel1)) - .build(), - ChannelzProtoUtil.toSubchannel(subchannel)); - - TestChannel subchannel2 = new TestChannel(); - subchannel.stats = toBuilder(subchannel.stats) - .setSubchannels(ImmutableList.of(subchannel1, subchannel2)) - .build(); - assertEquals( - subchannelProto - .toBuilder() - .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(subchannel1)) - .addSubchannelRef(ChannelzProtoUtil.toSubchannelRef(subchannel2)) - .build(), - ChannelzProtoUtil.toSubchannel(subchannel)); - } - - @Test - public void toGetTopChannelsResponse() { - // empty results - assertEquals( - GetTopChannelsResponse.newBuilder().setEnd(true).build(), - ChannelzProtoUtil.toGetTopChannelResponse( - new RootChannelList( - Collections.>emptyList(), true))); - - // 1 result, paginated - assertEquals( - GetTopChannelsResponse - .newBuilder() - .addChannel(channelProto) - .build(), - ChannelzProtoUtil.toGetTopChannelResponse( - new RootChannelList( - ImmutableList.>of(channel), false))); - - // 1 result, end - assertEquals( - GetTopChannelsResponse - .newBuilder() - .addChannel(channelProto) - .setEnd(true) - .build(), - ChannelzProtoUtil.toGetTopChannelResponse( - new RootChannelList( - ImmutableList.>of(channel), true))); - - // 2 results, end - TestChannel channel2 = new TestChannel(); - assertEquals( - GetTopChannelsResponse - .newBuilder() - .addChannel(channelProto) - .addChannel(ChannelzProtoUtil.toChannel(channel2)) - .setEnd(true) - .build(), - ChannelzProtoUtil.toGetTopChannelResponse( - new RootChannelList( - ImmutableList.>of(channel, channel2), true))); - } - - @Test - public void toGetServersResponse() { - // empty results - assertEquals( - GetServersResponse.getDefaultInstance(), - ChannelzProtoUtil.toGetServersResponse( - new ServerList(Collections.>emptyList(), false))); - - // 1 result, paginated - assertEquals( - GetServersResponse - .newBuilder() - .addServer(serverProto) - .build(), - ChannelzProtoUtil.toGetServersResponse( - new ServerList(ImmutableList.>of(server), false))); - - // 1 result, end - assertEquals( - GetServersResponse - .newBuilder() - .addServer(serverProto) - .setEnd(true) - .build(), - ChannelzProtoUtil.toGetServersResponse( - new ServerList(ImmutableList.>of(server), true))); - - TestServer server2 = new TestServer(); - // 2 results, end - assertEquals( - GetServersResponse - .newBuilder() - .addServer(serverProto) - .addServer(ChannelzProtoUtil.toServer(server2)) - .setEnd(true) - .build(), - ChannelzProtoUtil.toGetServersResponse( - new ServerList( - ImmutableList.>of(server, server2), true))); - } - - @Test - public void toGetServerSocketsResponse() { - // empty results - assertEquals( - GetServerSocketsResponse.getDefaultInstance(), - ChannelzProtoUtil.toGetServerSocketsResponse( - new ServerSocketsList(Collections.emptyList(), false))); - - // 1 result, paginated - assertEquals( - GetServerSocketsResponse - .newBuilder() - .addSocketRef(socketRef) - .build(), - ChannelzProtoUtil.toGetServerSocketsResponse( - new ServerSocketsList(ImmutableList.of(socket), false))); - - // 1 result, end - assertEquals( - GetServerSocketsResponse - .newBuilder() - .addSocketRef(socketRef) - .setEnd(true) - .build(), - ChannelzProtoUtil.toGetServerSocketsResponse( - new ServerSocketsList(ImmutableList.of(socket), true))); - - TestSocket socket2 = new TestSocket(); - // 2 results, end - assertEquals( - GetServerSocketsResponse - .newBuilder() - .addSocketRef(socketRef) - .addSocketRef(ChannelzProtoUtil.toSocketRef(socket2)) - .setEnd(true) - .build(), - ChannelzProtoUtil.toGetServerSocketsResponse( - new ServerSocketsList(ImmutableList.of(socket, socket2), true))); - } - - @Test - public void toSocketOptionLinger() { - assertEquals(sockOptLingerDisabled, ChannelzProtoUtil.toSocketOptionLinger(-1)); - assertEquals(sockOptlinger10s, ChannelzProtoUtil.toSocketOptionLinger(10)); - } - - @Test - public void toSocketOptionTimeout() { - assertEquals( - sockOptTimeout200ms, ChannelzProtoUtil.toSocketOptionTimeout("SO_TIMEOUT", 200)); - } - - @Test - public void toSocketOptionAdditional() { - assertEquals( - sockOptAdditional, - ChannelzProtoUtil.toSocketOptionAdditional("SO_MADE_UP_OPTION", "some-made-up-value")); - } - - @Test - public void toSocketOptionTcpInfo() { - assertEquals( - socketOptionTcpInfo, - ChannelzProtoUtil.toSocketOptionTcpInfo(channelzTcpInfo)); - } - - @Test - public void toSocketOptionsList() { - assertThat( - ChannelzProtoUtil.toSocketOptionsList( - new InternalChannelz.SocketOptions.Builder().build())) - .isEmpty(); - - assertThat( - ChannelzProtoUtil.toSocketOptionsList( - new InternalChannelz.SocketOptions.Builder().setSocketOptionLingerSeconds(10).build())) - .containsExactly(sockOptlinger10s); - - assertThat( - ChannelzProtoUtil.toSocketOptionsList( - new InternalChannelz.SocketOptions.Builder().setSocketOptionTimeoutMillis(200).build())) - .containsExactly(sockOptTimeout200ms); - - assertThat( - ChannelzProtoUtil.toSocketOptionsList( - new InternalChannelz.SocketOptions - .Builder() - .addOption("SO_MADE_UP_OPTION", "some-made-up-value") - .build())) - .containsExactly(sockOptAdditional); - - SocketOption otherOption = SocketOption - .newBuilder() - .setName("SO_MADE_UP_OPTION2") - .setValue("some-made-up-value2") - .build(); - assertThat( - ChannelzProtoUtil.toSocketOptionsList( - new InternalChannelz.SocketOptions.Builder() - .addOption("SO_MADE_UP_OPTION", "some-made-up-value") - .addOption("SO_MADE_UP_OPTION2", "some-made-up-value2") - .build())) - .containsExactly(sockOptAdditional, otherOption); - } - - @Test - public void channelTrace_withoutEvents() { - ChannelStats stats = toBuilder(channel.stats) - .setChannelTrace(new InternalChannelz.ChannelTrace.Builder() - .setNumEventsLogged(1234) - .setCreationTimeNanos(1000) - .build()) - .build(); - - ChannelData protoStats = channelData.toBuilder().setTrace(channelTrace).build(); - assertEquals(ChannelzProtoUtil.extractChannelData(stats), protoStats); - } - - @Test - public void channelTrace_withEvents() { - Event event1 = new Event.Builder() - .setDescription("event1") - .setSeverity(Severity.CT_ERROR) - .setTimestampNanos(12) - .setSubchannelRef(subchannel) - .build(); - Event event2 = new Event.Builder() - .setDescription("event2") - .setTimestampNanos(34) - .setSeverity(Severity.CT_INFO) - .setChannelRef(channel) - .build(); - - ChannelStats stats = - toBuilder(channel.stats) - .setChannelTrace( - new InternalChannelz.ChannelTrace.Builder() - .setNumEventsLogged(1234) - .setCreationTimeNanos(1000) - .setEvents(Arrays.asList(event1, event2)) - .build()) - .build(); - - ChannelTraceEvent protoEvent1 = ChannelTraceEvent - .newBuilder() - .setDescription("event1") - .setTimestamp(Timestamps.fromNanos(12)) - .setSeverity(ChannelTraceEvent.Severity.CT_ERROR) - .setSubchannelRef(subchannelRef) - .build(); - ChannelTraceEvent protoEvent2 = ChannelTraceEvent - .newBuilder() - .setDescription("event2") - .setTimestamp(Timestamps.fromNanos(34)) - .setSeverity(ChannelTraceEvent.Severity.CT_INFO) - .setChannelRef(channelRef) - .build(); - ChannelData protoStats = channelData - .toBuilder() - .setTrace(channelTrace - .toBuilder() - .addAllEvents(Arrays.asList(protoEvent1, protoEvent2)) - .build()) - .build(); - assertEquals(ChannelzProtoUtil.extractChannelData(stats), protoStats); - } - - private static ChannelStats.Builder toBuilder(ChannelStats stats) { - ChannelStats.Builder builder = new ChannelStats.Builder() - .setTarget(stats.target) - .setState(stats.state) - .setCallsStarted(stats.callsStarted) - .setCallsSucceeded(stats.callsSucceeded) - .setCallsFailed(stats.callsFailed) - .setLastCallStartedNanos(stats.lastCallStartedNanos); - if (!stats.subchannels.isEmpty()) { - builder.setSubchannels(stats.subchannels); - } - if (!stats.sockets.isEmpty()) { - builder.setSockets(stats.sockets); - } - return builder; - } - - - private static SocketOptions.Builder toBuilder(SocketOptions options) { - SocketOptions.Builder builder = new SocketOptions.Builder() - .setSocketOptionTimeoutMillis(options.soTimeoutMillis) - .setSocketOptionLingerSeconds(options.lingerSeconds); - for (Map.Entry entry : options.others.entrySet()) { - builder.addOption(entry.getKey(), entry.getValue()); - } - return builder; - } - - private static ServerStats.Builder toBuilder(ServerStats stats) { - return new ServerStats.Builder() - .setCallsStarted(stats.callsStarted) - .setCallsSucceeded(stats.callsSucceeded) - .setCallsFailed(stats.callsFailed) - .setLastCallStartedNanos(stats.lastCallStartedNanos) - .addListenSockets(stats.listenSockets); - } -} diff --git a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java b/services/src/test/java/io/grpc/services/ChannelzTestHelper.java deleted file mode 100644 index 6bd8e8bceb5..00000000000 --- a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2018 The 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. - */ - -package io.grpc.services; - -import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; -import io.grpc.ConnectivityState; -import io.grpc.InternalChannelz; -import io.grpc.InternalChannelz.ChannelStats; -import io.grpc.InternalChannelz.Security; -import io.grpc.InternalChannelz.ServerStats; -import io.grpc.InternalChannelz.SocketOptions; -import io.grpc.InternalChannelz.SocketStats; -import io.grpc.InternalChannelz.TransportStats; -import io.grpc.InternalInstrumented; -import io.grpc.InternalLogId; -import io.grpc.InternalWithLogId; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Collections; - -/** - * Test class definitions that will be used in the proto utils test as well as - * channelz service test. - */ -final class ChannelzTestHelper { - - static final class TestSocket implements InternalInstrumented { - private final InternalLogId id = InternalLogId.allocate("socket", /*details=*/ null); - TransportStats transportStats = new TransportStats( - /*streamsStarted=*/ 1, - /*lastLocalStreamCreatedTimeNanos=*/ 2, - /*lastRemoteStreamCreatedTimeNanos=*/ 3, - /*streamsSucceeded=*/ 4, - /*streamsFailed=*/ 5, - /*messagesSent=*/ 6, - /*messagesReceived=*/ 7, - /*keepAlivesSent=*/ 8, - /*lastMessageSentTimeNanos=*/ 9, - /*lastMessageReceivedTimeNanos=*/ 10, - /*localFlowControlWindow=*/ 11, - /*remoteFlowControlWindow=*/ 12); - SocketAddress local = new InetSocketAddress("10.0.0.1", 1000); - SocketAddress remote = new InetSocketAddress("10.0.0.2", 1000); - InternalChannelz.SocketOptions socketOptions - = new InternalChannelz.SocketOptions.Builder().build(); - Security security = null; - - @Override - public ListenableFuture getStats() { - SettableFuture ret = SettableFuture.create(); - ret.set( - new SocketStats( - transportStats, - local, - remote, - socketOptions, - security)); - return ret; - } - - @Override - public InternalLogId getLogId() { - return id; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("logId", getLogId()) - .toString(); - } - } - - static final class TestListenSocket implements InternalInstrumented { - private final InternalLogId id = InternalLogId.allocate("listensocket", /*details=*/ null); - SocketAddress listenAddress = new InetSocketAddress("10.0.0.1", 1234); - - @Override - public ListenableFuture getStats() { - SettableFuture ret = SettableFuture.create(); - ret.set( - new SocketStats( - /*data=*/ null, - listenAddress, - /*remote=*/ null, - new SocketOptions.Builder().addOption("listen_option", "listen_option_value").build(), - /*security=*/ null)); - return ret; - } - - @Override - public InternalLogId getLogId() { - return id; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("logId", getLogId()) - .toString(); - } - } - - static final class TestServer implements InternalInstrumented { - private final InternalLogId id = InternalLogId.allocate("server", /*details=*/ null); - ServerStats serverStats = new ServerStats( - /*callsStarted=*/ 1, - /*callsSucceeded=*/ 2, - /*callsFailed=*/ 3, - /*lastCallStartedNanos=*/ 4, - Collections.>emptyList()); - - @Override - public ListenableFuture getStats() { - SettableFuture ret = SettableFuture.create(); - ret.set(serverStats); - return ret; - } - - @Override - public InternalLogId getLogId() { - return id; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("logId", getLogId()) - .toString(); - } - } - - static final class TestChannel implements InternalInstrumented { - private final InternalLogId id = - InternalLogId.allocate("channel-or-subchannel", /*details=*/ null); - - ChannelStats stats = new ChannelStats.Builder() - .setTarget("sometarget") - .setState(ConnectivityState.READY) - .setCallsStarted(1) - .setCallsSucceeded(2) - .setCallsFailed(3) - .setLastCallStartedNanos(4) - .setSubchannels(Collections.emptyList()) - .setSockets(Collections.emptyList()) - .build(); - - @Override - public ListenableFuture getStats() { - SettableFuture ret = SettableFuture.create(); - ret.set(stats); - return ret; - } - - @Override - public InternalLogId getLogId() { - return id; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("logId", getLogId()) - .toString(); - } - } -} diff --git a/services/src/test/java/io/grpc/services/TempFileSinkTest.java b/services/src/test/java/io/grpc/services/TempFileSinkTest.java deleted file mode 100644 index f47f10569d6..00000000000 --- a/services/src/test/java/io/grpc/services/TempFileSinkTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2018, gRPC Authors All rights reserved. - * - * 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. - */ - -package io.grpc.services; - -import static org.junit.Assert.assertEquals; - -import io.grpc.binarylog.v1.GrpcLogEntry; -import java.io.DataInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Tests for {@link io.grpc.services.TempFileSink}. - */ -@RunWith(JUnit4.class) -public class TempFileSinkTest { - @Test - public void readMyWrite() throws Exception { - TempFileSink sink = new TempFileSink(); - GrpcLogEntry e1 = GrpcLogEntry.newBuilder() - .setCallId(1234) - .build(); - GrpcLogEntry e2 = GrpcLogEntry.newBuilder() - .setCallId(5678) - .build(); - sink.write(e1); - sink.write(e2); - sink.close(); - - DataInputStream input = new DataInputStream(new FileInputStream(sink.getPath())); - try { - GrpcLogEntry read1 = GrpcLogEntry.parseDelimitedFrom(input); - GrpcLogEntry read2 = GrpcLogEntry.parseDelimitedFrom(input); - - assertEquals(e1, read1); - assertEquals(e2, read2); - assertEquals(-1, input.read()); - } finally { - input.close(); - } - } - - @Test - public void writeAfterCloseIsSilent() throws IOException { - TempFileSink sink = new TempFileSink(); - sink.close(); - sink.write(GrpcLogEntry.newBuilder() - .setCallId(1234) - .build()); - } -} From 0c282c18d3726ae26a0ced61ea1051074de5565a Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 16 Apr 2021 16:39:57 -0700 Subject: [PATCH 20/20] Clean up bazel. --- services/BUILD.bazel | 6 ------ 1 file changed, 6 deletions(-) diff --git a/services/BUILD.bazel b/services/BUILD.bazel index 4673f54a28f..c1dd3ad353f 100644 --- a/services/BUILD.bazel +++ b/services/BUILD.bazel @@ -7,15 +7,10 @@ java_library( srcs = [ "src/main/java/io/grpc/protobuf/services/BinaryLogProvider.java", "src/main/java/io/grpc/protobuf/services/BinaryLogProviderImpl.java", - "src/main/java/io/grpc/services/BinaryLogProviderImpl.java", "src/main/java/io/grpc/protobuf/services/BinaryLogSink.java", - "src/main/java/io/grpc/services/BinaryLogSink.java", "src/main/java/io/grpc/protobuf/services/BinlogHelper.java", - "src/main/java/io/grpc/services/BinlogHelper.java", "src/main/java/io/grpc/protobuf/services/InetAddressUtil.java", - "src/main/java/io/grpc/services/InetAddressUtil.java", "src/main/java/io/grpc/protobuf/services/TempFileSink.java", - "src/main/java/io/grpc/services/TempFileSink.java", ], deps = [ "//api", @@ -32,7 +27,6 @@ java_library( name = "channelz", srcs = [ "src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java", - "src/main/java/io/grpc/services/ChannelzProtoUtil.java", "src/main/java/io/grpc/protobuf/services/ChannelzService.java", "src/main/java/io/grpc/services/ChannelzService.java", ],