Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
caddyfile: Support global config block; allow non-empty blocks w/ 0 keys
Matthew Holt committed
Aug 22, 2019 at 13:38 UTC
af25f0254e94eb2e2898ba495bfc3de209a49e79
8 files changed
+118
-28
caddyconfig/caddyfile/adapter.go
+4
-4
@@ -29,15 +29,15 @@ type Adapter struct {
29
}
30
31
// Adapt converts the Caddyfile config in body to Caddy JSON.
32
-func (a Adapter) Adapt(body []byte, options map[string]string) ([]byte, []caddyconfig.Warning, error) {
32
+func (a Adapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []caddyconfig.Warning, error) {
33
if a.ServerType == nil {
34
return nil, nil, fmt.Errorf("no server type")
35
}
36
if options == nil {
37
- options = make(map[string]string)
37
+ options = make(map[string]interface{})
38
}
39
40
- filename := options["filename"]
40
+ filename, _ := options["filename"].(string)
41
if filename == "" {
42
filename = "Caddyfile"
43
}
@@ -80,7 +80,7 @@ type ServerType interface {
80
// (e.g. CLI flags) and creates a Caddy
81
// config, along with any warnings or
82
// an error.
83
- Setup([]ServerBlock, map[string]string) (*caddy.Config, []caddyconfig.Warning, error)
83
+ Setup([]ServerBlock, map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error)
84
}
85
86
// Interface guard
caddyconfig/caddyfile/dispenser.go
+19
-4
@@ -186,10 +186,10 @@ func (d *Dispenser) File() string {
186
187
// Args is a convenience function that loads the next arguments
188
// (tokens on the same line) into an arbitrary number of strings
189
-// pointed to in targets. If there are fewer tokens available
190
-// than string pointers, the remaining strings will not be changed
191
-// and false will be returned. If there were enough tokens available
192
-// to fill the arguments, then true will be returned.
189
+// pointed to in targets. If there are not enough argument tokens
190
+// available to fill targets, false is returned and the remaining
191
+// targets are left unchanged. If all the targets are filled,
192
+// then true is returned.
193
func (d *Dispenser) Args(targets ...*string) bool {
194
for i := 0; i < len(targets); i++ {
195
if !d.NextArg() {
@@ -200,6 +200,21 @@ func (d *Dispenser) Args(targets ...*string) bool {
200
return true
201
}
202
203
+// AllArgs is like Args, but if there are more argument tokens
204
+// available than there are targets, false is returned. The
205
+// number of available argument tokens must match the number of
206
+// targets exactly to return true.
207
+func (d *Dispenser) AllArgs(targets ...*string) bool {
208
+ if !d.Args(targets...) {
209
+ return false
210
+ }
211
+ if d.NextArg() {
212
+ d.Prev()
213
+ return false
214
+ }
215
+ return true
216
+}
217
+
218
// RemainingArgs loads any more arguments (tokens on the same line)
219
// into a slice and returns them. Open curly brace tokens also indicate
220
// the end of arguments, and the curly brace is not included in
caddyconfig/caddyfile/parse.go
+1
-1
@@ -70,7 +70,7 @@ func (p *parser) parseAll() ([]ServerBlock, error) {
70
if err != nil {
71
return blocks, err
72
}
73
- if len(p.block.Keys) > 0 {
73
+ if len(p.block.Keys) > 0 || len(p.block.Segments) > 0 {
74
blocks = append(blocks, p.block)
75
}
76
if p.nesting > 0 {
caddyconfig/configadapters.go
+1
-1
@@ -22,7 +22,7 @@ import (
22
// Adapter is a type which can adapt a configuration to Caddy JSON.
23
// It returns the results and any warnings, or an error.
24
type Adapter interface {
25
- Adapt(body []byte, options map[string]string) ([]byte, []Warning, error)
25
+ Adapt(body []byte, options map[string]interface{}) ([]byte, []Warning, error)
26
}
27
28
// Warning represents a warning or notice related to conversion.
caddyconfig/httpcaddyfile/global.go
new
+53
@@ -0,0 +1,53 @@
1
+// Copyright 2015 Matthew Holt and The Caddy Authors
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+// http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package httpcaddyfile
16
+
17
+import (
18
+ "strconv"
19
+
20
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
21
+)
22
+
23
+func parseHTTPPort(d *caddyfile.Dispenser) (int, error) {
24
+ var httpPort int
25
+ for d.Next() {
26
+ var httpPortStr string
27
+ if !d.AllArgs(&httpPortStr) {
28
+ return 0, d.ArgErr()
29
+ }
30
+ var err error
31
+ httpPort, err = strconv.Atoi(httpPortStr)
32
+ if err != nil {
33
+ return 0, d.Errf("converting port '%s' to integer value: %v", httpPortStr, err)
34
+ }
35
+ }
36
+ return httpPort, nil
37
+}
38
+
39
+func parseHTTPSPort(d *caddyfile.Dispenser) (int, error) {
40
+ var httpsPort int
41
+ for d.Next() {
42
+ var httpsPortStr string
43
+ if !d.AllArgs(&httpsPortStr) {
44
+ return 0, d.ArgErr()
45
+ }
46
+ var err error
47
+ httpsPort, err = strconv.Atoi(httpsPortStr)
48
+ if err != nil {
49
+ return 0, d.Errf("converting port '%s' to integer value: %v", httpsPortStr, err)
50
+ }
51
+ }
52
+ return httpsPort, nil
53
+}
caddyconfig/httpcaddyfile/httptype.go
+38
-15
@@ -19,7 +19,6 @@ import (
19
"fmt"
20
"reflect"
21
"sort"
22
- "strconv"
22
"strings"
23
24
"github.com/caddyserver/caddy/v2"
@@ -44,7 +43,7 @@ type ServerType struct {
43
44
// Setup makes a config from the tokens.
45
func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
47
- options map[string]string) (*caddy.Config, []caddyconfig.Warning, error) {
46
+ options map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) {
47
var warnings []caddyconfig.Warning
48
49
var serverBlocks []serverBlock
@@ -55,6 +54,29 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
54
})
55
}
56
57
+ // global configuration
58
+ if len(serverBlocks) > 0 && len(serverBlocks[0].block.Keys) == 0 {
59
+ sb := serverBlocks[0]
60
+ for _, segment := range sb.block.Segments {
61
+ dir := segment.Directive()
62
+ var val interface{}
63
+ var err error
64
+ switch dir {
65
+ case "http_port":
66
+ val, err = parseHTTPPort(caddyfile.NewDispenser(segment))
67
+ case "https_port":
68
+ val, err = parseHTTPSPort(caddyfile.NewDispenser(segment))
69
+ default:
70
+ return nil, warnings, fmt.Errorf("unrecognized parameter name: %s", dir)
71
+ }
72
+ if err != nil {
73
+ return nil, warnings, fmt.Errorf("%s: %v", dir, err)
74
+ }
75
+ options[dir] = val
76
+ }
77
+ serverBlocks = serverBlocks[1:]
78
+ }
79
+
80
for _, sb := range serverBlocks {
81
// replace shorthand placeholders (which are
82
// convenient when writing a Caddyfile) with
@@ -77,11 +99,15 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
99
}
100
}
101
102
+ if len(sb.block.Keys) == 0 {
103
+ return nil, warnings, fmt.Errorf("server block without any key is global configuration, and if used, it must be first")
104
+ }
105
+
106
// extract matcher definitions
107
d := sb.block.DispenseDirective("matcher")
108
matcherDefs, err := st.parseMatcherDefinitions(d)
109
if err != nil {
84
- return nil, nil, err
110
+ return nil, warnings, err
111
}
112
113
for _, segment := range sb.block.Segments {
@@ -129,8 +155,8 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
155
156
// now that each server is configured, make the HTTP app
157
httpApp := caddyhttp.App{
132
- HTTPPort: tryInt(options["http-port"], &warnings),
133
- HTTPSPort: tryInt(options["https-port"], &warnings),
158
+ HTTPPort: tryInt(options["http_port"], &warnings),
159
+ HTTPSPort: tryInt(options["https_port"], &warnings),
160
Servers: servers,
161
}
162
@@ -494,17 +520,14 @@ func encodeMatcherSet(matchers map[string]caddyhttp.RequestMatcher) (map[string]
520
return msEncoded, nil
521
}
522
497
-// tryInt tries to convert str to an integer. If it fails, it downgrades
498
-// the error to a warning and returns 0.
499
-func tryInt(str string, warnings *[]caddyconfig.Warning) int {
500
- if str == "" {
501
- return 0
502
- }
503
- val, err := strconv.Atoi(str)
504
- if err != nil && warnings != nil {
505
- *warnings = append(*warnings, caddyconfig.Warning{Message: err.Error()})
523
+// tryInt tries to convert val to an integer. If it fails,
524
+// it downgrades the error to a warning and returns 0.
525
+func tryInt(val interface{}, warnings *[]caddyconfig.Warning) int {
526
+ intVal, ok := val.(int)
527
+ if val != nil && !ok && warnings != nil {
528
+ *warnings = append(*warnings, caddyconfig.Warning{Message: "not an integer type"})
529
}
507
- return val
530
+ return intVal
531
}
532
533
type matcherSetAndTokens struct {
cmd/commands.go
+1
-1
@@ -331,7 +331,7 @@ func cmdAdaptConfig() (int, error) {
331
fmt.Errorf("reading input file: %v", err)
332
}
333
334
- opts := make(map[string]string)
334
+ opts := make(map[string]interface{})
335
if *adaptCmdPrettyFlag {
336
opts["pretty"] = "true"
337
}
cmd/main.go
+1
-2
@@ -131,9 +131,8 @@ func loadConfig(configFile, adapterName string) ([]byte, error) {
131
132
// adapt config
133
if cfgAdapter != nil {
134
- adaptedConfig, warnings, err := cfgAdapter.Adapt(config, map[string]string{
134
+ adaptedConfig, warnings, err := cfgAdapter.Adapt(config, map[string]interface{}{
135
"filename": configFile,
136
- // TODO: all other options... (http-port, etc...)
136
})
137
if err != nil {
138
return nil, fmt.Errorf("adapting config using %s: %v", adapterName, err)