@ -28,25 +28,19 @@ import ( 
			
		
	
		
		
			
				
					type  ServerRoute  struct  { type  ServerRoute  struct  {  
			
		
	
		
		
			
				
						Group        string                        ` json:"group,omitempty" ` 	Group        string                        ` json:"group,omitempty" `  
			
		
	
		
		
			
				
						MatcherSets  [ ] map [ string ] json . RawMessage  ` json:"match,omitempty" ` 	MatcherSets  [ ] map [ string ] json . RawMessage  ` json:"match,omitempty" `  
			
		
	
		
		
			
				
					
						Apply        [ ] json . RawMessage             ` json:"apply,omitempty" ` 	Handle       [ ] json . RawMessage             ` json:"handle,omitempty" `  
			
				
				
			
		
	
		
		
			
				
					
						Respond      json . RawMessage               ` json:"respond,omitempty" ` 	Terminal     bool                          ` json:"terminal,omitempty" `  
			
				
				
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
						Terminal  bool  ` json:"terminal,omitempty" `  
			
		
	
		
		
	
		
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
						// decoded values
 	// decoded values
  
			
		
	
		
		
			
				
						matcherSets  [ ] MatcherSet 	matcherSets  [ ] MatcherSet  
			
		
	
		
		
			
				
					
						middleware   [ ] MiddlewareHandler 	handlers     [ ] MiddlewareHandler  
			
				
				
			
		
	
		
		
			
				
						responder    Handler  
			
		
	
		
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					// Empty returns true if the route has all zero/default values.
 // Empty returns true if the route has all zero/default values.
  
			
		
	
		
		
			
				
					func  ( sr  ServerRoute )  Empty ( )  bool  { func  ( sr  ServerRoute )  Empty ( )  bool  {  
			
		
	
		
		
			
				
						return  len ( sr . MatcherSets )  ==  0  && 	return  len ( sr . MatcherSets )  ==  0  &&  
			
		
	
		
		
			
				
					
							len ( sr . Apply )  ==  0  && 		len ( sr . Handle )  ==  0  &&  
			
				
				
			
		
	
		
		
			
				
					
							len ( sr . Respond )  ==  0  && 		len ( sr . handlers )  ==  0  &&  
			
				
				
			
		
	
		
		
			
				
							len ( sr . matcherSets )  ==  0  &&  
			
		
	
		
		
			
				
							len ( sr . middleware )  ==  0  &&  
			
		
	
		
		
			
				
							sr . responder  ==  nil  &&  
			
		
	
		
		
	
		
		
	
		
		
			
				
							! sr . Terminal  && 		! sr . Terminal  &&  
			
		
	
		
		
			
				
							sr . Group  ==  "" 		sr . Group  ==  ""  
			
		
	
		
		
			
				
					} }  
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -98,40 +92,27 @@ func (routes RouteList) Provision(ctx caddy.Context) error { 
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
							routes [ i ] . MatcherSets  =  nil  // allow GC to deallocate - TODO: Does this help?
 		routes [ i ] . MatcherSets  =  nil  // allow GC to deallocate - TODO: Does this help?
  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					
							// middleware
 		// handlers
  
			
				
				
			
		
	
		
		
			
				
					
							for  j ,  rawMsg  :=  range  route . Apply  { 		for  j ,  rawMsg  :=  range  route . Handle  {  
			
				
				
			
		
	
		
		
			
				
					
								mid ,  err  :=  ctx . LoadModuleInline ( "middleware" ,  "http.middleware" ,  rawMsg ) 			mh ,  err  :=  ctx . LoadModuleInline ( "handler" ,  "http.handlers" ,  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  { 			if  err  !=  nil  {  
			
		
	
		
		
			
				
					
									return  fmt . Errorf ( "loading responder module: %v"  ,  err ) 				return  fmt . Errorf ( "loading handler module in position %d: %v" ,  j ,  err )  
			
				
				
			
		
	
		
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
					
								routes [ i ] . responder  =  resp . ( Handler ) 			routes [ i ] . handlers  =  append ( routes [ i ] . handlers ,  mh . ( MiddlewareHandler ) )  
			
				
				
			
		
	
		
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
					
							routes [ i ] . Respond =  nil  // allow GC to deallocate - TODO: Does this help?
 		routes [ i ] . Handle  =  nil  // allow GC to deallocate - TODO: Does this help?
  
			
				
				
			
		
	
		
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
						return  nil 	return  nil  
			
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					
					// BuildCompositeRoute creates a chain of handlers by applying all the matching
 // BuildCompositeRoute creates a chain of handlers by
  
			
				
				
			
		
	
		
		
			
				
					
					// routes. The returned ResponseWriter should be used instead of rw.
 // applying all of the matching routes.
  
			
				
				
			
		
	
		
		
			
				
					
					func  ( routes  RouteList )  BuildCompositeRoute ( rw  http . ResponseWriter ,  req  * http . Request )  ( Handler ,  http . ResponseWriter )  { func  ( routes  RouteList )  BuildCompositeRoute ( rw  http . ResponseWriter ,  req  * http . Request )  Handler  {  
			
				
				
			
		
	
		
		
			
				
						mrw  :=  & middlewareResponseWriter { ResponseWriterWrapper :  & ResponseWriterWrapper { rw } }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
						if  len ( routes )  ==  0  { 	if  len ( routes )  ==  0  {  
			
		
	
		
		
			
				
					
							return  emptyHandler ,  mrw 		return  emptyHandler  
			
				
				
			
		
	
		
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
						var  mid  [ ] Middleware 	var  mid  [ ] Middleware  
			
		
	
		
		
			
				
						var  responder  Handler  
			
		
	
		
		
			
				
						groups  :=  make ( map [ string ] struct { } ) 	groups  :=  make ( map [ string ] struct { } )  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
						for  _ ,  route  :=  range  routes  { 	for  _ ,  route  :=  range  routes  {  
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
						
					 
					@ -140,9 +121,8 @@ func (routes RouteList) BuildCompositeRoute(rw http.ResponseWriter, req *http.Re 
			
		
	
		
		
			
				
								continue 			continue  
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					
							// if route is part of a group, ensure only
 		// if route is part of a group, ensure only the
  
			
				
				
			
		
	
		
		
			
				
					
							// the first matching route in the group is
 		// first matching route in the group is applied
  
			
				
				
			
		
	
		
		
			
				
							// applied
  
			
		
	
		
		
	
		
		
	
		
		
			
				
							if  route . Group  !=  ""  { 		if  route . Group  !=  ""  {  
			
		
	
		
		
			
				
								_ ,  ok  :=  groups [ route . Group ] 			_ ,  ok  :=  groups [ route . Group ]  
			
		
	
		
		
			
				
								if  ok  { 			if  ok  {  
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
						
					 
					@ -155,78 +135,48 @@ func (routes RouteList) BuildCompositeRoute(rw http.ResponseWriter, req *http.Re 
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
							// apply the rest of the route
 		// apply the rest of the route
  
			
		
	
		
		
			
				
					
							for  _ ,  m  :=  range  route . middleware  { 		for  _ ,  mh  :=  range  route . handlers  {  
			
				
				
			
		
	
		
		
			
				
					
								// we have to be sure to wrap m outside
 			// we have to be sure to wrap mh outside
  
			
				
				
			
		
	
		
		
			
				
					
								// of our current scope so that the
 			// of our current stack frame so that the
  
			
				
				
			
		
	
		
		
			
				
					
								// reference to this m isn't overwritten
 			// reference to this mh isn't overwritten
  
			
				
				
			
		
	
		
		
			
				
					
								// on the next iteration, leaving only
 			// on the next iteration, leaving the last
  
			
				
				
			
		
	
		
		
			
				
					
								// the last middleware in the chain as
 			// middleware in the chain as the ONLY
  
			
				
				
			
		
	
		
		
			
				
					
								// the ONLY middleware in the chain!
 			// middleware in the chain!
  
			
				
				
			
		
	
		
		
			
				
					
								mid  =  append ( mid ,  wrapMiddleware ( m ) ) 			mid  =  append ( mid ,  wrapMiddleware ( mh ) )  
			
				
				
			
		
	
		
		
			
				
							}  
			
		
	
		
		
			
				
							if  responder  ==  nil  {  
			
		
	
		
		
			
				
								responder  =  route . responder  
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
							// if this route is supposed to be last, don't
  
			
		
	
		
		
			
				
							// compile any more into the chain
  
			
		
	
		
		
			
				
							if  route . Terminal  { 		if  route . Terminal  {  
			
		
	
		
		
			
				
								break 			break  
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					
						// build the middleware stack, with the responder at the end
 	// build the middleware chain, with the responder at the end
  
			
				
				
			
		
	
		
		
			
				
					
						stack  :=  HandlerFunc ( func ( w  http . ResponseWriter ,  r  * http . Request )  error  { 	stack  :=  emptyHandler  
			
				
				
			
		
	
		
		
			
				
							if  responder  ==  nil  {  
			
		
	
		
		
			
				
								return  nil  
			
		
	
		
		
			
				
							}  
			
		
	
		
		
			
				
							mrw . allowWrites  =  true  
			
		
	
		
		
			
				
							return  responder . ServeHTTP ( w ,  r )  
			
		
	
		
		
			
				
						} )  
			
		
	
		
		
	
		
		
	
		
		
			
				
						for  i  :=  len ( mid )  -  1 ;  i  >=  0 ;  i --  { 	for  i  :=  len ( mid )  -  1 ;  i  >=  0 ;  i --  {  
			
		
	
		
		
			
				
							stack  =  mid [ i ] ( stack ) 		stack  =  mid [ i ] ( stack )  
			
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					
						return  stack ,  mrw 	return  stack  
			
				
				
			
		
	
		
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					// wrapMiddleware wraps m such that it can be correctly
 // wrapMiddleware wraps m such that it can be correctly
  
			
		
	
		
		
			
				
					
					// appended to a list of middleware. This separate closure
 // appended to a list of middleware. We can't do this
  
			
				
				
			
		
	
		
		
			
				
					
					// is necessary so that only the last middleware in a loop
 // directly in a loop because it relies on a reference
  
			
				
				
			
		
	
		
		
			
				
					
					// does not become the only middleware of the stack,
 // to mh not changing until the execution of its handler,
  
			
				
				
			
		
	
		
		
			
				
					
					// repeatedly executed (i.e. it is necessary to keep a
 // which is deferred by multiple func closures. In other
  
			
				
				
			
		
	
		
		
			
				
					
					// reference to this m outside of the scope of a loop)!
 // words, we need to pull this particular MiddlewareHandler
  
			
				
				
			
		
	
		
		
			
				
					
					func  wrapMiddleware ( m  MiddlewareHandler )  Middleware  { // pointer into its own stack frame to preserve it so it
  
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					// won't be overwritten in future loop iterations.
  
			
		
	
		
		
			
				
					func  wrapMiddleware ( mh  MiddlewareHandler )  Middleware  {  
			
		
	
		
		
			
				
						return  func ( next  HandlerFunc )  HandlerFunc  { 	return  func ( next  HandlerFunc )  HandlerFunc  {  
			
		
	
		
		
			
				
							return  func ( w  http . ResponseWriter ,  r  * http . Request )  error  { 		return  func ( w  http . ResponseWriter ,  r  * http . Request )  error  {  
			
		
	
		
		
			
				
								// TODO: This is where request tracing could be implemented; also
 			// TODO: This is where request tracing could be implemented; also
  
			
		
	
		
		
			
				
								// see below to trace the responder as well
 			// see below to trace the responder as well
  
			
		
	
		
		
			
				
								// TODO: Trace a diff of the request, would be cool too! see what changed since the last middleware (host, headers, URI...)
 			// TODO: Trace a diff of the request, would be cool too! see what changed since the last middleware (host, headers, URI...)
  
			
		
	
		
		
			
				
								// TODO: see what the std lib gives us in terms of stack tracing too
 			// TODO: see what the std lib gives us in terms of stack tracing too
  
			
		
	
		
		
			
				
					
								return  m . ServeHTTP ( w ,  r ,  next ) 			return  mh  . ServeHTTP ( w ,  r ,  next )  
			
				
				
			
		
	
		
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					type  middlewareResponseWriter  struct  {  
			
		
	
		
		
			
				
						* ResponseWriterWrapper  
			
		
	
		
		
			
				
						allowWrites  bool  
			
		
	
		
		
			
				
					}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					func  ( mrw  middlewareResponseWriter )  WriteHeader ( statusCode  int )  {  
			
		
	
		
		
			
				
						if  ! mrw . allowWrites  {  
			
		
	
		
		
			
				
							// technically, this is not true: middleware can write headers,
  
			
		
	
		
		
			
				
							// but only after the responder handler has returned; either the
  
			
		
	
		
		
			
				
							// responder did nothing with the response (sad face), or the
  
			
		
	
		
		
			
				
							// middleware wrapped the response and deferred the write
  
			
		
	
		
		
			
				
							panic ( "WriteHeader: middleware cannot write response headers" )  
			
		
	
		
		
			
				
						}  
			
		
	
		
		
			
				
						mrw . ResponseWriterWrapper . WriteHeader ( statusCode )  
			
		
	
		
		
			
				
					}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					func  ( mrw  middlewareResponseWriter )  Write ( b  [ ] byte )  ( int ,  error )  {  
			
		
	
		
		
			
				
						if  ! mrw . allowWrites  {  
			
		
	
		
		
			
				
							panic ( "Write: middleware cannot write to the response before responder" )  
			
		
	
		
		
			
				
						}  
			
		
	
		
		
			
				
						return  mrw . ResponseWriterWrapper . Write ( b )  
			
		
	
		
		
			
				
					}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					// Interface guard
  
			
		
	
		
		
			
				
					var  _  HTTPInterfaces  =  ( * middlewareResponseWriter ) ( nil )