Minimalist net/http middleware for golang

Related tags

interpose
Overview

interpose

Interpose is a minimalist net/http middleware framework for golang. It uses http.Handler as its core unit of functionality, minimizing complexity and maximizing inter-operability with other middleware frameworks.

All that it does is manage middleware. It comes with nothing baked in. You bring your own router, etc. See below for some well-baked examples.

Because of its reliance on the net/http standard, Interpose is out-of-the-box compatible with the Gorilla framework, goji, nosurf, and many other frameworks and standalone middleware.

Many projects claim to be http.Handler-compliant but actually just use http.Handlers to create a more complicated/less compatible abstraction. Therefore, a goal of the project is also to create adaptors so that non-http.Handler compliant middleware can still be used. As an example of this, an adaptor for Negroni middleware is available, meaning that any middleware that is Negroni compliant is also Interpose compliant. The same is true for Martini middleware.

API change

Please note that the API has recently changed. Previously, the framework applied middleware in LIFO order. Now it applies middleware in FIFO order.

Usage

To use, first:

go get github.com/carbocation/interpose

Basic usage example

Here is one example of using Interpose to execute middleware that adds the HTTP header "X-Server-Name" to every response.

Create a file main.go with the following:

package main

import (
	"fmt"
	"net/http"

	"github.com/carbocation/interpose"
)

func main() {
	middle := interpose.New()

	// Send a header telling the world this is coming from an Interpose Test Server
	// You could imagine setting Content-type application/json or other useful
	// headers in other circumstances.
	middle.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		rw.Header().Set("X-Server-Name", "Interpose Test Server")
	}))

	// In this example, we use a basic router with a catchall route that
	// matches any path. In other examples in this project, we use a
	// more advanced router.
	// The last middleware added is the last executed, so by adding the router
	// last, our other middleware get a chance to modify HTTP headers before our
	// router writes to the HTTP Body
	router := http.NewServeMux()
	middle.UseHandler(router)

	router.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to %s!", req.URL.Path)
	}))

	http.ListenAndServe(":3001", middle)
}

In the same path as that file, type go run main.go

Now launch your browser and point it to http://localhost:3001/world to see output.

Additional examples can be found below.

Philosophy

Interpose is a minimalist Golang middleware that uses only http.Handler and func(http.Handler)http.Handler. Interpose takes advantage of closures to create a stack of native net/http middleware. Unlike other middleware libraries which create their own net/http-like signatures, interpose uses literal net/http signatures, thus minimizing package lock-in and maximizing inter-compatibility.

Middleware is called in nested FIFO fashion, which means that the first middleware to be added will be the first middleware to be called. Because the middleware is nested, it actually means that the first middleware to be added gets the opportunity to make the first and the last calls in the stack. For example, if there are 3 middlewares added in order (0, 1, 2), the calls look like so:

//0 START
	//1 START
		//2 START
		//2 END
	//1 END
//0 END

Middleware

Here is a current list of Interpose compatible middleware that have pre-built examples working with Interpose. Any middleware that yields an http.Handler or a func(http.Handler)http.Handler should be compliant. Pull requests linking to other middleware are encouraged.

Middleware Usage example Author Description
Graceful Graceful example Tyler Bunnell Graceful HTTP Shutdown
secure Secure example Cory Jacobsen Middleware that implements a few quick security wins
Gorilla logger Gorilla log example Gorilla team Gorilla Apache CombinedLogger
Logrus Logrus example Dan Buch Logrus-based logger, also demonstrating how Negroni packages can be used in Interpose
Buffered output Buffer example zeebo Output buffering demonstrating how headers can be written after HTTP body is sent
nosurf nosurf example justinas A CSRF protection middleware for Go.
BasicAuth BasicAuth example Jeremy Saenz & Brendon Murphy HTTP BasicAuth - based on martini's auth middleware
Martini Auth Martini Auth example Jeremy Saenz & Brendon Murphy A basic HTTP Auth implementation that also demonstrates how Martini middleware packages can be used directly in Interpose with a simple wrapper.

Adaptors

Some frameworks that are not strictly http.Handler compliant use middleware that can be readily converted into Interpose-compliant middleware. So far, adaptors for Martini and Negroni have been created.

For example, to use github.com/urfave/negroni middleware in Interpose, you can use adaptors.FromNegroni:

	middle := interpose.New()

	// has signature `negroni.Handler`
	negroniMiddleware := negronilogrus.NewMiddleware()

	// Use the Negroni middleware within Interpose
	middle.Use(adaptors.FromNegroni(negroniMiddleware))

More examples

Routing, graceful shutdown, and headers

In this example, we use the graceful package to gracefully release connections after the shutdown signal is encountered. A more powerful router than in our prior example, Gorilla mux, is used. Also, we send the browser headers indicating that this came from a server named "Interpose Test Server."

package main

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

	"github.com/carbocation/interpose"
	"github.com/gorilla/mux"
	"github.com/stretchr/graceful"
)

func main() {
	middle := interpose.New()

	// Tell the browser which server this came from.
	// This modifies headers, so we want this to be called before
	// any middleware which might modify the body (in HTTP, the headers cannot be
	// modified after the body is modified)
	middle.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			rw.Header().Set("X-Server-Name", "Interpose Test Server")
			next.ServeHTTP(rw, req)
		})
	})

	// Apply the router. By adding it last, all of our other middleware will be
	// executed before the router, allowing us to modify headers before any
	// output has been generated.
	router := mux.NewRouter()
	middle.UseHandler(router)

	router.HandleFunc("/{user}", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page, %s!", mux.Vars(req)["user"])
	})

	// Launch and permit graceful shutdown, allowing up to 10 seconds for existing
	// connections to end
	graceful.Run(":3001", 10*time.Second, middle)
}

Combined logging and gzipping

Print an Apache CombinedLog-compatible log statement to StdOut and gzip the HTTP response it if the client has gzip capabilities:

package main

import (
	"compress/gzip"
	"fmt"
	"net/http"

	"github.com/carbocation/interpose"
	"github.com/carbocation/interpose/middleware"
	"github.com/gorilla/mux"
)

func main() {
	middle := interpose.New()

	// First apply any middleware that will not write output to http body

	// Log to stdout. Taken from Gorilla
	middle.Use(middleware.GorillaLog())

	// Gzip output that follows. Taken from Negroni
	middle.Use(middleware.NegroniGzip(gzip.DefaultCompression))

	// Now apply any middleware that modify the http body.
	router := mux.NewRouter()
	middle.UseHandler(router)

	router.HandleFunc("/{user}", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page, %s!", mux.Vars(req)["user"])
	})

	http.ListenAndServe(":3001", middle)
}

Wrapped middleware

Middleware can be wrapped around other middleware. In this example, we greet people who arrive at /{user}, but we offer a special greeting to those who arrive at /green/{user}:

package main

import (
	"fmt"
	"net/http"

	"github.com/carbocation/interpose"
	"github.com/carbocation/interpose/middleware"
	"github.com/gorilla/mux"
)

func main() {
	middle := interpose.New()

	// Invoke the Gorilla framework's combined logger
	middle.Use(middleware.GorillaLog())

	// Create a router to serve HTTP content at two paths
	// and tell our middleware about the router
	router := mux.NewRouter()
	middle.UseHandler(router)

	router.PathPrefix("/green").Subrouter().Handle("/{name}", Green(http.HandlerFunc(welcomeHandler)))
	router.Handle("/{name}", http.HandlerFunc(welcomeHandler))

	http.ListenAndServe(":3001", middle)
}

func welcomeHandler(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(rw, "Welcome to the home page, %s", mux.Vars(req)["name"])
}

func Green(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		rw.Header().Set("X-Favorite-Color", "green")
		next.ServeHTTP(rw, req)
		fmt.Fprint(rw, " who likes green")
	})
}

Nested middleware: adding headers for only some routes

In the last example, we applied different middleware to different routes. Here we will expand this idea to created fully nested middleware stacks within different routes. This approach, while more verbose than the last example, is arbitrarily powerful. In this example, routes starting with /green are given a special HTTP header X-Favorite-Color: green, but you can also imagine using this same approach to automatically apply the JSON content header for JSON requests, putting authentication in front of protected paths, etc.

package main

import (
	"compress/gzip"
	"fmt"
	"net/http"

	"github.com/carbocation/interpose"
	"github.com/carbocation/interpose/middleware"
	"github.com/gorilla/mux"
)

func main() {
	middle := interpose.New()

	// First call middleware that may manipulate HTTP headers, since
	// they must be called before the HTTP body is manipulated

	// Using Gorilla framework's combined logger
	middle.Use(middleware.GorillaLog())

	//Using Negroni's Gzip functionality
	middle.Use(middleware.NegroniGzip(gzip.DefaultCompression))

	// Now call middleware that can manipulate the HTTP body

	// Define the router. Note that we can add the router to our
	// middleware stack before we define the routes, if we want.
	router := mux.NewRouter()
	middle.UseHandler(router)

	// Configure our router
	router.HandleFunc("/{user}", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page, %s!", mux.Vars(req)["user"])
	})

	// Define middleware that will apply to only some routes
	greenMiddle := interpose.New()

	// Tell the main router to send /green requests to our subrouter.
	// Again, we can do this before defining the full middleware stack.
	router.Methods("GET").PathPrefix("/green").Handler(greenMiddle)

	// Within the secondary middleware, just like above, we want to call anything that
	// will modify the HTTP headers before anything that will modify the body
	greenMiddle.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		rw.Header().Set("X-Favorite-Color", "green")
	}))

	// Finally, define a sub-router based on our love of the color green
	// When you call any url such as http://localhost:3001/green/man , you will
	// also see an HTTP header sent called X-Favorite-Color with value "green"
	greenRouter := mux.NewRouter().Methods("GET").PathPrefix("/green").Subrouter() //Headers("Accept", "application/json")
	greenMiddle.UseHandler(greenRouter)

	greenRouter.HandleFunc("/{user}", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page, green %s!", mux.Vars(req)["user"])
	})

	http.ListenAndServe(":3001", middle)
}

For more examples, please look at the examples folder as well as its subfolder, the menagerie folder

Authors

Originally developed by carbocation. Please see the contributors file for an expanded list of contributors.

Issues
  • Any particular reason to use interpose instead of alice?

    Any particular reason to use interpose instead of alice?

    According the documentation middlewares are executed in non natural way (LIFO)

    This is being solved in another middleware stack https://github.com/justinas/alice .

    The signature of the middleware does not differ too (func(http.Handler) http.Handler)

    Is there any particalr reason why you did created your own implementation instead of using one which already exists (negroni, alice, tigertonic)

    opened by lindemannrichard 9
  • Basic auth

    Basic auth

    copied martini's basic auth middleware (with tests), made a small example with a public and protected area and added it to the readme with credits to the authors.

    opened by cryptix 5
  • Pre-compiling the middleware stack

    Pre-compiling the middleware stack

    The ServeHTTP method has this code:

    //Initialize with an empty http.Handler
    next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}))
    //Call the middleware stack in FIFO order
    for i := len(mw.Wares) - 1; i >= 0; i-- {
        next = mw.Wares[i](next)
    }
    //Finally, serve back up the chain
    next.ServeHTTP(w, req)
    

    I haven't done benchmarks, but do you think the chain could be compiled when the server starts listening, rather than on every HTTP request? And I bet we could get rid of that empty handler too.

    I've been working on a package modeled a little bit after interpose because it's an elegant way to stack middleware. I was able to tweak the process and get it down to this (mine stacks HandlerFuncs, not Handlers, but same idea):

    mw.stack = server.ServeHTTP // or whatever the main handler is (the "C" in A-B-C-B-A middlewares)
    for _, middleware := range mw.Wares {
        mw.stack = middleware(mw.stack)
    }
    

    That's just an example. Of course you could change the order and stuff. And obviously it requires knowing what the core or inner-most HTTP handler is, and then a call to some compilation function. But then the ServeHTTP method could be as simple as:

    func (mw *Middleware) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        mw.stack(w, req)
    }
    

    Not sure if that made any sense. But it's just a suggestion. This pattern might be able to eliminate that no-op handler and speed up request handling (though probably just a little bit).

    opened by mholt 5
  • Separate out the compose call.

    Separate out the compose call.

    Useful for when uses are not changing the Wares after composing so it doesn't have to compose on every call to ServeHTTP.

    enhancement 
    opened by doug 4
  • added example for gorilla/context

    added example for gorilla/context

    just a simple example of using gorilla/context

    opened by cryptix 2
  • gorilla/context - don't leave old requests in the map

    gorilla/context - don't leave old requests in the map

    Hi,

    after reading this article on different context passing techniques I noticed that we had this problem in the examples. From the article:

    The downsides? The global map and its mutexes may result in contention at high loads, and you need to call context.Clear() at the end of every request (i.e. on each handler). Forget to do that (or wrap your top-level server handler) and you'll open yourself up to a memory leak where old requests remain in the map. If you're writing middleware that uses gorilla/context, then you need to make sure your package user imports context calls context.ClearHandler on their handlers/router.

    opened by cryptix 2
  • change martini repo path

    change martini repo path

    It has changed from https://github.com/codegangsta/martini to https://github.com/go-martini/martini

    opened by kdar 1
  • Added basic nosurf wrapper and example

    Added basic nosurf wrapper and example

    This is just a bare minimum example of using justinas/nosurf.

    I think, we should at least add an optional http.Handler argument to pass to SetFailureHandler() but i'm not too sure how we should handle the whole Exempt*() thing. Maybe better add another example and/or documentation, that nosurf has to be setup by the dev if he needs full fledged excempt support.

    opened by cryptix 0
  • Updated auth middleware with BasicAuthFunc

    Updated auth middleware with BasicAuthFunc

    I decided to pass the request as well with the callback, this gives the user the freedom to chose how to pass on the callback.

    opened by cryptix 0
  • Update Negroni references

    Update Negroni references

    Negroni has been renamed from github.com/codegangsta/negroni to github.com/urfave/negroni.

    Updating the references to reflect this for the adaptors.

    opened by kisamoto 0
  • Two different independent middleware stack merged

    Two different independent middleware stack merged

    My intention is to have something like this:

                                     +----> a set of middleware +----> route handlers -+
                                     |                                                 |
                                     +                                                 |
                                   header                                              |
    Request -> logger -> CORS +--- based                                               +----> end request
                                   match                                               |
                                     +                                                 |
                                     |                                                 |
                                     +----> other set of middleware +> route handlers ++
    

    I've tried to adapt the example at https://github.com/carbocation/interpose#nested-middleware-adding-headers-for-only-some-routes to something that could work and ended up with https://github.com/fiatjaf/summadb/blob/two-different-submuxes/handle/main.go.

    However, it is not working. Although the matcher functions are being called and returning correctly, CORS doesn't work, and I don't know what else isn't working.

    How should I proceed?

    opened by fiatjaf 0
Owner
James Pirruccello
James Pirruccello
Idiomatic HTTP Middleware for Golang

Negroni Notice: This is the library formerly known as github.com/codegangsta/negroni -- Github will automatically redirect requests to this repository

null 7k Jul 23, 2021
A tiny http middleware for Golang with added handlers for common needs.

rye A simple library to support http services. Currently, rye provides a middleware handler which can be used to chain http handlers together while pr

InVision 97 Jun 6, 2021
A Go middleware that stores various information about your web application (response time, status code count, etc.)

Go stats handler stats is a net/http handler in golang reporting various metrics about your web application. This middleware has been developed and re

Florent Messa 579 Jul 1, 2021
A golang framework like koa.js

koa.go Expressive HTTP middleware framework for Golang to make web applications and APIs more enjoyable to write like Koa.js. Koa's middleware stack f

Ya Hui Liang(Ryou) 17 Jul 6, 2021
Go http.Hander based middleware stack with context sharing

wrap Package wrap creates a fast and flexible middleware stack for http.Handlers. Features small; core is only 13 LOC based on http.Handler interface;

go-on - web toolkit in Go 59 Aug 28, 2020
Painless middleware chaining for Go

Alice Alice provides a convenient way to chain your HTTP middleware functions and the app handler. In short, it transforms Middleware1(Middleware2(Mid

Justinas Stankevičius 2.3k Jul 13, 2021
gorilla/csrf provides Cross Site Request Forgery (CSRF) prevention middleware for Go web applications & services 🔒

gorilla/csrf gorilla/csrf is a HTTP middleware library that provides cross-site request forgery (CSRF) protection. It includes: The csrf.Protect middl

Gorilla Web Toolkit 676 Jul 19, 2021
A collection of useful middleware for Go HTTP services & web applications 🛃

gorilla/handlers Package handlers is a collection of handlers (aka "HTTP middleware") for use with Go's net/http package (or any framework supporting

Gorilla Web Toolkit 1.2k Jul 25, 2021
Simple middleware to rate-limit HTTP requests.

Tollbooth This is a generic middleware to rate-limit HTTP requests. NOTE 1: This library is considered finished. NOTE 2: Major version changes are bac

Didip Kerabat 2k Jul 27, 2021
Go package for easily rendering JSON, XML, binary data, and HTML templates responses.

Render Render is a package that provides functionality for easily rendering JSON, XML, text, binary data, and HTML templates. This package is based on

Cory Jacobsen 1.5k Jul 27, 2021
Lightweight Middleware for net/http

MuxChain MuxChain is a small package designed to complement net/http for specifying chains of handlers. With it, you can succinctly compose layers of

Stephen Searles 209 Sep 4, 2020
Simple, lightweight and faster response (JSON, JSONP, XML, YAML, HTML, File) rendering package for Go

Package renderer Simple, lightweight and faster response (JSON, JSONP, XML, YAML, HTML, File) rendering package for Go Installation Install the packag

Saddam H 224 Jul 16, 2021
Add interceptors to GO http.Client

mediary Add interceptors to http.Client and you will be able to Dump request and/or response to a Log Alter your requests before they are sent or resp

Here Mobility SDK 73 Jul 15, 2021
Go package that handles HTML, JSON, XML and etc. responses

gores http response utility library for Go this package is very small and lightweight, useful for RESTful APIs. installation go get github.com/alioygu

Ali OYGUR 96 Jul 3, 2021