diff options
author | Steffen Brüheim <ueffel@gmail.com> | 2021-03-30 02:47:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-29 18:47:19 -0600 |
commit | f35a7fa466ffb06c38dcb3216e30c13aa8e14ce5 (patch) | |
tree | 5fb5b40ad787a6db69cc1bec543fe6eed234748f /modules/caddyhttp/encode/caddyfile.go | |
parent | 75f797debdd6c4294497edba9889c6251a8542e7 (diff) |
encode,staticfiles: Content negotiation, precompressed files (#4045)
* encode: implement prefer setting
* encode: minimum_length configurable via caddyfile
* encode: configurable content-types which to encode
* file_server: support precompressed files
* encode: use ReponseMatcher for conditional encoding of content
* linting error & documentation of encode.PrecompressedOrder
* encode: allow just one response matcher
also change the namespace of the encoders back, I accidently changed to precompressed >.>
default matchers include a * to match to any charset, that may be appended
* rounding of the PR
* added integration tests for new caddyfile directives
* improved various doc strings (punctuation and typos)
* added json tag for file_server precompress order and encode matcher
* file_server: add vary header, remove accept-ranges when serving precompressed files
* encode: move Suffix implementation to precompressed modules
Diffstat (limited to 'modules/caddyhttp/encode/caddyfile.go')
-rw-r--r-- | modules/caddyhttp/encode/caddyfile.go | 139 |
1 files changed, 122 insertions, 17 deletions
diff --git a/modules/caddyhttp/encode/caddyfile.go b/modules/caddyhttp/encode/caddyfile.go index 2f11ca0..c45f159 100644 --- a/modules/caddyhttp/encode/caddyfile.go +++ b/modules/caddyhttp/encode/caddyfile.go @@ -15,7 +15,9 @@ package encode import ( - "fmt" + "net/http" + "strconv" + "strings" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig" @@ -40,21 +42,31 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) // UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax: // // encode [<matcher>] <formats...> { -// gzip [<level>] +// gzip [<level>] // zstd +// minimum_length <length> +// prefer <formats...> +// # response matcher block +// match { +// status <code...> +// header <field> [<value>] +// } +// # or response matcher single line syntax +// match [header <field> [<value>]] | [status <code...>] // } // // Specifying the formats on the first line will use those formats' defaults. func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + responseMatchers := make(map[string]caddyhttp.ResponseMatcher) for d.Next() { for _, arg := range d.RemainingArgs() { mod, err := caddy.GetModule("http.encoders." + arg) if err != nil { - return fmt.Errorf("finding encoder module '%s': %v", mod, err) + return d.Errf("finding encoder module '%s': %v", mod, err) } encoding, ok := mod.New().(Encoding) if !ok { - return fmt.Errorf("module %s is not an HTTP encoding", mod) + return d.Errf("module %s is not an HTTP encoding", mod) } if enc.EncodingsRaw == nil { enc.EncodingsRaw = make(caddy.ModuleMap) @@ -63,25 +75,118 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } for d.NextBlock(0) { - name := d.Val() - modID := "http.encoders." + name - unm, err := caddyfile.UnmarshalModule(d, modID) - if err != nil { - return err - } - encoding, ok := unm.(Encoding) - if !ok { - return fmt.Errorf("module %s is not an HTTP encoding; is %T", modID, unm) + switch d.Val() { + case "minimum_length": + if !d.NextArg() { + return d.ArgErr() + } + minLength, err := strconv.Atoi(d.Val()) + if err != nil { + return err + } + enc.MinLength = minLength + case "prefer": + var encs []string + for d.NextArg() { + encs = append(encs, d.Val()) + } + if len(encs) == 0 { + return d.ArgErr() + } + enc.Prefer = encs + case "match": + err := enc.parseNamedResponseMatcher(d.NewFromNextSegment(), responseMatchers) + if err != nil { + return err + } + matcher := responseMatchers["match"] + enc.Matcher = &matcher + default: + name := d.Val() + modID := "http.encoders." + name + unm, err := caddyfile.UnmarshalModule(d, modID) + if err != nil { + return err + } + encoding, ok := unm.(Encoding) + if !ok { + return d.Errf("module %s is not an HTTP encoding; is %T", modID, unm) + } + if enc.EncodingsRaw == nil { + enc.EncodingsRaw = make(caddy.ModuleMap) + } + enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil) } - if enc.EncodingsRaw == nil { - enc.EncodingsRaw = make(caddy.ModuleMap) - } - enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil) } } return nil } +// Parse the tokens of a named response matcher. +// +// match { +// header <field> [<value>] +// status <code...> +// } +// +// Or, single line syntax: +// +// match [header <field> [<value>]] | [status <code...>] +// +func (enc *Encode) parseNamedResponseMatcher(d *caddyfile.Dispenser, matchers map[string]caddyhttp.ResponseMatcher) error { + for d.Next() { + definitionName := d.Val() + + if _, ok := matchers[definitionName]; ok { + return d.Errf("matcher is defined more than once: %s", definitionName) + } + + matcher := caddyhttp.ResponseMatcher{} + for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); { + switch d.Val() { + case "header": + if matcher.Headers == nil { + matcher.Headers = http.Header{} + } + + // reuse the header request matcher's unmarshaler + headerMatcher := caddyhttp.MatchHeader(matcher.Headers) + err := headerMatcher.UnmarshalCaddyfile(d.NewFromNextSegment()) + if err != nil { + return err + } + + matcher.Headers = http.Header(headerMatcher) + case "status": + if matcher.StatusCode == nil { + matcher.StatusCode = []int{} + } + + args := d.RemainingArgs() + if len(args) == 0 { + return d.ArgErr() + } + + for _, arg := range args { + if len(arg) == 3 && strings.HasSuffix(arg, "xx") { + arg = arg[:1] + } + statusNum, err := strconv.Atoi(arg) + if err != nil { + return d.Errf("bad status value '%s': %v", arg, err) + } + matcher.StatusCode = append(matcher.StatusCode, statusNum) + } + default: + return d.Errf("unrecognized response matcher %s", d.Val()) + } + } + + matchers[definitionName] = matcher + } + return nil +} + // Interface guard var _ caddyfile.Unmarshaler = (*Encode)(nil) |