httpware

package module
v3.0.2 Latest Latest
Warning

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

Go to latest
Published: Dec 7, 2020 License: MIT Imports: 1 Imported by: 0

README

Build Status Maintainability Test Coverage Go Report Card GoDoc

httpware

Package httpware is a collection of middleware (net/http.Handler wrapper) and tripperware (net/http.RoundTripper wrapper)

  • CorrelationId gets or creates a correlation_id and adds it to the http.request Context and in the http.response header (in order to propagate this ID throught all microservices)
  • Metrics will use a given Recorder to collect inflight request(current parrallel request count), request duration and response size.
Name Middleware Tripperware
Authentication X
AuthenticationForwarder X
CorrelationId X X
Metrics X X
Interceptor X X
Skip X X
Enable X X
RateLimiter X X

Installation

go get -u github.com/gol4ng/httpware

Quick Start

Client side
package main

import (
	"net/http"
	"os"
	"runtime"
	
	"github.com/gol4ng/httpware/v3"
	"github.com/gol4ng/httpware/v3/tripperware"
)

func main(){
    // create http client with correlationId activated
    client := &http.Client{
        Transport: tripperware.CorrelationId(),
    }
    
    _, _ = client.Get("fake-address.foo")
    // request will have Header "Correlation-Id: <randomID(10)>"
}
Server side
package main

import (
	"fmt"
	"net/http"
	"os"
	"runtime"
	
	"github.com/gol4ng/httpware/v3"
	"github.com/gol4ng/httpware/v3/correlation_id"
	"github.com/gol4ng/httpware/v3/middleware"
)

func main(){
    port:= ":8001"
    srv := http.NewServeMux()
    srv.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
        // Correlation-Id come directly from the client or was generated by the middleware
        fmt.Println(request.Header.Get(correlation_id.HeaderName))
        writer.WriteHeader(http.StatusOK)
    })
    
    go func() {
        if err := http.ListenAndServe(port, middleware.CorrelationId()(srv)); err != nil {
            panic(err)
        }
    }()
}

Middlewares

A middleware is an http.Handler decorator, its role is to wrap arround http.Handler or another middleware

A middleware stack is a collection of middleware ([]Middleware) that acts has a single Middleware (Append/Prepend).

A stack allows you to DecorateHandler and DecorateHandlerFunc

Tripperwares

A tripperware is an http.RoundTripper decorator, its role is to wrap arround http.RoundTripper or another tripperware

A tripperware stack is a collection of tripperware ([]Tripperware) that acts has a single Tripperware (RoundTrip/DecorateClient/Append/Prepend).

A stack allows you to DecorateRoundTripper and DecorateRoundTripFunc.

Decorate existing client

You can decorate an existing http.Client

client := &http.Client{}
   
// when you decorate client you can set second arg to true if you want a copy
tripperware.CorrelationId().DecorateClient(client, false)
    
_, _ = client.Get("fake-address.foo")
// request will have Header "Correlation-Id: <randomID(10)>"
Act has http.RoundTripper

A tripperware can be use has an http.RoundTripper

tripperware.CorrelationId() == tripperware.CorrelationId()(http.DefaultTransport)

client := &http.Client{
    Transport:tripperware.CorrelationId(),
}
    
_, _ = client.Get("fake-address.foo")
// request will have Header "Correlation-Id: <randomID(10)>"
Become a tripperware stack

The Append and Prepend functions will convert a tripperware into a []Tripperware

roundTripper := tripperware.CorrelationId()
stack := roundTripper.Prepend(func(t http.RoundTripper) http.RoundTripper {
    return httpware.RoundTripFunc(func(req *http.Request) (*http.Response, error) {
        fmt.Println("http request headers :", req.Header)
        return t.RoundTrip(req)
    })
})

client := &http.Client{
    Transport:stack,
}

_, _ = client.Get("fake-address.foo")
// request will have Header "Correlation-Id: <randomID(10)>"

Stack computation

The stack is gonna be computed when you decorate the final round tripper.

m1, m2, m3 are middlewares

[m1, m2, m3].DecorateHandler(<yourHandler>) == m1( m2( m3( <yourHandler> ) ) )

t1 t2 t3 are tripperwares

[t1, t2, t3].DecorateRoundTripper(<yourTripper>) == t1( t2( t3( <yourTripper> ) ) )

	tripperware := func(before, after string) httpware.Tripperware {
		return func(t http.RoundTripper) http.RoundTripper {
			return httpware.RoundTripFunc(func(req *http.Request) (*http.Response, error) {
				defer fmt.Println(after) // will print string AFTER child round trip executed
				fmt.Println(before) // will print string BEFORE child round trip executed
				return t.RoundTrip(req)
			})
		}
	}

	stack := httpware.TripperwareStack(
		tripperware("A before", "A after"),
		tripperware("B before", "B after"),
		tripperware("C before", "C after"),
		tripperware("D before", "D after"),
	)
	// A( B( C( D( http.DefaultTransport ) ) ) )

	client := &http.Client{
		Transport: stack,
	}
	_, _ = client.Get("http://localhost/")
	//Output:
	//A before
	//B before
	//C before
	//D before
	//D after
	//C after
	//B after
	//A after

Enable/skip middleware or tripperware

Some times you need to have a dynamic middleware|tripperware stack

For example you need to have a middleware activated on debug mode If Enable false, the middleware will not be added to the middleware stack

    debug := true
    stack := httpware.MiddlewareStack(
        middleware.Enable(debug, middleware.CorrelationId()),
    )

You can dynamically skip a middleware with your own rule.
If the callback return true it will skip the execution of targeted middleware

    stack := httpware.MiddlewareStack(
        tripperware.Skip(func(request *http.Request) bool {
            return request.URL.Path == "/home"
        }, middleware.CorrelationId()),
    )

AppendIf PrependIf

For more convinience to build more complex middleware/tripperware stack you can use the AppendIf/PrependIf on Middleware and Middlewares

    debug := true
    stack := httpware.TripperwareStack(
        myTripperware(),
    )
    stack.AppendIf(debug, myOtherTripperware())
    stack.PrependIf(debug, myOtherTripperware2())
    debug := true
    myTripper := myTripperware(),
    stack := myTripper.AppendIf(debug, myOtherTripperware())
    stack.PrependIf(debug, myOtherTripperware2())

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NopMiddleware

func NopMiddleware(next http.Handler) http.Handler

NopMiddleware just return given http.Handler

func NopTripperware

func NopTripperware(next http.RoundTripper) http.RoundTripper

NopTripperware just return given http.RoundTripper

Types

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware represents an http server middleware it wraps an http.Handler with another one

func (Middleware) Append

func (m Middleware) Append(middlewares ...Middleware) Middlewares

Append will add given middlewares after existing one t1.Append(t2, t3) => [t1, t2, t3] t1.Append(t2, t3).DecorateHandler(<yourHandler>) == t1(t2(t3(<yourHandler>)))

func (Middleware) AppendIf

func (m Middleware) AppendIf(condition bool, middlewares ...Middleware) Middlewares

AppendIf will add given middlewares after existing one if condition=true t1.AppendIf(true, t2, t3) => [t1, t2, t3] t1.AppendIf(false, t2, t3) => [t1] t1.AppendIf(true, t2, t3).DecorateHandler(<yourHandler>) == t1(t2(t3(<yourHandler>)))

func (Middleware) Prepend

func (m Middleware) Prepend(middlewares ...Middleware) Middlewares

Prepend will add given middlewares before existing one t1.Prepend(t2, t3) => [t2, t3, t1] t1.Prepend(t2, t3).DecorateHandler(<yourHandler>) == t2(t3(t1(<yourHandler>)))

func (Middleware) PrependIf

func (m Middleware) PrependIf(condition bool, middlewares ...Middleware) Middlewares

PrependIf will add given middlewares before existing one if condition=true t1.PrependIf(true, t2, t3) => [t2, t3, t1] t1.PrependIf(false, t2, t3) => [t1] t1.PrependIf(true, t2, t3).DecorateHandler(<yourHandler>) == t2(t3(t1(<yourHandler>)))

type Middlewares

type Middlewares []Middleware

[t1, t2, t3].DecorateHandler(<yourHandler>) == t1(t2(t3(<yourHandler>)))

func MiddlewareStack

func MiddlewareStack(middlewares ...Middleware) Middlewares

MiddlewareStack allows you to stack multiple middleware collection in a specific order

Example
// create a middleware that adds a requestId header on each http-server request
addCustomResponseHeader := func(h http.Handler) http.Handler {
	return http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
		writer.Header().Add("custom-response-header", "wonderful header value")
		h.ServeHTTP(writer, req)
	})
}
// create a middleware that logs the response header on each call
logResponseHeaders := func(h http.Handler) http.Handler {
	return http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
		fmt.Println("http response headers : ", writer.Header())
		h.ServeHTTP(writer, req)
	})
}
// create the middleware stack
stack := httpware.MiddlewareStack(
	addCustomResponseHeader,
	logResponseHeaders,
)
// create a server
srv := http.NewServeMux()
srv.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
	request.Header.Get(correlation_id.HeaderName)
	writer.WriteHeader(http.StatusOK)
})

// apply the middlewares on the server
// note: this part is normally done on `http.ListenAndServe(":<serverPort>", stack.DecorateHandler(srv))`
h := stack.DecorateHandler(srv)

// fake a request
req := httptest.NewRequest(http.MethodGet, "/", nil)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
Output:

http response headers :  map[Custom-Response-Header:[wonderful header value]]

func (*Middlewares) Append

func (m *Middlewares) Append(middleware ...Middleware) Middlewares

Append will add given middleware after existing one [t1, t2].Append(t3, t4) => [t1, t2, t3, t4] [t1, t2].Append(t3, t4).DecorateHandler(<yourHandler>) == t1(t2(t3(t4(<yourHandler>))))

func (*Middlewares) AppendIf

func (m *Middlewares) AppendIf(condition bool, middleware ...Middleware) Middlewares

AppendIf will add given middleware after existing one if condition=true [t1, t2].AppendIf(true, t3, t4) => [t1, t2, t3, t4] [t1, t2].AppendIf(false, t3, t4) => [t1, t2] [t1, t2].AppendIf(t3, t4).DecorateHandler(<yourHandler>) == t1(t2(t3(t4(<yourHandler>))))

func (Middlewares) DecorateHandler

func (m Middlewares) DecorateHandler(handler http.Handler) http.Handler

DecorateHandler will decorate a given http.Handler with the given middlewares created by MiddlewareStack()

func (Middlewares) DecorateHandlerFunc

func (m Middlewares) DecorateHandlerFunc(handler http.HandlerFunc) http.Handler

DecorateHandler will decorate a given http.HandlerFunc with the given middleware collection created by MiddlewareStack()

func (*Middlewares) Prepend

func (m *Middlewares) Prepend(middleware ...Middleware) Middlewares

Prepend will add given middleware before existing one [t1, t2].Prepend(t3, t4) => [t3, t4, t1, t2] [t1, t2].Prepend(t3, t4).DecorateHandler(<yourHandler>) == t3(t4(t1(t2(<yourHandler>))))

func (*Middlewares) PrependIf

func (m *Middlewares) PrependIf(condition bool, middleware ...Middleware) Middlewares

PrependIf will add given middleware before existing one if condition=true [t1, t2].PrependIf(true, t3, t4) => [t3, t4, t1, t2] [t1, t2].PrependIf(false, t3, t4) => [t1, t2] [t1, t2].PrependIf(true, t3, t4).DecorateHandler(<yourHandler>) == t3(t4(t1(t2(<yourHandler>))))

type RoundTripFunc

type RoundTripFunc func(*http.Request) (*http.Response, error)

RoundTripFunc wraps a func to make it into an http.RoundTripper. Similar to http.HandleFunc.

func (RoundTripFunc) RoundTrip

func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements RoundTripper interface

type Tripperware

type Tripperware func(http.RoundTripper) http.RoundTripper

Tripperware represents an http client-side middleware (roundTripper middleware).

func (Tripperware) Append

func (t Tripperware) Append(tripperwares ...Tripperware) Tripperwares

Append will add given tripperwares after existing one t1.Append(t2, t3) == [t1, t2, t3] t1.Append(t2, t3).DecorateRoundTripper(<yourTripper>) == t1(t2(t3(<yourTripper>)))

func (Tripperware) AppendIf

func (t Tripperware) AppendIf(condition bool, tripperwares ...Tripperware) Tripperwares

AppendIf will add given tripperwares after existing one if condition=true t1.AppendIf(true, t2, t3) == [t1, t2, t3] t1.AppendIf(false, t2, t3) == [t1] t1.AppendIf(true, t2, t3).DecorateRoundTripper(<yourTripper>) == t1(t2(t3(<yourTripper>)))

func (Tripperware) DecorateClient

func (t Tripperware) DecorateClient(client *http.Client, clone bool) *http.Client

DecorateClient will decorate a given http.Client with the tripperware will return a clone of client if clone arg is true

func (Tripperware) Prepend

func (t Tripperware) Prepend(tripperwares ...Tripperware) Tripperwares

Prepend will add given tripperwares before existing one t1.Prepend(t2, t3) => [t2, t3, t1] t1.Prepend(t2, t3).DecorateRoundTripper(<yourTripper>) == t2(t3(t1(<yourTripper>)))

func (Tripperware) PrependIf

func (t Tripperware) PrependIf(condition bool, tripperwares ...Tripperware) Tripperwares

PrependIf will add given tripperwares before existing one if condition=true t1.PrependIf(true, t2, t3) => [t2, t3, t1] t1.PrependIf(false, t2, t3) => [t1] t1.PrependIf(true, t2, t3).DecorateRoundTripper(<yourTripper>) == t2(t3(t1(<yourTripper>)))

func (Tripperware) RoundTrip

func (t Tripperware) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements RoundTripper interface

type Tripperwares

type Tripperwares []Tripperware

[t1, t2, t3].DecorateRoundTripper(roundTripper) == t1(t2(t3(roundTripper)))

func TripperwareStack

func TripperwareStack(tripperwares ...Tripperware) Tripperwares

TripperwareStack allows to stack multi tripperware in order to decorate an http roundTripper

func (*Tripperwares) Append

func (t *Tripperwares) Append(tripperwares ...Tripperware) Tripperwares

Append will add given tripperwares after existing one [t1, t2].Append(true, t3, t4) == [t1, t2, t3, t4] [t1, t2].Append(t3, t4).DecorateRoundTripper(<yourTripper>) == t1(t2(t3(t4(<yourTripper>))))

func (*Tripperwares) AppendIf

func (t *Tripperwares) AppendIf(condition bool, tripperwares ...Tripperware) Tripperwares

Appendif will add given tripperwares after existing one if condition=true [t1, t2].AppendIf(true, t3, t4) == [t1, t2, t3, t4] [t1, t2].AppendIf(false, t3, t4) == [t1, t2] [t1, t2].AppendIf(true, t3, t4).DecorateRoundTripper(<yourTripper>) == t1(t2(t3(t4(<yourTripper>))))

func (Tripperwares) DecorateClient

func (t Tripperwares) DecorateClient(client *http.Client, clone bool) *http.Client

DecorateClient will decorate a given http.Client with the tripperware collection will return a clone of client if clone arg is true

func (Tripperwares) DecorateRoundTripFunc

func (t Tripperwares) DecorateRoundTripFunc(tripper RoundTripFunc) http.RoundTripper

DecorateRoundTripFunc will decorate a given RoundTripFunc with the tripperware collection

func (Tripperwares) DecorateRoundTripper

func (t Tripperwares) DecorateRoundTripper(tripper http.RoundTripper) http.RoundTripper

DecorateRoundTripper will decorate a given http.RoundTripper with the tripperware collection

func (*Tripperwares) Prepend

func (t *Tripperwares) Prepend(tripperwares ...Tripperware) Tripperwares

Prepend will add given tripperwares before existing one [t1, t2].Prepend(t3, t4) == [t3, t4, t1, t2] [t1, t2].Prepend(t3, t4).DecorateRoundTripper(<yourTripper>) == t3(t4(t1(t2(<yourTripper>))))

func (*Tripperwares) PrependIf

func (t *Tripperwares) PrependIf(condition bool, tripperwares ...Tripperware) Tripperwares

Prependif will add given tripperwares before existing one if condition=true [t1, t2].PrependIf(true, t3, t4) == [t1, t2, t3, t4] [t1, t2].PrependIf(false, t3, t4) == [t1, t2] [t1, t2].PrependIf(true, t3, t4).DecorateRoundTripper(<yourTripper>) == t1(t2(t3(t4(<yourTripper>))))

func (Tripperwares) RoundTrip

func (t Tripperwares) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements RoundTripper interface it will decorate the http-client request and use the default `http.DefaultTransport` RoundTripper use `TripperwareStack(<yourTripperwares>).Decorate(<yourTripper>)` if you don't want to use `http.DefaultTransport`

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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