Server Reflection

Note

This feature is experimental Issue #850.

It implements version v1alpha of the upstream standard, so we might expect subsequent versions of the service to emerge. Also, the Java/Scala API’s to enable this feature may still change in further versions of Pekko gRPC, and future versions of this feature may not work with services generated with older versions of Pekko gRPC.

There may be missing features and bugs in the current implementation. If you encounter any, you are welcome to share a reproducer in our issue tracker.

Server Reflection is a gRPC feature that allows ‘dynamic’ clients, such as command-line tools for debugging, to discover the protocol used by a gRPC server at run time. They can then use this metadata to implement things like completion and sending arbitrary commands.

This is achieved by providing a gRPC service that provides endpoints that can be used to query this information.

Providing

The generated service handler includes a convenience method to create a Pekko HTTP handler with your service together with Server Reflection:

Scala
sourceimport org.apache.pekko
import pekko.http.scaladsl._
import pekko.http.scaladsl.model._

import pekko.grpc.scaladsl.ServiceHandler
import pekko.grpc.scaladsl.ServerReflection

import example.myapp.helloworld.grpc._

// Create service handler with a fallback to a Server Reflection handler.
// `.withServerReflection` is a convenience method that contacts a partial
// function of the provided service with a reflection handler for that
// same service.
val greeter: HttpRequest => Future[HttpResponse] =
  GreeterServiceHandler.withServerReflection(new GreeterServiceImpl())

// Bind service handler servers to localhost:8080
val binding = Http().bindAndHandleAsync(
  greeter,
  interface = "127.0.0.1",
  port = 8080,
  connectionContext = HttpConnectionContext())
Java
sourceimport java.util.Arrays;

import org.apache.pekko.grpc.javadsl.ServiceHandler;
import org.apache.pekko.grpc.javadsl.ServerReflection;
import org.apache.pekko.http.javadsl.*;
import org.apache.pekko.http.javadsl.model.*;

import example.myapp.helloworld.grpc.*;

// Instantiate implementation
GreeterService impl = new GreeterServiceImpl();

// Bind service handler servers to localhost:8080
return Http.get(sys)
  .newServerAt("127.0.0.1", 8080)
  .bind(GreeterServiceHandlerFactory.createWithServerReflection(impl, sys));

For more advanced setups you will have to combine your partial handler with the ServerReflection handler explicitly.

For example, if you need to combine multiple services, or if you want to use an overload of the service factory methods. In these cases, the reflection service can be generated via ServerReflection and manually concatenated as described in the walkthrough section on serving multiple services:

Scala
source// Create service handlers
val greeterPartial: PartialFunction[HttpRequest, Future[HttpResponse]] =
  GreeterServiceHandler.partial(new GreeterServiceImpl(), "greeting-prefix")
val echoPartial: PartialFunction[HttpRequest, Future[HttpResponse]] =
  EchoServiceHandler.partial(new EchoServiceImpl())
// Create the reflection handler for multiple services
val reflection =
  ServerReflection.partial(List(GreeterService, EchoService))

// Concatenate the partial functions into a single handler
val handler =
  ServiceHandler.concatOrNotFound(
    greeterPartial,
    echoPartial,
    reflection),
Java
source// Create service handlers
Function<HttpRequest, CompletionStage<HttpResponse>> greetingPartial =
        GreeterServiceHandlerFactory.create(new GreeterServiceImpl(), "greeting-prefix", sys);
Function<HttpRequest, CompletionStage<HttpResponse>> echoPartial =
        EchoServiceHandlerFactory.create(new EchoServiceImpl(), sys);
// Create the reflection handler for multiple services
Function<HttpRequest, CompletionStage<HttpResponse>> reflectionPartial =
        ServerReflection.create(Arrays.asList(GreeterService.description, EchoService.description), sys);

// Concatenate the partial functions into a single handler
ServiceHandler.concatOrNotFound(
                        greetingPartial,
                        echoPartial,
                        reflectionPartial);

Consuming

The Server Reflection endpoint exposed above can be used for example to consume the service with grpc_cli:

$ ./bins/opt/grpc_cli call localhost:8080 helloworld.GreeterService.SayHello "name:\"foo\""
connecting to localhost:8080
Received initial metadata from server:
date : Wed, 08 Jan 2022 16:57:56 GMT
server : pekko-http/1.0.0
message: "Hello, foo"

Received trailing metadata from server:
date : Wed, 08 Jan 2020 16:57:56 GMT
Rpc succeeded with OK status