xhandler

package module
v0.0.0-...-1eb70cf Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2017 License: MIT Imports: 3 Imported by: 50

README

XHandler

godoc license Build Status Coverage

XHandler is a bridge between net/context and http.Handler.

It lets you enforce net/context in your handlers without sacrificing compatibility with existing http.Handlers nor imposing a specific router.

Thanks to net/context deadline management, xhandler is able to enforce a per request deadline and will cancel the context when the client closes the connection unexpectedly.

You may create your own net/context aware handler pretty much the same way as you would do with http.Handler.

Read more about xhandler on Dailymotion engineering blog.

Installing

go get -u github.com/rs/xhandler

Usage

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/rs/cors"
	"github.com/rs/xhandler"
	"golang.org/x/net/context"
)

type myMiddleware struct {
	next xhandler.HandlerC
}

func (h myMiddleware) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	ctx = context.WithValue(ctx, "test", "World")
	h.next.ServeHTTPC(ctx, w, r)
}

func main() {
	c := xhandler.Chain{}

	// Add close notifier handler so context is cancelled when the client closes
	// the connection
	c.UseC(xhandler.CloseHandler)

	// Add timeout handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	// Middleware putting something in the context
	c.UseC(func(next xhandler.HandlerC) xhandler.HandlerC {
		return myMiddleware{next: next}
	})

	// Mix it with a non-context-aware middleware handler
	c.Use(cors.Default().Handler)

	// Final handler (using handlerFuncC), reading from the context
	xh := xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		value := ctx.Value("test").(string)
		w.Write([]byte("Hello " + value))
	})

	// Bridge context aware handlers with http.Handler using xhandler.Handle()
	http.Handle("/test", c.Handler(xh))

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Using xmux

Xhandler comes with an optional context aware muxer forked from httprouter:

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/rs/xhandler"
	"github.com/rs/xmux"
	"golang.org/x/net/context"
)

func main() {
	c := xhandler.Chain{}

	// Append a context-aware middleware handler
	c.UseC(xhandler.CloseHandler)

	// Another context-aware middleware handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	mux := xmux.New()

	// Use c.Handler to terminate the chain with your final handler
	mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome %s!", xmux.Params(ctx).Get("name"))
	}))

	if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil {
		log.Fatal(err)
	}
}

See xmux for more examples.

Context Aware Middleware

Here is a list of net/context aware middleware handlers implementing xhandler.HandlerC interface.

Feel free to put up a PR linking your middleware if you have built one:

Middleware Author Description
xmux Olivier Poitrey HTTP request muxer
xlog Olivier Poitrey HTTP handler logger
xstats Olivier Poitrey A generic client for service instrumentation
xaccess Olivier Poitrey HTTP handler access logger with xlog and xstats
cors Olivier Poitrey Cross Origin Resource Sharing (CORS) support

Licenses

All source code is licensed under the MIT License.

Documentation

Overview

Package xhandler provides a bridge between http.Handler and net/context.

xhandler enforces net/context in your handlers without sacrificing compatibility with existing http.Handlers nor imposing a specific router.

Thanks to net/context deadline management, xhandler is able to enforce a per request deadline and will cancel the context in when the client close the connection unexpectedly.

You may create net/context aware middlewares pretty much the same way as you would with http.Handler.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func If

func If(cond func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool, condNext func(next HandlerC) HandlerC) func(next HandlerC) HandlerC

If is a special handler that will skip insert the condNext handler only if a condition applies at runtime.

Example
package main

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"context"

	"github.com/rs/xhandler"
)

func main() {
	c := xhandler.Chain{}

	// Add a timeout handler only if the URL path matches a prefix
	c.UseC(xhandler.If(
		func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool {
			return strings.HasPrefix(r.URL.Path, "/with-timeout/")
		},
		xhandler.TimeoutHandler(2*time.Second),
	))

	http.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})))
}
Output:

func New

func New(ctx context.Context, h HandlerC) http.Handler

New creates a conventional http.Handler injecting the provided root context to sub handlers. This handler is used as a bridge between conventional http.Handler and context aware handlers.

func TimeoutHandler

func TimeoutHandler(timeout time.Duration) func(next HandlerC) HandlerC

TimeoutHandler returns a Handler which adds a timeout to the context.

Child handlers have the responsability of obeying the context deadline and to return an appropriate error (or not) response in case of timeout.

Types

type Chain

type Chain []func(next HandlerC) HandlerC

Chain is a helper for chaining middleware handlers together for easier management.

Example
package main

import (
	"fmt"
	"net/http"
	"time"

	"context"

	"github.com/rs/cors"
	"github.com/rs/xhandler"
)

func main() {
	c := xhandler.Chain{}
	// Append a context-aware middleware handler
	c.UseC(xhandler.CloseHandler)

	// Mix it with a non-context-aware middleware handler
	c.Use(cors.Default().Handler)

	// Another context-aware middleware handler
	c.UseC(xhandler.TimeoutHandler(2 * time.Second))

	mux := http.NewServeMux()

	// Use c.Handler to terminate the chain with your final handler
	mux.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})))

	// You can reuse the same chain for other handlers
	mux.Handle("/api", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the API!")
	})))
}
Output:

func (*Chain) Add

func (c *Chain) Add(f ...interface{})

Add appends a variable number of additional middleware handlers to the middleware chain. Middleware handlers can either be context-aware or non-context aware handlers with the appropriate function signatures.

func (Chain) Handler

func (c Chain) Handler(xh HandlerC) http.Handler

Handler wraps the provided final handler with all the middleware appended to the chain and returns a new standard http.Handler instance. The context.Background() context is injected automatically.

func (Chain) HandlerC

func (c Chain) HandlerC(xh HandlerC) HandlerC

HandlerC wraps the provided final handler with all the middleware appended to the chain and returns a HandlerC instance.

func (Chain) HandlerCF

func (c Chain) HandlerCF(xhc HandlerFuncC) HandlerC

HandlerCF wraps the provided final handler func with all the middleware appended to the chain and returns a HandlerC instance.

HandlerCF is equivalent to:

c.HandlerC(xhandler.HandlerFuncC(xhc))

func (Chain) HandlerCtx

func (c Chain) HandlerCtx(ctx context.Context, xh HandlerC) http.Handler

HandlerCtx wraps the provided final handler with all the middleware appended to the chain and returns a new standard http.Handler instance.

func (Chain) HandlerF

func (c Chain) HandlerF(hf http.HandlerFunc) http.Handler

HandlerF is a helper to provide a standard http handler function (http.HandlerFunc) to Handler(). Your final handler won't have access to the context though.

func (Chain) HandlerFC

func (c Chain) HandlerFC(xhf HandlerFuncC) http.Handler

HandlerFC is a helper to provide a function (HandlerFuncC) to Handler().

HandlerFC is equivalent to:

c.Handler(xhandler.HandlerFuncC(xhc))

func (Chain) HandlerH

func (c Chain) HandlerH(h http.Handler) http.Handler

HandlerH is a helper to provide a standard http handler (http.HandlerFunc) to Handler(). Your final handler won't have access to the context though.

func (*Chain) Use

func (c *Chain) Use(f func(next http.Handler) http.Handler)

Use appends a standard http.Handler to the middleware chain without losing track of the context when inserted between two context aware handlers.

Caveat: the f function will be called on each request so you are better off putting any initialization sequence outside of this function.

func (*Chain) UseC

func (c *Chain) UseC(f func(next HandlerC) HandlerC)

UseC appends a context-aware handler to the middleware chain.

func (*Chain) With

func (c *Chain) With(f ...interface{}) *Chain

With creates a new middleware chain from an existing chain, extending it with additional middleware. Middleware handlers can either be context-aware or non-context aware handlers with the appropriate function signatures.

type HandlerC

type HandlerC interface {
	ServeHTTPC(context.Context, http.ResponseWriter, *http.Request)
}

HandlerC is a net/context aware http.Handler

func CloseHandler

func CloseHandler(next HandlerC) HandlerC

CloseHandler returns a Handler, cancelling the context when the client connection closes unexpectedly.

type HandlerFuncC

type HandlerFuncC func(context.Context, http.ResponseWriter, *http.Request)

HandlerFuncC type is an adapter to allow the use of ordinary functions as an xhandler.Handler. If f is a function with the appropriate signature, xhandler.HandlerFuncC(f) is a xhandler.Handler object that calls f.

func (HandlerFuncC) ServeHTTPC

func (f HandlerFuncC) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request)

ServeHTTPC calls f(ctx, w, r).

Jump to

Keyboard shortcuts

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