nelly

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2020 License: MIT Imports: 20 Imported by: 0

README

nelly

Build Status GoDoc Go Report Card Coverage

A high performance and modern HTTP Middleware for Golang (inspired by Kubernetes API server)

nelly

Introduction

Nelly is a minimal chaining middleware, that is designed to work directly with julienschmidt/httprouter for its high performance and lightweight implementation. Further, it provides some default and useful middleware handlers. Nelly is inspired by Kubernetes API server filters and the chaining middleware Alice which uses the traditional net/http handlers.

A list of supported handlers which are recommended to be used in the following order if they are chained:

Getting Started

A chian is an immutable list of a middleware handlers. Its handlers have an order where the request pass through the chain. The middleware handlers have the form

// nelly package
type Handler func(httprouter.Handle) httprouter.Handle

where the httprouter handle has the form

// httprouter package
type Handle func(http.ResponseWriter, *http.Request, Params)

To write a new middleware handler that could be chained to other middleware handlers as in the following example:

func someHandler() Handler {

	fn := func(h httprouter.Handle) httprouter.Handle {
		return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
			// middleware handler implementation goes here
			h(w, r, p)
		}
	}

	return fn
}

Further, any of the default middleware handlers that is provided by nelly could be used to create a new chain. To create a new chain form a set of handlers:

chain := nelly.NewChain(someHandler, otherHanlder, ...)

To wrap your handler appHandler (httprouter.Handle) with the created chain:

func appHandler() httprouter.Handle {
	fn := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		// httprouter.Handle implementation goes here
		return
	}
	return fn
}

chainedHandler := chain.Then(appHandler())

Then you can use the created chainedHandler with httprouter

// create new router
router := httprouter.New()

// use chainedHandler (httprouter.Handle)
router.GET("/", chainedHandler)
log.Fatal(http.ListenAndServe(":8080", router))

The requests will pass someHandler first, then otherHanlder till the end of the set of the passed handlers to NewChain in the same order, and finally to appHandler which is equivalent to:

someHandler(otherHanlder(...(appHandler()))

For more flexible usage of the created chain, you can use Append(handler) or Extend(chain):

  • Append - will return a new chain, leaving the original one untouched, and adds the specified middleware handlers as the last ones in the request flow of the original chain.

  • Extend - will return a new chain, leaving the original one untouched, and the specified chain as the last one in the request flow of the original chain.

// Create new chian
chain := nelly.NewChain(handler_A, handler_B)

// Append to the chain some handlers
chain_1 := chain.Append(handler_C, handler_D)

// Create another chain
chain_2 := nelly.NewChain(handler_E, handler_F)

// Create new chian by chaining chain1 with chain2
newChain := chain1.Extend(chain2)

// wrap the appHandler with the created chain
chainedHandler := chain2.Then(appHandler())

In previous example using chainedHandler in httprouter will pass the requests as follow

handler_A -> handler_B -> handler_C -> handler_D -> handler_E -> handler_F -> appHandler

Default Handlers

The Classic() version of nelly returns a new Chain with some default middleware handlers already in the chain with the following order:

WithPanicRecovery() -> WithLogging() -> WithInstrument() -> WithCacheControl()

To use classic version:

// create classic chain
classicChain := nelly.Classic()

// extend the classic chain with some default chain (WithCORS)
chian := classicChain.Append(WithCORS...)

Using any of the default handlers which implements middleware handler is recommended in the following order:

Recovery

Tracing (OpenTelemetry)

Nelly had a support for tracing using OpenTelemetry which has been deprecated inf the favour of othttp (OpenTelemetry HTTP Handler) which only support the traditional net/http handler. Unfortunately, it is not possible to use othttp directly with nelly middleware, but it could be used with julienschmidt/httprouter as follow:

// import "go.opentelemetry.io/otel/instrumentation/othttp"

// create new router
router := httprouter.New()

// use chainedHandler (httprouter.Handle)
router.GET("/", chainedHandler)

// wrap httprouter with OpenTelemetry http
otWrap := othttp.NewHandler(router, "server")

log.Fatal(http.ListenAndServe(":8080", otWrap))

The router will be wrapped with othttp handler which support the traditional net/http Handler.

Todo:

  • Improve metrics handler
  • Improve timeout for long running requests

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterMetrics added in v1.1.0

func RegisterMetrics()

RegisterMetrics registers metrics of all Nelly supported middlewares

Types

type CORSOpts added in v1.1.1

type CORSOpts struct {
	AllowedOriginPatterns []string
	AllowedMethods        []string
	AllowedHeaders        []string
	ExposedHeaders        []string
	AllowCredentials      bool
}

CORSOpts is the configuration that will be used by WithCORS

type Chain

type Chain struct {
	// contains filtered or unexported fields
}

Chain is a list of a middleware handlers. Chain is effectively immutable: once created, it will always hold the same set of handlers in the same order.

func Classic

func Classic() Chain

Classic returns a new Chain with some default middleware handlers already in the chain with the following order:

WithPanicRecovery (Panic Recovery Handler), WithLogging (Logging Handler), WithInstrument (Prometheus Metrics Handler), WithCacheControl (Cache-Control header handler)

func NewChain

func NewChain(handlers ...Handler) Chain

NewChain creates a new chain, with the given list of handlers.

func (Chain) Append

func (s Chain) Append(handlers ...Handler) Chain

Append extends a chain, adding the specified handlers as the last ones in the request flow.

Append returns a new chain, leaving the original one untouched.

func (Chain) Extend

func (s Chain) Extend(chain Chain) Chain

Extend extends a chain by adding the specified chain as the last one in the request flow.

Extend returns a new chain, leaving the original one untouched.

func (Chain) Then

Then chains the handlers and returns the final httprouter.Handle.

NewChain(m1, m2, m3).Then(h)

is equivalent to:

m1(m2(m3(h)))

When the request comes in, it will be passed to m1, then m2, then m3 and finally, the given handler (assuming every handlers calls the following one).

type Handler

type Handler func(httprouter.Handle) httprouter.Handle

A Handler (middleware handler) is a generic function that takes httprouter.Handle and retrun httprouter.Handle. It is differnet from the common signature of middleware handler that use http.Handler because it uses julienschmidt/httprouter instead.

func WithAuthSigningMethodHS256

func WithAuthSigningMethodHS256(secret string, audience string, issuer string) Handler

WithAuthSigningMethodHS256 handler authinticates requests with JWT token using HS256 algorithm

func WithAuthSigningMethodRS256

func WithAuthSigningMethodRS256(jwksEndpoint string, audience string, issuer string) Handler

WithAuthSigningMethodRS256 handler authinticates requests with JWT token using RS256 algorithm

func WithCORS

func WithCORS(opts CORSOpts) Handler

WithCORS handler is a simple CORS implementation that wraps an httprouter.Handle. Pass nil for allowedMethods and allowedHeaders to use the defaults. If allowedOriginPatterns is empty, no CORS support is installed.

func WithCacheControl

func WithCacheControl() Handler

WithCacheControl handler sets the Cache-Control header to "no-cache, private" because all servers are supposed to be protected by authn/authz. see https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#defining_optimal_cache-control_policy

func WithInstrument

func WithInstrument() Handler

WithInstrument handler wraps httprouter.Handle to record prometheus metrics Recorded metrics:

func WithLogging

func WithLogging() Handler

WithLogging handler wraps httprouter.Handle with logging

func WithPanicRecovery

func WithPanicRecovery() Handler

WithPanicRecovery handler wraps an httprouter.Handle to recover and log panics

func WithRequiredHeaderValues

func WithRequiredHeaderValues(requiredHeaderValues map[string]string) Handler

WithRequiredHeaderValues handler checks if a map of headers are set on requests. If any header from the map doesn't exist or don't equal the values in requiredHeaderValues, the handler will return StatusBadRequest

func WithRequiredHeaders

func WithRequiredHeaders(requiredHeaders []string) Handler

WithRequiredHeaders handler checks if a list of headers are set on requests. If any header from the list doesn't exist, the handler will return StatusBadRequest

func WithTimeoutForNonLongRunningRequests added in v1.1.1

func WithTimeoutForNonLongRunningRequests(requestTimeout time.Duration) Handler

WithTimeoutForNonLongRunningRequests handler times out non-long-running requests after the duration given by requestTimeout.

type JSONWebKeys

type JSONWebKeys struct {
	Kty string   `json:"kty"`
	Kid string   `json:"kid"`
	Use string   `json:"use"`
	N   string   `json:"n"`
	E   string   `json:"e"`
	X5c []string `json:"x5c"`
}

JSONWebKeys is a JSON Web Key

type Jwks

type Jwks struct {
	Keys []JSONWebKeys `json:"keys"`
}

Jwks is a set of keys which contains the public keys used to verify JWT issued by the authorization server and signed using the RS256 signing algorithm.

type StacktracePred

type StacktracePred func(httpStatus int) (logStacktrace bool)

StacktracePred returns true if a stacktrace should be logged for this status.

func StatusIsNot

func StatusIsNot(statuses ...int) StacktracePred

StatusIsNot returns a StacktracePred which will cause stacktraces to be logged for any status *not* in the given list.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL