summaryrefslogtreecommitdiff
path: root/caddyconfig
diff options
context:
space:
mode:
Diffstat (limited to 'caddyconfig')
-rw-r--r--caddyconfig/caddyfile/formatter.go140
-rw-r--r--caddyconfig/caddyfile/formatter_test.go195
-rwxr-xr-xcaddyconfig/caddyfile/parse.go2
-rwxr-xr-xcaddyconfig/caddyfile/parse_test.go4
-rw-r--r--caddyconfig/httpcaddyfile/addresses.go2
-rw-r--r--caddyconfig/httpcaddyfile/builtins.go28
-rw-r--r--caddyconfig/httpcaddyfile/directives.go62
-rw-r--r--caddyconfig/httpcaddyfile/httptype.go44
8 files changed, 429 insertions, 48 deletions
diff --git a/caddyconfig/caddyfile/formatter.go b/caddyconfig/caddyfile/formatter.go
new file mode 100644
index 0000000..e937208
--- /dev/null
+++ b/caddyconfig/caddyfile/formatter.go
@@ -0,0 +1,140 @@
+// 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 caddyfile
+
+import (
+ "bytes"
+ "io"
+ "unicode"
+)
+
+// Format formats a Caddyfile to conventional standards.
+func Format(body []byte) []byte {
+ reader := bytes.NewReader(body)
+ result := new(bytes.Buffer)
+
+ var (
+ commented,
+ quoted,
+ escaped,
+ environ,
+ lineBegin bool
+
+ firstIteration = true
+
+ indentation = 0
+
+ prev,
+ curr,
+ next rune
+
+ err error
+ )
+
+ for {
+ prev = curr
+ curr = next
+
+ if curr < 0 {
+ break
+ }
+
+ next, _, err = reader.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ next = -1
+ } else {
+ panic(err)
+ }
+ }
+
+ if firstIteration {
+ firstIteration = false
+ lineBegin = true
+ continue
+ }
+
+ if quoted {
+ if escaped {
+ escaped = false
+ } else {
+ if curr == '\\' {
+ escaped = true
+ }
+ if curr == '"' {
+ quoted = false
+ }
+ }
+ if curr == '\n' {
+ quoted = false
+ }
+ } else if commented {
+ if curr == '\n' {
+ commented = false
+ }
+ } else {
+ if curr == '"' {
+ quoted = true
+ }
+ if curr == '#' {
+ commented = true
+ }
+ if curr == '}' {
+ if environ {
+ environ = false
+ } else if indentation > 0 {
+ indentation--
+ }
+ }
+ if curr == '{' {
+ if unicode.IsSpace(next) {
+ indentation++
+
+ if !unicode.IsSpace(prev) {
+ result.WriteRune(' ')
+ }
+ } else {
+ environ = true
+ }
+ }
+ if lineBegin {
+ if curr == ' ' || curr == '\t' {
+ continue
+ } else {
+ lineBegin = false
+ if indentation > 0 {
+ for tabs := indentation; tabs > 0; tabs-- {
+ result.WriteRune('\t')
+ }
+ }
+ }
+ } else {
+ if prev == '{' &&
+ (curr == ' ' || curr == '\t') &&
+ (next != '\n' && next != '\r') {
+ curr = '\n'
+ }
+ }
+ }
+
+ if curr == '\n' {
+ lineBegin = true
+ }
+
+ result.WriteRune(curr)
+ }
+
+ return result.Bytes()
+}
diff --git a/caddyconfig/caddyfile/formatter_test.go b/caddyconfig/caddyfile/formatter_test.go
new file mode 100644
index 0000000..76eca00
--- /dev/null
+++ b/caddyconfig/caddyfile/formatter_test.go
@@ -0,0 +1,195 @@
+// 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 caddyfile
+
+import (
+ "testing"
+)
+
+func TestFormatBasicIndentation(t *testing.T) {
+ input := []byte(`
+ a
+b
+
+ c {
+ d
+}
+
+e { f
+}
+
+g {
+h {
+i
+}
+}
+
+j { k {
+l
+}
+}
+
+m {
+ n { o
+ }
+}
+`)
+ expected := []byte(`
+a
+b
+
+c {
+ d
+}
+
+e {
+ f
+}
+
+g {
+ h {
+ i
+ }
+}
+
+j {
+ k {
+ l
+ }
+}
+
+m {
+ n {
+ o
+ }
+}
+`)
+ testFormat(t, input, expected)
+}
+
+func TestFormatBasicSpacing(t *testing.T) {
+ input := []byte(`
+a{
+ b
+}
+
+c{ d
+}
+`)
+ expected := []byte(`
+a {
+ b
+}
+
+c {
+ d
+}
+`)
+ testFormat(t, input, expected)
+}
+
+func TestFormatEnvironmentVariable(t *testing.T) {
+ input := []byte(`
+{$A}
+
+b {
+{$C}
+}
+
+d { {$E}
+}
+`)
+ expected := []byte(`
+{$A}
+
+b {
+ {$C}
+}
+
+d {
+ {$E}
+}
+`)
+ testFormat(t, input, expected)
+}
+
+func TestFormatComments(t *testing.T) {
+ input := []byte(`
+# a "\n"
+
+# b {
+ c
+}
+
+d {
+e # f
+# g
+}
+
+h { # i
+}
+`)
+ expected := []byte(`
+# a "\n"
+
+# b {
+c
+}
+
+d {
+ e # f
+ # g
+}
+
+h {
+ # i
+}
+`)
+ testFormat(t, input, expected)
+}
+
+func TestFormatQuotesAndEscapes(t *testing.T) {
+ input := []byte(`
+"a \"b\" #c
+ d
+
+e {
+"f"
+}
+
+g { "h"
+}
+`)
+ expected := []byte(`
+"a \"b\" #c
+d
+
+e {
+ "f"
+}
+
+g {
+ "h"
+}
+`)
+ testFormat(t, input, expected)
+}
+
+func testFormat(t *testing.T, input, expected []byte) {
+ output := Format(input)
+ if string(output) != string(expected) {
+ t.Errorf("Expected:\n%s\ngot:\n%s", string(output), string(expected))
+ }
+}
diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go
index f376033..cdcac26 100755
--- a/caddyconfig/caddyfile/parse.go
+++ b/caddyconfig/caddyfile/parse.go
@@ -64,7 +64,7 @@ func replaceEnvVars(input []byte) ([]byte, error) {
}
// get the value of the environment variable
- envVarValue := []byte(os.Getenv(string(envVarName)))
+ envVarValue := []byte(os.ExpandEnv(os.Getenv(string(envVarName))))
// splice in the value
input = append(input[:begin],
diff --git a/caddyconfig/caddyfile/parse_test.go b/caddyconfig/caddyfile/parse_test.go
index 62a3998..e6d0501 100755
--- a/caddyconfig/caddyfile/parse_test.go
+++ b/caddyconfig/caddyfile/parse_test.go
@@ -256,7 +256,7 @@ func TestRecursiveImport(t *testing.T) {
return false
}
if len(got.Segments[0]) != 1 || len(got.Segments[1]) != 2 {
- t.Errorf("got unexpect tokens: %v", got.Segments)
+ t.Errorf("got unexpected tokens: %v", got.Segments)
return false
}
return true
@@ -351,7 +351,7 @@ func TestDirectiveImport(t *testing.T) {
return false
}
if len(got.Segments[0]) != 1 || len(got.Segments[1]) != 8 {
- t.Errorf("got unexpect tokens: %v", got.Segments)
+ t.Errorf("got unexpected tokens: %v", got.Segments)
return false
}
return true
diff --git a/caddyconfig/httpcaddyfile/addresses.go b/caddyconfig/httpcaddyfile/addresses.go
index 3aedb60..64c5d4f 100644
--- a/caddyconfig/httpcaddyfile/addresses.go
+++ b/caddyconfig/httpcaddyfile/addresses.go
@@ -45,7 +45,7 @@ import (
// key of its server block (specifying the host part), and each key may have
// a different port. And we definitely need to be sure that a site which is
// bound to be served on a specific interface is not served on others just
-// beceause that is more convenient: it would be a potential security risk
+// because that is more convenient: it would be a potential security risk
// if the difference between interfaces means private vs. public.
//
// So what this function does for the example above is iterate each server
diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go
index a085fcb..3b5a4f5 100644
--- a/caddyconfig/httpcaddyfile/builtins.go
+++ b/caddyconfig/httpcaddyfile/builtins.go
@@ -37,7 +37,7 @@ func init() {
RegisterHandlerDirective("redir", parseRedir)
RegisterHandlerDirective("respond", parseRespond)
RegisterHandlerDirective("route", parseRoute)
- RegisterHandlerDirective("handle", parseSegmentAsSubroute)
+ RegisterHandlerDirective("handle", parseHandle)
RegisterDirective("handle_errors", parseHandleErrors)
RegisterDirective("log", parseLog)
}
@@ -152,6 +152,18 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
// policy that is looking for any tag but the last one to be
// loaded won't find it, and TLS handshakes will fail (see end)
// of issue #3004)
+
+ // tlsCertTags maps certificate filenames to their tag.
+ // This is used to remember which tag is used for each
+ // certificate files, since we need to avoid loading
+ // the same certificate files more than once, overwriting
+ // previous tags
+ tlsCertTags, ok := h.State["tlsCertTags"].(map[string]string)
+ if !ok {
+ tlsCertTags = make(map[string]string)
+ h.State["tlsCertTags"] = tlsCertTags
+ }
+
tag, ok := tlsCertTags[certFilename]
if !ok {
// haven't seen this cert file yet, let's give it a tag
@@ -521,10 +533,15 @@ func parseLog(h Helper) ([]ConfigValue, error) {
var val namedCustomLog
if !reflect.DeepEqual(cl, new(caddy.CustomLog)) {
+ logCounter, ok := h.State["logCounter"].(int)
+ if !ok {
+ logCounter = 0
+ }
cl.Include = []string{"http.log.access"}
val.name = fmt.Sprintf("log%d", logCounter)
val.log = cl
logCounter++
+ h.State["logCounter"] = logCounter
}
configValues = append(configValues, ConfigValue{
Class: "custom_log",
@@ -533,12 +550,3 @@ func parseLog(h Helper) ([]ConfigValue, error) {
}
return configValues, nil
}
-
-// tlsCertTags maps certificate filenames to their tag.
-// This is used to remember which tag is used for each
-// certificate files, since we need to avoid loading
-// the same certificate files more than once, overwriting
-// previous tags
-var tlsCertTags = make(map[string]string)
-
-var logCounter int
diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go
index f82e2a8..e7a9686 100644
--- a/caddyconfig/httpcaddyfile/directives.go
+++ b/caddyconfig/httpcaddyfile/directives.go
@@ -114,6 +114,8 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
// Caddyfile tokens.
type Helper struct {
*caddyfile.Dispenser
+ // State stores intermediate variables during caddyfile adaptation.
+ State map[string]interface{}
options map[string]interface{}
warnings *[]caddyconfig.Warning
matcherDefs map[string]caddy.ModuleMap
@@ -161,6 +163,23 @@ func (h Helper) MatcherToken() (caddy.ModuleMap, bool, error) {
return matcherSetFromMatcherToken(h.Dispenser.Token(), h.matcherDefs, h.warnings)
}
+// ExtractMatcherSet is like MatcherToken, except this is a higher-level
+// method that returns the matcher set described by the matcher token,
+// or nil if there is none, and deletes the matcher token from the
+// dispenser and resets it as if this look-ahead never happened. Useful
+// when wrapping a route (one or more handlers) in a user-defined matcher.
+func (h Helper) ExtractMatcherSet() (caddy.ModuleMap, error) {
+ matcherSet, hasMatcher, err := h.MatcherToken()
+ if err != nil {
+ return nil, err
+ }
+ if hasMatcher {
+ h.Dispenser.Delete() // strip matcher token
+ }
+ h.Dispenser.Reset() // pretend this lookahead never happened
+ return matcherSet, nil
+}
+
// NewRoute returns config values relevant to creating a new HTTP route.
func (h Helper) NewRoute(matcherSet caddy.ModuleMap,
handler caddyhttp.MiddlewareHandler) []ConfigValue {
@@ -266,28 +285,31 @@ func sortRoutes(routes []ConfigValue) {
return false
}
- if len(iRoute.MatcherSetsRaw) == 1 && len(jRoute.MatcherSetsRaw) == 1 {
- // use already-decoded matcher, or decode if it's the first time seeing it
- iPM, jPM := decodedMatchers[i], decodedMatchers[j]
- if iPM == nil {
- var pathMatcher caddyhttp.MatchPath
- _ = json.Unmarshal(iRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
- decodedMatchers[i] = pathMatcher
- iPM = pathMatcher
- }
- if jPM == nil {
- var pathMatcher caddyhttp.MatchPath
- _ = json.Unmarshal(jRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
- decodedMatchers[j] = pathMatcher
- jPM = pathMatcher
- }
+ // use already-decoded matcher, or decode if it's the first time seeing it
+ iPM, jPM := decodedMatchers[i], decodedMatchers[j]
+ if iPM == nil && len(iRoute.MatcherSetsRaw) == 1 {
+ var pathMatcher caddyhttp.MatchPath
+ _ = json.Unmarshal(iRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
+ decodedMatchers[i] = pathMatcher
+ iPM = pathMatcher
+ }
+ if jPM == nil && len(jRoute.MatcherSetsRaw) == 1 {
+ var pathMatcher caddyhttp.MatchPath
+ _ = json.Unmarshal(jRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
+ decodedMatchers[j] = pathMatcher
+ jPM = pathMatcher
+ }
- // if there is only one path in the matcher, sort by
- // longer path (more specific) first
- if len(iPM) == 1 && len(jPM) == 1 {
- return len(iPM[0]) > len(jPM[0])
- }
+ // sort by longer path (more specific) first; missing
+ // path matchers are treated as zero-length paths
+ var iPathLen, jPathLen int
+ if iPM != nil {
+ iPathLen = len(iPM[0])
+ }
+ if jPM != nil {
+ jPathLen = len(jPM[0])
}
+ return iPathLen > jPathLen
}
return dirPositions[iDir] < dirPositions[jDir]
diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go
index aaec2e9..d880d97 100644
--- a/caddyconfig/httpcaddyfile/httptype.go
+++ b/caddyconfig/httpcaddyfile/httptype.go
@@ -42,6 +42,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
options map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) {
var warnings []caddyconfig.Warning
gc := counter{new(int)}
+ state := make(map[string]interface{})
// load all the server blocks and associate them with a "pile"
// of config values; also prohibit duplicate keys because they
@@ -133,14 +134,17 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
return nil, warnings, fmt.Errorf("%s:%d: unrecognized directive: %s", tkn.File, tkn.Line, dir)
}
- results, err := dirFunc(Helper{
+ h := Helper{
Dispenser: caddyfile.NewDispenser(segment),
options: options,
warnings: &warnings,
matcherDefs: matcherDefs,
parentBlock: sb.block,
groupCounter: gc,
- })
+ State: state,
+ }
+
+ results, err := dirFunc(h)
if err != nil {
return nil, warnings, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
}
@@ -169,9 +173,10 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
// now that each server is configured, make the HTTP app
httpApp := caddyhttp.App{
- HTTPPort: tryInt(options["http_port"], &warnings),
- HTTPSPort: tryInt(options["https_port"], &warnings),
- Servers: servers,
+ HTTPPort: tryInt(options["http_port"], &warnings),
+ HTTPSPort: tryInt(options["https_port"], &warnings),
+ DefaultSNI: tryString(options["default_sni"], &warnings),
+ Servers: servers,
}
// now for the TLS app! (TODO: refactor into own func)
@@ -326,7 +331,23 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
&warnings)
}
if adminConfig, ok := options["admin"].(string); ok && adminConfig != "" {
- cfg.Admin = &caddy.AdminConfig{Listen: adminConfig}
+ if adminConfig == "off" {
+ cfg.Admin = &caddy.AdminConfig{Disabled: true}
+ } else {
+ cfg.Admin = &caddy.AdminConfig{Listen: adminConfig}
+ }
+ }
+ if len(customLogs) > 0 {
+ if cfg.Logging == nil {
+ cfg.Logging = &caddy.Logging{
+ Logs: make(map[string]*caddy.CustomLog),
+ }
+ }
+ for _, ncl := range customLogs {
+ if ncl.name != "" {
+ cfg.Logging.Logs[ncl.name] = ncl.log
+ }
+ }
}
if len(customLogs) > 0 {
if cfg.Logging == nil {
@@ -985,12 +1006,12 @@ func sliceContains(haystack []string, needle string) bool {
return false
}
-// specifity returns len(s) minus any wildcards (*) and
+// specificity returns len(s) minus any wildcards (*) and
// placeholders ({...}). Basically, it's a length count
// that penalizes the use of wildcards and placeholders.
// This is useful for comparing hostnames and paths.
// However, wildcards in paths are not a sure answer to
-// the question of specificity. For exmaple,
+// the question of specificity. For example,
// '*.example.com' is clearly less specific than
// 'a.example.com', but is '/a' more or less specific
// than '/a*'?
@@ -1021,17 +1042,12 @@ func (c counter) nextGroup() string {
return name
}
-type matcherSetAndTokens struct {
- matcherSet caddy.ModuleMap
- tokens []caddyfile.Token
-}
-
type namedCustomLog struct {
name string
log *caddy.CustomLog
}
-// sbAddrAssocation is a mapping from a list of
+// sbAddrAssociation is a mapping from a list of
// addresses to a list of server blocks that are
// served on those addresses.
type sbAddrAssociation struct {