caddy/modules/caddyhttp/routes.go

113 lines
3 KiB
Go

package caddyhttp
import (
"encoding/json"
"fmt"
"net/http"
"bitbucket.org/lightcodelabs/caddy2"
)
// ServerRoute represents a set of matching rules,
// middlewares, and a responder for handling HTTP
// requests.
type ServerRoute struct {
Matchers map[string]json.RawMessage `json:"match"`
Apply []json.RawMessage `json:"apply"`
Respond json.RawMessage `json:"respond"`
Terminal bool `json:"terminal"`
// decoded values
matchers []RouteMatcher
middleware []MiddlewareHandler
responder Handler
}
// RouteList is a list of server routes that can
// create a middleware chain.
type RouteList []ServerRoute
// Provision sets up all the routes by loading the modules.
func (routes RouteList) Provision(ctx caddy2.Context) error {
for i, route := range routes {
// matchers
for modName, rawMsg := range route.Matchers {
val, err := ctx.LoadModule("http.matchers."+modName, rawMsg)
if err != nil {
return fmt.Errorf("loading matcher module '%s': %v", modName, err)
}
routes[i].matchers = append(routes[i].matchers, val.(RouteMatcher))
}
routes[i].Matchers = nil // allow GC to deallocate - TODO: Does this help?
// middleware
for j, rawMsg := range route.Apply {
mid, err := ctx.LoadModuleInline("middleware", "http.middleware", rawMsg)
if err != nil {
return fmt.Errorf("loading middleware module in position %d: %v", j, err)
}
routes[i].middleware = append(routes[i].middleware, mid.(MiddlewareHandler))
}
routes[i].Apply = nil // allow GC to deallocate - TODO: Does this help?
// responder
if route.Respond != nil {
resp, err := ctx.LoadModuleInline("responder", "http.responders", route.Respond)
if err != nil {
return fmt.Errorf("loading responder module: %v", err)
}
routes[i].responder = resp.(Handler)
}
routes[i].Respond = nil // allow GC to deallocate - TODO: Does this help?
}
return nil
}
// BuildHandlerChain creates a chain of handlers by
// applying all the matching routes.
func (routes RouteList) BuildHandlerChain(w http.ResponseWriter, r *http.Request) Handler {
if len(routes) == 0 {
return emptyHandler
}
var mid []Middleware
var responder Handler
mrw := &middlewareResponseWriter{ResponseWriterWrapper: &ResponseWriterWrapper{w}}
routeLoop:
for _, route := range routes {
for _, m := range route.matchers {
if !m.Match(r) {
continue routeLoop
}
}
for _, m := range route.middleware {
mid = append(mid, func(next HandlerFunc) HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
return m.ServeHTTP(mrw, r, next)
}
})
}
if responder == nil {
responder = route.responder
}
if route.Terminal {
break
}
}
// build the middleware stack, with the responder at the end
stack := HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
if responder == nil {
return nil
}
mrw.allowWrites = true
return responder.ServeHTTP(w, r)
})
for i := len(mid) - 1; i >= 0; i-- {
stack = mid[i](stack)
}
return stack
}