From af25f0254e94eb2e2898ba495bfc3de209a49e79 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 22 Aug 2019 13:38:37 -0600 Subject: caddyfile: Support global config block; allow non-empty blocks w/ 0 keys --- caddyconfig/caddyfile/adapter.go | 8 +++--- caddyconfig/caddyfile/dispenser.go | 23 ++++++++++++--- caddyconfig/caddyfile/parse.go | 2 +- caddyconfig/configadapters.go | 2 +- caddyconfig/httpcaddyfile/global.go | 53 +++++++++++++++++++++++++++++++++++ caddyconfig/httpcaddyfile/httptype.go | 53 +++++++++++++++++++++++++---------- 6 files changed, 116 insertions(+), 25 deletions(-) create mode 100644 caddyconfig/httpcaddyfile/global.go (limited to 'caddyconfig') diff --git a/caddyconfig/caddyfile/adapter.go b/caddyconfig/caddyfile/adapter.go index 377f77b..7a96e88 100644 --- a/caddyconfig/caddyfile/adapter.go +++ b/caddyconfig/caddyfile/adapter.go @@ -29,15 +29,15 @@ type Adapter struct { } // Adapt converts the Caddyfile config in body to Caddy JSON. -func (a Adapter) Adapt(body []byte, options map[string]string) ([]byte, []caddyconfig.Warning, error) { +func (a Adapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []caddyconfig.Warning, error) { if a.ServerType == nil { return nil, nil, fmt.Errorf("no server type") } if options == nil { - options = make(map[string]string) + options = make(map[string]interface{}) } - filename := options["filename"] + filename, _ := options["filename"].(string) if filename == "" { filename = "Caddyfile" } @@ -80,7 +80,7 @@ type ServerType interface { // (e.g. CLI flags) and creates a Caddy // config, along with any warnings or // an error. - Setup([]ServerBlock, map[string]string) (*caddy.Config, []caddyconfig.Warning, error) + Setup([]ServerBlock, map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) } // Interface guard diff --git a/caddyconfig/caddyfile/dispenser.go b/caddyconfig/caddyfile/dispenser.go index 66a3541..93f451e 100755 --- a/caddyconfig/caddyfile/dispenser.go +++ b/caddyconfig/caddyfile/dispenser.go @@ -186,10 +186,10 @@ func (d *Dispenser) File() string { // Args is a convenience function that loads the next arguments // (tokens on the same line) into an arbitrary number of strings -// pointed to in targets. If there are fewer tokens available -// than string pointers, the remaining strings will not be changed -// and false will be returned. If there were enough tokens available -// to fill the arguments, then true will be returned. +// pointed to in targets. If there are not enough argument tokens +// available to fill targets, false is returned and the remaining +// targets are left unchanged. If all the targets are filled, +// then true is returned. func (d *Dispenser) Args(targets ...*string) bool { for i := 0; i < len(targets); i++ { if !d.NextArg() { @@ -200,6 +200,21 @@ func (d *Dispenser) Args(targets ...*string) bool { return true } +// AllArgs is like Args, but if there are more argument tokens +// available than there are targets, false is returned. The +// number of available argument tokens must match the number of +// targets exactly to return true. +func (d *Dispenser) AllArgs(targets ...*string) bool { + if !d.Args(targets...) { + return false + } + if d.NextArg() { + d.Prev() + return false + } + return true +} + // RemainingArgs loads any more arguments (tokens on the same line) // into a slice and returns them. Open curly brace tokens also indicate // the end of arguments, and the curly brace is not included in diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go index 3356f25..1862ad1 100755 --- a/caddyconfig/caddyfile/parse.go +++ b/caddyconfig/caddyfile/parse.go @@ -70,7 +70,7 @@ func (p *parser) parseAll() ([]ServerBlock, error) { if err != nil { return blocks, err } - if len(p.block.Keys) > 0 { + if len(p.block.Keys) > 0 || len(p.block.Segments) > 0 { blocks = append(blocks, p.block) } if p.nesting > 0 { diff --git a/caddyconfig/configadapters.go b/caddyconfig/configadapters.go index 6e5d530..c539176 100644 --- a/caddyconfig/configadapters.go +++ b/caddyconfig/configadapters.go @@ -22,7 +22,7 @@ import ( // Adapter is a type which can adapt a configuration to Caddy JSON. // It returns the results and any warnings, or an error. type Adapter interface { - Adapt(body []byte, options map[string]string) ([]byte, []Warning, error) + Adapt(body []byte, options map[string]interface{}) ([]byte, []Warning, error) } // Warning represents a warning or notice related to conversion. diff --git a/caddyconfig/httpcaddyfile/global.go b/caddyconfig/httpcaddyfile/global.go new file mode 100644 index 0000000..067d06e --- /dev/null +++ b/caddyconfig/httpcaddyfile/global.go @@ -0,0 +1,53 @@ +// 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 ( + "strconv" + + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" +) + +func parseHTTPPort(d *caddyfile.Dispenser) (int, error) { + var httpPort int + for d.Next() { + var httpPortStr string + if !d.AllArgs(&httpPortStr) { + return 0, d.ArgErr() + } + var err error + httpPort, err = strconv.Atoi(httpPortStr) + if err != nil { + return 0, d.Errf("converting port '%s' to integer value: %v", httpPortStr, err) + } + } + return httpPort, nil +} + +func parseHTTPSPort(d *caddyfile.Dispenser) (int, error) { + var httpsPort int + for d.Next() { + var httpsPortStr string + if !d.AllArgs(&httpsPortStr) { + return 0, d.ArgErr() + } + var err error + httpsPort, err = strconv.Atoi(httpsPortStr) + if err != nil { + return 0, d.Errf("converting port '%s' to integer value: %v", httpsPortStr, err) + } + } + return httpsPort, nil +} diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go index 00ce2cf..047d559 100644 --- a/caddyconfig/httpcaddyfile/httptype.go +++ b/caddyconfig/httpcaddyfile/httptype.go @@ -19,7 +19,6 @@ import ( "fmt" "reflect" "sort" - "strconv" "strings" "github.com/caddyserver/caddy/v2" @@ -44,7 +43,7 @@ type ServerType struct { // Setup makes a config from the tokens. func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, - options map[string]string) (*caddy.Config, []caddyconfig.Warning, error) { + options map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) { var warnings []caddyconfig.Warning var serverBlocks []serverBlock @@ -55,6 +54,29 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, }) } + // global configuration + if len(serverBlocks) > 0 && len(serverBlocks[0].block.Keys) == 0 { + sb := serverBlocks[0] + for _, segment := range sb.block.Segments { + dir := segment.Directive() + var val interface{} + var err error + switch dir { + case "http_port": + val, err = parseHTTPPort(caddyfile.NewDispenser(segment)) + case "https_port": + val, err = parseHTTPSPort(caddyfile.NewDispenser(segment)) + default: + return nil, warnings, fmt.Errorf("unrecognized parameter name: %s", dir) + } + if err != nil { + return nil, warnings, fmt.Errorf("%s: %v", dir, err) + } + options[dir] = val + } + serverBlocks = serverBlocks[1:] + } + for _, sb := range serverBlocks { // replace shorthand placeholders (which are // convenient when writing a Caddyfile) with @@ -77,11 +99,15 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, } } + if len(sb.block.Keys) == 0 { + return nil, warnings, fmt.Errorf("server block without any key is global configuration, and if used, it must be first") + } + // extract matcher definitions d := sb.block.DispenseDirective("matcher") matcherDefs, err := st.parseMatcherDefinitions(d) if err != nil { - return nil, nil, err + return nil, warnings, err } for _, segment := range sb.block.Segments { @@ -129,8 +155,8 @@ 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), + HTTPPort: tryInt(options["http_port"], &warnings), + HTTPSPort: tryInt(options["https_port"], &warnings), Servers: servers, } @@ -494,17 +520,14 @@ func encodeMatcherSet(matchers map[string]caddyhttp.RequestMatcher) (map[string] return msEncoded, nil } -// tryInt tries to convert str to an integer. If it fails, it downgrades -// the error to a warning and returns 0. -func tryInt(str string, warnings *[]caddyconfig.Warning) int { - if str == "" { - return 0 - } - val, err := strconv.Atoi(str) - if err != nil && warnings != nil { - *warnings = append(*warnings, caddyconfig.Warning{Message: err.Error()}) +// tryInt tries to convert val to an integer. If it fails, +// it downgrades the error to a warning and returns 0. +func tryInt(val interface{}, warnings *[]caddyconfig.Warning) int { + intVal, ok := val.(int) + if val != nil && !ok && warnings != nil { + *warnings = append(*warnings, caddyconfig.Warning{Message: "not an integer type"}) } - return val + return intVal } type matcherSetAndTokens struct { -- cgit v1.2.3