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
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 - Swagger UI

Enable swagger UI for server.

Prerequisite

We will use swag to generate swagger UI config files.

Option 1: With RK CMD

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

# Install swag with rk
$ rk install swag

Option 2: From swag the official repository:

$ go get -u github.com/swaggo/swag/cmd/swag

Installation

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

General options

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

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

Swagger options

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

Quick start

1.Create boot.yaml

In order to make rk-boot finds out available swagger config files, we need to add echo.sw.jsonPath in boot.yaml file.

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    sw:
      enabled: true
      jsonPath: "docs"
#      path: "sw"        # Default value is "sw", change it as needed
#      headers: []       # Headers that will be set while accessing swagger UI main page.

2.Create main.go

In order to generate swagger config with swag, we need to add comments as bellow.

package main

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

// @title RK Swagger for Echo
// @version 1.0
// @description This is a greeter service with rk-boot.

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

	// Register handler
	echoEntry := boot.GetEntry("greeter").(*rkecho.EchoEntry)
	echoEntry.Echo.GET("/v1/greeter", Greeter)

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

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

// @Summary Greeter service
// @Id 1
// @version 1.0
// @produce application/json
// @Param name query string true "Input name"
// @Success 200 {object} GreeterResponse
// @Router /v1/greeter [get]
func Greeter(ctx echo.Context) error {
	return ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.QueryParam("name")),
	})
}

// Response.
type GreeterResponse struct {
	Message string
}

3.Generate swagger config

$ swag init

# swagger.json, swagger.yaml, docs.go files will be generated under ./docs folder.
$ tree
.
├── boot.yaml
├── docs
│   ├── docs.go
│   ├── swagger.json
│   └── swagger.yaml
├── go.mod
├── go.sum
└── main.go

1 directory, 7 files

4.Validate

Swagger: http://localhost:8080/sw

Cheers

2 - Common Service

Enable common service for server.

Overview

Common service contains builtin API commonly used by user.

Path Description
/rk/v1/apis List APIs in current EchoEntry.
/rk/v1/certs List CertEntry.
/rk/v1/configs List ConfigEntry.
/rk/v1/deps List dependencies related application, entire contents of go.mod file would be returned.
/rk/v1/entries List all Entries.
/rk/v1/gc Trigger GC
/rk/v1/healthy Get application healthy status.
/rk/v1/info Get application and process info.
/rk/v1/license Get license related application, entire contents of LICENSE file would be returned.
/rk/v1/logs List logger related entries.
/rk/v1/git Get git information.
/rk/v1/readme Get contents of README file.
/rk/v1/req List prometheus metrics of requests.
/rk/v1/sys Get OS stat.
/rk/v1/tv Get HTML page of /tv.

Installation

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

General options

These are general options to start an echo server with rk-boot

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

CommonService options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    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-echo/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
{"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.

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true     # Enable common service
    sw:
      enabled: true     # Enable swagger UI

Validate

http://localhost:8080/sw

sw-common

Cheers

3 - 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-echo

General options

These are general options to start an echo server with rk-boot

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

TV options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    tv:
      enabled: true     # Enable TV

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-echo/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

4 - Prometheus client

Enable prometheus client for server.

Installation

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

General options

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

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

Prometheus options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    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-echo/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-echo/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
	echoEntry := boot.GetEntry("greeter").(*rkecho.EchoEntry)
	set := rkprom.NewMetricsSet("rk", "demo", echoEntry.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.

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    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

5 - Middleware logging

Enable RPC logging interceptor/middleware for server.

Installation

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

General options

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

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

Logging options

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

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-05T23:35:17.104+0800    INFO    boot/echo_entry.go:631   Bootstrapping EchoEntry. {"eventId": "08d11a34-472a-4785-a98c-144a3417d7f0", "entryName": "greeter", "entryType": "EchoEntry", "port": 8080, "interceptorsCount": 1, "swEnabled": false, "tlsEnabled": false, "commonServiceEnabled": false, "tvEnabled": false, "promPath": "/metrics", "promPort": 8080}

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-06-25T01:30:45.144023+08:00
startTime=2021-06-25T01:30:45.143767+08:00
elapsedNano=255948
timezone=CST
ids={"eventId":"3332e575-43d8-4bfe-84dd-45b5fc5fb104","requestId":"3332e575-43d8-4bfe-84dd-45b5fc5fb104","traceId":"65b9aa7a9705268bba492fdf4a0e5652"}
app={"appName":"rk-echo","appVersion":"master-xxx","entryName":"greeter","entryType":"EchoEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/rk/v1/healthy","apiProtocol":"HTTP/1.1","apiQuery":"","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:60718
operation=/rk/v1/healthy
resCode=200
eventStatus=Ended
EOE

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      loggingZap:
        enabled: true        # Enable logging interceptor/middleware, by default, stdout would be destination of log

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-echo/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-05T23:42:35.588164+08:00
startTime=2021-07-05T23:42:35.588095+08:00
elapsedNano=69414
timezone=CST
ids={"eventId":"9b874eea-b16b-4c46-b0f5-d2b7cff6844e"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"EchoEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/rk/v1/healthy","apiProtocol":"HTTP/1.1","apiQuery":"","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:56274
operation=/rk/v1/healthy
resCode=200
eventStatus=Ended
EOE

Cheers

4.JSON format

---
echo:
  - 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.

---
echo:
  - 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 Greeter(ctx echo.Context) error {
	rkechoctx.GetLogger(ctx).Info("Received request")

	return ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.QueryParam("name")),
	})
}
2021-07-06T02:04:55.306+0800    INFO    basic/main.go:39        Received request        {"requestId": "b8522178-9fac-47d3-b866-ce43e119f7a3"}

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 Greeter(ctx echo.Context) error {
	event := rkechoctx.GetEvent(ctx)
	event.AddPair("key", "value")

	return ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.QueryParam("name")),
	})
}
------------------------------------------------------------------------
endTime=2021-07-06T02:08:40.929323+08:00
startTime=2021-07-06T02:08:40.929191+08:00
elapsedNano=132017
timezone=CST
ids={"eventId":"8f5cfe20-12c7-4208-995a-b113c7fc46a1","requestId":"8f5cfe20-12c7-4208-995a-b113c7fc46a1"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"EchoEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/greeter","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={"key":"value"}
timing={}
remoteAddr=localhost:57311
operation=/v1/greeter
resCode=200
eventStatus=Ended
EOE

Cheers

6 - Middleware metrics

Enable RPC metrics interceptor/middleware for server.

Installation

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

General options

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

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

Metrics options

name description type default value
echo.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 echo entry name
entryType echo 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
resCode Response code, eg: 200

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    prom:
      enabled : true         # Enable prometheus client in order to export metrics
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      metricsProm:
        enabled: true        # Enable metrics interceptor/middleware

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-echo/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

7 - Middleware 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-echo

General options

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

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

Meta options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      meta:
        enabled: true        # Enable meta interceptor/middleware

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-echo/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 -vs -X GET localhost:8080/rk/v1/healthy
  ...
  < X-Request-Id: c2ec237e-e141-45af-a4ff-89ce8a9d0636
  < X-Rk-App-Name: rk-demo
  < X-Rk-App-Unix-Time: 2021-07-06T02:46:31.977902+08:00
  < X-Rk-App-Version: master-f414049
  < X-Rk-Received-Time: 2021-07-06T02:46:31.977902+08:00
  ...
  {"healthy":true}

Cheers

4.Override requestId

func Greeter(ctx echo.Context) error {
    // Override request id
	rkechoctx.SetHeaderToClient(ctx, rkechoctx.RequestIdKey, "request-id-override")
    // We expect new request id attached to logger
	rkechoctx.GetLogger(ctx).Info("Received request")

	return ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.QueryParam("name")),
	})
}
$ curl -vs -X GET "localhost:8080/v1/greeter?name=rk-dev"
...
< X-Request-Id: request-id-override
< X-Rk-App-Name: rk-demo
< X-Rk-App-Unix-Time: 2021-07-06T02:49:34.27756+08:00
< X-Rk-App-Version: master-f414049
< X-Rk-Received-Time: 2021-07-06T02:49:34.27756+08:00
...
{"Message":"Hello rk-dev!"}

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

2021-07-06T02:49:34.277+0800    INFO    basic/main.go:41        Received request        {"requestId": "request-id-override"}
------------------------------------------------------------------------
endTime=2021-07-06T02:49:34.27773+08:00
startTime=2021-07-06T02:49:34.277544+08:00
elapsedNano=185412
timezone=CST
ids={"eventId":"request-id-override","requestId":"request-id-override"}
app={"appName":"rk-demo","appVersion":"master-f414049","entryName":"greeter","entryType":"EchoEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/greeter","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:57600
operation=/v1/greeter
resCode=200
eventStatus=Ended
EOE

Cheers

5.Override header prefix

---
echo:
  - 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-06T02:52:53.035941+08:00
< X-Override-App-Version: master-f414049
< X-Override-Received-Time: 2021-07-06T02:52:53.035941+08:00
< X-Request-Id: c3097361-1833-4bba-9867-e8d1f2fb2207
...
{"healthy":true}

Cheers

8 - Middleware tracing

Enable RPC tracing interceptor/middleware for server.

Installation

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

General options

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

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

Tracing options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      tracingTelemetry:
        enabled: true        # Enable tracing interceptor/middleware
        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-echo/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}
[
        {
                "SpanContext": {
                        "TraceID": "4b59250ca328450045efe841a443e77d",
                        "SpanID": "4d3545e0c0da05c3",
                        "TraceFlags": "01",
                        "TraceState": null,
                        "Remote": false
                },
                "Parent": {
                        "TraceID": "00000000000000000000000000000000",
                        "SpanID": "0000000000000000",
                        "TraceFlags": "00",
                        "TraceState": null,
                        "Remote": true
                },
                "SpanKind": 2,
                "Name": "/rk/v1/healthy",
                "StartTime": "2021-07-06T03:06:50.871097+08:00",
                "EndTime": "2021-07-06T03:06:50.871167677+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.

---
echo:
  - 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
---
echo:
  - 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

9 - Middleware auth

Enable RPC auth interceptor/middleware for server.

Installation

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

General options

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

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

Auth options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: 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-echo/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":[]
    }
}

Cheers

4.With X-API-Key auth type

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

Cheers

5.Ignore paths

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

Cheers

10 - Middleware 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-echo

General options

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

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

Rate limit options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter
    port: 8080
    enabled: true
    commonService:
      enabled: true          # Enable common service for testing
    interceptors:
      rateLimit:
        enabled: true
        algorithm: "leakyBucket"
        reqPerSec: 100
        paths:
          - path: "/rk/v1/healthy"
            reqPerSec: 0

2.Create main.go

package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-echo/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
{
    "error":{
        "code":429,
        "status":"Too Many Requests",
        "message":"",
        "details":[
            "slow down your request"
        ]
    }
}

Cheers

11 - Middleware timeout

Enable timeout interceptor/middleware for the server.

Installation

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

General options

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

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

Timeout options

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

Quick start

1.Create boot.yaml

---
echo:
  - 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/v1/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-echo/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/gc
{
    "error":{
        "code":408,
        "status":"Request Timeout",
        "message":"Request timed out!",
        "details":[]
    }
}

Cheers

12 - Middleware gzip

Enable gzip interceptor/middleware for the server.

Installation

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

General options

These are general options to start an echo server with rk-boot

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

Gzip options

name description type default value
echo.interceptors.gzip.enabled Enable gzip interceptor boolean false
echo.interceptors.gzip.level Provide level of compression, options are noCompression, bestSpeed, bestCompression, defaultCompression, huffmanOnly. string defaultCompression

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      gzip:
        enabled: true                 # Optional, default: false
        level: bestSpeed              # Optional, options: [noCompression, bestSpeed, bestCompression, defaultCompression, huffmanOnly]

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/labstack/echo/v4"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-echo/boot"
	"io"
	"net/http"
	"strings"
)

// @title RK Swagger for Echo
// @version 1.0
// @description This is a greeter service with rk-boot.

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

	// Register handler
    echoEntry := boot.GetEntry("greeter").(*rkecho.EchoEntry)
	echoEntry.Echo.POST("/v1/post", post)

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

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

// PostResponse Response of Post.
type PostResponse struct {
	ReceivedMessage string
}

// post Handler.
func post(ctx echo.Context) error {
	buf := new(strings.Builder)
	io.Copy(buf, ctx.Request().Body)

	ctx.JSON(http.StatusOK, &PostResponse{
		ReceivedMessage: fmt.Sprintf("%s", buf.String()),
	})

	return nil
}

3.Validate

Send request

$ echo 'this is message' | gzip | curl --compressed --data-binary @- -H "Content-Encoding: gzip" -H "Accept-Encoding: gzip" localhost:8080/v1/post
{"ReceivedMessage":"this is message\n"}

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-echo

General options

These are general options to start an echo server with rk-boot

name description type default value
echo.name The name of echo server string N/A
echo.port The port of echo server integer nil, server won’t start
echo.enabled Enable echo entry bool false
echo.description Description of echo 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
echo.interceptors.jwt.enabled Enable JWT interceptor boolean false
echo.interceptors.jwt.signingKey Required, Provide signing key. string ""
echo.interceptors.jwt.ignorePrefix Provide ignoring path prefix. []string []
echo.interceptors.jwt.signingKeys Provide signing keys as scheme of :. []string []
echo.interceptors.jwt.signingAlgo Provide signing algorithm. string HS256
echo.interceptors.jwt.tokenLookup Provide token lookup scheme, please see bellow description. string “header:Authorization”
echo.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

---
echo:
  - 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 rkdemo

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
    _ "github.com/rookie-ninja/rk-echo/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"
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"invalid or expired jwt",
        "details":[
            "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-echo

General options

These are general options to start an echo server with rk-boot

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

CORS options

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

Quick start

1.Create boot.yaml

---
echo:
  - 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-echo/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 echo.interceptors.cors.allowOrigins to http://localhost:8080 in order to block request.

---
echo:
  - 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-echo

General options

These are general options to start an echo server with rk-boot

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

Secure options

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

Quick start

1.Create boot.yaml

---
echo:
  - 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-echo/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-echo

General options

These are general options to start an echo server with rk-boot

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

CSRF options

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

Quick start

1.Create boot.yaml

---
echo:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    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/labstack/echo/v4"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-echo/boot"
	"net/http"
)

// @title RK Swagger for Echo
// @version 1.0
// @description This is a greeter service with rk-boot.

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

	// Register handler
	echoEntry := boot.GetEntry("greeter").(*rkecho.EchoEntry)
	echoEntry.Echo.POST("/v1/greeter", Greeter)
	echoEntry.Echo.GET("/v1/greeter", Greeter)

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

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

// @Summary Greeter service
// @Id 1
// @version 1.0
// @produce application/json
// @Param name query string true "Input name"
// @Success 200 {object} GreeterResponse
// @Router /v1/greeter [get]
func Greeter(ctx echo.Context) error {
	return ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.QueryParam("name")),
	})
}

// Response.
type GreeterResponse struct {
	Message string
}

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