Skip to content

Repository files navigation

Release Reference DeepWiki Test

Insights Insights

go-middleware

Server-side and client-side middleware chaining library for Go.

Features

  • HTTP server-side middleware chain
  • HTTP client-side middleware chain
  • Powerful chaining
  • Simple

HTTP server-side middleware pattern in go:

// ServerMiddleware is the server side middleware.
type ServerMiddleware interface {
    ServerMiddleware(next http.Handler) http.Handler
}

// ServerMiddlewareFunc type is an adapter to allow the
// use of ordinary function as server middleware.
type ServerMiddlewareFunc func(next http.Handler) http.Handler

func (f ServerMiddlewareFunc) ServerMiddleware(next http.Handler) http.Handler {
    return f(next)
}

HTTP client-side middleware pattern in go:

// ClientMiddleware is the client side middleware.
type ClientMiddleware interface {
    ClientMiddleware(next http.RoundTripper) http.RoundTripper
}

// ClientMiddlewareFunc type is an adapter to allow the
// use of ordinary function as client middleware.
type ClientMiddlewareFunc func(next http.RoundTripper) http.RoundTripper

func (f ClientMiddlewareFunc) ClientMiddleware(next http.RoundTripper) http.RoundTripper {
    return f(next)
}

Usages

Server-side middleware chaining

This example applies logging middleware and timeout middleware to the server's hello handler. Server-side middlewares can be easily appended to the chain.

func main() {
    chain := middleware.ServerMiddlewareChain{} // Create an empty chain.
    chain = chain.AppendFunc(logging, timeout)  // Append middlewares.
    handler := chain.TerminateFunc(hello)       // Finalize with the handler.

    err := http.ListenAndServe(":8080", handler)
    if err != nil {
        panic(err)
    }
}

// logging prints access info.
func logging(next http.Handler) http.Handler {
    return middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.Proto, r.Method, r.RequestURI)
        next.ServeHTTP(w, r)
    })
}

// timeout applies request timeout of 2 seconds.
func timeout(next http.Handler) http.Handler {
    return http.TimeoutHandler(next, time.Second, "request timeout")
}

// hello says hello after sleep. Maybe timeout.
func hello(w http.ResponseWriter, r *http.Request) {
    sleep := time.Second * time.Duration(rand.IntN(5))
    log.Println("sleeping:", sleep)
    time.Sleep(sleep)
    _, _ = w.Write([]byte("ok"))
}

The following methods are defined for the middleware chain. See the godoc for details.

// The middleware chain is a middleware itself.
func ServerMiddleware(next http.Handler) http.Handler

// Terminate the chain with the handler.
func Terminate(h http.Handler) http.Handler
func TerminateFunc(h http.HandlerFunc) http.Handler

// Add middlewares to the chain.
func Append(ms ...ServerMiddleware) ServerMiddlewareChain
func AppendFunc(ms ...ServerMiddlewareFunc) ServerMiddlewareChain
func Prepend(ms ...ServerMiddleware) ServerMiddlewareChain
func PrependFunc(ms ...ServerMiddlewareFunc) ServerMiddlewareChain
func InsertAt(index int, ms ...ServerMiddleware) ServerMiddlewareChain
func InsertAtFunc(index int, ms ...ServerMiddlewareFunc) ServerMiddlewareChain
func InsertBeforeAll(ms ...ServerMiddleware) ServerMiddlewareChain
func InsertBeforeAllFunc(ms ...ServerMiddlewareFunc) ServerMiddlewareChain
func InsertAfterAll(ms ...ServerMiddleware) ServerMiddlewareChain
func InsertAfterAllFunc(ms ...ServerMiddlewareFunc) ServerMiddlewareChain
func InsertAll(ms ...ServerMiddleware) ServerMiddlewareChain
func InsertAllFunc(ms ...ServerMiddlewareFunc) ServerMiddlewareChain

Client-side middleware chaining

This example applies logging middleware and timeout middleware to the http client. Here a dummy round tripper is used as the final round tripper.. Client-side middlewares can be easily appended to the chain.

func main() {
    chain := middleware.ClientMiddlewareChain{} // Create an empty chain.
    chain = chain.AppendFunc(logging, timeout)  // Append middlewares.
    roundTripper := chain.TerminateFunc(hello)  // Finalize with the handler.

    r, _ := http.NewRequest(http.MethodGet, "http://localhost:8080/hello", nil)
    w, err := roundTripper.RoundTrip(r)
    if err != nil {
        log.Println(err)
    } else {
        body, _ := io.ReadAll(w.Body)
        log.Println(string(body))
    }
}

// logging prints request info.
func logging(next http.RoundTripper) http.RoundTripper {
    return middleware.RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
        log.Println(r.Proto, r.Method, r.RequestURI)
        return next.RoundTrip(r)
    })
}

// timeout applies request timeout of 2 seconds.
func timeout(next http.RoundTripper) http.RoundTripper {
    return middleware.RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
        ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
        defer cancel()
        r = r.WithContext(ctx)
        return next.RoundTrip(r)
    })
}

// hello returns dummy response.
func hello(r *http.Request) (*http.Response, error) {
    sleep := time.Second * time.Duration(rand.IntN(5))
    log.Println("sleeping:", sleep)
    select {
    case <-r.Context().Done():
        return nil, r.Context().Err()
    case <-time.After(sleep):
        return &http.Response{
            StatusCode: http.StatusOK,
            Body:       io.NopCloser(strings.NewReader("hello")),
        }, nil
    }
}

The following methods are defined for the middleware chain. See the godoc for details.

// The middleware chain is a middleware itself.
func ClientMiddleware(next http.RoundTripper) http.RoundTripper

// Terminate the chain with the round tripper.
func Terminate(rt http.RoundTripper) http.RoundTripper
func TerminateFunc(rt RoundTripperFunc) http.RoundTripper

// Add middlewares to the chain.
func Append(ms ...ClientMiddleware) ClientMiddlewareChain
func AppendFunc(ms ...ClientMiddlewareFunc) ClientMiddlewareChain
func Prepend(ms ...ClientMiddleware) ClientMiddlewareChain
func PrependFunc(ms ...ClientMiddlewareFunc) ClientMiddlewareChain
func InsertAt(index int, ms ...ClientMiddleware) ClientMiddlewareChain
func InsertAtFunc(index int, ms ...ClientMiddlewareFunc) ClientMiddlewareChain
func InsertBeforeAll(ms ...ClientMiddleware) ClientMiddlewareChain
func InsertBeforeAllFunc(ms ...ClientMiddlewareFunc) ClientMiddlewareChain
func InsertAfterAll(ms ...ClientMiddleware) ClientMiddlewareChain
func InsertAfterAllFunc(ms ...ClientMiddlewareFunc) ClientMiddlewareChain
func InsertAll(ms ...ClientMiddleware) ClientMiddlewareChain
func InsertAllFunc(ms ...ClientMiddlewareFunc) ClientMiddlewareChain

Docs & Examples

References

Releases

Contributors

Languages