@ -28,25 +28,19 @@ import ( 
			
		
	
		
			
				
					type  ServerRoute  struct  {  
			
		
	
		
			
				
						Group        string                        ` json:"group,omitempty" `  
			
		
	
		
			
				
						MatcherSets  [ ] map [ string ] json . RawMessage  ` json:"match,omitempty" `  
			
		
	
		
			
				
						Apply        [ ] json . RawMessage             ` json:"apply,omitempty" `  
			
		
	
		
			
				
						Respond      json . RawMessage               ` json:"respond,omitempty" `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
						Handle       [ ] json . RawMessage             ` json:"handle,omitempty" `  
			
		
	
		
			
				
						Terminal     bool                          ` json:"terminal,omitempty" `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
						// decoded values
  
			
		
	
		
			
				
						matcherSets  [ ] MatcherSet  
			
		
	
		
			
				
						middleware   [ ] MiddlewareHandler  
			
		
	
		
			
				
						responder    Handler  
			
		
	
		
			
				
						handlers     [ ] MiddlewareHandler  
			
		
	
		
			
				
					}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					// Empty returns true if the route has all zero/default values.
  
			
		
	
		
			
				
					func  ( sr  ServerRoute )  Empty ( )  bool  {  
			
		
	
		
			
				
						return  len ( sr . MatcherSets )  ==  0  &&  
			
		
	
		
			
				
							len ( sr . Apply )  ==  0  &&  
			
		
	
		
			
				
							len ( sr . Respond )  ==  0  &&  
			
		
	
		
			
				
							len ( sr . matcherSets )  ==  0  &&  
			
		
	
		
			
				
							len ( sr . middleware )  ==  0  &&  
			
		
	
		
			
				
							sr . responder  ==  nil  &&  
			
		
	
		
			
				
							len ( sr . Handle )  ==  0  &&  
			
		
	
		
			
				
							len ( sr . handlers )  ==  0  &&  
			
		
	
		
			
				
							! sr . Terminal  &&  
			
		
	
		
			
				
							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?
  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
							// 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 )  
			
		
	
		
			
				
							// handlers
  
			
		
	
		
			
				
							for  j ,  rawMsg  :=  range  route . Handle  {  
			
		
	
		
			
				
								mh ,  err  :=  ctx . LoadModuleInline ( "handler" ,  "http.handlers" ,  rawMsg )  
			
		
	
		
			
				
								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  
			
		
	
		
			
				
					}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					// BuildCompositeRoute creates a chain of handlers by applying all the matching
  
			
		
	
		
			
				
					// routes. The returned ResponseWriter should be used instead of rw.
  
			
		
	
		
			
				
					func  ( routes  RouteList )  BuildCompositeRoute ( rw  http . ResponseWriter ,  req  * http . Request )  ( Handler ,  http . ResponseWriter )  {  
			
		
	
		
			
				
						mrw  :=  & middlewareResponseWriter { ResponseWriterWrapper :  & ResponseWriterWrapper { rw } }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					// BuildCompositeRoute creates a chain of handlers by
  
			
		
	
		
			
				
					// applying all of the matching routes.
  
			
		
	
		
			
				
					func  ( routes  RouteList )  BuildCompositeRoute ( rw  http . ResponseWriter ,  req  * http . Request )  Handler  {  
			
		
	
		
			
				
						if  len ( routes )  ==  0  {  
			
		
	
		
			
				
							return  emptyHandler ,  mrw  
			
		
	
		
			
				
							return  emptyHandler  
			
		
	
		
			
				
						}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
						var  mid  [ ] Middleware  
			
		
	
		
			
				
						var  responder  Handler  
			
		
	
		
			
				
						groups  :=  make ( map [ string ] struct { } )  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
						for  _ ,  route  :=  range  routes  {  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -140,9 +121,8 @@ func (routes RouteList) BuildCompositeRoute(rw http.ResponseWriter, req *http.Re 
			
		
	
		
			
				
								continue  
			
		
	
		
			
				
							}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
							// if route is part of a group, ensure only
  
			
		
	
		
			
				
							// the first matching route in the group is
  
			
		
	
		
			
				
							// applied
  
			
		
	
		
			
				
							// if route is part of a group, ensure only the
  
			
		
	
		
			
				
							// first matching route in the group is applied
  
			
		
	
		
			
				
							if  route . Group  !=  ""  {  
			
		
	
		
			
				
								_ ,  ok  :=  groups [ route . Group ]  
			
		
	
		
			
				
								if  ok  {  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -155,78 +135,48 @@ func (routes RouteList) BuildCompositeRoute(rw http.ResponseWriter, req *http.Re 
			
		
	
		
			
				
							}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
							// apply the rest of the route
  
			
		
	
		
			
				
							for  _ ,  m  :=  range  route . middleware {  
			
		
	
		
			
				
								// we have to be sure to wrap m outside
  
			
		
	
		
			
				
								// of our current scop e so that the
  
			
		
	
		
			
				
								// reference to this m isn't overwritten
  
			
		
	
		
			
				
								// on the next iteration, leaving only 
  
			
		
	
		
			
				
								// the last  middleware in the chain as
  
			
		
	
		
			
				
								// the ONLY  middleware in the chain!
  
			
		
	
		
			
				
								mid  =  append ( mid ,  wrapMiddleware ( m ) )  
			
		
	
		
			
				
							}  
			
		
	
		
			
				
							if  responder  ==  nil  {   
			
		
	
		
			
				
								responder  =  route . responder   
			
		
	
		
			
				
							}  
			
		
	
		
			
				
							for  _ ,  mh   :=  range  route . handlers {  
			
		
	
		
			
				
								// we have to be sure to wrap mh  outside
  
			
		
	
		
			
				
								// of our current stack fram e so that the
  
			
		
	
		
			
				
								// reference to this mh  isn't overwritten
  
			
		
	
		
			
				
								// on the next iteration, leaving the last 
  
			
		
	
		
			
				
								// middleware in the chain as the ONLY 
  
			
		
	
		
			
				
								// middleware in the chain!
  
			
		
	
		
			
				
								mid  =  append ( mid ,  wrapMiddleware ( mh  ) )  
			
		
	
		
			
				
							}  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
							// if this route is supposed to be last, don't
  
			
		
	
		
			
				
							// compile any more into the chain
  
			
		
	
		
			
				
							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 )  
			
		
	
		
			
				
						} )  
			
		
	
		
			
				
						// build the middleware chain, with the responder at the end
  
			
		
	
		
			
				
						stack  :=  emptyHandler  
			
		
	
		
			
				
						for  i  :=  len ( mid )  -  1 ;  i  >=  0 ;  i --  {  
			
		
	
		
			
				
							stack  =  mid [ i ] ( stack )  
			
		
	
		
			
				
						}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
						return  stack ,  mrw  
			
		
	
		
			
				
						return  stack  
			
		
	
		
			
				
					}  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					// wrapMiddleware wraps m such that it can be correctly
  
			
		
	
		
			
				
					// appended to a list of middleware. This separate closure
  
			
		
	
		
			
				
					// is necessary so that only the last middleware in a loop
  
			
		
	
		
			
				
					// does not become the only middleware of the stack,
  
			
		
	
		
			
				
					// repeatedly executed (i.e. it is necessary to keep a
  
			
		
	
		
			
				
					// reference to this m outside of the scope of a loop)!
  
			
		
	
		
			
				
					func  wrapMiddleware ( m  MiddlewareHandler )  Middleware  {  
			
		
	
		
			
				
					// appended to a list of middleware. We can't do this
  
			
		
	
		
			
				
					// directly in a loop because it relies on a reference
  
			
		
	
		
			
				
					// to mh not changing until the execution of its handler,
  
			
		
	
		
			
				
					// which is deferred by multiple func closures. In other
  
			
		
	
		
			
				
					// words, we need to pull this particular MiddlewareHandler
  
			
		
	
		
			
				
					// 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 ( w  http . ResponseWriter ,  r  * http . Request )  error  {  
			
		
	
		
			
				
								// TODO: This is where request tracing could be implemented; also
  
			
		
	
		
			
				
								// 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: 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 )