| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // Copyright 2015 Matthew Holt and The Caddy Authors | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  | // limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package httpcaddyfile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/caddyserver/caddy/v2" | 
					
						
							|  |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig" | 
					
						
							|  |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" | 
					
						
							|  |  |  | 	"github.com/caddyserver/caddy/v2/modules/caddyhttp" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // defaultDirectiveOrder specifies the order | 
					
						
							|  |  |  | // to apply directives in HTTP routes. | 
					
						
							|  |  |  | var defaultDirectiveOrder = []string{ | 
					
						
							|  |  |  | 	"rewrite", | 
					
						
							| 
									
										
										
										
											2019-12-12 15:46:13 -07:00
										 |  |  | 	"strip_prefix", | 
					
						
							|  |  |  | 	"strip_suffix", | 
					
						
							|  |  |  | 	"uri_replace", | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	"try_files", | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 	"basicauth", | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	"headers", | 
					
						
							| 
									
										
										
										
											2019-09-11 18:48:37 -06:00
										 |  |  | 	"request_header", | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	"encode", | 
					
						
							|  |  |  | 	"templates", | 
					
						
							|  |  |  | 	"redir", | 
					
						
							| 
									
										
										
										
											2019-09-16 11:04:18 -06:00
										 |  |  | 	"respond", | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	"reverse_proxy", | 
					
						
							| 
									
										
										
										
											2019-09-11 12:02:35 -06:00
										 |  |  | 	"php_fastcgi", | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	"file_server", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RegisterDirective registers a unique directive dir with an | 
					
						
							|  |  |  | // associated unmarshaling (setup) function. When directive dir | 
					
						
							|  |  |  | // is encountered in a Caddyfile, setupFunc will be called to | 
					
						
							|  |  |  | // unmarshal its tokens. | 
					
						
							|  |  |  | func RegisterDirective(dir string, setupFunc UnmarshalFunc) { | 
					
						
							|  |  |  | 	if _, ok := registeredDirectives[dir]; ok { | 
					
						
							|  |  |  | 		panic("directive " + dir + " already registered") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	registeredDirectives[dir] = setupFunc | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RegisterHandlerDirective is like RegisterDirective, but for | 
					
						
							|  |  |  | // directives which specifically output only an HTTP handler. | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  | // Directives registered with this function will always have | 
					
						
							|  |  |  | // an optional matcher token as the first argument. | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) { | 
					
						
							|  |  |  | 	RegisterDirective(dir, func(h Helper) ([]ConfigValue, error) { | 
					
						
							|  |  |  | 		if !h.Next() { | 
					
						
							|  |  |  | 			return nil, h.ArgErr() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		matcherSet, ok, err := h.MatcherToken() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if ok { | 
					
						
							| 
									
										
										
										
											2019-11-04 13:43:39 -07:00
										 |  |  | 			// strip matcher token; we don't need to | 
					
						
							|  |  |  | 			// use the return value here because a | 
					
						
							|  |  |  | 			// new dispenser should have been made | 
					
						
							|  |  |  | 			// solely for this directive's tokens, | 
					
						
							|  |  |  | 			// with no other uses of same slice | 
					
						
							|  |  |  | 			h.Dispenser.Delete() | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		h.Dispenser.Reset() // pretend this lookahead never happened | 
					
						
							|  |  |  | 		val, err := setupFunc(h) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return h.NewRoute(matcherSet, val), nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Helper is a type which helps setup a value from | 
					
						
							|  |  |  | // Caddyfile tokens. | 
					
						
							|  |  |  | type Helper struct { | 
					
						
							|  |  |  | 	*caddyfile.Dispenser | 
					
						
							| 
									
										
										
										
											2019-09-30 09:11:30 -06:00
										 |  |  | 	options     map[string]interface{} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	warnings    *[]caddyconfig.Warning | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	matcherDefs map[string]caddy.ModuleMap | 
					
						
							| 
									
										
										
										
											2019-08-21 15:50:02 -06:00
										 |  |  | 	parentBlock caddyfile.ServerBlock | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-30 09:11:30 -06:00
										 |  |  | // Option gets the option keyed by name. | 
					
						
							|  |  |  | func (h Helper) Option(name string) interface{} { | 
					
						
							|  |  |  | 	return h.options[name] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 15:50:02 -06:00
										 |  |  | // Caddyfiles returns the list of config files from | 
					
						
							|  |  |  | // which tokens in the current server block were loaded. | 
					
						
							|  |  |  | func (h Helper) Caddyfiles() []string { | 
					
						
							|  |  |  | 	// first obtain set of names of files involved | 
					
						
							|  |  |  | 	// in this server block, without duplicates | 
					
						
							|  |  |  | 	files := make(map[string]struct{}) | 
					
						
							|  |  |  | 	for _, segment := range h.parentBlock.Segments { | 
					
						
							|  |  |  | 		for _, token := range segment { | 
					
						
							|  |  |  | 			files[token.File] = struct{}{} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// then convert the set into a slice | 
					
						
							|  |  |  | 	filesSlice := make([]string, 0, len(files)) | 
					
						
							|  |  |  | 	for file := range files { | 
					
						
							|  |  |  | 		filesSlice = append(filesSlice, file) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return filesSlice | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // JSON converts val into JSON. Any errors are added to warnings. | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  | func (h Helper) JSON(val interface{}) json.RawMessage { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	return caddyconfig.JSON(val, h.warnings) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MatcherToken assumes the current token is (possibly) a matcher, and | 
					
						
							|  |  |  | // if so, returns the matcher set along with a true value. If the current | 
					
						
							|  |  |  | // token is not a matcher, nil and false is returned. Note that a true | 
					
						
							|  |  |  | // value may be returned with a nil matcher set if it is a catch-all. | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | func (h Helper) MatcherToken() (caddy.ModuleMap, bool, error) { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	if !h.NextArg() { | 
					
						
							|  |  |  | 		return nil, false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return matcherSetFromMatcherToken(h.Dispenser.Token(), h.matcherDefs, h.warnings) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewRoute returns config values relevant to creating a new HTTP route. | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | func (h Helper) NewRoute(matcherSet caddy.ModuleMap, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	handler caddyhttp.MiddlewareHandler) []ConfigValue { | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  | 	mod, err := caddy.GetModule(caddy.GetModuleID(handler)) | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  | 		*h.warnings = append(*h.warnings, caddyconfig.Warning{ | 
					
						
							|  |  |  | 			File:    h.File(), | 
					
						
							|  |  |  | 			Line:    h.Line(), | 
					
						
							|  |  |  | 			Message: err.Error(), | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	var matcherSetsRaw []caddy.ModuleMap | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	if matcherSet != nil { | 
					
						
							|  |  |  | 		matcherSetsRaw = append(matcherSetsRaw, matcherSet) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return []ConfigValue{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Class: "route", | 
					
						
							|  |  |  | 			Value: caddyhttp.Route{ | 
					
						
							|  |  |  | 				MatcherSetsRaw: matcherSetsRaw, | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 				HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(handler, "handler", mod.ID.Name(), h.warnings)}, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewBindAddresses returns config values relevant to adding | 
					
						
							|  |  |  | // listener bind addresses to the config. | 
					
						
							|  |  |  | func (h Helper) NewBindAddresses(addrs []string) []ConfigValue { | 
					
						
							|  |  |  | 	return []ConfigValue{{Class: "bind", Value: addrs}} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewVarsRoute returns config values relevant to adding a | 
					
						
							|  |  |  | // "vars" wrapper route to the config. | 
					
						
							|  |  |  | func (h Helper) NewVarsRoute(route caddyhttp.Route) []ConfigValue { | 
					
						
							|  |  |  | 	return []ConfigValue{{Class: "var", Value: route}} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ConfigValue represents a value to be added to the final | 
					
						
							|  |  |  | // configuration, or a value to be consulted when building | 
					
						
							|  |  |  | // the final configuration. | 
					
						
							|  |  |  | type ConfigValue struct { | 
					
						
							|  |  |  | 	// The kind of value this is. As the config is | 
					
						
							|  |  |  | 	// being built, the adapter will look in the | 
					
						
							|  |  |  | 	// "pile" for values belonging to a certain | 
					
						
							|  |  |  | 	// class when it is setting up a certain part | 
					
						
							|  |  |  | 	// of the config. The associated value will be | 
					
						
							|  |  |  | 	// type-asserted and placed accordingly. | 
					
						
							|  |  |  | 	Class string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The value to be used when building the config. | 
					
						
							|  |  |  | 	// Generally its type is associated with the | 
					
						
							|  |  |  | 	// name of the Class. | 
					
						
							|  |  |  | 	Value interface{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	directive string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // serverBlock pairs a Caddyfile server block | 
					
						
							|  |  |  | // with a "pile" of config values, keyed by class | 
					
						
							|  |  |  | // name. | 
					
						
							|  |  |  | type serverBlock struct { | 
					
						
							|  |  |  | 	block caddyfile.ServerBlock | 
					
						
							|  |  |  | 	pile  map[string][]ConfigValue // config values obtained from directives | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ( | 
					
						
							|  |  |  | 	// UnmarshalFunc is a function which can unmarshal Caddyfile | 
					
						
							|  |  |  | 	// tokens into zero or more config values using a Helper type. | 
					
						
							|  |  |  | 	// These are passed in a call to RegisterDirective. | 
					
						
							|  |  |  | 	UnmarshalFunc func(h Helper) ([]ConfigValue, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// UnmarshalHandlerFunc is like UnmarshalFunc, except the | 
					
						
							|  |  |  | 	// output of the unmarshaling is an HTTP handler. This | 
					
						
							|  |  |  | 	// function does not need to deal with HTTP request matching | 
					
						
							|  |  |  | 	// which is abstracted away. Since writing HTTP handlers | 
					
						
							|  |  |  | 	// with Caddyfile support is very common, this is a more | 
					
						
							|  |  |  | 	// convenient way to add a handler to the chain since a lot | 
					
						
							|  |  |  | 	// of the details common to HTTP handlers are taken care of | 
					
						
							|  |  |  | 	// for you. These are passed to a call to | 
					
						
							|  |  |  | 	// RegisterHandlerDirective. | 
					
						
							|  |  |  | 	UnmarshalHandlerFunc func(h Helper) (caddyhttp.MiddlewareHandler, error) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var registeredDirectives = make(map[string]UnmarshalFunc) |