Commit d33256f1 authored by Matthew Holt's avatar Matthew Holt

Refactor: Middleware chain uses Handler instead of HandlerFunc

parent db2cd9e9
......@@ -20,7 +20,7 @@ import (
// Browse is an http.Handler that can show a file listing when
// directories in the given paths are specified.
type Browse struct {
Next middleware.HandlerFunc
Next middleware.Handler
Root string
Configs []BrowseConfig
}
......@@ -83,9 +83,9 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
Configs: configs,
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(next middleware.Handler) middleware.Handler {
browse.Next = next
return browse.ServeHTTP
return browse
}, nil
}
......@@ -95,11 +95,11 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
info, err := os.Stat(filename)
if err != nil {
return b.Next(w, r)
return b.Next.ServeHTTP(w, r)
}
if !info.IsDir() {
return b.Next(w, r)
return b.Next.ServeHTTP(w, r)
}
// See if there's a browse configuration to match the path
......@@ -192,7 +192,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
}
// Didn't qualify; pass-thru
return b.Next(w, r)
return b.Next.ServeHTTP(w, r)
}
// parse returns a list of browsing configurations
......
......@@ -39,15 +39,15 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
return nil
})
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(next middleware.Handler) middleware.Handler {
handler.Next = next
return handler.ServeHTTP
return handler
}, nil
}
// ErrorHandler handles HTTP errors (or errors from other middleware).
type ErrorHandler struct {
Next middleware.HandlerFunc
Next middleware.Handler
ErrorPages map[int]string // map of status code to filename
LogFile string
Log *log.Logger
......@@ -61,7 +61,7 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
}
}()
status, err := h.Next(w, r)
status, err := h.Next.ServeHTTP(w, r)
if err != nil {
h.Log.Printf("[ERROR %d %s] %v", status, r.URL.Path, err)
......
......@@ -24,12 +24,12 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
return nil, err
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(next middleware.Handler) middleware.Handler {
return Ext{
Next: next,
Extensions: extensions,
Root: root,
}.ServeHTTP
}
}, nil
}
......@@ -37,7 +37,7 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
// It tries extensions in the order listed in Extensions.
type Ext struct {
// Next handler in the chain
Next middleware.HandlerFunc
Next middleware.Handler
// Path to ther root of the site
Root string
......@@ -57,7 +57,7 @@ func (e Ext) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
}
}
}
return e.Next(w, r)
return e.Next.ServeHTTP(w, r)
}
// parse sets up an instance of extension middleware
......
......@@ -26,8 +26,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
rules = append(rules, rule)
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) (int, error) {
return func(next middleware.Handler) middleware.Handler {
return middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
servedFcgi := false
for _, rule := range rules {
......@@ -97,11 +97,11 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
}
if !servedFcgi {
return next(w, r)
return next.ServeHTTP(w, r)
}
return 0, nil
}
})
}, nil
}
......
......@@ -11,29 +11,28 @@ import (
"github.com/mholt/caddy/middleware"
)
// Gzip is a http.Handler middleware type which gzips HTTP responses.
// Gzip is a middleware type which gzips HTTP responses.
type Gzip struct {
Next middleware.HandlerFunc
Next middleware.Handler
}
// New creates a new gzip middleware instance.
func New(c middleware.Controller) (middleware.Middleware, error) {
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
gz := Gzip{Next: next}
return gz.ServeHTTP
return func(next middleware.Handler) middleware.Handler {
return Gzip{Next: next}
}, nil
}
// ServeHTTP serves a gzipped response if the client supports it.
func (g Gzip) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
return g.Next(w, r)
return g.Next.ServeHTTP(w, r)
}
w.Header().Set("Content-Encoding", "gzip")
gzipWriter := gzip.NewWriter(w)
defer gzipWriter.Close()
gz := gzipResponseWriter{Writer: gzipWriter, ResponseWriter: w}
return g.Next(gz, r)
return g.Next.ServeHTTP(gz, r)
}
// gzipResponeWriter wraps the underlying Write method
......
......@@ -12,7 +12,7 @@ import (
// Headers is middleware that adds headers to the responses
// for requests matching a certain path.
type Headers struct {
Next middleware.HandlerFunc
Next middleware.Handler
Rules []HeaderRule
}
......@@ -26,7 +26,7 @@ func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
}
}
}
return h.Next(w, r)
return h.Next.ServeHTTP(w, r)
}
type (
......
......@@ -10,7 +10,7 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
return nil, err
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return Headers{Next: next, Rules: rules}.ServeHTTP
return func(next middleware.Handler) middleware.Handler {
return Headers{Next: next, Rules: rules}
}, nil
}
......@@ -39,8 +39,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
return nil
})
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return Logger{Next: next, Rules: rules}.ServeHTTP
return func(next middleware.Handler) middleware.Handler {
return Logger{Next: next, Rules: rules}
}, nil
}
......@@ -48,13 +48,13 @@ func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
for _, rule := range l.Rules {
if middleware.Path(r.URL.Path).Matches(rule.PathScope) {
responseRecorder := middleware.NewResponseRecorder(w)
status, err := l.Next(responseRecorder, r)
status, err := l.Next.ServeHTTP(responseRecorder, r)
rep := middleware.NewReplacer(r, responseRecorder)
rule.Log.Println(rep.Replace(rule.Format))
return status, err
}
}
return l.Next(w, r)
return l.Next.ServeHTTP(w, r)
}
func parse(c middleware.Controller) ([]LogRule, error) {
......@@ -103,7 +103,7 @@ func parse(c middleware.Controller) ([]LogRule, error) {
}
type Logger struct {
Next middleware.HandlerFunc
Next middleware.Handler
Rules []LogRule
}
......
......@@ -21,7 +21,7 @@ type Markdown struct {
Root string
// Next HTTP handler in the chain
Next middleware.HandlerFunc
Next middleware.Handler
// The list of markdown configurations
Configs []MarkdownConfig
......@@ -58,9 +58,9 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
Configs: mdconfigs,
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(next middleware.Handler) middleware.Handler {
md.Next = next
return md.ServeHTTP
return md
}, nil
}
......@@ -122,7 +122,7 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
}
// Didn't qualify to serve as markdown; pass-thru
return md.Next(w, r)
return md.Next.ServeHTTP(w, r)
}
// parse creates new instances of Markdown middleware.
......
......@@ -6,26 +6,29 @@ import "net/http"
type (
// Generator represents the outer layer of a middleware that
// parses tokens to configure the middleware instance.
//
// Note: This type will be moved into a different package in the future.
Generator func(Controller) (Middleware, error)
// Middleware is the middle layer which represents the traditional
// idea of middleware: it is passed the next HandlerFunc in the chain
// and returns the inner layer, which is the actual Handler.
Middleware func(HandlerFunc) HandlerFunc
// idea of middleware: it chains one Handler to the next by being
// passed the next Handler in the chain.
//
// Note: This type will be moved into a different package in the future.
Middleware func(Handler) Handler
// HandlerFunc is like http.HandlerFunc except it returns a status code
// and an error. It is the inner-most layer which serves individual
// requests. The status code is for the client's benefit; the error
// Handler is like http.Handler except ServeHTTP returns a status code
// and an error. The status code is for the client's benefit; the error
// value is for the server's benefit. The status code will be sent to
// the client while the error value will be logged privately. Sometimes,
// an error status code (4xx or 5xx) may be returned with a nil error
// when there is no reason to log the error on the server.
//
// If a HandlerFunc returns an error (status >= 400), it should NOT
// write to the response. This philosophy makes middleware.HandlerFunc
// different from http.HandlerFunc: error handling should happen at
// the application layer or in dedicated error-handling middleware
// only, rather than with an "every middleware for itself" paradigm.
// write to the response. This philosophy makes middleware.Handler
// different from http.Handler: error handling should happen at the
// application layer or in dedicated error-handling middleware only
// rather than with an "every middleware for itself" paradigm.
//
// The application or error-handling middleware should incorporate logic
// to ensure that the client always gets a proper response according to
......@@ -38,10 +41,6 @@ type (
// response for a status code >= 400. When ANY handler writes to the
// response, it should return a status code < 400 to signal others to
// NOT write to the response again, which would be erroneous.
HandlerFunc func(http.ResponseWriter, *http.Request) (int, error)
// Handler is like http.Handler except ServeHTTP returns a status code
// and an error. See HandlerFunc documentation for more information.
Handler interface {
ServeHTTP(http.ResponseWriter, *http.Request) (int, error)
}
......@@ -127,3 +126,13 @@ type (
Err(string) error
}
)
// HandlerFunc is a convenience type like http.HandlerFunc, except
// ServeHTTP returns a status code and an error. See Handler
// documentation for more information.
type HandlerFunc func(http.ResponseWriter, *http.Request) (int, error)
// ServeHTTP implements the Handler interface.
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
return f(w, r)
}
......@@ -25,8 +25,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
rules = append(rules, rule)
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) (int, error) {
return func(next middleware.Handler) middleware.Handler {
return middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
for _, rule := range rules {
if middleware.Path(r.URL.Path).Matches(rule.from) {
......@@ -59,8 +59,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
}
}
return next(w, r)
}
return next.ServeHTTP(w, r)
})
}, nil
}
......
......@@ -38,8 +38,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
}
}
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) (int, error) {
return func(next middleware.Handler) middleware.Handler {
return middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
for _, rule := range redirects {
if middleware.Path(r.URL.Path).Matches(rule.From) {
if rule.From == "/" {
......@@ -51,8 +51,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
return 0, nil
}
}
return next(w, r)
}
return next.ServeHTTP(w, r)
})
}, nil
}
......
......@@ -29,16 +29,16 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
}
// TODO: Why can't we just return an http.Handler here instead?
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) (int, error) {
return func(next middleware.Handler) middleware.Handler {
return middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
for _, rule := range rewrites {
if r.URL.Path == rule.From {
r.URL.Path = rule.To
break
}
}
return next(w, r)
}
return next.ServeHTTP(w, r)
})
}, nil
}
......
......@@ -16,7 +16,7 @@ type (
// websocket endpoints.
WebSockets struct {
// Next is the next HTTP handler in the chain for when the path doesn't match
Next middleware.HandlerFunc
Next middleware.Handler
// Sockets holds all the web socket endpoint configurations
Sockets []WSConfig
......@@ -46,7 +46,7 @@ func (ws WebSockets) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
}
// Didn't match a websocket path, so pass-thru
return ws.Next(w, r)
return ws.Next.ServeHTTP(w, r)
}
// New constructs and configures a new websockets middleware instance.
......@@ -115,8 +115,8 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
GatewayInterface = envGatewayInterface
ServerSoftware = envServerSoftware
return func(next middleware.HandlerFunc) middleware.HandlerFunc {
return WebSockets{Next: next, Sockets: websocks}.ServeHTTP
return func(next middleware.Handler) middleware.Handler {
return WebSockets{Next: next, Sockets: websocks}
}, nil
}
......
......@@ -28,7 +28,7 @@ var servers = make(map[string]*Server)
type Server struct {
config config.Config
fileServer middleware.Handler
stack middleware.HandlerFunc
stack middleware.Handler
}
// New creates a new Server and registers it with the list
......@@ -118,8 +118,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}()
status, _ := s.stack(w, r)
status, _ := s.stack.ServeHTTP(w, r)
// Fallback error response in case error handling wasn't chained in
if status >= 400 {
w.WriteHeader(status)
fmt.Fprintf(w, "%d %s", status, http.StatusText(status))
......@@ -144,7 +145,7 @@ func (s *Server) buildStack() error {
// compile is an elegant alternative to nesting middleware function
// calls like handler1(handler2(handler3(finalHandler))).
func (s *Server) compile(layers []middleware.Middleware) {
s.stack = s.fileServer.ServeHTTP // core app layer
s.stack = s.fileServer // core app layer
for i := len(layers) - 1; i >= 0; i-- {
s.stack = layers[i](s.stack)
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment