Emulator support

ClientBuilder dedicated support

Some APIs (such as Datastore and PubSub) provide emulators in the Cloud SDK. Client libraries in some other languages automatically use emulators if specific environment variables are set, but the Google Cloud Libraries for .NET deliberately do not do this, to avoid accidentally using an emulator when production was expected or vice versa.

The packages for the following APIs support emulators directly in the way described below:

  • PubSub
  • Firestore
  • Bigtable
  • Spanner
  • Datastore
  • Storage

In these packages, the client builder type has an EmulatorDetection property which can be set to one of the following values:

  • None (the default): Ignores the presence or absence of emulator configuration.
  • ProductionOnly: Always connects to the production servers, but throws an exception if an emulator configuration is detected that would suggest connecting to an emulator is expected.
  • EmulatorOnly: Always connect to the emulator, throwing an exception if no emulator configuration is detected.
  • EmulatorOrProduction: Connect to the emulator if an emulator configuration is detected, or production otherwise. This is a convenient option, but risks damage to production databases or running up unexpected bills if tests are accidentally run in production due to the emulator configuration being absent unexpectedly. (Using separate projects for production and testing is a best practice for preventing the first issue, but may be unrealistic for small or hobby projects.)

Here emulator configuration presence is usually interpreted as "appropriate environment variables being set", but it is possible that in the future there will be other conventions for configuring emulators.

For example, the following code creates a PubSub SubscriberServiceApiClient that will connect to the PubSub emulator if the PUBSUB_EMULATOR_HOST environment variable is set, but will otherwise connect to the production environment.

SubscriberServiceApiClient client = new SubscriberServiceApiClientBuilder
{
    EmulatorDetection = EmulatorDetection.EmulatorOrProduction
}.Build();

Note that the PubSub SubscriberClient and PublisherClient classes don't have builders. Instead, they have nested ClientCreationSettings with emulator detection. The following code connects to the PubSub emulator for a SubscriberClient:

SubscriptionName subscription = SubscriptionName.FromProjectSubscription("projectId", "subscriptionId");
SubscriberClient client = new SubscriberClientBuilder
{
    SubscriptionName = subscription,
    EmulatorDetection = EmulatorDetection.EmulatorOrProduction
}.Build();

Manually connecting to an emulator

If you need to connect to an emulator directly (for example because it is not yet supported in the library for the API you're using), simply use the appropriate client builder, set the endpoint to the host and port the emulator is listening on, and set the credentials to to ChannelCredentials.Insecure.

Example for PubSub (although the techniques above are preferred):

// For example, "localhost:8615"
string emulatorHostAndPort = Environment.GetEnvironmentVariable("PUBSUB_EMULATOR_HOST");

PublisherServiceApiClient client = new PublisherServiceApiClientBuilder
{
    Endpoint = emulatorHostAndPort,
    ChannelCredentials = ChannelCredentials.Insecure
}.Build();

Grpc.Net.Client and .NET Core 3.1

As of GAX v4, the default gRPC implementation (where available) is Grpc.Net.Client. (See the transport selection documentation for more details.) By default, .NET Core 3.1 does not support unencrypted HTTP/2 connections - whereas this is required for emulator connections.

Without any additional code, .NET Core 3.1 connections to the emulator will fail with a variety of error messages, including:

System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.

and

Grpc.Core.RpcException : Status(StatusCode="Internal", Detail="Bad gRPC response. Response protocol downgraded to HTTP/1.1.")

This can easily be fixed using an AppContext switch:

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

This code is deliberately not included in the client libraries, as it is an application-wide switch that you should consider carefully before enabling. In most cases we expect that it's safe and appropriate when testing an application against an emulator, but that's not a decision that the client libraries can reasonably take for themselves.

This switch is not required when running .NET 6 or above.