gRPC streams

Envoy Mobile provides support for gRPC as a thin interface built on top of its HTTP APIs.

gRPC APIs are designed to be used in conjunction with protobuf libraries such as SwiftProtobuf and Java Protobuf.

Envoy Mobile implements the gRPC protocol, accepting and returning serialized protobuf models.

Note

In the future, Envoy Mobile will provide much more comprehensive integration with gRPC and protobuf, utilizing annotations for enhanced functionality.

Quick start

Below are some quick examples for getting started with gRPC streams. See the individual class references in the later sections of this page for in-depth information on how each type is used.

Start and interact with a gRPC stream in Kotlin:

val headers = GRPCRequestHeadersBuilder(scheme = "https", authority = "envoyproxy.io", path = "/pb.api.v1.Foo/GetBar")
  .build()

val streamClient = AndroidStreamClientBuilder(application).build()
  GRPCClient(streamClient)
    .newGRPCStreamPrototype()
    .setOnResponseHeaders { headers, endStream ->
      Log.d("MainActivity", "Headers received: $headers, end stream: $endStream")
    }
    .setOnResponseMessage { messageData in
      Log.d("MainActivity", "Received gRPC message")
    }
    .setOnResponseTrailers { trailers in
      Log.d("MainActivity", "Trailers received: $trailers")
    }
    .setOnError { ... }
    .setOnCancel { ... }
    .start(Executors.newSingleThreadExecutor())
    .sendHeaders(headers, false)
    .sendMessage(...)
    ...
    .close()

Start and interact with a gRPC stream in Swift:

let headers = GRPCRequestHeadersBuilder(scheme: "https", authority: "envoyproxy.io", path: "/pb.api.v1.Foo/GetBar")
  .build()

let streamClient = try StreamClientBuilder().build()
GRPCClient(streamClient: streamClient)
  .newGRPCStreamPrototype()
  .setOnResponseHeaders { headers, endStream in
    print("Headers received: \(headers), end stream: \(endStream)")
  }
  .setOnResponseMessage { messageData in
    print("Received gRPC message")
  }
  .setOnResponseTrailers { trailers in
    print("Trailers received: \(trailers)")
  }
  .setOnError { ... }
  .setOnCancel { ... }
  .start(queue: .main)
  .sendHeaders(headers, endStream: false)
  .sendMessage(...)
  ...
  .close()

GRPCClient

The GRPCClient type provides the ability to start gRPC streams, and is backed by Envoy Mobile’s StreamClient interface (typically instantiated using a StreamClientBuilder).

To create a GRPCClient, simply create a stream client and pass it to the initializer:

grpcClient = GRPCClient(streamClient)

This client can then be used with the types outlined below for starting gRPC streams.

GRPCRequestHeaders

Envoy Mobile provides a GRPCRequestHeadersBuilder which acts very similarly to the RequestHeadersBuilder type. Upon calling build(), it returns a GRPCRequestHeaders instance - a subclass of RequestHeaders configured specifically for gRPC streams.

To start a gRPC stream, first create an instance of GRPCRequestHeaders using the GRPCRequestHeadersBuilder.

Kotlin:

val headers = GRPCRequestHeadersBuilder("https", "envoyproxy.io", "/pb.api.v1.Foo/GetBar")
  .add("x-foo", "123")
  ...
  .build()

Swift:

let headers = GRPCRequestHeadersBuilder(scheme: "https", authority: "envoyproxy.io", path: "/pb.api.v1.Foo/GetBar")
  .add(name: "x-foo", value: "123")
  ...
  .build()

GRPCStreamPrototype

A GRPCStreamPrototype is used to configure gRPC streams prior to starting them by assigning callbacks to be invoked when response data is received on the stream.

Typically, consumers should listen to onMessage and use a protobuf library to deserialize the complete protobuf message data.

To create a GRPCStreamPrototype, use an instance of GRPCClient.

Kotlin:

val prototype = grpcClient
  .newGRPCStreamPrototype()
  .setOnResponseHeaders { headers, endStream ->
    Log.d("MainActivity", "Headers received: $headers, end stream: $endStream")
  }
  .setOnResponseMessage { messageData ->
    Log.d("MainActivity", "Received gRPC message")
  }
  .setOnResponseTrailers { trailers ->
    Log.d("MainActivity", "Trailers received: $trailers")
  }
  .setOnError { ... }
  .setOnCancel { ... }

Swift:

let prototype = grpcClient
  .newGRPCStreamPrototype()
  .setOnResponseHeaders { headers, endStream in
    print("Headers received: \(headers), end stream: \(endStream)")
  }
  .setOnResponseMessage { messageData in
    print("Received gRPC message")
  }
  .setOnResponseTrailers { trailers in
    print("Trailers received: \(trailers)")
  }
  .setOnError { ... }
  .setOnCancel { ... }

GRPCStream

gRPC streams are started by calling start() on a GRPCStreamPrototype.

Doing so returns a GRPCStream which allows the sender to interact with the stream.

The sendMessage function should be invoked with the serialized data from a protobuf message. The emitter will then transform the provided data into the gRPC wire format and send it over the stream.

Kotlin:

val streamClient = AndroidStreamClientBuilder()
  ...
  .build()
val grpcClient = GRPCClient(streamClient)

val requestHeaders = GRPCRequestHeadersBuilder()
  ...
  .build()
val prototype = grpcClient
  .newGRPCStreamPrototype()
  ...
val stream = prototype
  .start(Executors.newSingleThreadExecutor())
  .sendHeaders(...)
  .sendMessage(...)

...
stream.close(...)

Swift:

let streamClient = StreamClientBuilder()
  ...
  .build()
let grpcClient = GRPCClient(streamClient: streamClient)

let requestHeaders = GRPCRequestHeadersBuilder()
  ...
  .build()
let prototype = grpcClient
  .newGRPCStreamPrototype()
  ...
let stream = prototype
  .start(queue: .main)
  .sendHeaders(...)
  .sendMessage(...)

...
stream.close(...)