Skip to content

rs/xhandler

Repository files navigation

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.