mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -04:00 
			
		
		
		
	diagnostics: AppendUnique(), restructure sets, add metrics, fix bugs
This commit is contained in:
		
							parent
							
								
									703cf7bf8b
								
							
						
					
					
						commit
						6b3c2212a1
					
				
							
								
								
									
										3
									
								
								caddy.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								caddy.go
									
									
									
									
									
								
							| @ -44,6 +44,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/mholt/caddy/caddyfile" | 	"github.com/mholt/caddy/caddyfile" | ||||||
|  | 	"github.com/mholt/caddy/diagnostics" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Configurable application parameters | // Configurable application parameters | ||||||
| @ -573,6 +574,8 @@ func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bo | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	diagnostics.Set("num_server_blocks", len(sblocks)) | ||||||
|  | 
 | ||||||
| 	return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate) | 	return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -152,18 +152,18 @@ func Run() { | |||||||
| 
 | 
 | ||||||
| 	// Begin diagnostics (these are no-ops if diagnostics disabled) | 	// Begin diagnostics (these are no-ops if diagnostics disabled) | ||||||
| 	diagnostics.Set("caddy_version", appVersion) | 	diagnostics.Set("caddy_version", appVersion) | ||||||
| 	// TODO: plugins |  | ||||||
| 	diagnostics.Set("num_listeners", len(instance.Servers())) | 	diagnostics.Set("num_listeners", len(instance.Servers())) | ||||||
|  | 	diagnostics.Set("server_type", serverType) | ||||||
| 	diagnostics.Set("os", runtime.GOOS) | 	diagnostics.Set("os", runtime.GOOS) | ||||||
| 	diagnostics.Set("arch", runtime.GOARCH) | 	diagnostics.Set("arch", runtime.GOARCH) | ||||||
| 	diagnostics.Set("cpu", struct { | 	diagnostics.Set("cpu", struct { | ||||||
| 		NumLogical int    `json:"num_logical"` | 		BrandName  string `json:"brand_name,omitempty"` | ||||||
| 		AESNI      bool   `json:"aes_ni"` | 		NumLogical int    `json:"num_logical,omitempty"` | ||||||
| 		BrandName  string `json:"brand_name"` | 		AESNI      bool   `json:"aes_ni,omitempty"` | ||||||
| 	}{ | 	}{ | ||||||
|  | 		BrandName:  cpuid.CPU.BrandName, | ||||||
| 		NumLogical: runtime.NumCPU(), | 		NumLogical: runtime.NumCPU(), | ||||||
| 		AESNI:      cpuid.CPU.AesNi(), | 		AESNI:      cpuid.CPU.AesNi(), | ||||||
| 		BrandName:  cpuid.CPU.BrandName, |  | ||||||
| 	}) | 	}) | ||||||
| 	diagnostics.StartEmitting() | 	diagnostics.StartEmitting() | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,6 +20,8 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/mholt/caddy/diagnostics" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Parse parses the input just enough to group tokens, in | // Parse parses the input just enough to group tokens, in | ||||||
| @ -369,6 +371,7 @@ func (p *parser) directive() error { | |||||||
| 
 | 
 | ||||||
| 	// The directive itself is appended as a relevant token | 	// The directive itself is appended as a relevant token | ||||||
| 	p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor]) | 	p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor]) | ||||||
|  | 	diagnostics.AppendUnique("directives", dir) | ||||||
| 
 | 
 | ||||||
| 	for p.Next() { | 	for p.Next() { | ||||||
| 		if p.Val() == "{" { | 		if p.Val() == "{" { | ||||||
|  | |||||||
| @ -24,6 +24,8 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"github.com/mholt/caddy/diagnostics" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // tlsHandler is a http.Handler that will inject a value | // tlsHandler is a http.Handler that will inject a value | ||||||
| @ -97,6 +99,13 @@ func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||||||
| 
 | 
 | ||||||
| 	if checked { | 	if checked { | ||||||
| 		r = r.WithContext(context.WithValue(r.Context(), MitmCtxKey, mitm)) | 		r = r.WithContext(context.WithValue(r.Context(), MitmCtxKey, mitm)) | ||||||
|  | 		if mitm { | ||||||
|  | 			go diagnostics.AppendUnique("mitm", "likely") | ||||||
|  | 		} else { | ||||||
|  | 			go diagnostics.AppendUnique("mitm", "unlikely") | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		go diagnostics.AppendUnique("mitm", "unknown") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if mitm && h.closeOnMITM { | 	if mitm && h.closeOnMITM { | ||||||
|  | |||||||
| @ -29,7 +29,6 @@ import ( | |||||||
| 	"github.com/mholt/caddy/caddyfile" | 	"github.com/mholt/caddy/caddyfile" | ||||||
| 	"github.com/mholt/caddy/caddyhttp/staticfiles" | 	"github.com/mholt/caddy/caddyhttp/staticfiles" | ||||||
| 	"github.com/mholt/caddy/caddytls" | 	"github.com/mholt/caddy/caddytls" | ||||||
| 	"github.com/mholt/caddy/diagnostics" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const serverType = "http" | const serverType = "http" | ||||||
| @ -206,8 +205,6 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	diagnostics.Set("num_sites", len(h.siteConfigs)) |  | ||||||
| 
 |  | ||||||
| 	// we must map (group) each config to a bind address | 	// we must map (group) each config to a bind address | ||||||
| 	groups, err := groupSiteConfigsByListenAddr(h.siteConfigs) | 	groups, err := groupSiteConfigsByListenAddr(h.siteConfigs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
| @ -346,7 +346,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	go diagnostics.AppendUniqueString("user_agent", r.Header.Get("User-Agent")) | 	go diagnostics.AppendUnique("user_agent", r.Header.Get("User-Agent")) | ||||||
| 
 | 
 | ||||||
| 	// copy the original, unchanged URL into the context | 	// copy the original, unchanged URL into the context | ||||||
| 	// so it can be referenced by middlewares | 	// so it can be referenced by middlewares | ||||||
|  | |||||||
| @ -25,6 +25,8 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/mholt/caddy/diagnostics" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // configGroup is a type that keys configs by their hostname | // configGroup is a type that keys configs by their hostname | ||||||
| @ -98,6 +100,23 @@ func (cg configGroup) GetConfigForClient(clientHello *tls.ClientHelloInfo) (*tls | |||||||
| // | // | ||||||
| // This method is safe for use as a tls.Config.GetCertificate callback. | // This method is safe for use as a tls.Config.GetCertificate callback. | ||||||
| func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { | func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { | ||||||
|  | 	go diagnostics.Append("client_hello", struct { | ||||||
|  | 		NoSNI             bool                  `json:"no_sni,omitempty"` | ||||||
|  | 		CipherSuites      []uint16              `json:"cipher_suites,omitempty"` | ||||||
|  | 		SupportedCurves   []tls.CurveID         `json:"curves,omitempty"` | ||||||
|  | 		SupportedPoints   []uint8               `json:"points,omitempty"` | ||||||
|  | 		SignatureSchemes  []tls.SignatureScheme `json:"sig_scheme,omitempty"` | ||||||
|  | 		ALPN              []string              `json:"alpn,omitempty"` | ||||||
|  | 		SupportedVersions []uint16              `json:"versions,omitempty"` | ||||||
|  | 	}{ | ||||||
|  | 		NoSNI:             clientHello.ServerName == "", | ||||||
|  | 		CipherSuites:      clientHello.CipherSuites, | ||||||
|  | 		SupportedCurves:   clientHello.SupportedCurves, | ||||||
|  | 		SupportedPoints:   clientHello.SupportedPoints, | ||||||
|  | 		SignatureSchemes:  clientHello.SignatureSchemes, | ||||||
|  | 		ALPN:              clientHello.SupportedProtos, | ||||||
|  | 		SupportedVersions: clientHello.SupportedVersions, | ||||||
|  | 	}) | ||||||
| 	cert, err := cfg.getCertDuringHandshake(strings.ToLower(clientHello.ServerName), true, true) | 	cert, err := cfg.getCertDuringHandshake(strings.ToLower(clientHello.ServerName), true, true) | ||||||
| 	return &cert.Certificate, err | 	return &cert.Certificate, err | ||||||
| } | } | ||||||
|  | |||||||
| @ -113,7 +113,7 @@ func Set(key string, val interface{}) { | |||||||
| // Append appends value to a list named key. | // Append appends value to a list named key. | ||||||
| // If key is new, a new list will be created. | // If key is new, a new list will be created. | ||||||
| // If key maps to a type that is not a list, | // If key maps to a type that is not a list, | ||||||
| // an error is logged, and this is a no-op. | // a panic is logged, and this is a no-op. | ||||||
| // | // | ||||||
| // TODO: is this function needed/useful? | // TODO: is this function needed/useful? | ||||||
| func Append(key string, value interface{}) { | func Append(key string, value interface{}) { | ||||||
| @ -142,66 +142,38 @@ func Append(key string, value interface{}) { | |||||||
| 	bufferMu.Unlock() | 	bufferMu.Unlock() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AppendUniqueString adds value to a set named key. | // AppendUnique adds value to a set  namedkey. | ||||||
| // Set items are unordered. Values in the set | // Set items are unordered. Values in the set | ||||||
| // are unique, but repeat values are counted. | // are unique, but how many times they are | ||||||
|  | // appended is counted. | ||||||
| // | // | ||||||
| // If key is new, a new set will be created. | // If key is new, a new set will be created for | ||||||
| // If key maps to a type that is not a string | // values with that key. If key maps to a type | ||||||
| // set, an error is logged, and this is a no-op. | // that is not a counting set, a panic is logged, | ||||||
| func AppendUniqueString(key, value string) { | // and this is a no-op. | ||||||
|  | func AppendUnique(key string, value interface{}) { | ||||||
| 	if !enabled { | 	if !enabled { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	bufferMu.Lock() | 	bufferMu.Lock() | ||||||
| 	if bufferItemCount >= maxBufferItems { |  | ||||||
| 		bufferMu.Unlock() |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	bufVal, inBuffer := buffer[key] | 	bufVal, inBuffer := buffer[key] | ||||||
| 	mapVal, mapOk := bufVal.(map[string]int) | 	setVal, setOk := bufVal.(countingSet) | ||||||
| 	if inBuffer && !mapOk { | 	if inBuffer && !setOk { | ||||||
| 		bufferMu.Unlock() | 		bufferMu.Unlock() | ||||||
| 		log.Printf("[PANIC] Diagnostics: key %s already used for non-map value", key) | 		log.Printf("[PANIC] Diagnostics: key %s already used for non-counting-set value", key) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if mapVal == nil { | 	if setVal == nil { | ||||||
| 		buffer[key] = map[string]int{value: 1} | 		// ensure the buffer is not too full, then add new unique value | ||||||
|  | 		if bufferItemCount >= maxBufferItems { | ||||||
|  | 			bufferMu.Unlock() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		buffer[key] = countingSet{value: 1} | ||||||
| 		bufferItemCount++ | 		bufferItemCount++ | ||||||
| 	} else if mapOk { | 	} else if setOk { | ||||||
| 		mapVal[value]++ | 		// unique value already exists, so just increment counter | ||||||
| 	} | 		setVal[value]++ | ||||||
| 	bufferMu.Unlock() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // AppendUniqueInt adds value to a set named key. |  | ||||||
| // Set items are unordered. Values in the set |  | ||||||
| // are unique, but repeat values are counted. |  | ||||||
| // |  | ||||||
| // If key is new, a new set will be created. |  | ||||||
| // If key maps to a type that is not an integer |  | ||||||
| // set, an error is logged, and this is a no-op. |  | ||||||
| func AppendUniqueInt(key string, value int) { |  | ||||||
| 	if !enabled { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	bufferMu.Lock() |  | ||||||
| 	if bufferItemCount >= maxBufferItems { |  | ||||||
| 		bufferMu.Unlock() |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	bufVal, inBuffer := buffer[key] |  | ||||||
| 	mapVal, mapOk := bufVal.(map[int]int) |  | ||||||
| 	if inBuffer && !mapOk { |  | ||||||
| 		bufferMu.Unlock() |  | ||||||
| 		log.Printf("[PANIC] Diagnostics: key %s already used for non-map value", key) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if mapVal == nil { |  | ||||||
| 		buffer[key] = map[int]int{value: 1} |  | ||||||
| 		bufferItemCount++ |  | ||||||
| 	} else if mapOk { |  | ||||||
| 		mapVal[value]++ |  | ||||||
| 	} | 	} | ||||||
| 	bufferMu.Unlock() | 	bufferMu.Unlock() | ||||||
| } | } | ||||||
| @ -209,7 +181,7 @@ func AppendUniqueInt(key string, value int) { | |||||||
| // Increment adds 1 to a value named key. | // Increment adds 1 to a value named key. | ||||||
| // If it does not exist, it is created with | // If it does not exist, it is created with | ||||||
| // a value of 1. If key maps to a type that | // a value of 1. If key maps to a type that | ||||||
| // is not an integer, an error is logged, | // is not an integer, a panic is logged, | ||||||
| // and this is a no-op. | // and this is a no-op. | ||||||
| func Increment(key string) { | func Increment(key string) { | ||||||
| 	incrementOrDecrement(key, true) | 	incrementOrDecrement(key, true) | ||||||
|  | |||||||
| @ -21,13 +21,16 @@ | |||||||
| // collection/aggregation functions. Call StartEmitting() when you are | // collection/aggregation functions. Call StartEmitting() when you are | ||||||
| // ready to begin sending diagnostic updates. | // ready to begin sending diagnostic updates. | ||||||
| // | // | ||||||
| // When collecting metrics (functions like Set, Append*, or Increment), | // When collecting metrics (functions like Set, AppendUnique, or Increment), | ||||||
| // it may be desirable and even recommended to run invoke them in a new | // it may be desirable and even recommended to invoke them in a new | ||||||
| // goroutine (use the go keyword) in case there is lock contention; | // goroutine (use the go keyword) in case there is lock contention; | ||||||
| // they are thread-safe (unless noted), and you may not want them to | // they are thread-safe (unless noted), and you may not want them to | ||||||
| // block the main thread of execution. However, sometimes blocking | // block the main thread of execution. However, sometimes blocking | ||||||
| // may be necessary too; for example, adding startup metrics to the | // may be necessary too; for example, adding startup metrics to the | ||||||
| // buffer before the call to StartEmitting(). | // buffer before the call to StartEmitting(). | ||||||
|  | // | ||||||
|  | // This package is designed to be as fast and space-efficient as reasonably | ||||||
|  | // possible, so that it does not disrupt the flow of execution. | ||||||
| package diagnostics | package diagnostics | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| @ -122,11 +125,6 @@ func emit(final bool) error { | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// ensure we won't slam the diagnostics server |  | ||||||
| 		if reply.NextUpdate < 1*time.Second { |  | ||||||
| 			reply.NextUpdate = defaultUpdateInterval |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// make sure we didn't send the update too soon; if so, | 		// make sure we didn't send the update too soon; if so, | ||||||
| 		// just wait and try again -- this is a special case of | 		// just wait and try again -- this is a special case of | ||||||
| 		// error that we handle differently, as you can see | 		// error that we handle differently, as you can see | ||||||
| @ -151,6 +149,11 @@ func emit(final bool) error { | |||||||
| 	// schedule the next update using our default update | 	// schedule the next update using our default update | ||||||
| 	// interval because the server might be healthy later | 	// interval because the server might be healthy later | ||||||
| 
 | 
 | ||||||
|  | 	// ensure we won't slam the diagnostics server | ||||||
|  | 	if reply.NextUpdate < 1*time.Second { | ||||||
|  | 		reply.NextUpdate = defaultUpdateInterval | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// schedule the next update (if this wasn't the last one and | 	// schedule the next update (if this wasn't the last one and | ||||||
| 	// if the remote server didn't tell us to stop sending) | 	// if the remote server didn't tell us to stop sending) | ||||||
| 	if !final && !reply.Stop { | 	if !final && !reply.Stop { | ||||||
| @ -216,6 +219,30 @@ type Payload struct { | |||||||
| 	Data map[string]interface{} `json:"data,omitempty"` | 	Data map[string]interface{} `json:"data,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // countingSet implements a set that counts how many | ||||||
|  | // times a key is inserted. It marshals to JSON in a | ||||||
|  | // way such that keys are converted to values next | ||||||
|  | // to their associated counts. | ||||||
|  | type countingSet map[interface{}]int | ||||||
|  | 
 | ||||||
|  | // MarshalJSON implements the json.Marshaler interface. | ||||||
|  | // It converts the set to an array so that the values | ||||||
|  | // are JSON object values instead of keys, since keys | ||||||
|  | // are difficult to query in databases. | ||||||
|  | func (s countingSet) MarshalJSON() ([]byte, error) { | ||||||
|  | 	type Item struct { | ||||||
|  | 		Value interface{} `json:"value"` | ||||||
|  | 		Count int         `json:"count"` | ||||||
|  | 	} | ||||||
|  | 	var list []Item | ||||||
|  | 
 | ||||||
|  | 	for k, v := range s { | ||||||
|  | 		list = append(list, Item{Value: k, Count: v}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return json.Marshal(list) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| var ( | var ( | ||||||
| 	// httpClient should be used for HTTP requests. It | 	// httpClient should be used for HTTP requests. It | ||||||
| 	// is configured with a timeout for reliability. | 	// is configured with a timeout for reliability. | ||||||
| @ -253,7 +280,7 @@ var ( | |||||||
| const ( | const ( | ||||||
| 	// endpoint is the base URL to remote diagnostics server; | 	// endpoint is the base URL to remote diagnostics server; | ||||||
| 	// the instance ID will be appended to it. | 	// the instance ID will be appended to it. | ||||||
| 	endpoint = "https://diagnostics-staging.caddyserver.com/update/" // TODO: make configurable, "http://localhost:8081/update/" | 	endpoint = "https://diagnostics-staging.caddyserver.com/update/" // TODO: make configurable, "http://localhost:8085/update/" | ||||||
| 
 | 
 | ||||||
| 	// defaultUpdateInterval is how long to wait before emitting | 	// defaultUpdateInterval is how long to wait before emitting | ||||||
| 	// more diagnostic data. This value is only used if the | 	// more diagnostic data. This value is only used if the | ||||||
|  | |||||||
							
								
								
									
										57
									
								
								plugins.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								plugins.go
									
									
									
									
									
								
							| @ -53,29 +53,59 @@ var ( | |||||||
| 
 | 
 | ||||||
| // DescribePlugins returns a string describing the registered plugins. | // DescribePlugins returns a string describing the registered plugins. | ||||||
| func DescribePlugins() string { | func DescribePlugins() string { | ||||||
|  | 	pl := ListPlugins() | ||||||
|  | 
 | ||||||
| 	str := "Server types:\n" | 	str := "Server types:\n" | ||||||
| 	for name := range serverTypes { | 	for _, name := range pl["server_types"] { | ||||||
| 		str += "  " + name + "\n" | 		str += "  " + name + "\n" | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// List the loaders in registration order |  | ||||||
| 	str += "\nCaddyfile loaders:\n" | 	str += "\nCaddyfile loaders:\n" | ||||||
| 	for _, loader := range caddyfileLoaders { | 	for _, name := range pl["caddyfile_loaders"] { | ||||||
| 		str += "  " + loader.name + "\n" | 		str += "  " + name + "\n" | ||||||
| 	} |  | ||||||
| 	if defaultCaddyfileLoader.name != "" { |  | ||||||
| 		str += "  " + defaultCaddyfileLoader.name + "\n" |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(eventHooks) > 0 { | 	if len(eventHooks) > 0 { | ||||||
| 		// List the event hook plugins |  | ||||||
| 		str += "\nEvent hook plugins:\n" | 		str += "\nEvent hook plugins:\n" | ||||||
| 		for hookPlugin := range eventHooks { | 		for _, name := range pl["event_hooks"] { | ||||||
| 			str += "  hook." + hookPlugin + "\n" | 			str += "  hook." + name + "\n" | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Let's alphabetize the rest of these... | 	str += "\nOther plugins:\n" | ||||||
|  | 	for _, name := range pl["others"] { | ||||||
|  | 		str += "  " + name + "\n" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return str | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ListPlugins makes a list of the registered plugins, | ||||||
|  | // keyed by plugin type. | ||||||
|  | func ListPlugins() map[string][]string { | ||||||
|  | 	p := make(map[string][]string) | ||||||
|  | 
 | ||||||
|  | 	// server type plugins | ||||||
|  | 	for name := range serverTypes { | ||||||
|  | 		p["server_types"] = append(p["server_types"], name) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// caddyfile loaders in registration order | ||||||
|  | 	for _, loader := range caddyfileLoaders { | ||||||
|  | 		p["caddyfile_loaders"] = append(p["caddyfile_loaders"], loader.name) | ||||||
|  | 	} | ||||||
|  | 	if defaultCaddyfileLoader.name != "" { | ||||||
|  | 		p["caddyfile_loaders"] = append(p["caddyfile_loaders"], defaultCaddyfileLoader.name) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// event hook plugins | ||||||
|  | 	if len(eventHooks) > 0 { | ||||||
|  | 		for name := range eventHooks { | ||||||
|  | 			p["event_hooks"] = append(p["event_hooks"], name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// alphabetize the rest of the plugins | ||||||
| 	var others []string | 	var others []string | ||||||
| 	for stype, stypePlugins := range plugins { | 	for stype, stypePlugins := range plugins { | ||||||
| 		for name := range stypePlugins { | 		for name := range stypePlugins { | ||||||
| @ -89,12 +119,11 @@ func DescribePlugins() string { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sort.Strings(others) | 	sort.Strings(others) | ||||||
| 	str += "\nOther plugins:\n" |  | ||||||
| 	for _, name := range others { | 	for _, name := range others { | ||||||
| 		str += "  " + name + "\n" | 		p["others"] = append(p["others"], name) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return str | 	return p | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ValidDirectives returns the list of all directives that are | // ValidDirectives returns the list of all directives that are | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user