middleware

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2022 License: MIT Imports: 13 Imported by: 4

README

go-middleware

GoDoc License [![Build Status][circleci-badge]][circleci-link] Report Card GoCover

Golang HTTP middleware

Installation

Since go-middleware is an open source project and repository is public you can simply install it with go get:

$ go get github.com/tiny-go/middleware
Currently available middleware
  • BodyClose - closes request body for each request
  • ContextDeadline - sets request timeout (demands additional logic in your app)
  • PanicRecover - catches the panics inside our chain, can be used as error handler (similar to try/catch) with corresponding panic handler
  • SetHeaders - provides an easy way to set response headers
  • JwtHS256 - verifies JWT (JSON Web Token) signed with HMAC signing method and parses its body to the provided receiver that is going to be available to next handlers through the request context
  • Codec - searches for suitable request/response codecs according to "Content-Type"/"Accept" headers and puts them into the context
Experimental middleware

It means that work is still in progress, a lot of things can be changed or even completely removed

  • AsyncRequest - allows to set request timeout (for HTTP request) and async timeout (for background execution), if request has not been processed during request timeout - middleware returns request ID and HTTP code 202 (Accepted). You can make a new request wtih given request ID later to obtain the result. The result can be provided only once and won't be available after that anymore. If handler did not finish its task during async timeout - middleware sends an HTTP error with code 408 (RequestTimeout) executing next async request with current request ID.
Examples
  1. Build the handler using middleware chaining functions:
  • New() - start the chain. Can accept 0 (zero) or more arguments.

  • Use() - add middleware to existing chain.

  • Then() - set the final handler (which is http.Handler).

    package main
    
    import (
    	"log"
    	"net/http"
    	"os"
    
    	"github.com/tiny-go/middleware"
    	"github.com/tiny-go/codec/driver"
    	"github.com/tiny-go/codec/driver/json"
    	"github.com/tiny-go/codec/driver/xml"
    )
    
    var (
        panicHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
    	    panic("something went wrong")
        }
    )
    
    func main() {
    	http.Handle(
    		"/",
    		middleware.
    			// with HTTP panic handler
    			New(middleware.PanicRecover(middleware.PanicHandler)).
    			Use(middleware.BodyClose).
    			Use(middleware.Codec(driver.DummyRegistry{&json.JSON{}, &xml.XML{}})).
    			Then(panicHandler),
    	)
    	log.Fatal(http.ListenAndServe(":8080", nil))
    }
    
    
  1. Build the handler with Chain() (variadic) func which accepts the next list of argument types:
  • http.Handler and everything else that implements this interface (for instance, http.HandlerFunc)

  • func(w http.ResponseWriter, r *http.Request)

  • MiddlewareFunc/func(http.ResponseWriter, *http.Request, http.Handler)

  • Middleware/func(http.Handler) http.Handler

    There is no sense to provide entire example since import and variable declaration sections are going to be the same, only main() func is going to be changed:

    func main() {
    	http.Handle(
    		"/",
    		middleware.Chain(
    			// with custom panic handler
    			middleware.PanicRecover(func(_ http.ResponseWriter, r interface{}) {
    				if r != nil {
    					log.Println(r)
    				}
    			}),
    			middleware.BodyClose,
    			panicHandler,
    		),
    	)
    	log.Fatal(http.ListenAndServe(":8080", nil))
    }
    

Documentation

Index

Constants

View Source
const StatusNoResponse = 444

StatusNoResponse is returned when request is canceled

Variables

View Source
var (
	// ErrNotCompleted - current job was not completed.
	ErrNotCompleted = errors.New("task has not been completed")
	// ErrNotStarted - current job was not started.
	ErrNotStarted = errors.New("job has not been started")
	// ErrAlreadyDone - current job has been already done.
	ErrAlreadyDone = errors.New("job already completed")
)
View Source
var DefaultTimeFormat = time.RFC1123

DefaultTimeFormat contains default date/time layout to be used across middlewares of the package.

Functions

func Bearer

func Bearer(r *http.Request) (string, bool)

Bearer gets the bearer out of a given request object.

func BodyClose

func BodyClose(next http.Handler) http.Handler

BodyClose is a middleware that closes the request body after each request.

func Chain

func Chain(handlers ...interface{}) http.Handler

Chain builds a http.Handler from passed arguments. It accepts different kinds of argument types:

  • MiddlewareFunc
  • func(http.ResponseWriter, *http.Request, http.Handler)
  • Middleware
  • func(http.Handler) http.Handler
  • http.Handler
  • func(w http.ResponseWriter, r *http.Request)

Keep in mind:

- by passing http.Handler/http.HandlerFunc instead of Middleware you lose control over the next Middleware (no way to cancel it), but if you only need to put something to the context (and do not have any logic after calling "next") there is no sense to build Middleware func around

- even if you do not pass any handlers blobHandler will be executed.

func ClaimsFromContextTo

func ClaimsFromContextTo(ctx context.Context, recv interface{}) (err error)

ClaimsFromContextTo retrieves claims from context and assigns to the provided receiver.

func ContextHandler

func ContextHandler(next http.Handler) http.Handler

ContextHandler reads from context.Done channel to handle deadline/timeout

func Go

func Go(ctx context.Context, handler func(stop <-chan struct{}) error) error

Go function runs the handler and processes its execuion in a new goroutine. It also passes Done channel to the handler. Once channel is closed handler should be able to stop its goroutine to avoid resource leaks.

func RequestCodecFromContext

func RequestCodecFromContext(ctx context.Context) codec.Codec

RequestCodecFromContext pulls the Codec from a request context or returns nil.

func RequestID added in v1.1.0

func RequestID(next http.Handler) http.Handler

RequestID is a middleware that injects a request ID into the context of each request. A request ID is a string (randomly generated UUID).

func RequestIDFromContext added in v1.1.0

func RequestIDFromContext(ctx context.Context) string

RequestIDFromContext pulls request ID from the context or empty string.

func ResponseCodecFromContext

func ResponseCodecFromContext(ctx context.Context) codec.Codec

ResponseCodecFromContext pulls the Codec from a request context or returns nil.

Types

type BaseController

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

BaseController provides ability to register/unregister middleware for HTTP methods. This is a basic Controller implementation.

func NewBaseController

func NewBaseController() *BaseController

NewBaseController is a constructor func for a basic HTTP controller.

func (*BaseController) AddMiddleware

func (bc *BaseController) AddMiddleware(method string, chain ...Middleware) Controller

AddMiddleware adds middleware funcs to existing ones for provided HTTP method.

func (*BaseController) Init

func (bc *BaseController) Init() error

Init does nothing. This is a default function to avoid explicit declaration when controller does not require any Init logic.

func (*BaseController) Middleware

func (bc *BaseController) Middleware(method string) Middleware

Middleware returns middleware func registered for provided method or an empty list.

type Claims

type Claims interface {
	Valid() error
}

Claims interface.

type ClaimsFactory

type ClaimsFactory func() Claims

ClaimsFactory is a func that returns a new custom claims when called.

type Codecs

type Codecs interface {
	// Lookup should find appropriate Codec by MimeType or return nil if not found.
	Lookup(mimeType string) codec.Codec
}

Codecs represents any kind of codec registry, thus it can be global registry or a custom list of codecs that is supposed to be used for particular route.

type Controller

type Controller interface {
	// AddMiddleware should make the provided chain available by HTTP method.
	AddMiddleware(method string, chain ...Middleware) Controller
	// Middleware should return the list of middleware functions registered for provided method.
	Middleware(method string) Middleware
}

Controller represents simple HTTP controller containing middleware for each method.

type HandlerTask

type HandlerTask interface {
	// Do should execute provided closure.
	Do(context.Context, func(<-chan struct{}) error)
	// Status should return the status of current job/task.
	Status() JobStatus
	// Complete is supposed to be called inside Do's closure when job is done in
	// order to change job status and be able to return the result by calling
	// Resolve() func.
	Complete(interface{}, error) error
	// Resolve returns the result (which should be returned from the clousere using
	// Complete()) and error.
	Resolve() (interface{}, error)
}

HandlerTask represents sync/async handler task.

func GetHandlerTask

func GetHandlerTask(ctx context.Context) (HandlerTask, bool)

GetHandlerTask extracts current job from context.

type JWTParser

type JWTParser interface {
	Parse(jwt string, recv *Claims) error
}

JWTParser interface is responsible for token validation, meanwhile it parses token to the provided receiver.

type JobStatus

type JobStatus int

JobStatus represents the status of asynchronous task.

const (
	// StatusWaiting is initial status - job is not started yet.
	StatusWaiting JobStatus = iota
	// StatusInProgress indicates that job is started but not finished yet.
	StatusInProgress
	// StatusDone indicates that task was done.
	StatusDone
)

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware in itself simply takes a http.Handler as the first parameter, wraps it and returns a new http.Handler for the server to call (wraps handlers with closures according to the principle of Russian doll).

func AsyncRequest

func AsyncRequest(reqTimeout, asyncTimeout, keepResult time.Duration) Middleware

AsyncRequest func creates a middleware that provides a mechanism to run the handler in a background (if HTTP request timeout was reached) and keep result until it is demanded again or result expires. Function parameters:

reqTimeout - time allotted for processing HTTP request, if request has not been processed completely - returns an ID of request (to retrieve result later).

asyncTimeout - maximum time for async job to be done (actual context deadline), this logic should be implemented in asynchronous handler or skipped - in that case handler cannot be interrupted.

keepResult - at the expiration of a given period of time the result will be unavailable (deleted).

NOTE: Do not use defer statements to check the status of task, send error or any response when using PanicRecover middleware.

func Codec

func Codec(fn errors.HandlerFunc, codecs Codecs) Middleware

Codec middleware searches for suitable request/response codecs according to "Content-Type"/"Accept" headers and puts the correct codecs into the context.

func ContextDeadline

func ContextDeadline(timeout time.Duration) Middleware

ContextDeadline adds timeout to request's context.

func JWT

func JWT(parser JWTParser, cf ClaimsFactory) Middleware

JWT is a JSON Web token middleware that parses token with provided parser to the provided Claims receiver and puts it to the request context.

func New

func New(middlewares ...Middleware) Middleware

New is a Middleware constructor func. The call without arguments returns an empty Middleware.

Usage (all examples below are equal):

  • mw.New().Use(mwOne, mwTwo, mwThree).Then(handler)
  • mw.New(mwOne).Use(mwTwo, mwThree).Then(handler)
  • mw.New(mwOne, mwTwo, mwThree).Then(handler)

func PanicRecover

func PanicRecover(onPanic OnPanic) Middleware

PanicRecover returns a middleware that recovers from the panic.

A trivial example (retrieving an error from the panic and sending to the client using errors package) is:

package main

import (
    "log"
    "net/http"

    "github.com/tiny-go/errors"
    "github.com/tiny-go/middleware"
)

var (
    panicHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
        panic("something went wrong")
    }
)

func main() {
    http.Handle(
      "/",
      mw.
          // with HTTP panic handler
          New(mw.PanicRecover(errors.Send)).
          Then(panicHandler),
    )
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func RequestLimiter

func RequestLimiter(fn errors.HandlerFunc, maxConcurrentRequests int) Middleware

RequestLimiter middleware apply concurrent request limit TODO: provide a choise either "wait" or "send error"

func SetHeaders

func SetHeaders(headers map[string]string) Middleware

SetHeaders allows to set response headers.

func Throttle

func Throttle(count int64, duration time.Duration) Middleware

Throttle provides a middleware that limits the amount of messages processed per unit of time. Note that middleware func can be defined globally to be shared by multiple handlers

Example:

var mw = Throttle(10, time.Second)

http.Handle("/one", mw.Then(handlerOne))
http.Handle("/two", mw.Then(handlerTwo))

func (Middleware) Then

func (mw Middleware) Then(final http.Handler) http.Handler

Then injects handler into middleware chain.

func (Middleware) Use

func (mw Middleware) Use(middlewares ...Middleware) Middleware

Use transforms provided middleware function(s) (including current one) into a single middleware func.

type MiddlewareFunc

type MiddlewareFunc func(http.ResponseWriter, *http.Request, http.Handler)

MiddlewareFunc is a classic middleware - decides itself whether to call next handler or return an HTTP error directly to the client and breaks the chain.

type OnPanic

type OnPanic func(http.ResponseWriter, interface{})

OnPanic is a function that should contain the logic responsible for any kind of error/panic reporting (to the console, file, directly to the client or simply ignore them).

Jump to

Keyboard shortcuts

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