This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Basic

Basic user guide which could operate by user turn on/off by modifying boot.yaml.

Overview

In basic user guide, we will introduce how to enable bellow functionalities by modifying boot.yaml.

Functionality Description
GRPC Gateway Enable grpc-gateway
Swagger UI Enable swagger UI
Common Service Enable common service
RK TV Enable RK TV
Prometheus Client Enable prometheus client
Logging Enable logging middleware
Metrics Enable prometheus middleware
Meta Enable meta middleware
Tracing Enable openTelemetry tracing middleware
Auth Enable basic & ApiKey auth middleware

1 - GRPC Gateway

Enable grpc-gateway.

Overview

In traditional way, we need to add a bunch of codes in order to enable grpc-gateway,

With bootstrapper, grpc-gateway will be automatically started and bind to one single port with grpc for convenience.

Steps to enable grpc-gateway:

  1. Write gw_mapping.yaml
  2. Write buf.yaml & buf.gen.yaml
  3. Generate gateway file based on gw_mapping.yaml
  4. Register gateway function into GRPC server

Prerequisite

Install required tools.

We recommend use rk install them easily.

# Install RK CLI
$ go get -u github.com/rookie-ninja/rk/cmd/rk

# List available installation
$ rk install
COMMANDS:
    buf                      install buf on local machine
    cfssl                    install cfssl on local machine
    cfssljson                install cfssljson on local machine
    gocov                    install gocov on local machine
    golangci-lint            install golangci-lint on local machine
    mockgen                  install mockgen on local machine
    pkger                    install pkger on local machine
    protobuf                 install protobuf on local machine
    protoc-gen-doc           install protoc-gen-doc on local machine
    protoc-gen-go            install protoc-gen-go on local machine
    protoc-gen-go-grpc       install protoc-gen-go-grpc on local machne
    protoc-gen-grpc-gateway  install protoc-gen-grpc-gateway on local machine
    protoc-gen-openapiv2     install protoc-gen-openapiv2 on local machine
    swag                     install swag on local machine
    rk-std                   install rk standard environment on local machine
    help, h                  Shows a list of commands or help for one command

# Install buf, protoc-gen-go, protoc-gen-go-grpc, protoc-gen-grpc-gateway, protoc-gen-openapiv2
$ rk install protoc-gen-go
$ rk install protoc-gen-go-grpc
$ rk install protoc-gen-go-grpc-gateway
$ rk install protoc-gen-openapiv2
$ rk install buf
Tool Description Installation
protobuf protocol buffer Install
buf Help you create consistent Protobuf APIs that preserve compatibility and comply with design best-practices. Install
protoc-gen-go Plugin for the Google protocol buffer compiler to generate Go code.. Install
protoc-gen-go-grpc This project aims to provide that HTTP+JSON interface to your gRPC service. Install
protoc-gen-grpc-gateway plugin for Google protocol buffer compiler to generate a reverse-proxy, which converts incoming RESTful HTTP/1 requests gRPC invocation. Install
protoc-gen-openapiv2 plugin for Google protocol buffer compiler to generate open API config file. Install

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Quick start

1.Create api/v1/greeter.proto

syntax = "proto3";

package api.v1;

option go_package = "api/v1/greeter";

service Greeter {
  rpc Greeter (GreeterRequest) returns (GreeterResponse) {}
}

message GreeterRequest {
  string name = 1;
}

message GreeterResponse {
  string message = 1;
}

2.Create api/v1/gw_mapping.yaml

type: google.api.Service
config_version: 3

# Please refer google.api.Http in https://github.com/googleapis/googleapis/blob/master/google/api/http.proto file for details.
http:
  rules:
    - selector: api.v1.Greeter.Greeter
      get: /api/v1/greeter

3.Create buf.yaml

version: v1beta1
name: github.com/rk-dev/rk-demo
build:
  roots:
    - api

4.Create buf.gen.yaml

version: v1beta1
plugins:
  # protoc-gen-go needs to be installed, generate go files based on proto files
  - name: go
    out: api/gen
    opt:
     - paths=source_relative
  # protoc-gen-go-grpc needs to be installed, generate grpc go files based on proto files
  - name: go-grpc
    out: api/gen
    opt:
      - paths=source_relative
      - require_unimplemented_servers=false
  # protoc-gen-grpc-gateway needs to be installed, generate grpc-gateway go files based on proto files
  - name: grpc-gateway
    out: api/gen
    opt:
      - paths=source_relative
      - grpc_api_configuration=api/v1/gw_mapping.yaml
  # protoc-gen-openapiv2 needs to be installed, generate swagger config files based on proto files
  - name: openapiv2
    out: api/gen
    opt:
      - grpc_api_configuration=api/v1/gw_mapping.yaml

5.Compile proto file

$ buf generate

There will be bellow files generated.

$ tree api/gen 
api/gen
└── v1
    ├── greeter.pb.go
    ├── greeter.pb.gw.go
    ├── greeter.swagger.json
    └── greeter_grpc.pb.go
 
1 directory, 4 files

6.Create boot.yaml

grpc.gw.gwMappingFilePaths is not required, however, we strongly recommend specify this path since bootstrapper will use this in bellow:

  1. /rk/v1/apis
  2. RK TV
---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    gwMappingFilePaths:
      - "api/v1/gw_mapping.yaml"    # Boot will look for gateway mapping files and load information into memory

7. Create main.go

package main

import (
	"context"
	"fmt"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-demo/api/gen/v1"
	_ "github.com/rookie-ninja/rk-grpc/boot"
	"google.golang.org/grpc"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

    // ***************************************
    // ******* Register GRPC & Gateway *******
    // ***************************************

	// Get grpc entry with name
	grpcEntry := boot.GetGrpcEntry("greeter")
    // Register grpc registration function
	grpcEntry.AddRegFuncGrpc(registerGreeter)
    // Register grpc-gateway registration function
	grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint)

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

// Implementation of [type GrpcRegFunc func(server *grpc.Server)]
func registerGreeter(server *grpc.Server) {
	greeter.RegisterGreeterServer(server, &GreeterServer{})
}

// Implementation of grpc service defined in proto file
type GreeterServer struct{}

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	return &greeter.GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.Name),
	}, nil
}

8.Full structure

$ tree
.
├── api
│   ├── gen
│   │   └── v1
│   │       ├── greeter.pb.go
│   │       ├── greeter.pb.gw.go
│   │       ├── greeter.swagger.json
│   │       └── greeter_grpc.pb.go
│   └── v1
│       ├── greeter.proto
│       └── gw_mapping.yaml
├── boot.yaml
├── buf.gen.yaml
├── buf.yaml
├── go.mod
├── go.sum
└── main.go

4 directories, 12 files

9.Validate

$ go run main.go
$ curl "localhost:8080/api/v1/greeter?name=rk-dev"
{"message":"Hello rk-dev!"}

Cheers

Gateway server option

RK introduced gateway server options with bellow functionality.

rkgrpc.RkGwServerMuxOptions

Functionality Description
HttpErrorHandler Mainly copied from original gateway error handler. RK wrap errors to RK style error response.
MarshallerOption protojson.MarshalOptions{UseProtoNames: false, EmitUnpopulated: true},UnmarshalOptions: protojson.UnmarshalOptions{}}
Metadata Inject x-forwarded-method, x-forwarded-path, x-forwarded-scheme, x-forwarded-user-agent and x-forwarded-remote-addr into grpc metadata
OutgoingHeaderMatcher Bypass all grpc metadata to http header without prefix
IncomingHeaderMatcher Bypass all http header to grpc metadata without prefix

1.Enable rkServerOption

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    enableRkGwOption: true          # Enable rk style grpc gateway server option
    gwMappingFilePaths:
      - "api/v1/gw_mapping.yaml"    # Bootstrapper will look for gateway mapping files and load information into memory
    interceptors:
      loggingZap:
        enabled: true               # Enable logging interceptor for validation

2.Validate log

$ curl "localhost:8080/api/v1/greeter?name=rk-dev"

gwMethod, gwPath, gwScheme, gwUserAgent would be logged

------------------------------------------------------------------------
endTime=2021-07-09T21:03:43.518106+08:00
...
payloads={"grpcMethod":"Greeter","grpcService":"api.v1.Greeter","grpcType":"unaryServer","gwMethod":"GET","gwPath":"/v1/greeter","gwScheme":"http","gwUserAgent":"curl/7.64.1"}
...

3.Validate incoming metadata

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
    // Print incoming headers
	fmt.Println(rkgrpcctx.GetIncomingHeaders(ctx))

	return &greeter.GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.Name),
	}, nil
}
map[:authority:[0.0.0.0:8080] accept:[*/*] content-type:[application/grpc] user-agent:[grpc-go/1.38.0] x-forwarded-for:[::1] x-forwarded-host:[localhost:8080] x-forwarded-method:[GET] x-forwarded-path:[/v1/greeter] x-forwarded-remote-addr:[[::1]:57082] x-forwarded-scheme:[http] x-forwarded-user-agent:[curl/7.64.1]]

4.Override gateway server option for marshaller

In some cases, we may hope to override marshaller options. For example, returning underscore style restful result instead of camelcase.

Please refer to bellow repository for detailed explanations.

grpc:
  - name: greeter                                     # Required
    port: 8080                                        # Required
    enabled: true                                     # Required
    enableRkGwOption: true                            # Optional, default: false
    gwOption:                                         # Optional, default: nil
      marshal:                                        # Optional, default: nil
        multiline: false                              # Optional, default: false
        emitUnpopulated: false                        # Optional, default: false
        indent: ""                                    # Optional, default: false
        allowPartial: false                           # Optional, default: false
        useProtoNames: false                          # Optional, default: false
        useEnumNumbers: false                         # Optional, default: false
      unmarshal:                                      # Optional, default: nil
        allowPartial: false                           # Optional, default: false
        discardUnknown: false                         # Optional, default: false

Cheers

Error mapping

It is important to understand grpc-gateway error to grpc error mapping.

Here is the default error mapping defind in grpc-gateway.

GRPC Error GRPC Error Str Gateway(Http) Error Code Gateway(Http) Error Str
0 OK 200 OK
1 CANCELLED 408 Request Timeout
2 UNKNOWN 500 Internal Server Error
3 INVALID_ARGUMENT 400 Bad Request
4 DEADLINE_EXCEEDED 504 Gateway Timeout
5 NOT_FOUND 404 Not Found
6 ALREADY_EXISTS 409 Conflict
7 PERMISSION_DENIED 403 Forbidden
8 RESOURCE_EXHAUSTED 429 Too Many Requests
9 FAILED_PRECONDITION 400 Bad Request
10 ABORTED 409 Conflict
11 OUT_OF_RANGE 400 Bad Request
12 UNIMPLEMENTED 501 Not Implemented
13 INTERNAL 500 Internal Server Error
14 UNAVAILABLE 503 Service Unavailable
15 DATA_LOSS 500 Internal Server Error
16 UNAUTHENTICATED 401 Unauthorized

1.Validate error(Standard go error)

Based on default error mapping, we expect 500 response code.

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	return nil, errors.New("error triggered manually")
}
$ curl "localhost:8080/v1/greeter?name=rk-dev"
{
    "error":{
        "code":500,
        "status":"Internal Server Error",
        "message":"error triggered manually",
        "details":[]
    }
}

2.Validate error(grpc error)

grpc needs to specify errors with status.New().

We recommend use rkerror to generate errors as bellow.

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	return nil, rkerror.PermissionDenied("permission denied manually").Err()
}
curl "localhost:8080/v1/greeter?name=rk-dev"
{
    "error":{
        "code":403,
        "status":"Forbidden",
        "message":"permission denied manually",
        "details":[
            {
                "code":7,
                "status":"PermissionDenied",
                "message":"[from-grpc] permission denied manually"
            }
        ]
    }
}

Cheers

2 - Swagger UI

Enable swagger UI for server.

Prerequisite

In order to enable swagger, we need to enable grpc-gateway first.

Follow steps at prerequisite

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Swagger options

name description type default value
grpc.sw.enabled Enable swagger service over gRpc server boolean false
grpc.sw.path The path access swagger service from web string /sw
grpc.sw.jsonPath Where the swagger.json files are stored locally string ""
grpc.sw.headers Headers would be sent to caller as scheme of [key:value] []string []

Quick start

1.Create api/v1/greeter.proto

syntax = "proto3";

package api.v1;

option go_package = "api/v1/greeter";

service Greeter {
  rpc Greeter (GreeterRequest) returns (GreeterResponse) {}
}

message GreeterRequest {
  string name = 1;
}

message GreeterResponse {
  string message = 1;
}

2.Create api/v1/gw_mapping.yaml

type: google.api.Service
config_version: 3

# Please refer google.api.Http in https://github.com/googleapis/googleapis/blob/master/google/api/http.proto file for details.
http:
  rules:
    - selector: api.v1.Greeter.Greeter
      get: /api/v1/greeter

3.Create buf.yaml

version: v1beta1
name: github.com/rk-dev/rk-demo
build:
  roots:
    - api

4.Create buf.gen.yaml

version: v1beta1
plugins:
  # protoc-gen-go needs to be installed, generate go files based on proto files
  - name: go
    out: api/gen
    opt:
     - paths=source_relative
  # protoc-gen-go-grpc needs to be installed, generate grpc go files based on proto files
  - name: go-grpc
    out: api/gen
    opt:
      - paths=source_relative
      - require_unimplemented_servers=false
  # protoc-gen-grpc-gateway needs to be installed, generate grpc-gateway go files based on proto files
  - name: grpc-gateway
    out: api/gen
    opt:
      - paths=source_relative
      - grpc_api_configuration=api/v1/gw_mapping.yaml
  # protoc-gen-openapiv2 needs to be installed, generate swagger config files based on proto files
  - name: openapiv2
    out: api/gen
    opt:
      - grpc_api_configuration=api/v1/gw_mapping.yaml

5.Compile proto file

$ buf generate

There will be bellow files generated.

$ tree api/gen 
api/gen
└── v1
    ├── greeter.pb.go
    ├── greeter.pb.gw.go
    ├── greeter.swagger.json
    └── greeter_grpc.pb.go
 
1 directory, 4 files

6.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    enableRkGwOption: true          # Enable RK style server options
    gwMappingFilePaths:
      - "api/v1/gw_mapping.yaml"    # Bootstrapper will look for gateway mapping files and load information into memory
    sw:
      enabled: true                 # Enable swagger
      jsonPath: "api/gen/v1"            # Provide swagger config file path

7. Create main.go

package main

import (
	"context"
	"fmt"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-demo/api/gen/v1"
	"github.com/rookie-ninja/rk-grpc/boot"
	"google.golang.org/grpc"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

    // ***************************************
    // ******* Register GRPC & Gateway *******
    // ***************************************

	// Get grpc entry with name
	grpcEntry := boot.GetEntry("greeter").(*rkgrpc.GrpcEntry)
    // Register grpc registration function
	grpcEntry.AddRegFuncGrpc(registerGreeter)
    // Register grpc-gateway registration function
	grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint)

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

// Implementation of [type GrpcRegFunc func(server *grpc.Server)]
func registerGreeter(server *grpc.Server) {
	greeter.RegisterGreeterServer(server, &GreeterServer{})
}

// Implementation of grpc service defined in proto file
type GreeterServer struct{}

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	return &greeter.GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.Name),
	}, nil
}

8.Full structure

$ tree
.
├── api
│   ├── gen
│   │   └── v1
│   │       ├── greeter.pb.go
│   │       ├── greeter.pb.gw.go
│   │       ├── greeter.swagger.json
│   │       └── greeter_grpc.pb.go
│   └── v1
│       ├── greeter.proto
│       └── gw_mapping.yaml
├── boot.yaml
├── buf.gen.yaml
├── buf.yaml
├── go.mod
├── go.sum
└── main.go

4 directories, 12 files

9.Validate

Swagger: http://localhost:8080/sw

Cheers

3 - Common Service

Enable common service for server.

Overview

Common service contains builtin API commonly used by user.

grpc-gateway Path GRPC Method Description
/rk/v1/apis rk.api.v1.RkcCommonService.Apis List APIs in current GrpcEntry.
/rk/v1/certs rk.api.v1.RkcCommonService.Certs List CertEntry.
/rk/v1/configs rk.api.v1.RkcCommonService.Configs List ConfigEntry.
/rk/v1/deps rk.api.v1.RkcCommonService.Deps List dependencies related application, entire contents of go.mod file would be returned.
/rk/v1/entries rk.api.v1.RkcCommonService.Entries List all Entries.
/rk/v1/gc rk.api.v1.RkcCommonService.Gc Trigger GC
/rk/v1/healthy rk.api.v1.RkcCommonService.Healthy Get application healthy status.
/rk/v1/info rk.api.v1.RkcCommonService.Info Get application and process info.
/rk/v1/license rk.api.v1.RkcCommonService.License Get license related application, entire contents of LICENSE file would be returned.
/rk/v1/logs rk.api.v1.RkcCommonService.Logs List logger related entries.
/rk/v1/git rk.api.v1.RkcCommonService.Git Get git information.
/rk/v1/readme rk.api.v1.RkcCommonService.Readme Get contents of README file.
/rk/v1/req rk.api.v1.RkcCommonService.Req List prometheus metrics of requests.
/rk/v1/sys rk.api.v1.RkcCommonService.Sys Get OS stat.
/rk/v1/tv rk.api.v1.RkcCommonService.Tv Get HTML page of /tv.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

CommonService options

name description type default value
grpc.commonService.enabled Enable embedded common service boolean false

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter
    port: 8080
    enabled: true
    enableReflection: true    # Enable reflection in order to use grpcurl for validation
    commonService:
      enabled: true           # Enable common service

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Install grpcurl from the official site. https://github.com/fullstorydev/grpcurl

# List grpc services at port 8080 without TLS
# Expect RkCommonService since we enabled common services.
$ grpcurl -plaintext localhost:8080 list                           
grpc.reflection.v1alpha.ServerReflection
rk.api.v1.RkCommonService

# List grpc methods in rk.api.v1.RkCommonService
$ grpcurl -plaintext localhost:8080 list rk.api.v1.RkCommonService            
rk.api.v1.RkCommonService.Apis
rk.api.v1.RkCommonService.Certs
rk.api.v1.RkCommonService.Configs
rk.api.v1.RkCommonService.Deps
rk.api.v1.RkCommonService.Entries
rk.api.v1.RkCommonService.Gc
rk.api.v1.RkCommonService.GwErrorMapping
rk.api.v1.RkCommonService.Healthy
rk.api.v1.RkCommonService.Info
rk.api.v1.RkCommonService.License
rk.api.v1.RkCommonService.Logs
rk.api.v1.RkCommonService.Readme
rk.api.v1.RkCommonService.Req
rk.api.v1.RkCommonService.Sys
rk.api.v1.RkCommonService.Git

# Send request to rk.api.v1.RkCommonService.Healthy
$ grpcurl -plaintext localhost:8080 rk.api.v1.RkCommonService.Healthy
{
    "healthy": true
}

Cheers

4.Enable swagger UI

In order to visualise common service in swagger UI, we need to enable swagger in boot.yaml as bellow.

---
grpc:
  - name: greeter
    port: 8080
    enabled: true
    enableReflection: true    # Enable reflection in order to use grpcurl for validation
    commonService:
      enabled: true           # Enable common service
    sw:
      enabled: true

Validate

http://localhost:8080/sw

sw-common

Cheers

4 - RK TV

Enable RK TV for server.

Overview

RK TV is a web UI contains service information including APIs, process info, metrics etc.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

TV options

name description type default value
grpc.tv.enabled Enable RK TV boolean false

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    tv:
      enabled: true                 # Enable TV

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Validate

http://localhost:8080/rk/v1/tv

tv

Cheers

5 - Prometheus client

Enable prometheus client for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Prometheus options

name description type default value
grpc.prom.enabled Enable prometheus boolean false
grpc.prom.path Path of prometheus string /metrics
grpc.prom.pusher.enabled Enable prometheus pusher bool false
grpc.prom.pusher.jobName Job name would be attached as label while pushing to remote pushgateway string ""
grpc.prom.pusher.remoteAddress PushGateWay address, could be form of http://x.x.x.x or x.x.x.x string ""
grpc.prom.pusher.intervalMs Push interval in milliseconds string 1000
grpc.prom.pusher.basicAuth Basic auth used to interact with remote pushgateway, form of [user:pass] string ""
grpc.prom.pusher.cert.ref Reference of rkentry.CertEntry string ""

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    prom:
      enabled: true                 # Enable prometheus client
#      path: "metrics"              # Default value is "metrics", set path as needed.

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Validate

http://localhost:8080/metrics

prom

Cheers

4.Add value to prometheus client

In order to add custom metrics into current prometheus client, we need to clarify bellow concept.

prom

Name Description
MetricsSet RK defined data structure could be used register Counter, Gauge, Histogram and Summary
Prometheus Registerer User need to register MetricsSet into registerer
Prometheus Counter A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.
Prometheus Gauge A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
Prometheus Histogram A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. It also provides a sum of all observed values.
Prometheus Summary Similar to a histogram, a summary samples observations (usually things like request durations and response sizes).
Prometheus Namespace Prometheus metrics was consist of namespace_subSystem_metricsName
Prometheus SubSystem Prometheus metrics was consist of namespace_subSystem_metricsName
package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-grpc/boot"
	"github.com/rookie-ninja/rk-prom"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Create a metrics set into prometheus.Registerer
	grpcEntry := boot.GetEntry("greeter").(*rkgrpc.GrpcEntry)
	set := rkprom.NewMetricsSet("rk", "demo", grpcEntry.GwEntry.PromEntry.Registerer)

	// Register counter, gauge, histogram, summary
	set.RegisterCounter("my_counter", "label")
	set.RegisterGauge("my_gauge", "label")
	set.RegisterHistogram("my_histogram", []float64{}, "label")
	set.RegisterSummary("my_summary", rkprom.SummaryObjectives, "label")

	// Increase counter, gauge, histogram, summary with label value
	set.GetCounterWithValues("my_counter", "value").Inc()
	set.GetGaugeWithValues("my_gauge", "value").Add(1.0)
	set.GetHistogramWithValues("my_histogram", "value").Observe(0.1)
	set.GetSummaryWithValues("my_summary", "value").Observe(0.1)

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

5.Validate metrics

Validate

http://localhost:8080/metrics

prom

Cheers

6.Push metrics to Pushgateway

Turn on pusher in boot.yaml.

---
grpc:
  - name: greeter                             # Name of grpc entry
    port: 8080                                # Port of grpc entry
    enabled: true                             # Enable grpc entry
    prom:
      enabled: true                           # Enable prometheus client
      pusher:
        enabled : true                        # Enable backend job push metrics to remote pushgateway
        jobName: "demo"                       # Name of current push job
        remoteAddress: "localhost:9091"       # Remote address of pushgateway
        intervalMs: 2000                      # Push interval in milliseconds
#        basicAuth: "user:pass"               # Basic auth of pushgateway
#        cert:
#          ref: "ref"                         # Cert reference defined in CertEntry. Please see advanced user guide for details.

Start pushgateway locally for testing

$ docker run prom/pushgateway -p 9091:9091

Validate metrics at local pushgateway

http://localhost:9091

pushgateway

Cheers

6 - Interceptor logging

Enable RPC logging interceptor/middleware for server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Logging options

name description type default value
grpc.interceptors.loggingZap.enabled Enable log interceptor boolean false
grpc.interceptors.loggingZap.zapLoggerEncoding json or console string console
grpc.interceptors.loggingZap.zapLoggerOutputPaths Output paths []string stdout
grpc.interceptors.loggingZap.eventLoggerEncoding json or console string console
grpc.interceptors.loggingZap.eventLoggerOutputPaths Output paths []string stdout

Concept

In order to monitor every RPC request, we introduce EventLogger and ZapLogger

ZapLogger

RK bootstrapper adopt zap as the logger. lumberjack as the logger rotation.

Example

2021-07-09T23:52:13.667+0800    INFO    boot/grpc_entry.go:694  Bootstrapping grpcEntry.        {"eventId": "9bc192fb-567c-45d4-8775-7a097b0dab04", "entryName": "greeter", "entryType": "GrpcEntry", "grpcPort": 8080, "commonServiceEnabled": true, "tlsEnabled": false, "gwEnabled": true, "reflectionEnabled": false, "swEnabled": false, "tvEnabled": false, "promEnabled": false, "gwClientTlsEnabled": false, "gwServerTlsEnabled": false}

EventLogger

RK bootstrapper treat RPC request as an Event, and record every RPC request into Event type in rk-query.

Field Description
endTime As name described
startTime As name described
elapsedNano Elapsed time for RPC in nanoseconds
timezone As name described
ids Contains three different ids(eventId, requestId and traceId). If meta interceptor was enabled or event.SetRequestId() was called by user, then requestId would be attached. eventId would be the same as requestId if meta interceptor was enabled. If trace interceptor was enabled, then traceId would be attached.
app Contains appName, appVersion, entryName, entryType.
env Contains arch, az, domain, hostname, localIP, os, realm, region. realm, region, az, domain were retrieved from environment variable named as REALM, REGION, AZ and DOMAIN. “*” means empty environment variable.
payloads Contains RPC related metadata
error Contains errors if occur
counters Set by calling event.SetCounter() by user.
pairs Set by calling event.AddPair() by user.
timing Set by calling event.StartTimer() and event.EndTimer() by user.
remoteAddr As name described
operation RPC method name
resCode Response code of RPC
eventStatus Ended or InProgress

Example

------------------------------------------------------------------------
endTime=2021-07-09T23:44:09.81483+08:00
startTime=2021-07-09T23:44:09.814784+08:00
elapsedNano=46065
timezone=CST
ids={"eventId":"67d64dab-f3ea-4b77-93d0-6782caf4cfee"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"GrpcEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"grpcMethod":"Healthy","grpcService":"rk.api.v1.RkCommonService","grpcType":"unaryServer","gwMethod":"","gwPath":"","gwScheme":"","gwUserAgent":""}
error={}
counters={}
pairs={"healthy":"true"}
timing={}
remoteAddr=localhost:58205
operation=/rk.api.v1.RkCommonService/Healthy
resCode=OK
eventStatus=Ended
EOE

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    commonService:
      enabled: true                 # Enable common service for testing
    interceptors:
      loggingZap:
        enabled: true

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Send request

$ curl -X GET localhost:8080/rk/v1/healthy
{"healthy":true}

Log

------------------------------------------------------------------------
endTime=2021-07-09T23:44:09.81483+08:00
startTime=2021-07-09T23:44:09.814784+08:00
elapsedNano=46065
timezone=CST
ids={"eventId":"67d64dab-f3ea-4b77-93d0-6782caf4cfee"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"GrpcEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"grpcMethod":"Healthy","grpcService":"rk.api.v1.RkCommonService","grpcType":"unaryServer","gwMethod":"","gwPath":"","gwScheme":"","gwUserAgent":""}
error={}
counters={}
pairs={"healthy":"true"}
timing={}
remoteAddr=localhost:58205
operation=/rk.api.v1.RkCommonService/Healthy
resCode=OK
eventStatus=Ended
EOE

Cheers

4.JSON format

---
grpc:
  - name: greeter
    ...
    interceptors:
      loggingZap:
        ...
        zapLoggerEncoding: "json"          # Override to json format, option: json or console
        eventLoggerEncoding: "json"        # Override to json format, option: json or console

Cheers

5.Output paths

By default, logs will be rotated by 1GB and compressed.

---
grpc:
  - name: greeter
    ...
    interceptors:
      loggingZap:
        ...
        zapLoggerOutputPaths: ["logs/app.log"]        # Override output paths, option: json or console
        eventLoggerOutputPaths: ["logs/event.log"]    # Override output paths, option: json or console

Cheers

5.Get RPC scope logger

A new zap logger instance with requestId(if exist enabled by meta interceptor) will be created for every RPC request.

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	rkgrpcctx.GetLogger(ctx).Info("Received request")

	return &greeter.GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.Name),
	}, nil
}
2021-07-09T23:50:39.318+0800    INFO    basic/main.go:36        Received request        {"requestId": "c33698f2-3071-48d4-9d92-b1aa311e6c06"}

Cheers

6.Modify Event

A new event logger instance will be created for every RPC request.

User can add pairs, counters, errors in event which will be logged as soon as RPC finish.

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	event := rkgrpcctx.GetEvent(ctx)
	event.AddPair("key", "value")

	return &greeter.GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.Name),
	}, nil
}
------------------------------------------------------------------------
endTime=2021-07-09T23:52:39.103351+08:00
startTime=2021-07-09T23:52:39.10332+08:00
elapsedNano=31154
timezone=CST
ids={"eventId":"92001951-80c1-4dda-8f14-f920834f5c61","requestId":"92001951-80c1-4dda-8f14-f920834f5c61"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"GrpcEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"grpcMethod":"Greeter","grpcService":"api.v1.Greeter","grpcType":"unaryServer","gwMethod":"","gwPath":"","gwScheme":"","gwUserAgent":""}
error={}
counters={}
pairs={"key":"value"}
timing={}
remoteAddr=localhost:58269
operation=/api.v1.Greeter/Greeter
resCode=OK
eventStatus=Ended
EOE

Cheers

7 - Interceptor metrics

Enable RPC metrics interceptor/middleware for server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Logging options

name description type default value
grpc.interceptors.metricsProm.enabled Enable metrics interceptor boolean false

Concept

By default, metrics interceptor/middleware will record bellow metrics.

Metrics name Metrics type Description
elapsedNano Summary The time elapsed for RPC.
resCode Counter The counter of RPC with resCode.
errors Counter The counter of RPC with errors if occurs.

All of three metrics have the same labels as bellow:

Label name Description
entryName gRPC entry name
entryType gRPC entry type
realm OS environment variable: REALM, eg: rk
region OS environment variable: REGION, eg: beijing
az OS environment variable: AZ, eg: beijing-1
domain OS environment variable: DOMAIN, eg: prod
instance Hostname
appVersion Retrieved from AppInfoEntry
appName Retrieved from AppInfoEntry
restMethod Restful API method, eg: GET
restPath Restful API path, eg: /rk/v1/healthy
type Entry type, eg: grpc
resCode Response code, eg: OK

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    commonService:
      enabled: true                 # Enable common service for testing
    prom:
      enabled: true                 # Enable prometheus client
    interceptors:
      metricsProm:
        enabled: true

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Send request

$ curl -X GET localhost:8080/rk/v1/healthy
{"healthy":true}

Prometheus client:

http://localhost:8080/metrics

prom-inter

Cheers

8 - Interceptor meta

Enable RPC meta interceptor/middleware for server.

Overview

Meta interceptor/middleware will attach bellow headers to server response.

Header key Description
X-Request-Id Request id generated by the interceptor.
X-[Prefix]-App Application name.
X-[Prefix]-App-Version Version of application.
X-[Prefix]-App-Unix-Time Unix time of current application.
X-[Prefix]-Request-Received-Time Time of current request received by application.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Meta options

name description type default value
grpc.interceptors.meta.enabled Enable meta interceptor boolean false
grpc.interceptors.meta.prefix Header key was formed as X--XXX string RK

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    commonService:
      enabled: true                 # Enable common service for testing
    interceptors:
      meta:
        enabled: true

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

At above boot.yaml, we didn’t enable enableRkGwOption, as a result, the headers returned from server will have prefix of Grpc-Metadata

$ curl -vs -X GET localhost:8080/rk/v1/healthy
  ...
  < Grpc-Metadata-Content-Type: application/grpc
  < Grpc-Metadata-X-Request-Id: cb8c0c77-39cd-4e15-89d2-66ba30fa7a46
  < Grpc-Metadata-X-Rk-App-Name: rk-demo
  < Grpc-Metadata-X-Rk-App-Unix-Time: 2021-07-10T00:19:58.170222+08:00
  < Grpc-Metadata-X-Rk-App-Version: master-f414049
  < Grpc-Metadata-X-Rk-Received-Time: 2021-07-10T00:19:58.170222+08:00
  ...
  {"healthy":true}

If we enable enableRkGwOption, then Grpc-Metadata will be removed!

---
grpc:
  ...
  enableRkGwOption: true 
$ curl -vs -X GET localhost:8080/rk/v1/healthy
  ...
  < X-Request-Id: 2ae18df6-d132-42ce-841c-571f19a88787
  < X-Rk-App-Name: rk-demo
  < X-Rk-App-Unix-Time: 2021-07-10T00:24:10.281868+08:00
  < X-Rk-App-Version: master-f414049
  < X-Rk-Received-Time: 2021-07-10T00:24:10.281868+08:00
  ...
  {"healthy":true}

Cheers

4.Override requestId

func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {
	// Override request id
	rkgrpcctx.AddHeaderToClient(ctx, rkginctx.RequestIdKey, "request-id-override")
	// We expect new request id attached to logger
	rkgrpcctx.GetLogger(ctx).Info("Received request")

	return &greeter.GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.Name),
	}, nil
}

The headers bellow look weird, since there are two X-Request-Id, this is because grpc will merge metadata if we set it multiple times.

However, at client side, RK will read the last one with same key.

$ curl -vs -X GET "localhost:8080/v1/greeter?name=rk-dev"
...
< X-Request-Id: 4e858644-f2e2-48b2-adec-a1d329497abf
< X-Request-Id: request-id-override
< X-Rk-App-Name: rk-demo
< X-Rk-App-Unix-Time: 2021-07-10T00:29:19.030555+08:00
< X-Rk-App-Version: master-f414049
< X-Rk-Received-Time: 2021-07-10T00:29:19.030555+08:00
...
{"Message":"Hello rk-dev!"}

If we enabled logging interceptor/middleware, then we expect request as bellow

2021-07-10T00:29:19.030+0800    INFO    basic/main.go:40        Received request        {"requestId": "request-id-override"}
------------------------------------------------------------------------
endTime=2021-07-10T00:29:19.030673+08:00
startTime=2021-07-10T00:29:19.030535+08:00
elapsedNano=138133
timezone=CST
ids={"eventId":"request-id-override","requestId":"request-id-override"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"GrpcEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"grpcMethod":"Greeter","grpcService":"api.v1.Greeter","grpcType":"unaryServer","gwMethod":"GET","gwPath":"/v1/greeter","gwScheme":"http","gwUserAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:58601
operation=/api.v1.Greeter/Greeter
resCode=OK
eventStatus=Ended
EOE

Cheers

5.Override header prefix

---
grpc:
  - name: greeter
    ...
    interceptors:
      meta:
        enabled: true        # Enable meta interceptor/middleware
        prefix: "Override"   # Override prefix which formed as X-[Prefix]-xxx
$ curl -vs -X GET localhost:8080/rk/v1/healthy
...
< X-Override-App-Name: rk-demo
< X-Override-App-Unix-Time: 2021-07-10T00:34:59.974205+08:00
< X-Override-App-Version: master-f414049
< X-Override-Received-Time: 2021-07-10T00:34:59.974205+08:00
< X-Request-Id: 4deded12-2d39-42a5-b740-b4f74dd73c90
...
{"healthy":true}

Cheers

9 - Interceptor rate limit

Enable rate limit interceptor/middleware for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Rate limit options

name description type default value
grpc.interceptors.rateLimit.enabled Enable rate limit interceptor boolean false
grpc.interceptors.rateLimit.algorithm Provide algorithm, tokenBucket and leakyBucket are available options string tokenBucket
grpc.interceptors.rateLimit.reqPerSec Request per second globally int 0
grpc.interceptors.rateLimit.paths.path gRPC full name string ""
grpc.interceptors.rateLimit.paths.reqPerSec Request per second by gRPC full method name int 0

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      rateLimit:
        enabled: false
        algorithm: "leakyBucket"
        reqPerSec: 0
        paths:
          - path: "/rk.api.v1.RkCommonService/Healthy"
            reqPerSec: 0

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Send request

$ grpcurl -plaintext localhost:8080 rk.api.v1.RkCommonService.Healthy
Error invoking method "rk.api.v1.RkCommonService.Healthy": rpc error: code = ResourceExhausted desc = failed to query for service descriptor "rk.api.v1.RkCommonService": Slow down your request
$ curl -X GET localhost:8080/rk/v1/healthy
{
    "error":{
        "code":429,
        "status":"Too Many Requests",
        "message":"Slow down your request.",
        "details":[
            {
                "code":8,
                "status":"ResourceExhausted",
                "message":"[from-grpc] Slow down your request."
            }
        ]
    }
}

Cheers

10 - Interceptor tracing

Enable RPC tracing interceptor/middleware for server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Tracing options

name description type default value
grpc.interceptors.tracingTelemetry.enabled Enable tracing interceptor boolean false
grpc.interceptors.tracingTelemetry.exporter.file.enabled Enable file exporter boolean false
grpc.interceptors.tracingTelemetry.exporter.file.outputPath Export tracing info to files string stdout
grpc.interceptors.tracingTelemetry.exporter.jaeger.agent.enabled Export tracing info to jaeger agent boolean false
grpc.interceptors.tracingTelemetry.exporter.jaeger.agent.host As name described string localhost
grpc.interceptors.tracingTelemetry.exporter.jaeger.agent.port As name described int 6831
grpc.interceptors.tracingTelemetry.exporter.jaeger.collector.enabled Export tracing info to jaeger collector boolean false
grpc.interceptors.tracingTelemetry.exporter.jaeger.collector.endpoint As name described string http://localhost:16368/api/trace
grpc.interceptors.tracingTelemetry.exporter.jaeger.collector.username As name described string ""
grpc.interceptors.tracingTelemetry.exporter.jaeger.collector.password As name described string ""

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                   # Name of grpc entry
    port: 8080                      # Port of grpc entry
    enabled: true                   # Enable grpc entry
    commonService:
      enabled: true                 # Enable common service for testing
    interceptors:
      tracingTelemetry:
        enabled: true
        exporter:
          file:
            enabled: true
            outputPath: "stdout"

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Send request

$ grpcurl -plaintext localhost:8080 rk.api.v1.RkCommonService.Healthy
{
    "healthy": true
}

By default, tracing information will be exported to stdout

[
        {
                "SpanContext": {
                        "TraceID": "479bd29a93042619372da50163ae75e3",
                        "SpanID": "3b8e3ceba6741c23",
                        "TraceFlags": "01",
                        "TraceState": null,
                        "Remote": false
                },
                "Parent": {
                        "TraceID": "00000000000000000000000000000000",
                        "SpanID": "0000000000000000",
                        "TraceFlags": "00",
                        "TraceState": null,
                        "Remote": true
                },
                "SpanKind": 2,
                "Name": "/rk.api.v1.RkCommonService/Healthy",
                "StartTime": "2021-07-10T00:42:56.302578+08:00",
                "EndTime": "2021-07-10T00:42:56.30263999+08:00",
                ....
                "InstrumentationLibrary": {
                        "Name": "greeter",
                        "Version": "semver:0.20.0"
                }
        }
]

Cheers

4.Export tracing log to file

There will be delay for the couple of seconds before flushing to log file.

---
grpc:
  - name: greeter
    ...
    interceptors:
      tracingTelemetry:
        enabled: true                       # Enable tracing interceptor/middleware
        exporter:
          file:
            enabled: true
            outputPath: "logs/tracing.log"  # Log to files

Cheers

5.Export to jaeger

Start jaeger-all-in-one for testing

$ docker run -d --name jaeger \
    -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
    -p 5775:5775/udp \
    -p 6831:6831/udp \
    -p 6832:6832/udp \
    -p 5778:5778 \
    -p 16686:16686 \
    -p 14268:14268 \
    -p 14250:14250 \
    -p 9411:9411 \
    jaegertracing/all-in-one:1.23
---
grpc:
  - name: greeter
    ...
    interceptors:
      tracingTelemetry:
        enabled: true                                          # Enable tracing interceptor/middleware
        exporter:
          jaeger:
            agent:
              enabled: true                                    # Export to jaeger agent
#              host: ""                                        # Optional, default: localhost
#              port: 0                                         # Optional, default: 6831
#            collector:
#              enabled: true                                   # Optional, default: false
#              endpoint: ""                                    # Optional, default: http://localhost:14268/api/traces
#              username: ""                                    # Optional, default: ""
#              password: ""                                    # Optional, default: ""

Jaeger:

http://localhost:16686/

jaeger

Cheers

11 - Interceptor auth

Enable RPC auth interceptor/middleware for server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Auth options

name description type default value
grpc.interceptors.auth.enabled Enable auth interceptor boolean false
grpc.interceptors.auth.basic Basic auth credentials as scheme of user:pass []string []
grpc.interceptors.auth.apiKey API key auth []string []
grpc.interceptors.auth.ignorePrefix The paths of prefix that will be ignored by interceptor []string []

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter
    port: 8080
    enabled: true
    enableRkGwOption: true
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      auth:
        enabled: true        # Enable auth interceptor/middleware
        basic: ["user:pass"] # Enable basic auth

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

$ curl  -X GET localhost:8080/rk/v1/healthy
# This is RK style error code if unauthorized
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"Missing authorization, provide one of bellow auth header:[Basic Auth]",
        "details":[
            {
                "code":16,
                "status":"Unauthenticated",
                "message":"[from-grpc] Missing authorization, provide one of bellow auth header:[Basic Auth]"
            }
        ]
    }
}

Cheers

4.With X-API-Key auth type

---
grpc:
  - name: greeter
    ...
    interceptors:
      auth:
        enabled: true        # Enable auth interceptor/middleware
        apiKey: ["token"]    # Enable X-API-Key auth

Cheers

5.Ignore paths

---
grpc:
  - name: greeter
    ...
    interceptors:
      auth:
        enabled: true                                         # Enable auth interceptor/middleware
        basic: ["user:pass"]                                  # Enable basic auth
        ignorePrefix: ["/rk.api.v1.RkCommonService/Healthy"]  # Ignoring path with prefix

Cheers

12 - Interceptor timeout

Enable timeout interceptor/middleware for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a grpc server with rk-boot

name description type default value Required
grpc.name The name of grpc server string “”, server won’t start Required
grpc.port The port of grpc server integer 0, server won’t start Required
grpc.enabled Enable grpc entry bool false Required
grpc.description Description of grpc entry. string "" Optional
grpc.enableReflection Enable grpc server reflection boolean false Optional
grpc.enableRkGwOption Enable RK style gateway server options. boolean false Optional
grpc.noRecvMsgSizeLimit Disable grpc server side receive message size limit boolean false Optional
grpc.gwMappingFilePaths The grpc gateway mapping file path []string [] Optional

Timeout options

name description type default value
grpc.interceptors.timeout.enabled Enable timeout interceptor boolean false
grpc.interceptors.timeout.timeoutMs Global timeout in milliseconds. int 5000
grpc.interceptors.timeout.paths.path Full path string ""
grpc.interceptors.timeout.paths.timeoutMs Timeout in milliseconds by full path int 5000

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true                                 # Enable common service for testing
    interceptors:
      timeout:
        enabled: true                               # Optional, default: false
        timeoutMs: 5000                             # Optional, default: 5000
        paths: 
          - path: "/rk.api.v1.RkCommonService/Gc"   # Optional, default: ""
            timeoutMs: 1                            # Optional, default: 5000

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

Send request

$ grpcurl -plaintext localhost:8080 rk.api.v1.RkCommonService.Gc
ERROR:
  Code: Canceled
  Message: Request timed out!
  Details:
  1)	{"@type":"type.googleapis.com/rk.api.v1.ErrorDetail","code":1,"message":"[from-grpc] Request timed out!","status":"Canceled"}
$ curl -X GET localhost:8080/rk/v1/gc
{
    "error":{
        "code":408,
        "status":"Request Timeout",
        "message":"Request timed out!",
        "details":[
            {
                "code":1,
                "status":"Canceled",
                "message":"[from-grpc] Request timed out!"
            }
        ]
    }
}

Cheers

13 - Middleware jwt

Enable jwt interceptor/middleware for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a gRPC server with rk-boot

name description type default value
grpc.name The name of grpc server string N/A
grpc.port The port of grpc server integer nil, server won’t start
grpc.enabled Enable grpc entry bool false
grpc.description Description of grpc entry. string ""

JWT options

In order to make swagger UI and RK tv work under JWT without JWT token, we need to ignore prefixes of paths as bellow.

jwt:
  ...
  ignorePrefix:
   - "/rk/v1/tv"
   - "/sw"
   - "/rk/v1/assets"
name description type default value
grpc.interceptors.jwt.enabled Enable JWT interceptor boolean false
grpc.interceptors.jwt.signingKey Required, Provide signing key. string ""
grpc.interceptors.jwt.ignorePrefix Provide ignoring path prefix. []string []
grpc.interceptors.jwt.signingKeys Provide signing keys as scheme of :. []string []
grpc.interceptors.jwt.signingAlgo Provide signing algorithm. string HS256
grpc.interceptors.jwt.tokenLookup Provide token lookup scheme, please see bellow description. string “header:Authorization”
grpc.interceptors.jwt.authScheme Provide auth scheme. string Bearer

The supported scheme of tokenLookup

// Optional. Default value "header:Authorization".
// Possible values:
// - "header:<name>"
// - "query:<name>"
// - "param:<name>"
// - "cookie:<name>"
// - "form:<name>"
// Multiply sources example:
// - "header: Authorization,cookie: myowncookie"

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      jwt:
        enabled: true                 # Optional, default: false
        signingKey: "my-secret"       # Required

2.Create main.go

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

  • with valid jwt token
$ curl localhost:8080/rk/v1/healthy -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.EpM5XBzTJZ4J8AfoJEcJrjth8pfH28LWdjLo90sYb9g"
{"healthy":true}
  • with invalid jwt token
$ curl localhost:8080/rk/v1/healthy -H "Authorization: Bearer invalid-jwt-token"
{
    "code":16,
    "message":"invalid or expired jwt",
    "details":[
        {
            "@type":"type.googleapis.com/rk.api.v1.ErrorDetail",
            "code":16,
            "status":"Unauthenticated",
            "message":"[from-grpc] invalid or expired jwt"
        },
        {
            "@type":"type.googleapis.com/rk.api.v1.ErrorDetail",
            "code":2,
            "status":"Unknown",
            "message":"token contains an invalid number of segments"
        }
    ]
}

Cheers

14 - Middleware cors

Enable CORS interceptor/middleware for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a gRPC server with rk-boot

name description type default value
grpc.name The name of grpc server string N/A
grpc.port The port of grpc server integer nil, server won’t start
grpc.enabled Enable grpc entry bool false
grpc.description Description of grpc entry. string ""

CORS options

Interceptor for grpc-gateway.

name description type default value
grpc.interceptors.cors.enabled Enable cors interceptor boolean false
grpc.interceptors.cors.allowOrigins Provide allowed origins with wildcard enabled. []string *
grpc.interceptors.cors.allowMethods Provide allowed methods returns as response header of OPTIONS request. []string All http methods
grpc.interceptors.cors.allowHeaders Provide allowed headers returns as response header of OPTIONS request. []string Headers from request
grpc.interceptors.cors.allowCredentials Returns as response header of OPTIONS request. bool false
grpc.interceptors.cors.exposeHeaders Provide exposed headers returns as response header of OPTIONS request. []string ""
grpc.interceptors.cors.maxAge Provide max age returns as response header of OPTIONS request. int 0

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      cors:
        enabled: true                 # Optional, default: false
        # Accept all origins from localhost
        allowOrigins:
          - "http://localhost:*"      # Optional, default: *

2.Create main.go

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Create cors.html

<!DOCTYPE html>
<html>
<body>

<h1>CORS Test</h1>

<p>Call http://localhost:8080/rk/v1/healthy</p>

<script type="text/javascript">
    window.onload = function() {
        var apiUrl = 'http://localhost:8080/rk/v1/healthy';
        fetch(apiUrl).then(response => response.json()).then(data => {
            document.getElementById("res").innerHTML = data["healthy"]
        }).catch(err => {
            document.getElementById("res").innerHTML = err
        });
    };
</script>

<h4>Response: </h4>
<p id="res"></p>

</body>
</html>

4.Directory hierarchy

.
├── boot.yaml
├── cors.html
├── go.mod
├── go.sum
└── main.go

0 directories, 5 files

5.Validate

Open cors.html

6.With blocking CORS

Set grpc.interceptors.cors.allowOrigins to http://localhost:8080 in order to block request.

---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      cors:
        enabled: true                 # Optional, default: false
        allowOrigins:
          - "http://localhost:8080"   # Optional, default: *

Cheers

15 - Middleware secure

Enable secure interceptor/middleware for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a gRPC server with rk-boot

name description type default value
grpc.name The name of grpc server string N/A
grpc.port The port of grpc server integer nil, server won’t start
grpc.enabled Enable grpc entry bool false
grpc.description Description of grpc entry. string ""

Secure options

name description type default value
grpc.interceptors.secure.enabled Enable secure interceptor boolean false
grpc.interceptors.secure.xssProtection X-XSS-Protection header value. string “1; mode=block”
grpc.interceptors.secure.contentTypeNosniff X-Content-Type-Options header value. string nosniff
grpc.interceptors.secure.xFrameOptions X-Frame-Options header value. string SAMEORIGIN
grpc.interceptors.secure.hstsMaxAge Strict-Transport-Security header value. int 0
grpc.interceptors.secure.hstsExcludeSubdomains Excluding subdomains of HSTS. bool false
grpc.interceptors.secure.hstsPreloadEnabled Enabling HSTS preload. bool false
grpc.interceptors.secure.contentSecurityPolicy Content-Security-Policy header value. string ""
grpc.interceptors.secure.cspReportOnly Content-Security-Policy-Report-Only header value. bool false
grpc.interceptors.secure.referrerPolicy Referrer-Policy header value. string ""
grpc.interceptors.secure.ignorePrefix Ignoring path prefix. []string []

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      secure:
        enabled: true                 # Optional, default: false

2.Create main.go

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

$ curl -vs localhost:8080/rk/v1/healthy
  ...
  < X-Content-Type-Options: nosniff
  < X-Frame-Options: SAMEORIGIN
  < X-Xss-Protection: 1; mode=block
  < Date: Sun, 05 Dec 2021 18:32:24 GMT
  < Content-Length: 17
  <
  ...

Cheers

16 - Middleware CSRF

Enable CSRF interceptor/middleware for the server.

Installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc

General options

These are general options to start a gRPC server with rk-boot

name description type default value
grpc.name The name of grpc server string N/A
grpc.port The port of grpc server integer nil, server won’t start
grpc.enabled Enable grpc entry bool false
grpc.description Description of grpc entry. string ""

CSRF options

Interceptor for grpc-gateway.

name description type default value
grpc.interceptors.csrf.enabled Enable csrf interceptor boolean false
grpc.interceptors.csrf.tokenLength Provide the length of the generated token. int 32
grpc.interceptors.csrf.tokenLookup Provide csrf token lookup rules, please see code comments for details. string “header:X-CSRF-Token”
grpc.interceptors.csrf.cookieName Provide name of the CSRF cookie. This cookie will store CSRF token. string _csrf
grpc.interceptors.csrf.cookieDomain Domain of the CSRF cookie. string ""
grpc.interceptors.csrf.cookiePath Path of the CSRF cookie. string ""
grpc.interceptors.csrf.cookieMaxAge Provide max age (in seconds) of the CSRF cookie. int 86400
grpc.interceptors.csrf.cookieHttpOnly Indicates if CSRF cookie is HTTP only. bool false
grpc.interceptors.csrf.cookieSameSite Indicates SameSite mode of the CSRF cookie. Options: lax, strict, none, default string default
grpc.interceptors.csrf.ignorePrefix Ignoring path prefix. []string []

Quick start

1.Create boot.yaml

---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    interceptors:
      csrf:
        enabled: true                 # Optional, default: false

2.Create main.go

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"fmt"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-grpc/boot"
	"net/http"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Register handler in grpc-gateway
	grpcEntry := boot.GetEntry("greeter").(*rkgrpc.GrpcEntry)
	grpcEntry.GwMux.HandlePath("GET", "/v1/greeter", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
		w.Write([]byte(fmt.Sprintf("Hello %s!", r.URL.Query().Get("name"))))
	})

	grpcEntry.GwMux.HandlePath("POST", "/v1/greeter", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
		w.Write([]byte(fmt.Sprintf("Hello %s!", r.URL.Query().Get("name"))))
	})

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

3.Validate

  • Send GET request, no csrf validation expected and a new cookie should be there.
$ curl -X GET -vs localhost:8080/v1/greeter
  ...
  < HTTP/1.1 200 OK
  < Content-Type: application/json; charset=UTF-8
  < Set-Cookie: _csrf=WyOJLwzhfUGAMDHglkuIRucdpalxolWg; Expires=Mon, 06 Dec 2021 18:38:45 GMT
  < Vary: Cookie
  < Date: Sun, 05 Dec 2021 18:38:45 GMT
  < Content-Length: 22
  <
  {"Message":"Hello *!"}
  • POST method with happy case
$ curl -X POST -v --cookie "_csrf=my-test-csrf-token" -H "X-CSRF-Token:my-test-csrf-token" localhost:8080/v1/greeter
  ...
  > Cookie: _csrf=my-test-csrf-token
  > X-CSRF-Token:my-test-csrf-token
  >
  < HTTP/1.1 200 OK
  < Content-Type: application/json; charset=UTF-8
  < Set-Cookie: _csrf=my-test-csrf-token; Expires=Mon, 06 Dec 2021 18:40:13 GMT
  < Vary: Cookie
  < Date: Sun, 05 Dec 2021 18:40:13 GMT
  < Content-Length: 22
  <
  {"Message":"Hello *!"}
  • POST method with invalid csrf
$ curl -X POST -v -H "X-CSRF-Token:my-test-csrf-token" localhost:8080/v1/greeter
  ...
  > X-CSRF-Token:my-test-csrf-token
  >
  < HTTP/1.1 403 Forbidden
  < Content-Type: application/json; charset=UTF-8
  < Date: Sun, 05 Dec 2021 18:41:00 GMT
  < Content-Length: 92
  <
  {"error":{"code":403,"status":"Forbidden","message":"invalid csrf token","details":[null]}}

Cheers