2019-04-12 10:42:55 +08:00
|
|
|
package caddyhttp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"bitbucket.org/lightcodelabs/caddy2"
|
|
|
|
)
|
|
|
|
|
2019-05-15 04:14:05 +08:00
|
|
|
// ServerRoute represents a set of matching rules,
|
|
|
|
// middlewares, and a responder for handling HTTP
|
|
|
|
// requests.
|
|
|
|
type ServerRoute struct {
|
2019-04-12 10:42:55 +08:00
|
|
|
Matchers map[string]json.RawMessage `json:"match"`
|
|
|
|
Apply []json.RawMessage `json:"apply"`
|
|
|
|
Respond json.RawMessage `json:"respond"`
|
|
|
|
|
2019-05-11 11:07:02 +08:00
|
|
|
Terminal bool `json:"terminal"`
|
2019-04-12 10:42:55 +08:00
|
|
|
|
|
|
|
// decoded values
|
|
|
|
matchers []RouteMatcher
|
|
|
|
middleware []MiddlewareHandler
|
|
|
|
responder Handler
|
|
|
|
}
|
|
|
|
|
2019-05-15 04:14:05 +08:00
|
|
|
// RouteList is a list of server routes that can
|
|
|
|
// create a middleware chain.
|
|
|
|
type RouteList []ServerRoute
|
2019-04-12 10:42:55 +08:00
|
|
|
|
2019-05-15 04:14:05 +08:00
|
|
|
// Provision sets up all the routes by loading the modules.
|
2019-05-17 06:05:38 +08:00
|
|
|
func (routes RouteList) Provision(ctx caddy2.Context) error {
|
2019-05-15 04:14:05 +08:00
|
|
|
for i, route := range routes {
|
|
|
|
// matchers
|
|
|
|
for modName, rawMsg := range route.Matchers {
|
2019-05-17 06:05:38 +08:00
|
|
|
val, err := ctx.LoadModule("http.matchers."+modName, rawMsg)
|
2019-05-15 04:14:05 +08:00
|
|
|
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 {
|
2019-05-17 06:05:38 +08:00
|
|
|
mid, err := ctx.LoadModuleInline("middleware", "http.middleware", rawMsg)
|
2019-05-15 04:14:05 +08:00
|
|
|
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 {
|
2019-05-17 06:05:38 +08:00
|
|
|
resp, err := ctx.LoadModuleInline("responder", "http.responders", route.Respond)
|
2019-05-15 04:14:05 +08:00
|
|
|
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 {
|
2019-04-12 10:42:55 +08:00
|
|
|
if len(routes) == 0 {
|
|
|
|
return emptyHandler
|
|
|
|
}
|
|
|
|
|
|
|
|
var mid []Middleware
|
|
|
|
var responder Handler
|
|
|
|
mrw := &middlewareResponseWriter{ResponseWriterWrapper: &ResponseWriterWrapper{w}}
|
|
|
|
|
2019-04-26 03:54:48 +08:00
|
|
|
routeLoop:
|
2019-04-12 10:42:55 +08:00
|
|
|
for _, route := range routes {
|
|
|
|
for _, m := range route.matchers {
|
2019-04-26 03:54:48 +08:00
|
|
|
if !m.Match(r) {
|
|
|
|
continue routeLoop
|
2019-04-12 10:42:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
2019-05-11 11:07:02 +08:00
|
|
|
if route.Terminal {
|
2019-04-12 10:42:55 +08:00
|
|
|
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
|
|
|
|
}
|