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