negroniの実装をちゃんと理解できていなかったので自分で書き直してみる

以前、ミドルウェアをうまく扱うパッケージとしてnegroniを紹介したが、改めてコードを読むとちゃんと理解できていなかったので自分で書き直してみる。車輪の再発明GitHubに上げるまでもないのでここに残す。

GitHub - urfave/negroni: Idiomatic HTTP Middleware for Golang

package chain

import (
    "net/http"
)

// Handler is an interface.
type Handler interface {
    ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

// HandlerFunc is an adapter to allow the use of ordinary functions as HTTP handlers.
type HandlerFunc func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)

// ServeHTTP simply calls f(w, r, next).
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    f(w, r, next)
}

// Middleware is a stack of middlwares.
type Middleware struct {
    handler Handler
    next    *Middleware
}

// ServeHTTP calls ServeHTTP of next middleware.
func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    m.handler.ServeHTTP(w, r, m.next.ServeHTTP)
}

// Wrap converts http.Handler into chain.Handler
func Wrap(h http.Handler) Handler {
    return HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        h.ServeHTTP(w, r)
        next(w, r)
    })
}

// New is the constructor of Middlewre
func New(handlers ...Handler) Middleware {
    return *build(handlers)
}

func build(handlers []Handler) *Middleware {
    var next *Middleware

    if len(handlers) == 0 {
        return voidMiddleware()
    } else if len(handlers) > 1 {
        next = build(handlers[1:])
    } else {
        next = voidMiddleware()
    }

    return &Middleware{handlers[0], next}
}

func voidMiddleware() *Middleware {
    return &Middleware{
        handler: HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
        next:    &Middleware{},
    }
}

メモ。

  • ミドルウェアスタックをlinked listで実装している。
  • 配列からlinked listを構成するときに再帰している。
  • インターフェースの使い方うまいなー(なんかパターンがありそうな)。

使い方は以下の通り。

package main

import (
    "./chain"
    "net/http"
)

func hello1(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    w.Write([]byte("Hello1\n"))
    next(w, r)
}

func hello2(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    w.Write([]byte("Hello2\n"))
    next(w, r)
}

func hello3(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    w.Write([]byte("Hello3\n"))
    next(w, r)
}

// MyHandler is a sample handler.
type MyHandler struct{}

// ServeHTTP greet to world.
func (mh MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, world!\n"))
}

func main() {
    n := chain.New(
        chain.HandlerFunc(hello1),
        chain.HandlerFunc(hello2),
        chain.HandlerFunc(hello3),
        chain.Wrap(MyHandler{}),
    )
    http.ListenAndServe(":8888", n)
}

参考

takayukinakata.hatenablog.com