diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle index 64bb6a8f1f0..c7a1db79f38 100644 --- a/benchmarks/build.gradle +++ b/benchmarks/build.gradle @@ -18,6 +18,7 @@ dependencies { implementation project(':thrift') implementation 'com.squareup.retrofit2:converter-jackson' + implementation 'com.google.protobuf:protobuf-java-util' implementation 'io.grpc:grpc-okhttp' implementation 'io.grpc:grpc-netty-shaded' implementation 'org.awaitility:awaitility' diff --git a/dependencies.yml b/dependencies.yml index 81c2a8648e8..2833b299382 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -5,7 +5,7 @@ boms: - com.fasterxml.jackson:jackson-bom:2.11.0 - io.dropwizard.metrics:metrics-bom:4.1.9 - - io.grpc:grpc-bom:1.29.0 + - io.grpc:grpc-bom:1.30.0 - io.micrometer:micrometer-bom:1.5.1 # NOTE: When changing this, re-evaluate netty-tcnative-boringssl-static below - io.netty:netty-bom:4.1.50.Final @@ -107,7 +107,7 @@ com.google.j2objc: # See: https://github.com/grpc/grpc-java/blob/master/build.gradle # (Switch to the right tag and look for 'protobufVersion' and 'protocVersion'.) com.google.protobuf: - protobuf-java: { version: &PROTOBUF_VERSION '3.11.4' } + protobuf-java: { version: &PROTOBUF_VERSION '3.12.0' } protobuf-java-util: version: *PROTOBUF_VERSION exclusions: diff --git a/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java b/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java index 6026b26dee4..0edb4738bb6 100644 --- a/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java +++ b/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java @@ -18,7 +18,7 @@ /** */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.29.0)", + value = "by gRPC proto compiler (version 1.30.0)", comments = "Source: hello.proto") public final class HelloServiceGrpc { diff --git a/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java b/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java index b290746a2b7..3531fac6d7c 100644 --- a/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java +++ b/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java @@ -33,7 +33,7 @@ public interface HelloRequestOrBuilder extends /** * Protobuf type {@code example.grpc.hello.HelloRequest} */ - public static final class HelloRequest extends + public static final class HelloRequest extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:example.grpc.hello.HelloRequest) HelloRequestOrBuilder { @@ -120,6 +120,7 @@ private HelloRequest( * string name = 1; * @return The name. */ + @java.lang.Override public java.lang.String getName() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { @@ -136,6 +137,7 @@ public java.lang.String getName() { * string name = 1; * @return The bytes for name. */ + @java.lang.Override public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; @@ -599,7 +601,7 @@ public interface HelloReplyOrBuilder extends /** * Protobuf type {@code example.grpc.hello.HelloReply} */ - public static final class HelloReply extends + public static final class HelloReply extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:example.grpc.hello.HelloReply) HelloReplyOrBuilder { @@ -686,6 +688,7 @@ private HelloReply( * string message = 1; * @return The message. */ + @java.lang.Override public java.lang.String getMessage() { java.lang.Object ref = message_; if (ref instanceof java.lang.String) { @@ -702,6 +705,7 @@ public java.lang.String getMessage() { * string message = 1; * @return The bytes for message. */ + @java.lang.Override public com.google.protobuf.ByteString getMessageBytes() { java.lang.Object ref = message_; diff --git a/grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java b/grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java index 2cfc7768242..b851de3ceff 100644 --- a/grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java +++ b/grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java @@ -73,7 +73,6 @@ import io.grpc.ServerMethodDefinition; import io.grpc.ServerServiceDefinition; import io.grpc.Status; -import io.grpc.protobuf.services.ProtoReflectionService; /** * The framed {@link GrpcService} implementation. @@ -89,13 +88,13 @@ final class FramedGrpcService extends AbstractHttpService implements GrpcService private final Set supportedSerializationFormats; @Nullable private final MessageMarshaller jsonMarshaller; + @Nullable + private final ProtoReflectionServiceInterceptor protoReflectionServiceInterceptor; private final int maxOutboundMessageSizeBytes; private final boolean useBlockingTaskExecutor; private final boolean unsafeWrapRequestBuffers; private final boolean useClientTimeoutHeader; private final String advertisedEncodingsHeader; - @Nullable - private final ProtoReflectionService protoReflectionService; private final Map defaultHeaders; @@ -107,11 +106,11 @@ final class FramedGrpcService extends AbstractHttpService implements GrpcService CompressorRegistry compressorRegistry, Set supportedSerializationFormats, Consumer jsonMarshallerCustomizer, + @Nullable ProtoReflectionServiceInterceptor protoReflectionServiceInterceptor, int maxOutboundMessageSizeBytes, boolean useBlockingTaskExecutor, boolean unsafeWrapRequestBuffers, boolean useClientTimeoutHeader, - @Nullable ProtoReflectionService protoReflectionService, int maxInboundMessageSizeBytes) { this.registry = requireNonNull(registry, "registry"); this.routes = requireNonNull(routes, "routes"); @@ -119,8 +118,8 @@ final class FramedGrpcService extends AbstractHttpService implements GrpcService this.compressorRegistry = requireNonNull(compressorRegistry, "compressorRegistry"); this.supportedSerializationFormats = supportedSerializationFormats; this.useClientTimeoutHeader = useClientTimeoutHeader; - this.protoReflectionService = protoReflectionService; jsonMarshaller = jsonMarshaller(registry, supportedSerializationFormats, jsonMarshallerCustomizer); + this.protoReflectionServiceInterceptor = protoReflectionServiceInterceptor; this.maxOutboundMessageSizeBytes = maxOutboundMessageSizeBytes; this.useBlockingTaskExecutor = useBlockingTaskExecutor; this.unsafeWrapRequestBuffers = unsafeWrapRequestBuffers; @@ -256,7 +255,7 @@ public void serviceAdded(ServiceConfig cfg) { maxInboundMessageSizeBytes = (int) Math.min(cfg.maxRequestLength(), Integer.MAX_VALUE); } - if (protoReflectionService != null) { + if (protoReflectionServiceInterceptor != null) { final Map grpcServices = cfg.server().config().virtualHosts().stream() .flatMap(host -> host.serviceConfigs().stream()) @@ -269,62 +268,66 @@ public void serviceAdded(ServiceConfig cfg) { .collect(toImmutableMap(def -> def.getServiceDescriptor().getName(), Function.identity(), (a, b) -> a)); - protoReflectionService.notifyOnBuild(new Server() { - @Override - public Server start() { - throw new UnsupportedOperationException(); - } + protoReflectionServiceInterceptor.setServer(newDummyServer(grpcServices)); + } + } - @Override - public List getServices() { - return ImmutableList.copyOf(grpcServices.values()); - } + private static Server newDummyServer(Map grpcServices) { + return new Server() { + @Override + public Server start() { + throw new UnsupportedOperationException(); + } - @Override - public List getImmutableServices() { - // NB: This will probably go away in favor of just getServices above, so we - // implement both the same. - // https://github.com/grpc/grpc-java/issues/4600 - return getServices(); - } + @Override + public List getServices() { + return ImmutableList.copyOf(grpcServices.values()); + } - @Override - public List getMutableServices() { - // Armeria does not have the concept of mutable services. - return ImmutableList.of(); - } + @Override + public List getImmutableServices() { + // NB: This will probably go away in favor of just getServices above, so we + // implement both the same. + // https://github.com/grpc/grpc-java/issues/4600 + return getServices(); + } - @Override - public Server shutdown() { - throw new UnsupportedOperationException(); - } + @Override + public List getMutableServices() { + // Armeria does not have the concept of mutable services. + return ImmutableList.of(); + } - @Override - public Server shutdownNow() { - throw new UnsupportedOperationException(); - } + @Override + public Server shutdown() { + throw new UnsupportedOperationException(); + } - @Override - public boolean isShutdown() { - throw new UnsupportedOperationException(); - } + @Override + public Server shutdownNow() { + throw new UnsupportedOperationException(); + } - @Override - public boolean isTerminated() { - throw new UnsupportedOperationException(); - } + @Override + public boolean isShutdown() { + throw new UnsupportedOperationException(); + } - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) { - throw new UnsupportedOperationException(); - } + @Override + public boolean isTerminated() { + throw new UnsupportedOperationException(); + } - @Override - public void awaitTermination() { - throw new UnsupportedOperationException(); - } - }); - } + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) { + throw new UnsupportedOperationException(); + } + + @Override + public void awaitTermination() { + throw new UnsupportedOperationException(); + } + }; } @Override diff --git a/grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java b/grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java index a88d0a36683..ba2b8eb9283 100644 --- a/grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java +++ b/grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java @@ -52,6 +52,7 @@ import io.grpc.BindableService; import io.grpc.CompressorRegistry; import io.grpc.DecompressorRegistry; +import io.grpc.ServerInterceptors; import io.grpc.ServerServiceDefinition; import io.grpc.protobuf.services.ProtoReflectionService; @@ -71,6 +72,9 @@ public final class GrpcServiceBuilder { @Nullable private CompressorRegistry compressorRegistry; + @Nullable + private ProtoReflectionServiceInterceptor protoReflectionServiceInterceptor; + private Set supportedSerializationFormats = DEFAULT_SUPPORTED_SERIALIZATION_FORMATS; private int maxInboundMessageSizeBytes = ArmeriaMessageDeframer.NO_MAX_INBOUND_MESSAGE_SIZE; @@ -87,9 +91,6 @@ public final class GrpcServiceBuilder { private boolean useClientTimeoutHeader = true; - @Nullable - private ProtoReflectionService protoReflectionService; - GrpcServiceBuilder() {} /** @@ -107,10 +108,11 @@ public GrpcServiceBuilder addService(ServerServiceDefinition service) { */ public GrpcServiceBuilder addService(BindableService bindableService) { if (bindableService instanceof ProtoReflectionService) { - checkState(protoReflectionService == null, + checkState(protoReflectionServiceInterceptor == null, "Attempting to add a ProtoReflectionService but one is already present. " + "ProtoReflectionService must only be added once."); - protoReflectionService = (ProtoReflectionService) bindableService; + protoReflectionServiceInterceptor = new ProtoReflectionServiceInterceptor(); + return addService(ServerInterceptors.intercept(bindableService, protoReflectionServiceInterceptor)); } return addService(bindableService.bindService()); @@ -308,11 +310,11 @@ public GrpcService build() { firstNonNull(compressorRegistry, CompressorRegistry.getDefaultInstance()), supportedSerializationFormats, jsonMarshallerCustomizer, + protoReflectionServiceInterceptor, maxOutboundMessageSizeBytes, useBlockingTaskExecutor, unsafeWrapRequestBuffers, useClientTimeoutHeader, - protoReflectionService, maxInboundMessageSizeBytes); return enableUnframedRequests ? new UnframedGrpcService(grpcService) : grpcService; } diff --git a/grpc/src/main/java/com/linecorp/armeria/server/grpc/ProtoReflectionServiceInterceptor.java b/grpc/src/main/java/com/linecorp/armeria/server/grpc/ProtoReflectionServiceInterceptor.java new file mode 100644 index 00000000000..b1ecf7e66ec --- /dev/null +++ b/grpc/src/main/java/com/linecorp/armeria/server/grpc/ProtoReflectionServiceInterceptor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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: + * + * https://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 com.linecorp.armeria.server.grpc; + +import javax.annotation.Nullable; + +import io.grpc.Context; +import io.grpc.Contexts; +import io.grpc.InternalServer; +import io.grpc.Metadata; +import io.grpc.Server; +import io.grpc.ServerCall; +import io.grpc.ServerCall.Listener; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; + +final class ProtoReflectionServiceInterceptor implements ServerInterceptor { + + @Nullable + private Server server; + + @Override + public Listener interceptCall(ServerCall call, Metadata headers, + ServerCallHandler next) { + // Should set server before calling this + assert server != null; + + final Context context = Context.current().withValue(InternalServer.SERVER_CONTEXT_KEY, server); + return Contexts.interceptCall(context, call, headers, next); + } + + public void setServer(Server server) { + this.server = server; + } +} diff --git a/it/server/build.gradle b/it/server/build.gradle index 13cea6beb03..388ed45ddb7 100644 --- a/it/server/build.gradle +++ b/it/server/build.gradle @@ -2,5 +2,7 @@ dependencies { testImplementation project(':grpc') testImplementation 'io.grpc:grpc-core' testImplementation 'io.grpc:grpc-interop-testing' + testImplementation 'io.grpc:grpc-okhttp' + testImplementation 'io.grpc:grpc-testing' testImplementation 'org.apache.hbase:hbase-shaded-client' }