@ -114,37 +114,45 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, 
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
							// extract matcher definitions
 		// extract matcher definitions
  
			
		
	
		
		
			
				
					
							d  :=  sb . block . DispenseDirective ( "matcher" ) 		matcherDefs  :=  make ( map [ string ] caddy . ModuleMap )  
			
				
				
			
		
	
		
		
			
				
					
							matcherDefs ,  err  :=  parseMatcherDefinitions ( d ) 		for  _ ,  segment  :=  range  sb . block . Segments  {  
			
				
				
			
		
	
		
		
			
				
					
							if  err  !=  nil  { 			if  dir  :=  segment . Directive ( ) ;  strings . HasPrefix ( dir ,  matcherPrefix )  {  
			
				
				
			
		
	
		
		
			
				
					
								return  nil ,  warnings ,  err 				d  :=  sb . block . DispenseDirective ( dir )  
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
									err  :=  parseMatcherDefinitions ( d ,  matcherDefs )  
			
		
	
		
		
			
				
									if  err  !=  nil  {  
			
		
	
		
		
			
				
										return  nil ,  warnings ,  err  
			
		
	
		
		
			
				
									}  
			
		
	
		
		
			
				
								}  
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
							for  _ ,  segment  :=  range  sb . block . Segments  { 		for  _ ,  segment  :=  range  sb . block . Segments  {  
			
		
	
		
		
			
				
								dir  :=  segment . Directive ( ) 			dir  :=  segment . Directive ( )  
			
		
	
		
		
			
				
					
								if  dir  ==  "matcher"  { 
 
			
				
				
			
		
	
		
		
			
				
					
									// TODO: This is a special case because we pre-processed it; handle this better
 			if  strings . HasPrefix ( dir ,  matcherPrefix )  {  
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
									// matcher definitions were pre-processed
  
			
		
	
		
		
			
				
									continue 				continue  
			
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
					
								if  dirFunc ,  ok  :=  registeredDirectives [ dir ] ;  ok  { 
 
			
				
				
			
		
	
		
		
			
				
					
									results ,  err  :=  dirFunc ( Helper { 			dirFunc ,  ok  :=  registeredDirectives [ dir ]  
			
				
				
			
		
	
		
		
			
				
					
										Dispenser :    caddyfile . NewDispenser ( segment ) , 			if  ! ok  {  
			
				
				
			
		
	
		
		
			
				
										options :      options ,  
			
		
	
		
		
			
				
										warnings :     & warnings ,  
			
		
	
		
		
			
				
										matcherDefs :  matcherDefs ,  
			
		
	
		
		
			
				
										parentBlock :  sb . block ,  
			
		
	
		
		
			
				
									} )  
			
		
	
		
		
			
				
									if  err  !=  nil  {  
			
		
	
		
		
			
				
										return  nil ,  warnings ,  fmt . Errorf ( "parsing caddyfile tokens for '%s': %v" ,  dir ,  err )  
			
		
	
		
		
			
				
									}  
			
		
	
		
		
			
				
									for  _ ,  result  :=  range  results  {  
			
		
	
		
		
			
				
										result . directive  =  dir  
			
		
	
		
		
			
				
										sb . pile [ result . Class ]  =  append ( sb . pile [ result . Class ] ,  result )  
			
		
	
		
		
			
				
									}  
			
		
	
		
		
			
				
								}  else  {  
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
									tkn  :=  segment [ 0 ] 				tkn  :=  segment [ 0 ]  
			
		
	
		
		
			
				
									return  nil ,  warnings ,  fmt . Errorf ( "%s:%d: unrecognized directive: %s" ,  tkn . File ,  tkn . Line ,  dir ) 				return  nil ,  warnings ,  fmt . Errorf ( "%s:%d: unrecognized directive: %s" ,  tkn . File ,  tkn . Line ,  dir )  
			
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
								results ,  err  :=  dirFunc ( Helper {  
			
		
	
		
		
			
				
									Dispenser :    caddyfile . NewDispenser ( segment ) ,  
			
		
	
		
		
			
				
									options :      options ,  
			
		
	
		
		
			
				
									warnings :     & warnings ,  
			
		
	
		
		
			
				
									matcherDefs :  matcherDefs ,  
			
		
	
		
		
			
				
									parentBlock :  sb . block ,  
			
		
	
		
		
			
				
								} )  
			
		
	
		
		
			
				
								if  err  !=  nil  {  
			
		
	
		
		
			
				
									return  nil ,  warnings ,  fmt . Errorf ( "parsing caddyfile tokens for '%s': %v" ,  dir ,  err )  
			
		
	
		
		
			
				
								}  
			
		
	
		
		
			
				
								for  _ ,  result  :=  range  results  {  
			
		
	
		
		
			
				
									result . directive  =  dir  
			
		
	
		
		
			
				
									sb . pile [ result . Class ]  =  append ( sb . pile [ result . Class ] ,  result )  
			
		
	
		
		
			
				
								}  
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
					
 
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -372,10 +380,63 @@ func (st *ServerType) serversFromPairings( 
			
		
	
		
		
			
				
									} 				}  
			
		
	
		
		
			
				
									sort . SliceStable ( dirRoutes ,  func ( i ,  j  int )  bool  { 				sort . SliceStable ( dirRoutes ,  func ( i ,  j  int )  bool  {  
			
		
	
		
		
			
				
										iDir ,  jDir  :=  dirRoutes [ i ] . directive ,  dirRoutes [ j ] . directive 					iDir ,  jDir  :=  dirRoutes [ i ] . directive ,  dirRoutes [ j ] . directive  
			
		
	
		
		
			
				
										if  iDir  ==  jDir  {  
			
		
	
		
		
			
				
											// TODO: we really need to refactor this into a separate function or method...
  
			
		
	
		
		
			
				
											// sub-sort by path matcher length, if there's only one
  
			
		
	
		
		
			
				
											iRoute  :=  dirRoutes [ i ] . Value . ( caddyhttp . Route )  
			
		
	
		
		
			
				
											jRoute  :=  dirRoutes [ j ] . Value . ( caddyhttp . Route )  
			
		
	
		
		
			
				
											if  len ( iRoute . MatcherSetsRaw )  ==  1  &&  len ( jRoute . MatcherSetsRaw )  ==  1  {  
			
		
	
		
		
			
				
												// for slightly better efficiency, only decode the path matchers once,
  
			
		
	
		
		
			
				
												// then just store them arbitrarily in the decoded MatcherSets field,
  
			
		
	
		
		
			
				
												// ours should be the only thing in there
  
			
		
	
		
		
			
				
												var  iPM ,  jPM  caddyhttp . MatchPath  
			
		
	
		
		
			
				
												if  len ( iRoute . MatcherSets )  ==  1  {  
			
		
	
		
		
			
				
													iPM  =  iRoute . MatcherSets [ 0 ] [ 0 ] . ( caddyhttp . MatchPath )  
			
		
	
		
		
			
				
												}  
			
		
	
		
		
			
				
												if  len ( jRoute . MatcherSets )  ==  1  {  
			
		
	
		
		
			
				
													jPM  =  jRoute . MatcherSets [ 0 ] [ 0 ] . ( caddyhttp . MatchPath )  
			
		
	
		
		
			
				
												}  
			
		
	
		
		
			
				
												// if it's our first time seeing this route's path matcher, decode it
  
			
		
	
		
		
			
				
												if  iPM  ==  nil  {  
			
		
	
		
		
			
				
													var  pathMatcher  caddyhttp . MatchPath  
			
		
	
		
		
			
				
													_  =  json . Unmarshal ( iRoute . MatcherSetsRaw [ 0 ] [ "path" ] ,  & pathMatcher )  
			
		
	
		
		
			
				
													iRoute . MatcherSets  =  caddyhttp . MatcherSets { { pathMatcher } }  
			
		
	
		
		
			
				
													iPM  =  pathMatcher  
			
		
	
		
		
			
				
												}  
			
		
	
		
		
			
				
												if  jPM  ==  nil  {  
			
		
	
		
		
			
				
													var  pathMatcher  caddyhttp . MatchPath  
			
		
	
		
		
			
				
													_  =  json . Unmarshal ( jRoute . MatcherSetsRaw [ 0 ] [ "path" ] ,  & pathMatcher )  
			
		
	
		
		
			
				
													jRoute . MatcherSets  =  caddyhttp . MatcherSets { { pathMatcher } }  
			
		
	
		
		
			
				
													jPM  =  pathMatcher  
			
		
	
		
		
			
				
												}  
			
		
	
		
		
			
				
												// finally, if there is only one path in the
  
			
		
	
		
		
			
				
												// matcher, sort by longer path first
  
			
		
	
		
		
			
				
												if  len ( iPM )  ==  1  &&  len ( jPM )  ==  1  {  
			
		
	
		
		
			
				
													return  len ( iPM [ 0 ] )  >  len ( jPM [ 0 ] )  
			
		
	
		
		
			
				
												}  
			
		
	
		
		
			
				
											}  
			
		
	
		
		
			
				
										}  
			
		
	
		
		
			
				
										return  dirPositions [ iDir ]  <  dirPositions [ jDir ] 					return  dirPositions [ iDir ]  <  dirPositions [ jDir ]  
			
		
	
		
		
			
				
									} ) 				} )  
			
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
								// add all the routes piled in from directives
  
			
		
	
		
		
			
				
								for  _ ,  r  :=  range  dirRoutes  { 			for  _ ,  r  :=  range  dirRoutes  {  
			
		
	
		
		
			
				
									// as a special case, group rewrite directives so that they are mutually exclusive;
  
			
		
	
		
		
			
				
									// this means that only the first matching rewrite will be evaluated, and that's
  
			
		
	
		
		
			
				
									// probably a good thing, since there should never be a need to do more than one
  
			
		
	
		
		
			
				
									// rewrite (I think?), and cascading rewrites smell bad... imagine these rewrites:
  
			
		
	
		
		
			
				
									//     rewrite /docs/json/* /docs/json/index.html
  
			
		
	
		
		
			
				
									//     rewrite /docs/*      /docs/index.html
  
			
		
	
		
		
			
				
									// (We use this on the Caddy website, or at least we did once.) The first rewrite's
  
			
		
	
		
		
			
				
									// result is also matched by the second rewrite, making the first rewrite pointless.
  
			
		
	
		
		
			
				
									// See issue #2959.
  
			
		
	
		
		
			
				
									if  r . directive  ==  "rewrite"  {  
			
		
	
		
		
			
				
										route  :=  r . Value . ( caddyhttp . Route )  
			
		
	
		
		
			
				
										route . Group  =  "rewriting"  
			
		
	
		
		
			
				
										r . Value  =  route  
			
		
	
		
		
			
				
									}  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
									handlerSubroute . Routes  =  append ( handlerSubroute . Routes ,  r . Value . ( caddyhttp . Route ) ) 				handlerSubroute . Routes  =  append ( handlerSubroute . Routes ,  r . Value . ( caddyhttp . Route ) )  
			
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
					
 
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -480,17 +541,16 @@ func matcherSetFromMatcherToken( 
			
		
	
		
		
			
				
						if  tkn . Text  ==  "*"  { 	if  tkn . Text  ==  "*"  {  
			
		
	
		
		
			
				
							// match all requests == no matchers, so nothing to do
 		// match all requests == no matchers, so nothing to do
  
			
		
	
		
		
			
				
							return  nil ,  true ,  nil 		return  nil ,  true ,  nil  
			
		
	
		
		
			
				
					
						}  else  if  strings . HasPrefix ( tkn . Text ,  "/" )  ||  strings . HasPrefix ( tkn . Text ,  "=/" )  { 	}  else  if  strings . HasPrefix ( tkn . Text ,  "/" )  {  
			
				
				
			
		
	
		
		
	
		
		
			
				
							// convenient way to specify a single path match
 		// convenient way to specify a single path match
  
			
		
	
		
		
			
				
							return  caddy . ModuleMap { 		return  caddy . ModuleMap {  
			
		
	
		
		
			
				
								"path" :  caddyconfig . JSON ( caddyhttp . MatchPath { tkn . Text } ,  warnings ) , 			"path" :  caddyconfig . JSON ( caddyhttp . MatchPath { tkn . Text } ,  warnings ) ,  
			
		
	
		
		
			
				
							} ,  true ,  nil 		} ,  true ,  nil  
			
		
	
		
		
			
				
					
						}  else  if  strings . HasPrefix ( tkn . Text ,  "match:" )  { 	}  else  if  strings . HasPrefix ( tkn . Text ,  matcherPrefix )  {  
			
				
				
			
		
	
		
		
	
		
		
			
				
							// pre-defined matcher
 		// pre-defined matcher
  
			
		
	
		
		
			
				
					
							matcherName  :=  strings . TrimPrefix ( tkn . Text ,  "match:" ) 		m ,  ok  :=  matcherDefs [ tkn . Text ]  
			
				
				
			
		
	
		
		
			
				
							m ,  ok  :=  matcherDefs [ matcherName ]  
			
		
	
		
		
	
		
		
			
				
							if  ! ok  { 		if  ! ok  {  
			
		
	
		
		
			
				
					
								return  nil ,  false ,  fmt . Errorf ( "unrecognized matcher name: %+v" ,  matcherName ) 			return  nil ,  false ,  fmt . Errorf ( "unrecognized matcher name: %+v" ,  tkn . Text )  
			
				
				
			
		
	
		
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
							return  m ,  true ,  nil 		return  m ,  true ,  nil  
			
		
	
		
		
			
				
						} 	}  
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -577,35 +637,37 @@ func (st *ServerType) compileEncodedMatcherSets(sblock caddyfile.ServerBlock) ([ 
			
		
	
		
		
			
				
						return  matcherSetsEnc ,  nil 	return  matcherSetsEnc ,  nil  
			
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					
					func  parseMatcherDefinitions ( d  * caddyfile . Dispenser )  ( map [ string ] caddy . ModuleMap ,  error )  { func  parseMatcherDefinitions ( d  * caddyfile . Dispenser ,  matchers  map [ string ] caddy . ModuleMap )  error  {  
			
				
				
			
		
	
		
		
			
				
						matchers  :=  make ( map [ string ] caddy . ModuleMap )  
			
		
	
		
		
	
		
		
			
				
						for  d . Next ( )  { 	for  d . Next ( )  {  
			
		
	
		
		
			
				
							definitionName  :=  d . Val ( ) 		definitionName  :=  d . Val ( )  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
							if  _ ,  ok  :=  matchers [ definitionName ] ;  ok  {  
			
		
	
		
		
			
				
								return  fmt . Errorf ( "matcher is defined more than once: %s" ,  definitionName )  
			
		
	
		
		
			
				
							}  
			
		
	
		
		
			
				
							matchers [ definitionName ]  =  make ( caddy . ModuleMap )  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
							for  nesting  :=  d . Nesting ( ) ;  d . NextBlock ( nesting ) ;  { 		for  nesting  :=  d . Nesting ( ) ;  d . NextBlock ( nesting ) ;  {  
			
		
	
		
		
			
				
								matcherName  :=  d . Val ( ) 			matcherName  :=  d . Val ( )  
			
		
	
		
		
			
				
								mod ,  err  :=  caddy . GetModule ( "http.matchers."  +  matcherName ) 			mod ,  err  :=  caddy . GetModule ( "http.matchers."  +  matcherName )  
			
		
	
		
		
			
				
								if  err  !=  nil  { 			if  err  !=  nil  {  
			
		
	
		
		
			
				
					
									return  nil ,  fmt . Errorf ( "getting matcher module '%s': %v" ,  matcherName ,  err ) 				return  fmt . Errorf ( "getting matcher module '%s': %v" ,  matcherName ,  err )  
			
				
				
			
		
	
		
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
								unm ,  ok  :=  mod . New ( ) . ( caddyfile . Unmarshaler ) 			unm ,  ok  :=  mod . New ( ) . ( caddyfile . Unmarshaler )  
			
		
	
		
		
			
				
								if  ! ok  { 			if  ! ok  {  
			
		
	
		
		
			
				
					
									return  nil ,  fmt . Errorf ( "matcher module '%s' is not a Caddyfile unmarshaler" ,  matcherName ) 				return  fmt . Errorf ( "matcher module '%s' is not a Caddyfile unmarshaler" ,  matcherName )  
			
				
				
			
		
	
		
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
								err  =  unm . UnmarshalCaddyfile ( d . NewFromNextTokens ( ) ) 			err  =  unm . UnmarshalCaddyfile ( d . NewFromNextTokens ( ) )  
			
		
	
		
		
			
				
								if  err  !=  nil  { 			if  err  !=  nil  {  
			
		
	
		
		
			
				
					
									return  nil ,  err 				return  err  
			
				
				
			
		
	
		
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
								rm ,  ok  :=  unm . ( caddyhttp . RequestMatcher ) 			rm ,  ok  :=  unm . ( caddyhttp . RequestMatcher )  
			
		
	
		
		
			
				
								if  ! ok  { 			if  ! ok  {  
			
		
	
		
		
			
				
					
									return  nil ,  fmt . Errorf ( "matcher module '%s' is not a request matcher" ,  matcherName ) 				return  fmt . Errorf ( "matcher module '%s' is not a request matcher" ,  matcherName )  
			
				
				
			
		
	
		
		
			
				
								}  
			
		
	
		
		
			
				
								if  _ ,  ok  :=  matchers [ definitionName ] ;  ! ok  {  
			
		
	
		
		
			
				
									matchers [ definitionName ]  =  make ( caddy . ModuleMap )  
			
		
	
		
		
	
		
		
			
				
								} 			}  
			
		
	
		
		
			
				
								matchers [ definitionName ] [ matcherName ]  =  caddyconfig . JSON ( rm ,  nil ) 			matchers [ definitionName ] [ matcherName ]  =  caddyconfig . JSON ( rm ,  nil )  
			
		
	
		
		
			
				
							} 		}  
			
		
	
		
		
			
				
						} 	}  
			
		
	
		
		
			
				
					
						return  matchers ,  nil 	return  nil  
			
				
				
			
		
	
		
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					func  encodeMatcherSet ( matchers  map [ string ] caddyhttp . RequestMatcher )  ( caddy . ModuleMap ,  error )  { func  encodeMatcherSet ( matchers  map [ string ] caddyhttp . RequestMatcher )  ( caddy . ModuleMap ,  error )  {  
			
		
	
	
		
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
					@ -643,5 +705,7 @@ type sbAddrAssociation struct { 
			
		
	
		
		
			
				
						serverBlocks  [ ] serverBlock 	serverBlocks  [ ] serverBlock  
			
		
	
		
		
			
				
					} }  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					const  matcherPrefix  =  "@"  
			
		
	
		
		
			
				
					
 
			
		
	
		
		
			
				
					// Interface guard
 // Interface guard
  
			
		
	
		
		
			
				
					var  _  caddyfile . ServerType  =  ( * ServerType ) ( nil ) var  _  caddyfile . ServerType  =  ( * ServerType ) ( nil )