gRPC virtual service architecture

A developer or tester uses a web browser to access the console. The console manages the virtual service. The system under test (application under test) connects directly to the virtual service on different ports.
gRPC architecture

Configuration

Proto files

Traffic Parrot needs to know about the schema of your messages.

One way to do this is to provide your protocol buffer files. We currently support the proto3 specification.

To do this, simply copy your .proto files into the trafficparrot-x.y.z/proto directory and Traffic Parrot will use these definitions during record/replay.

Server reflection

Traffic Parrot can discover the schema of your messages during recording if you have server reflection enabled in your gRPC server.

Discovered schemas are saved in the trafficparrot-x.y.z/proto-bin directory and are used during replay.

To enable server reflection please follow the instructions for your gRPC server language:

SSL/TLS

Typically, gRPC is authenticated using SSL/TLS

Traffic Parrot has the following properties that can be used to configure SSL/TLS:
  • trafficparrot.virtualservice.grpc.tls.port
    The port number to use when recording/replaying TLS traffic (default 5551)
  • trafficparrot.virtualservice.grpc.requireClientAuthentication
    Set to true when client authentication is required and false otherwise
  • trafficparrot.virtualservice.grpc.serverTrustPemUrl
    X.509 certificate collection file in PEM format that will be trusted by the virtual gRPC server
  • trafficparrot.virtualservice.grpc.serverPemUrl
    X.509 certificate chain file in PEM format that will be the public key of the virtual gRPC server
  • trafficparrot.virtualservice.grpc.serverKeyUrl
    PKCS#8 private key file in PEM format that will be the private key of the virtual gRPC server
  • trafficparrot.virtualservice.grpc.clientTrustPemUrl
    X.509 certificate collection file in PEM format that will be trusted by the virtual gRPC client
  • trafficparrot.virtualservice.grpc.clientOverrideAuthority
    Overrides the authority used with TLS and HTTP virtual hosting. It does not change what host is actually connected to. Is commonly in the form host:port
If client authentication is required use the following additional properties:
  • trafficparrot.virtualservice.grpc.clientPemUrl
    X.509 certificate chain file in PEM format that will be the public key of the virtual gRPC client
  • trafficparrot.virtualservice.grpc.clientKeyUrl
    PKCS#8 private key file in PEM format that will be the private key of the virtual gRPC client

The URL property values can be set to classpath entries e.g. classpath:certificates/grpc/server.pem

By default Traffic Parrot has properties that correspond to the certificates found in the trafficparrot-x.y.z/certificates/grpc directory.

Alternatively, you can specify file URL entries e.g. file:///path/to/server.pem

The property trafficparrot.virtualservice.grpc.non.tls.port specifies a port that can be used if SSL/TLS is not required (default 5552).

Performance

The following properties have a significant impact on gRPC performance during replay:

Property Description
trafficparrot.virtualservice.grpc.server.replay.proto.cache.milliseconds Proto files can be cached in memory during replay to improve performance. The default value of 0 means do not cache at all.
trafficparrot.virtualservice.grpc.server.replay.proto.cache.populate.on.startup When true the proto file cache will be populated immediately on startup.
trafficparrot.virtualservice.mapping.cache.milliseconds Mapping files can be cached in memory during replay to improve performance. The default value of 0 means do not cache at all.
trafficparrot.virtualservice.mapping.cache.populate.on.startup When true the mapping file cache will be populated immediately on startup.
trafficparrot.virtualservice.grpc.server.boss.threads The number of boss threads the server uses to process gRPC network requests. Typically best set to either DOUBLE_NUMBER_OF_PROCESSORS or NUMBER_OF_PROCESSORS based on the number of available processors the system has.
trafficparrot.virtualservice.grpc.server.worker.threads The number of worker threads the server uses to process gRPC network requests. Typically best set to either DOUBLE_NUMBER_OF_PROCESSORS or NUMBER_OF_PROCESSORS based on the number of available processors the system has.
trafficparrot.virtualservice.grpc.server.receiveThreads The number of background threads used to process gRPC requests and find mock responses once offloaded from the worker and boss threads. Typically best set to either DOUBLE_NUMBER_OF_PROCESSORS or NUMBER_OF_PROCESSORS based on the number of available processors the system has.
trafficparrot.virtualservice.grpc.server.sendThreads The number of background threads used to send the gRPC mock responses determined by the receive threads. Typically best set to either DOUBLE_NUMBER_OF_PROCESSORS or NUMBER_OF_PROCESSORS based on the number of available processors the system has.
trafficparrot.virtualservice.grpc.server.replay.maxMessagesWaitingToBeSent When the rate at which request messages are received exceeds the rate at which response messages can be produced (e.g. if there is complex response behaviour that takes time) then response messages will queue up waiting to be sent. This property controls the maximum number of messages in that queue and therefore how much memory is required. When the queue is full, requests will block waiting for the responses to catch up.

Recording and replaying gRPC

Introduction

During recording, incoming and outgoing messages are matched up to provide mappings. Then upon playback receipt of a matching incoming message will trigger generation of an outgoing message.

This diagram shows how two production systems connect:
gRPC unary communication

One system sends a gRPC request message to another system, which consumes the messages and returns a gRPC response message to the first system. If our goal is to test the system-under-test in isolation, we must record these interactions in order to replay them.

Recording and replaying gRPC messages using provided proto files

As you can see in the diagram below, the virtual service acts as a proxy between the gRPC client and gRPC server. The provided proto files are used to determine the message schema of the request and response messages for the requested gRPC method. Mappings are saved representing request/response pairs that have been recorded.

Recording gRPC messages using provided proto files
Steps to perform a recording using provided proto files
  1. Start Traffic Parrot
  2. Configure Traffic Parrot with your proto files
  3. Go to the gRPC record page
  4. Enter the host and port of the gRPC server to record
  5. Click "Start recording"
  6. Reconfigure your system under test to connect to the virtual service gRPC server (by default this is localhost:5552)
  7. Execute a test case to allow Traffic Parrot to record requests and responses. They will appear on the Messages and Mappings lists on the web page as shown on Mappings and messages lists during recording image.
  8. Click "Stop recording"

As the system under test generates messages and receives responses they are paired into mappings and listed in the 'Mappings' table.

Mappings list during a gRPC recording

On replay, the provided proto files are again used to determine the schema of the requested gRPC method. The request is matched against the mappings to find the corresponding response message.

Replaying gRPC messages using provided proto files
Steps to perform a replay using provided proto files
  1. Start Traffic Parrot
  2. Configure Traffic Parrot with your proto files
  3. Reconfigure your system under test to connect to the virtual service gRPC server (by default this is localhost:5552)
  4. Traffic Parrot will replay recorded mappings when it is not in record mode

Recording and replaying gRPC messages using gRPC server reflection

As you can see in the diagram below, the virtual service acts as a proxy between the gRPC client and gRPC server. Server reflection is used to pull the proto files definitions from the gRPC server for the requested gRPC method. These proto files are saved for later use during replay. Mappings are saved representing request/response pairs that have been recorded.

Recording gRPC messages using gRPC server reflection
Steps to perform a recording using gRPC server reflection
  1. Start Traffic Parrot
  2. Go to the gRPC record page
  3. Enter the host and port of the gRPC server to record
  4. Click "Start recording"
  5. Reconfigure your system under test to connect to the virtual service gRPC server (by default this is localhost:5552)
  6. Execute a test case to allow Traffic Parrot to record requests and responses. They will appear on the Messages and Mappings lists on the web page as shown on Mappings and messages lists during recording image.
  7. Click "Stop recording"

As the system under test generates messages and receives responses they are paired into mappings and listed in the 'Mappings' table.

Mappings list during a gRPC recording

On replay, the recored proto files used to determine the schema of the requested gRPC method. The request is matched against the mappings to find the corresponding response message.

Replaying gRPC messages using recorded proto files
Steps to perform a replay using provided proto files
  1. Start Traffic Parrot
  2. Reconfigure your system under test to connect to the virtual service gRPC server (by default this is localhost:5552)
  3. Traffic Parrot will replay recorded mappings when it is not in record mode

Existing mappings

If there are existing mappings present before a recording starts, these mappings will not be used to return responses instead of recording a new response.

For example, if there is a mapping for service helloworld.Greeter/SayHello then any traffic to helloworld.Greeter/SayHello will record a new response and the existing mapping will not be considered.

Please contact support@trafficparrot.com if you have a requirement for a mixed recording mode similar to HTTP recording where existing mappings during recording are used to provide responses instead of recording a new response.

Multiple servers

You can record multiple gRPC servers at the same time using the following syntax:

com.example.Service/method -> host1:port1
com.example.packageA.* -> host2:port2
com.example.packageB.* -> host3:port3

The incoming request service and method name are matched top down against the list of server pattern choices. The patterns are regular expressions, where .* can be used as a wildcard.

Add/Edit gRPC mappings

Usage

gRPC Protocol Buffers as JSON

Traffic Parrot uses JSON to represent gRPC Protocol Buffers message payloads. It stores the gRPC Protocol Buffers request and response message payloads as JSON.

This means, the request and response body can be edited as if they were a JSON body. You are free to change the JSON as you wish, so long as the edits are compatible with the underlying proto message schema that the JSON represents.

Editing a mapping

First, go to gRPC in the top navigation bar and click Add/Edit.

Add gRPC mapping screenshot

Fill in the Request/Response fields and click Save to configure a mapping.

After saving the mapping, it will appear in the list of mappings.

gRPC mapping list

Clicking the edit button will allow you to edit an existing mapping.

Edit gRPC mapping screenshot

Edit gRPC status exception screenshot

Supported features
  • Edit status code and message for StatusRuntimeException responses
  • Edit exception metadata, using either simple key: value pairs or a details object
  • Edit response message values
  • Edit response message structure (e.g. delete optional fields, add items to list, add nested fields)
  • Use helpers in the response body
  • Use parts of the request message in the response message by extracting with JsonPath
  • Match request messages using the request body matchers (e.g. JsonPath, contains, regexp) on the JSON representation of the message
  • Match request messages by key: value pairs in the request metadata
  • Use priority to set up a preference order for matching mappings
Currently, editing non-exception metadata is not yet possible. Please contact support@trafficparrot.com to be notified of when this functionality is ready.
Using skeletons

When adding a mapping manually, you can also select from the message skeleton dropdown which will populate the Request/Response fields with a message skeleton for that message type. To configure skeletons see how to configure skeletons.

If you use google.protobuf.Any type in Traffic Parrot skeletons, it will be interpreted as a StringValue. For example:
message TestMessage {
    google.protobuf.Any any_field = 1;
}
will generate a skeleton with body JSON representation:
{
  "anyField": {
    "@type": "type.googleapis.com/google.protobuf.StringValue",
    "value": ""
  }
}

Exception details object

gRPC allows for returning one or more custom detail objects in exception responses. Traffic Parrot currently supports specifying a single details object:

Edit gRPC exception details object screenshot

You must specify the fully qualified type e.g. com.example.Details as well as the JSON representation of the object.

There are two exception details formats supported:
  • Status Key - Using the google.rpc.Status approach (most modern gRPC error handling code uses this approach)
  • Proto Key - Using the keyForProto approach (legacy approach for most customers)

Priority

The request priority can be set in order to set up a preference order for matching mappings.

The highest priority value is 1. If two or more mappings both match a request, the mapping with the higher priority will be used to provide the response. The default priority is 5.

This can be useful, if you want a "catch-all" mapping that returns a general response for most requests and specific mappings on top that return more specific responses.

Request metadata

Request metadata can be specified as key: value pairs. This means that Traffic Parrot will only match requests that contain the specified metadata values.

You can use the property trafficparrot.virtualservice.grpc.exclude.request.headers.from.matching to exclude request headers from matching. For example typical headers such as content-type,user-agent,grpc-accept-encoding are not typically useful to match on.

Configuring message skeletons

To populate the list of message skeletons, you need to configure Traffic Parrot with your proto files.

Then you can select the name of a gRPC method from the list and a sample request and response value will be generated, containing the default values for the message.

gRPC skeleton

Mapping JSON

Traffic Parrot saves gRPC mappings in JSON files in the grpc-mappings directory, which can be version controlled in e.g. Git or SVN and edited directly on the file system in your IDE or text editor.

Here is a representative sample of the possible JSON schema that can be used:
{
  "id" : "ef7b503d-c4b0-40d4-89c6-2a0afa83f940",
  "name" : "example.json",
  "request" : {
    "urlPath" : "example.ExampleService/ExampleOperation",
    "method" : "ANY",
    "headers" : {
      "Protocol" : {
        "equalTo" : "GRPC"
      }
    },
    "bodyPatterns" : [ {
      "equalToJson" : {
        "RequestName" : ""
      }
    } ]
  },
  "response" : {
    "status" : 0,
    "jsonBody" : {
      "Success" : {
        "StringValue" : ""
      }
    }
  },
  "uuid" : "ef7b503d-c4b0-40d4-89c6-2a0afa83f940"
}
Example of response body with complex template string value, which are best to edit via the UI or move to a template file:
{
  "response" : {
    "status" : 0,
    "body" : "{\r\n    \"type\": \"response\",\r\n    \"items\": [\r\n        {{#each (jsonPathList request.body '$.items') }}\r\n        { \"id\": {{ jsonPath this '$.id' }}, \"status\": \"OK\" }{{#unless @last}},{{/unless}}\r\n        {{/each}}\r\n    ]\r\n}"
  }
}
Example of response body with reference to a template file:
{
  "response" : {
    "status" : 0,
    "body" : "{{>data/responses/example}}",
  }
}
Example of request matcher with string value:
{
  "request" : {
    "bodyPatterns" : [ {
      "matchesJsonPath" : "$[?(@.id == 1 && 2 in @.items[*].id)]"
    } ]
  }
}
Example of simple error code response with error message:
{
  "response" : {
    "status" : 13,
    "body" : "Last name is required"
  },
}
Example of error code response with error status details object using status key convention:
{
  "response" : {
    "status" : 2,
    "headers" : {
      "grpc-status-details-bin" : "{\n  \"error\" : \"some error information\",\n  \"@type\" : \"type.googleapis.com/com.example.Details\"\n}"
    }
  }
}
Example of error code response with error status details object using proto key convention:
{
  "response" : {
    "status" : 2,
    "headers" : {
      "com.example.Details-bin" : "{\r\n  \"error\": \"some error information\"\r\n}"
    }
  }
}
Fields of interest:
  • status allows specifying the response status code
  • jsonBody allows specifying the response body as a JSON object
  • equalToJson allows specifying the expected value as a JSON object, which is also possible for matchesJson
  • matchesJsonPath allows specifying the expected value as a string, which is also possible for contains, doesNotContain, matches (matches regex) and doesNotMatch (does not match regex)

gRPC request matchers

When Traffic Parrot receives a request, it will try to simulate the system it is replacing by sending back a response to the client that sent the request. To decide which response to send, it will go through all the request to response mappings it has available to find the response to be returned. For more details how request matching works, see Request matching.

There are several matchers available to match gRPC requests, depending on the attribute.

gRPC method
Will match only the selected gRPC method name. For example:
helloworld.Greeter/SayHello
Request metadata
A new line separated list of request metadata keys and values, for example:
Key1: Value1
Key2: Value2
Request body (payload) matchers

Traffic Parrot uses JSON to represent gRPC Protocol Buffers message payloads.

For example, if you define a service:
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}
then, if you choose "equal to JSON representation" as the request body matcher and populate the request body with:
{
  "greeting": "Hello World!"
}
then Traffic Parrot will match this request any time it receives a gRPC request message with greeting "Hello World!".

You can use not only the "equal to JSON representation" matcher but any of the ones described below.

Matcher name Matcher Id Description
any any Any gRPC request payload will match.
equal to JSON representation equalToJson Check that the received request payload has all attributes equal to the ones specified in the mapping as JSON.
matches JSON representation matchesJson

Check that the received request payload matches (allowing for special wildcard tokens) attributes specified in the mapping as JSON.

Tokens allowed:
  • {{ anyValue }} - matches any value
  • {{ anyNumber }} - matches any whole or decimal number
  • {{ anyElements }} - matches any number of sub-elements (child-nodes)
For example a "matches JSON" request body matcher:
{
  "name": "{{ anyValue }}",
  "lastName": "{{ anyValue }}",
  "age": "{{ anyNumber }}",
  "children": "{{ anyElements }}"
}
will match a request payload JSON representation:
{
  "name": "Bob",
  "lastName": "Smith",
  "age": 37,
  "children": [{"name": "sam"}, {"name": "mary"}]
}
matches JSONPath matchesJsonPath Check that the received request payload, when converted by Traffic Parrot to JSON, matches JSONPath specified in the mapping. For example, given a proto message:
message UserRequest {
  string name = 1;
  string lastName = 2;
  int32 age = 3;
  repeated Child children = 4;
}
when Traffic Parrot receives a "UserRequest" it will convert it to JSON for matching purposes. A sample "UserRequest" message, when received by Traffic Parrot and converted to JSON would look like this:
{
  "name": "John",
  "lastName": "Smith",
  "age": 10,
  "children": [
    {
      "name": "Hello"
    }
  ]
}
And then, a sample "matches JSONPath" matcher to match the message above could look like this:
$[?(@.name =~ /J[a-z]+/ && @.lastName == 'Smith')]
For more examples see the request matching documentation.
does not contain doesNotContain Check that the received request payload, when converted to JSON, contains the sequence of characters specified in the mapping
contains contains Check that the received request payload, when converted to JSON, does not contain the sequence of characters specified in the mapping
matches regex matches Check that the received request payload, when converted to JSON, matches the regexp specified in the mapping. For example, given a proto message:
message UserRequest {
  string name = 1;
  string lastName = 2;
  int32 age = 3;
  repeated Child children = 4;
}
when Traffic Parrot receives a "UserRequest" it will convert it to JSON for matching purposes. A sample "UserRequest" message, when received by Traffic Parrot and converted to JSON would look like this:
{
  "name": "John",
  "lastName": "Smith",
  "age": 10,
  "children": [
    {
      "name": "Hello"
    }
  ]
}
And then, a sample "matches regex" matcher to match the message above could look like this:
.*"name": "J[a-z]+".*"lastName": "Smith".*
does not match regexp doesNotMatch Check that the received request payload, when converted to JSON, does not match the regexp specified in the mapping. See "matches regexp" above for more details.

gRPC streaming modes

Introduction

Traffic Parrot currently supports the following gRPC streaming modes:
  • Unary RPC
  • Server streaming RPC
  • Bidirectional streaming RPC
Please contact support@trafficparrot.com to be notified when other streaming modes are supported.

Unary RPC

In this mode:
  • There is one request and one response per RPC call.
  • Traffic Parrot represents this as a single request/response mapping.

Server streaming RPC

In this mode:
  • There is one request and multiple responses per RPC call.
  • Traffic Parrot represents this as multiple request/response mappings.
  • Each mapping has a MessageNumber in the mapping file that represents the order of the responses.
  • Each mapping has a CallId in the mapping file that represents which RPC call the mapping belongs to.
  • The request matcher is duplicated for each mapping.
  • Example server streaming configuration: grpc-server-streaming-example.zip

Bidirectional streaming RPC

In this mode:
  • There is one or more request and one or more responses per RPC call.
  • Traffic Parrot represents this as multiple request/response mappings.
  • Each mapping has a MessageNumber in the mapping file that represents the order of the responses.
  • Each mapping has a CallId in the mapping file that represents which RPC call the mapping belongs to.
  • The request matcher is duplicated for each mapping.
  • Each request is matched against the mappings and produces one or more responses.
  • Example bidirectional streaming configuration: grpc-bidirectional-streaming-example.zip

Proof of concept with on-premises installation at a large enterprise

See proof of concept in the user guide.

Dynamic responses and custom extensions

Old version warning!

This documentation is for an old version of Traffic Parrot. There is a more recent Traffic Parrot version available for download at trafficparrot.com

Browse documentation for recent version