How we use gRPC to build a client/server system in Go
Tags: go, protobuf, grpc
Published: 2017-10-10 09:50:59

---
title: How we use gRPC to build a client/server system in Go
published: true
description: This post is a technical presentation on how we use gRPC (and Protobuf) to build a robust client/server system.
tags: golang, protobuf, grpc
---

*This was originally posted on [Medium](https://medium.com/pantomath/how-we-use-grpc-to-build-a-client-server-system-in-go-dd20045fa1c2)*

# How we use gRPC to build a client/server system in Go

![](https://cdn-images-1.medium.com/max/2000/1*QIjD685SfIfFV-xXtdIofg.jpeg)
*Photo by [Igor Ovsyannykov](https://unsplash.com/photos/6uWHZ74S5Rk?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

This post is a technical presentation on how we use [gRPC](https://grpc.io/) (and [Protobuf](https://developers.google.com/protocol-buffers/)) to build a robust client/server system.

I won’t go into the details of why we chose gRPC as the main communication protocol between our client and server, a lot of great posts already cover the subject (like [this one](https://grpc.io/blog/vendastagrpc), [this one](https://stackoverflow.com/questions/43682366/how-is-grpc-different-from-rest) or [this one](https://thenewstack.io/grpc-lean-mean-communication-protocol-microservices/)).
But just to get the big picture: we are building a client-server system, using [Go](https://golang.org/), that needs to be fast, reliable, and that scales (thus we chose gRPC). We wanted the communication between the client and the server to be as small as possible, as secure as possible and fully compliant with both ends (thus we chose [Protobuf](https://developers.google.com/protocol-buffers/)).

We also wanted to expose another interface on the server side, just in case we add a client that has no compatible gRPC implementation: a traditional REST interface. And we wanted that at (almost) no cost.

## Context

We will start building a very simple client/server system, in Go, that will just exchange dummy messages. Once both communicate and understand each other, we’ll add extra features, such as TLS support, authentication, and a REST API.

The rest of this articles assumes you understand basic Go programming. It also assumes that you have `protobuf` package installed, and the `protoc` command available (once again, many posts cover that topic, and there’s the [official documentation](https://developers.google.com/protocol-buffers/docs/proto3)).<br>
You will also need to install Go dependencies, such as the [go implementation for protobuf](http://github.com/golang/protobuf), and [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway).

All the code shown in this post is available at [https://gitlab.com/pantomath-io/demo-grpc](https://gitlab.com/pantomath-io/demo-grpc). So feel free to get the repository, and use the tags to navigate in it. The repository should be placed in the `src` folder of your `$GOPATH`:

    $ go get -v -d gitlab.com/pantomath-io/demo-grpc
    $ cd $GOPATH/src/gitlab.com/pantomath-io/demo-grpc

## Defining the protocol

*git tag:* [init-protobuf-definition](https://gitlab.com/pantomath-io/demo-grpc/tree/init-protobuf-definiton)

![](https://cdn-images-1.medium.com/max/1600/1*N8g6AM5x4n64RCXYUjwtYQ.jpeg)
*Photo by [Mark Rasmuson](https://unsplash.com/photos/yri82tuk2TQ?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

First of all, you need to define the protocol, i.e. to define what can be said between client and server, and how. This is where Protobuf comes into play. It allows you to define two things: Services and Messages. A `service` is a collection of actions the server can perform at the client’s request, a `message` is the content of this request. To simplify, you can say that `service` defines **actions,** while `message` defines **objects**.

Write the following in `api/api.proto`:

```protobuf
syntax = "proto3";
package api;

message PingMessage {
  string greeting = 1;
}

service Ping {
  rpc SayHello(PingMessage) returns (PingMessage) {}
}
```

So, you defined 2 things: a `service` called `Ping` that exposes a function called `SayHello` with an incoming `PingMessage` and returns a `PingMessage` ; and a `message` called `PingMessage` that consists in a single field called `greeting` which is a `string`.<br> You also specified that you are using the `proto3` syntax, as opposed to `proto2 `(see [documentation](https://developers.google.com/protocol-buffers/docs/proto3)).

This file is not usable like this: it needs to get compiled. Compiling the proto file means generating code for your chosen language, that your application will actually call.<br> In a shell, `cd` to the root directory of your project, and run the following command:

```bash
$ protoc -I api/ \
    -I${GOPATH}/src \
    --go_out=plugins=grpc:api \
    api/api.proto
```

This command generates the file `api/api.pb.go`, a Go source file that implements the gRPC code your application will use. You can look at it, but you shouldn’t change it (as it will be overwritten every time you run `protoc`).

You also need to define the function called by the `service Ping`, so create a file named `api/handler.go`:

```go
package api

import (
  "log"

  "golang.org/x/net/context"
)

// Server represents the gRPC server
type Server struct {
}

// SayHello generates response to a Ping request
func (s *Server) SayHello(ctx context.Context, in *PingMessage) (*PingMessage, error) {
  log.Printf("Receive message %s", in.Greeting)
  return &PingMessage{Greeting: "bar"}, nil
}
```

* the `Server` `struct` is just an abstraction of the server. It allows to “attach some resources to your server, making them available during the RPC calls;
* the `SayHello` function is the one defined in the Protobuf file, as the `rpc` call for the `Ping` `service`. If you don’t define it, you won’t be able to create the gRPC server;
* `SayHello` takes a `PingMessage` as parameter, and returns a `PingMessage`. The `PingMessage` `struct` is defined in the `api.pb.go` file auto-generated from the `api.proto` definition. The function also has a `Context` parameter (see further presentation in the official [blog post](https://blog.golang.org/context)). You’ll see later what use you can do of the `Context`. On the other side, it also returns an `error`, in case something bad happens.

## Creating the simplest server

*git tag:* [init-server](https://gitlab.com/pantomath-io/demo-grpc/tags/init-server)

![](https://cdn-images-1.medium.com/max/1600/1*Gy5Z3hBbB3r1GUkB75D_eg.jpeg)
*Photo by [Nathan Dumlao](https://unsplash.com/photos/3CS0Ne4wBPw?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

Now you have a protocol in place, you can create a simple server that implements the `service` and understands the `message`. Take your favorite editor and create the file `server/main.go`:

```go
package main

import (
  "fmt"
  "log"
  "net"

  "gitlab.com/pantomath-io/demo-grpc/api"
  "google.golang.org/grpc"
)

// main start a gRPC server and waits for connection
func main() {
  // create a listener on TCP port 7777
  lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 7777))
  if err != nil {
    log.Fatalf("failed to listen: %v", err)
  }

  // create a server instance
  s := api.Server{}

  // create a gRPC server object
  grpcServer := grpc.NewServer()

  // attach the Ping service to the server
  api.RegisterPingServer(grpcServer, &s)

  // start the server
  if err := grpcServer.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %s", err)
  }
}
```

Let me break down to code to make it clearer:

* note that you import the `api` package, so that the Protobuf `service` handlers and the `Server` `struct` are available;
* the `main` function starts by creating a TCP listener on the port you want to bind your gRPC server to;
* then the rest is pretty straight forward: you create an instance of your `Server`, create an instance of a gRPC server, register the `service`, and start the gRPC server.

You can compile your code to get a server binary:

```bash
$ go build -i -v -o bin/server gitlab.com/pantomath-io/demo-grpc/server
```

## Creating the simplest client

*git tag:* [init-client](https://gitlab.com/pantomath-io/demo-grpc/tree/init-client)

![](https://cdn-images-1.medium.com/max/1600/1*c6G6qANKUoyZn3awfRii1g.jpeg)
*Photo by [Clem Onojeghuo](https://unsplash.com/photos/RLJnH4Mt9A0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

The client also imports the `api` package, so that the `message` and the `service` are available. So create the file `client/main.go`:

```go
package main

import (
  "log"

  "gitlab.com/pantomath-io/demo-grpc/api"
  "golang.org/x/net/context"
  "google.golang.org/grpc"
)

func main() {
  var conn *grpc.ClientConn

  conn, err := grpc.Dial(":7777", grpc.WithInsecure())
  if err != nil {
    log.Fatalf("did not connect: %s", err)
  }
  defer conn.Close()

  c := api.NewPingClient(conn)

  response, err := c.SayHello(context.Background(), &api.PingMessage{Greeting: "foo"})
  if err != nil {
    log.Fatalf("Error when calling SayHello: %s", err)
  }
  log.Printf("Response from server: %s", response.Greeting)
}
```

Once again, the break down is pretty straight forward:

* the `main` function instantiates a client connection, on the TCP port the server is bound to;
* note the `defer` call to properly close the connection when the function returns;
* the `c` variable is a client for the the `Ping` service, that calls the `SayHello` function, passing a `PingMessage` to it.

You can compile your code to get a client binary:

```bash
$ go build -i -v -o bin/client gitlab.com/pantomath-io/demo-grpc/client
```

## Make them talk

You’ve just built a client and a server, so fire them in two terminals to test them:

```bash
$ bin/server
2006/01/02 15:04:05 Receive message foo
```
```bash
$ bin/client
2006/01/02 15:04:05 Response from server: bar
```

## Tool to ease your life

*git tag:* [init-makefile](https://gitlab.com/pantomath-io/demo-grpc/blob/init-makefile/Makefile)

Now the API, the client and the server are working, you may prefer to have a Makefile to compile the code, clean your folder, manage dependencies, etc.

So create this `Makefile` at the root of the project folder. Explaining this file is beyond the scope of this post, and it mostly uses compile command you already spawned previously.

To use the `Makefile` , try calling the following:

```bash
$ make help
api                            Auto-generate grpc go sources
build_client                   Build the binary file for client
build_server                   Build the binary file for server
clean                          Remove previous builds
dep                            Get the dependencies
help                           Display this help screen
```


## Secure the communication

*git tag:* [add-ssl](https://gitlab.com/pantomath-io/demo-grpc/tree/add-ssl)

![](https://cdn-images-1.medium.com/max/1600/1*k_WFayrFR6ciCOLn8fWI7g.jpeg)
*Photo by [Nathaniel Tetteh](https://unsplash.com/photos/-4AFJ5yujcE?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

The client and the servers talk to each other, over [HTTP/2](https://http2.github.io/) (transport layer on gRPC). The messages are binary data(thanks to Protobuf), but the communication is in plaintext. Fortunately, gRPC has SSL/TLS integration, that can be used to authenticate the server (from the client’s perspective), and to encrypt message exchanges.

You don’t need to change anything to the protocol: it remains the same. The changes take place in the gRPC object creation, on both client and server side. Note that if you change only one side, the connection won’t work.

Before you change anything in the code, you need to create a [self-signed SSL certificate](https://en.wikipedia.org/wiki/Self-signed_certificate). The purpose of this post is not to explain how to do that, but the [official OpenSSL documentation](https://www.openssl.org/docs/manmaster/man1/) (`genrsa`, `req`, `x509`) can answer your question about it (DigitalOcean also has a [nice and complete tutorial](https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs) about it). Meanwhile, you can just use the files provided in the `cert` folder. The following commands have been used to generate the files:

```bash
$ openssl genrsa -out cert/server.key 2048
$ openssl req -new -x509 -sha256 -key cert/server.key -out cert/server.crt -days 3650
$ openssl req -new -sha256 -key cert/server.key -out cert/server.csr
$ openssl x509 -req -sha256 -in cert/server.csr -signkey cert/server.key -out cert/server.crt -days 3650
```

You can proceed and update the server definition to use the certificate and the key:

```go
package main

import (
  "fmt"
  "log"
  "net"

  "gitlab.com/pantomath-io/demo-grpc/api"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials"
)

// main starts a gRPC server and waits for connection
func main() {
  // create a listener on TCP port 7777
  lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "localhost", 7777))
  if err != nil {
    log.Fatalf("failed to listen: %v", err)
  }

  // create a server instance
  s := api.Server{}

  // Create the TLS credentials
  creds, err := credentials.NewServerTLSFromFile("cert/server.crt", "cert/server.key")
  if err != nil {
    log.Fatalf("could not load TLS keys: %s", err)
  }

  // Create an array of gRPC options with the credentials
  opts := []grpc.ServerOption{grpc.Creds(creds)}

  // create a gRPC server object
  grpcServer := grpc.NewServer(opts...)

  // attach the Ping service to the server
  api.RegisterPingServer(grpcServer, &s)

  // start the server
  if err := grpcServer.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %s", err)
  }
}
```

So what changed?

* you created a [credentials](https://godoc.org/google.golang.org/grpc/credentials) object (called `creds`) from your certificate and key files;
* you created a `grpc.ServerOption` array and placed your credentials object in it;
* when creating the grpc server, you provided the constructor with you array of `grpc.ServerOption`;
* you must have noticed that you need to precisely specify the IP you bind your server to, so that the IP matches the FQDN used in the certificate.

Note that `grpc.NewServer()` is a variadic function, so you can pass it any number of trailing arguments. You created an array of options so that we can add other options later on.

If you compile your `server` now, and use the `client` you already have, the connection won’t work, and both sides will throw an error.

* the server report the client is not handshaking with TLS:

```bash
2006/01/02 15:04:05 grpc: Server.Serve failed to complete security handshake from "localhost:64018": tls: first record does not look like a TLS handshake
```

* the client has its connection closed before it can do anything:

```bash
2006/01/02 15:04:05 transport: http2Client.notifyError got notified that the client transport was broken read tcp localhost:64018->127.0.0.1:7777: read: connection reset by peer.
2006/01/02 15:04:05 Error when calling SayHello: rpc error: code = Internal desc = transport is closing
```

You need to use the exact same certificate file on the client side. So edit the `client/main.go` file:

```go
package main

import (
  "log"

  "gitlab.com/pantomath-io/demo-grpc/api"
  "golang.org/x/net/context"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials"
)

func main() {
  var conn *grpc.ClientConn

// Create the client TLS credentials
  creds, err := credentials.NewClientTLSFromFile("cert/server.crt", "")
  if err != nil {
    log.Fatalf("could not load tls cert: %s", err)
  }

  // Initiate a connection with the server
  conn, err = grpc.Dial("localhost:7777", grpc.WithTransportCredentials(creds))
  if err != nil {
    log.Fatalf("did not connect: %s", err)
  }
  defer conn.Close()

  c := api.NewPingClient(conn)

  response, err := c.SayHello(context.Background(), &api.PingMessage{Greeting: "foo"})
  if err != nil {
    log.Fatalf("error when calling SayHello: %s", err)
  }
  log.Printf("Response from server: %s", response.Greeting)
}
```

The changes on the client side are pretty much the same as on the server:

* you created a [credentials](https://godoc.org/google.golang.org/grpc/credentials) object with the certificate file. Note that the client **do not use** the certificate key, the key is private to the server;
* you added an option to the `grpc.Dial()` function, using your credentials object. Note that the `grpc.Dial()` function is also a variadic function, so it accepts any number of options;
* same server note applies for the client: you need to use the same FQDN to connect to the server as the one used in the certificate, or the transport authentication handshake will fail.

Both sides use credentials, so they should be able to talk just as before, but in an encrypted way. You can compile the code:

```bash
$ make
```

And run both sides in separate terminals:

```bash
$ bin/server
2006/01/02 15:04:05 Receive message foo
```
```bash
$ bin/client
2006/01/02 15:04:05 Response from server: bar
```

## Identify the client

*git tag:* [add-auth](https://gitlab.com/pantomath-io/demo-grpc/tree/add-auth)

![](https://cdn-images-1.medium.com/max/1600/1*9hIUb4TpxYkHehxMb7KpEw.jpeg)
*Photo by [chuttersnap](https://unsplash.com/photos/984e1Fk4E04?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

Another interesting feature of the gRPC server is the ability to intercept a request from the client. The client can inject information on the transport layer. You can use that feature to identify your client, because the SSL implementation authenticates the server (via the certificate), but not the client (all your clients are using the same certificate).

So you’ll update the client side to inject metadata on every call (like a login and password), and the server side to check these credentials for every incoming call.

On the client side, you just need to specify a `DialOption` on your `grpc.Dial()` call. But that `DialOption`has some constraints. Edit your `client/main.go` file:

```go
package main

import (
  "log"

  "gitlab.com/pantomath-io/demo-grpc/api"
  "golang.org/x/net/context"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials"
)

// Authentication holds the login/password
type Authentication struct {
  Login    string
  Password string
}

// GetRequestMetadata gets the current request metadata
func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
  return map[string]string{
    "login":    a.Login,
    "password": a.Password,
  }, nil
}

// RequireTransportSecurity indicates whether the credentials requires transport security
func (a *Authentication) RequireTransportSecurity() bool {
  return true
}

func main() {
  var conn *grpc.ClientConn

// Create the client TLS credentials
  creds, err := credentials.NewClientTLSFromFile("cert/server.crt", "")
  if err != nil {
    log.Fatalf("could not load tls cert: %s", err)
 }

  // Setup the login/pass
  auth := Authentication{
    Login:    "john",
    Password: "doe",
  }

  // Initiate a connection with the server
  conn, err = grpc.Dial("localhost:7777", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&auth))
  if err != nil {
    log.Fatalf("did not connect: %s", err)
   }
   defer conn.Close()

  c := api.NewPingClient(conn)

  response, err := c.SayHello(context.Background(), &api.PingMessage{Greeting: "foo"})
  if err != nil {
    log.Fatalf("error when calling SayHello: %s", err)
  }
  log.Printf("Response from server: %s", response.Greeting)
}
```

* You define a struct to hold the collection on fields you want to inject in your rcp calls. In our case, just a login and password, but you can imagine any fields you want;
* The `auth` variable holds the values you’ll be using;
* You use `grpc.WithPerRPCCredentials()` function to create a `DialOption` object to the `grpc.Dial()` function;
* Note that the `grpc.WithPerRPCCredentials()` function takes an interface as parameter, so your `Authentication` structure should comply to that interface. From the [documentation](https://godoc.org/google.golang.org/grpc/credentials#PerRPCCredentials), you know you should implement 2 methods on your structure: `GetRequestMetadata` and `RequireTransportSecurity`.
* So you define `GetRequestMetadata` function that just returns a map of your `Authentication` structure;
* And finally, you define `RequireTransportSecurity` function, that tells your `grpc` client if it should inject metadata at the transport level. In our current case, it always returns true, but you could have it return the value of a configuration boolean, for instance.

The client is up to push extra data during its calls to the server, but the server does not care, right now. So you need to tell him to check these metadata. Open `server/main.go` and update it:

```go
package main

import (
  "fmt"
  "log"
  "net"
  "strings"

  "golang.org/x/net/context"

  "gitlab.com/pantomath-io/demo-grpc/api"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials"
  "google.golang.org/grpc/metadata"
)

// private type for Context keys
type contextKey int

const (
  clientIDKey contextKey = iota
)

// authenticateAgent check the client credentials
func authenticateClient(ctx context.Context, s *api.Server) (string, error) {
  if md, ok := metadata.FromIncomingContext(ctx); ok {
    clientLogin := strings.Join(md["login"], "")
    clientPassword := strings.Join(md["password"], "")

    if clientLogin != "john" {
      return "", fmt.Errorf("unknown user %s", clientLogin)
    }
    if clientPassword != "doe" {
      return "", fmt.Errorf("bad password %s", clientPassword)
    }

    log.Printf("authenticated client: %s", clientLogin)
    return "42", nil
  }

  return "", fmt.Errorf("missing credentials")
}

// unaryInterceptor calls authenticateClient with current context
func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
  s, ok := info.Server.(*api.Server)
  if !ok {
    return nil, fmt.Errorf("unable to cast server")
  }
  clientID, err := authenticateClient(ctx, s)
  if err != nil {
    return nil, err
  }

  ctx = context.WithValue(ctx, clientIDKey, clientID)
  return handler(ctx, req)
}

// main start a gRPC server and waits for connection
func main() {
  // create a listener on TCP port 7777
  lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "localhost", 7777))
  if err != nil {
    log.Fatalf("failed to listen: %v", err)
  }

  // create a server instance
  s := api.Server{}

  // Create the TLS credentials
  creds, err := credentials.NewServerTLSFromFile("cert/server.crt", "cert/server.key")
  if err != nil {
    log.Fatalf("could not load TLS keys: %s", err)
  }

  // Create an array of gRPC options with the credentials
  opts := []grpc.ServerOption{grpc.Creds(creds),
    grpc.UnaryInterceptor(unaryInterceptor)}

  // create a gRPC server object
  grpcServer := grpc.NewServer(opts...)

  // attach the Ping service to the server
  api.RegisterPingServer(grpcServer, &s)

  // start the server
  if err := grpcServer.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %s", err)
  }
}
```

Once again, let me break down this for you:

* you add a new `grpc.ServerOption` to the array you created before (see why it’s an array, now?): `grpc.UnaryInterceptor`. And you pass a [reference to a function](https://godoc.org/google.golang.org/grpc#UnaryServerInterceptor) to that function, so it knows who to call. The rest of the main code does not change;
* you have to define the `unaryInterceptor` function, considering it gets a bunch of parameters:

1.  a `context.Context` object, containing your data, and that will exist during all the lifetime of the request;
1.  an `interface{}` which is the inbound parameter of the RPC call;
1.  a `UnaryServerInfo` struct which contains a bunch of information about the call (such as the `Server` abstraction object, and the method call by the client);
1.  a `UnaryHandler` struct which is the handler invoked by `UnaryServerInterceptor` to complete the normal execution of a unary RPC (i.e. an handler to what happens when the `UnaryInterceptor` returns).

* the `unaryInterceptor` function makes sure the `grpc.UnaryServerInfo` has the right server abstraction, and call the authentication function, `authenticateClient`;
* you define the `authenticateClient` function with your authentication logic — very very simple in this example. Note that it receives the `context.Context` as parameter, and extract the metadata from it. It checks the user, and returns its ID (in the form of a `string`, with a hypothetical error.
* if the `unaryInterceptor` gets no error from the `authenticateClient` function, it pushes the `clientID` in the `context.Context` object, so that the rest of the execution chain can use it (remember the `handler` gets the `context.Context` object as parameter?);
* Note that you created your `type` and `const` to reference the `clientID` in the `context.Context` map. This is just an handy way to avoid naming conflict and to allow constant reference.

You can compile the code:

```bash
$ make
```

And run both sides in separate terminals:

```bash
$ bin/server
2006/01/02 15:04:05 authenticated client: john
2006/01/02 15:04:05 Receive message foo
```

```bash
$ bin/client
2006/01/02 15:04:05 Response from server: bar
```

Obviously, your authentication logic will probably be smarter, comparing credentials against a database. The easy part of it is: your authentication function gets your abstraction of a `Server`, and this structure can hold your database handler.

## Open to REST

*git tag:* [add-rest](https://gitlab.com/pantomath-io/demo-grpc/tree/add-rest)

![](https://cdn-images-1.medium.com/max/1600/1*dpCAELNImICQxw8UxA0bsA.jpeg)
*Photo by [Rio Hodges](https://unsplash.com/photos/nNPONh2aXeQ?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

One last thing: you have a pretty neat server, client and protocol; serialized, encrypted and authenticated. But there is a important limit: your client needs to be gRPC compliant, that is be in the list of [supported platforms](https://grpc.io/about/#osp). To avoid that limit, we can open the server to a REST gateway, allowing REST clients to perform requests it. Luckily, there is a gRPC [protoc plugin](https://github.com/grpc-ecosystem/grpc-gateway) to generate a reverse-proxy server which translates a RESTful JSON API into gRPC. We can use a few line of pure Go code to serve that reverse-proxy.

So edit your `api/api.proto` file to add some extra information:

```protobuf
syntax = "proto3";
package api;

import "google/api/annotations.proto";

message PingMessage {
  string greeting = 1;
}

service Ping {
  rpc SayHello(PingMessage) returns (PingMessage) {
    option (google.api.http) = {
      post: "/1/ping"
      body: "*"
    };
  }
}
```

The `annotations.proto` import allows `protoc` to understand the `option` set later in the file. And the `option` defines that method and the path to the endpoint.

Update the `Makefile` to add a target for this new `Protobuf` compilation:

```bash
SERVER_OUT := "bin/server"
CLIENT_OUT := "bin/client"
API_OUT := "api/api.pb.go"
API_REST_OUT := "api/api.pb.gw.go"
PKG := "gitlab.com/pantomath-io/demo-grpc"
SERVER_PKG_BUILD := "${PKG}/server"
CLIENT_PKG_BUILD := "${PKG}/client"
PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)

.PHONY: all api server client

all: server client

api/api.pb.go: api/api.proto
  
 -I api/ \
    -I${GOPATH}/src \
    -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
    --go_out=plugins=grpc:api \
    api/api.proto

api/api.pb.gw.go: api/api.proto
  
 -I api/ \
    -I${GOPATH}/src \
    -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
    --grpc-gateway_out=logtostderr=true:api \
    api/api.proto

api: api/api.pb.go api/api.pb.gw.go ## Auto-generate grpc go sources

dep: ## Get the dependencies
  
 get -v -d ./...

server: dep api ## Build the binary file for server
  
 build -i -v -o $(SERVER_OUT) $(SERVER_PKG_BUILD)

client: dep api ## Build the binary file for client
  
 build -i -v -o $(CLIENT_OUT) $(CLIENT_PKG_BUILD)

clean: ## Remove previous builds
  
 $(SERVER_OUT) $(CLIENT_OUT) $(API_OUT) $(API_REST_OUT)

help: ## Display this help screen
  
 -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
```

Generate the Go code for the gateway (the file `api/api.pb.gw.go` will be generated — just as `api/api.pb.go`, don’t edit it, it will be updated by compilation):

```bash
$ make api
```

The change on the server side is more important. The `grpc.Serve()` function is a blocking function, that returns only on error (or can get killed by a signal). As we need to start another server (the REST interface), we need this call to be non-blocking. Fortunately, we have **goroutines** just for that. And there is a trick on the authentication. As the REST gateway is just a reverse-proxy, it acts as a client from the gRPC perspective. Thus it needs to use a `WithPerRPCCredentials` option when dialing the server.

Head to your `server/main.go` file:

```go
package main

import (
  "fmt"
  "log"
  "net"
  "net/http"
  "strings"

  "github.com/grpc-ecosystem/grpc-gateway/runtime"
  "golang.org/x/net/context"
  "gitlab.com/pantomath-io/demo-grpc/api"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials"
  "google.golang.org/grpc/metadata"
)

// private type for Context keys
type contextKey int

const (
  clientIDKey contextKey = iota
)

func credMatcher(headerName string) (mdName string, ok bool) {
  if headerName == "Login" || headerName == "Password" {
    return headerName, true
  }
  return "", false
}

// authenticateAgent check the client credentials
func authenticateClient(ctx context.Context, s *api.Server) (string, error) {
  if md, ok := metadata.FromIncomingContext(ctx); ok {
    clientLogin := strings.Join(md["login"], "")
    clientPassword := strings.Join(md["password"], "")

    if clientLogin != "john" {
      return "", fmt.Errorf("unknown user %s", clientLogin)
    }
    if clientPassword != "doe" {
      return "", fmt.Errorf("bad password %s", clientPassword)
    }

    log.Printf("authenticated client: %s", clientLogin)
    return "42", nil
  }
  return "", fmt.Errorf("missing credentials")
}

// unaryInterceptor call authenticateClient with current context
func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
  s, ok := info.Server.(*api.Server)
  if !ok {
    return nil, fmt.Errorf("unable to cast server")
  }
  clientID, err := authenticateClient(ctx, s)
  if err != nil {
    return nil, err
  }

  ctx = context.WithValue(ctx, clientIDKey, clientID)
  return handler(ctx, req)
}

func startGRPCServer(address, certFile, keyFile string) error {
  // create a listener on TCP port
  lis, err := net.Listen("tcp", address)
  if err != nil {
    return fmt.Errorf("failed to listen: %v", err)
  }

  // create a server instance
  s := api.Server{}

  // Create the TLS credentials
  creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
  if err != nil {
    return fmt.Errorf("could not load TLS keys: %s", err)
  }

  // Create an array of gRPC options with the credentials
  opts := []grpc.ServerOption{grpc.Creds(creds),
    grpc.UnaryInterceptor(unaryInterceptor)}

  // create a gRPC server object
  grpcServer := grpc.NewServer(opts...)

  // attach the Ping service to the server
  api.RegisterPingServer(grpcServer, &s)

  // start the server
  log.Printf("starting HTTP/2 gRPC server on %s", address)
  if err := grpcServer.Serve(lis); err != nil {
    return fmt.Errorf("failed to serve: %s", err)
  }

  return nil
}

func startRESTServer(address, grpcAddress, certFile string) error {
  ctx := context.Background()
  ctx, cancel := context.WithCancel(ctx)
  defer cancel()

  mux := runtime.NewServeMux(runtime.WithIncomingHeaderMatcher(credMatcher))

  creds, err := credentials.NewClientTLSFromFile(certFile, "")
  if err != nil {
    return fmt.Errorf("could not load TLS certificate: %s", err)
  }

  // Setup the client gRPC options
  opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}

  // Register ping
  err = api.RegisterPingHandlerFromEndpoint(ctx, mux, grpcAddress, opts)
  if err != nil {
    return fmt.Errorf("could not register service Ping: %s", err)
  }

  log.Printf("starting HTTP/1.1 REST server on %s", address)
  http.ListenAndServe(address, mux)

  return nil
}

// main start a gRPC server and waits for connection
func main() {
  grpcAddress := fmt.Sprintf("%s:%d", "localhost", 7777)
  restAddress := fmt.Sprintf("%s:%d", "localhost", 7778)
  certFile := "cert/server.crt"
  keyFile := "cert/server.key"

  // fire the gRPC server in a goroutine
  go func() {
    err := startGRPCServer(grpcAddress, certFile, keyFile)
    if err != nil {
      log.Fatalf("failed to start gRPC server: %s", err)
    }
  }()

  // fire the REST server in a goroutine
  go func() {
    err := startRESTServer(restAddress, grpcAddress, certFile)
    if err != nil {
      log.Fatalf("failed to start gRPC server: %s", err)
    }
  }()

  // infinite loop
  log.Printf("Entering infinite loop")
  select {}
}
```

So what happened?

* you moved all the code for the gRPC server creation in a goroutine with a dedicated function (`startGRPCServer`), so it does not block the `main`;
* you create a new goroutine with a dedicated function (`startRESTServer`) where you create an HTTP/1.1 server;
* in `startRESTServer` where you create the REST gateway, you start by getting the `context.Context` background object (i.e. the root of the context tree). Then, you create a request multiplexer object, `mux`, with an option: `runtime.WithIncomingHeaderMatcher`. This option takes a function reference as parameter, `credMatch`, and is called for every HTTP header from the incoming request. The function evaluates whether or not the HTTP header should be passed to the gRPC context;
* you defined the `credMatch` function to match the credentials header, allowing them to be metadata in the gRPC context. This is how you have your authentication working, because the reverse-proxy uses the HTTP headers it receives when it connects to the gRPC server;
* you also create a `credentials.NewClientTLSFromFile`, to be used as a `grpc.DialOption`, just like you did in the client side;
* you register your api endpoint, i.e. you make the link between your multiplexer, you gRPC server, using the context and the gRPC options;
* and finally, you start an HTTP/1.1 server, and wait for incoming connections;
* aside to your goroutine, you use a blocking `select` call, so that your program does not end right away.

Now build the whole project, so you can test the REST interface:

```bash
$ make
```

And run both sides in separate terminals:

```bash
$ bin/server
2006/01/02 15:04:05 Entering infinite loop
2006/01/02 15:04:05 starting HTTP/1.1 REST server on localhost:7778
2006/01/02 15:04:05 starting HTTP/2 gRPC server on localhost:7777
2006/01/02 15:04:05 authenticated client: john
2006/01/02 15:04:05 Receive message foo

$ curl -H "login:john" -H "password:doe" -X POST -d '{"greeting":"foo"}' '
{"greeting":"bar"}
```

## One last swag…

*git tag:* [add-swagger](https://gitlab.com/pantomath-io/demo-grpc/tree/add-swagger)

![](https://cdn-images-1.medium.com/max/1600/1*lg78b_518eFveLDKDsA5hQ.jpeg)
*Photo by [Lorenzo Castagnone](https://unsplash.com/photos/ETNcibiiWOs?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*

The REST gateway is cool, but it would be even cooler to generate documentation from it, right?

You can do that for free, using a [protoc plugin](https://github.com/grpc-ecosystem/grpc-gateway) to generate a [swagger](https://swagger.io/) json file:

```bash
protoc -I api/ \
  -I${GOPATH}/src \
  -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --swagger_out=logtostderr=true:api \
  api/api.proto
```

This will generate a `api/api.swagger.json` file. As all generated code from Protobuf compilation, you should not edit it, but you can use it, and it can update it when you change your definition file.

You can add the compilation command in the `Makefile`.

## Conclusion

You have a fully functional gRPC client and server, with SSL encryption & authentication, client identification, and a REST gateway (with its swagger file). Where to go from here?

You can push a little on the REST gateway, to make it HTTPS instead of HTTP. You can obviously add more complex data structure on your Protobuf, alongside with more `service`. You can benefit from HTTP/2 features, such as [streaming](https://grpc.io/docs/guides/concepts.html#bidirectional-streaming-rpc), either from client to server, or from server to client, or bidirectional (but that’s only for gRPC, not for the REST, based on HTTP/1.1).

*Many thanks to [Charles Francoise](https://dev.to/loderunner) who co-wrote this paper and [https://gitlab.com/pantomath-io/demo-grpc](https://gitlab.com/pantomath-io/demo-grpc).*

*****

*We are currently working on Pantomath. Pantomath is a modern, open-source monitoring solution, built for performance, that bridges the gaps across all levels of your company. The well-being of your infrastructure is everyone’s business. Keep up with [the project](goo.gl/tcxtXq)*